+# Repository management
.svn
-*~
+
+# Editors
*.kate-swp
+*~
.*.swp
-.classpath
-.idea
-.metadata*
.project
-.settings
-AdminSettings.php
-LocalSettings.php
-StartProfiler.php
cscope.files
cscope.out
-favicon.ico
+## NetBeans
nbproject*
project.index
-static*
-tags
+
+# MediaWiki install & usage
cache/*.cdb
images/[0-9a-f]
images/archive
images/deleted
images/temp
images/thumb
+## Extension:EasyTimeline
images/timeline
images/tmp
-maintenance/dev/data
maintenance/.mweval_history
maintenance/.mwsql_history
+maintenance/dev/data
+AdminSettings.php
+LocalSettings.php
+StartProfiler.php
+
+# Operating systems
+## Mac OS X
+.DS_Store
+
+# Misc
+.classpath
+.idea
+.metadata*
+.settings
+favicon.ico
+static*
+tags
resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
tests/jasmine/lib/jasmine-1.0.1/jasmine-html.js
tests/jasmine/lib/jasmine-1.0.1/jasmine.js
-
-# legacy stuff
-skins/common
{
"predef": [
"mediaWiki",
- "jQuery",
"QUnit"
],
"immed": true,
"latedef": true,
"newcap": true,
+ "noarg": true,
"noempty": true,
+ "nonew": true,
+ "regexp": false,
"undef": true,
+ "strict": false,
"trailing": true,
"laxbreak": true,
"smarttabs": true,
+ "multistr": true,
- "browser": true
+ "browser": true,
+ "jquery": true,
+
+ "nomen": true,
+ "onevar": false
}
* Grunny
* Harry Burt
* Ireas
+* Jacob Block
* Jaska Zedlik
* Jeremy Baron
* Jidanni
* The user right 'upload_by_url' is no longer given to sysops by default.
This only affects installations which have $wgAllowCopyUploads set to true.
* Removed f-prot support from $wgAntivirusSetup.
-* $wgDBerrorLogInUTC to log error in $wgDBerrorLog using an UTC date instead
- of the wiki timezone set by $wgLocalTimezone.
+* New variable $wgDBerrorLogTZ to provide dates in the error log in a
+ different timezone than the wiki timezone set by $wgLocalTimezone.
=== New features in 1.20 ===
* Added TitleIsAlwaysKnown hook which gets called when determining if a page exists.
certain namespace can be moved.
* Added SpecialPageBeforeExecute hook which gets called before SpecialPage::execute.
* Added SpecialPageAfterExecute hook which gets called after SpecialPage::execute.
+* Added ORMTable, ORMRow and ORMResult classes for additional abstraction of
+ database interaction.
+* Added CacheHelper and associated SpecialCachedPage and CachedAction helper classes.
* (bug 32341) Add upload by URL domain limitation.
* &useskin=default will now always display the default skin. Useful for users with a
preference for the non-default skin to look at something using the default skin.
* (bug 35685) api.php URL and other entry point URLs are now listed on
Special:Version
* Edit notices can now be translated.
-* (bug 35680) jQuery upgraded to 1.7.2.
+* jQuery upgraded to 1.8.
* jQuery UI upgraded to 1.8.22.
* (bug 35705) QUnit upgraded from v1.2.0 to v1.8.0.
* (bug 37604) jquery.cookie upgraded to 2011 version.
the new deletelogentry permission is required for this.
* (bug 14237) Allow PAGESINCATEGORY to distinguish between 'all', 'pages', 'files'
and 'subcats'
+* (bug 38362) Make Special:Listuser includeable on wiki pages.
+* Added support in jquery.localize for placeholder attributes.
+* (bug 38151) Implemented mw.user.getRights for getting and caching the current
+ user's user rights.
+* Session storage can now configured independently of general object cache
+ storage, by using $wgSessionCacheType. $wgSessionsInMemcached has been
+ renamed to $wgSessionsInObjectCache, with the old name retained for backwards
+ compatibility. When this feature is enabled, the expiry time can now be
+ configured with $wgObjectCacheSessionExpiry.
+* Added a Redis client for object caching.
+* Implemented mw.user.getGroups for getting and caching user groups.
+* (bug 37830) Added $wgRequirePasswordforEmailChange to control whether password
+ confirmation is required for changing an email address or not.
+* HTMLForm mutators can now be chained (they return $this)
+* A new message, "api-error-filetype-banned-type", is available for formatting
+ API upload errors due to the file extension blacklist.
+* jsMessage: Redesigned in Vector/Monobook as floating bubble with auto-hide.
+* New hook 'ParserTestGlobals' allows to set globals before running parser tests.
=== Bug fixes in 1.20 ===
* (bug 30245) Use the correct way to construct a log page title.
was initialized.
* (bug 38093) Gender of changed user groups missing in Special:Log/rights
* (bug 35893) Special:Block needs to load mediawiki.special.block.js.
+* (bug 37331) ResourceLoader modules sometimes execute twice in Firefox
+* (bug 31644) GlobalUsage, CentralAuth and AbuseLog extensions should not use
+ insecure links to foreign wikis in the WikiMap.
+* (bug 36073) Avoid duplicate element IDs on File pages
+* (bug 25095) Special:Categories should also include the first relevant item
+ when "from" is filled.
+* (bug 35526) jquery.tablesorter now uses a stable sort.
+* (bug 38953) --memory-limit switch not working for runJobs.php.
+* (bug 33037) Make subpage of Special:newfiles control how many files
+ are returned, like in previous versions.
+* (bug 36524) "Show" options on Special:RecentChanges and Special:RecentChangesLinked
+ are now remembered between successive clicks.
=== API changes in 1.20 ===
* (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
* (bug 38190) Add "required" flag to some token params for hint in api docs.
* (bug 27567) Add file repo support to prop=duplicatefiles.
* (bug 27610) Add archivename for non-latest image version to list=filearchive
+* (bug 38231) Add xml parse tree to action=parse.
+* Watchlist notification timestamp may be queried by page and may be updated via the API.
+* (bug 38904) prop=revisions&rvstart=... no longer blows up when continuing.
+* (bug 39032) ApiQuery generates help in constructor.
+* (bug 11142) Improve file extension blacklist error reporting in API upload
=== Languages updated in 1.20 ===
and only applies to MyISAM or similar DBs. Those should only be used
for archived sites anyway. We can't get edit conflicts on such sites,
so the WikiPage code wasn't useful there either.
+* Deprecated mw.user.name in favour of mw.user.getName.
+* Deprecated mw.user.anonymous in favour of mw.user.isAnon.
+* Deprecated DatabaseBase functions newFromParams(), newFromType(), set(),
+ quote_ident(), and escapeLike() were removed.
== Compatibility ==
'AbortNewAccount': Return false to cancel explicit account creation.
$user: the User object about to be created (read-only, incomplete)
-&$msg: out parameter: name of error message to display on abort
+&$msg: out parameter: HTML to display on abort
'ActionBeforeFormDisplay': before executing the HTMLForm object
$name: name of the action
&$title: Title object of the image
&$file: File object, or false if it doesn't exist
&$frameParams: Various parameters with special meanings; see documentation in
- includes/Linker.php for Linker::makeImageLink2
+ includes/Linker.php for Linker::makeImageLink
&$handlerParams: Various parameters with special meanings; see documentation in
- includes/Linker.php for Linker::makeImageLink2
+ includes/Linker.php for Linker::makeImageLink
&$time: Timestamp of file in 'YYYYMMDDHHIISS' string form, or false for current
&$res: Final HTML output, used if you return false
maintenance/parserTests.inc
$parser: Parser object created
+'ParserTestGlobals': Allows to define globals for parser tests.
+&$globals: Array with all the globals which should be set for parser tests.
+ The arrays keys serve as the globals names, its values are the globals values.
+
'ParserTestTables': alter the list of tables to duplicate when parser tests
are run. Use when page save hooks require the presence of custom tables
to ensure that tests continue to run properly.
// Give hooks a chance to alter the form, adding extra fields or text etc
wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) );
- $form = new HTMLForm( $this->fields, $this->getContext() );
+ $form = new HTMLForm( $this->fields, $this->getContext(), $this->getName() );
$form->setSubmitCallback( array( $this, 'onSubmit' ) );
// Retain query parameters (uselang etc)
* request.
*/
function performAction() {
- global $wgAjaxExportList, $wgOut, $wgUser;
+ global $wgAjaxExportList, $wgUser;
if ( empty( $this->mode ) ) {
return;
}
}
- $wgOut = null;
wfProfileOut( __METHOD__ );
}
}
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
- $t = $this->getTitle()->getPrefixedText();
- $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
- $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
+ $this->mContent = wfMsgNoTrans( 'missing-revision', $oldid );
if ( $oldid ) {
# $this->mRevision might already be fetched by getOldIDFromRequest()
"<div class='patrollink'>" .
wfMsgHtml(
'markaspatrolledlink',
- Linker::link(
+ Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'markaspatrolledtext' ),
array(),
'action' => 'markpatrolled',
'rcid' => $rcid,
'token' => $token,
- ),
- array( 'known', 'noclasses' )
+ )
)
) .
'</div>'
LogEventsList::showLogExtract(
$outputPage,
'block',
- $user->getUserPage()->getPrefixedText(),
+ $user->getUserPage(),
'',
array(
'lim' => 1,
wfRunHooks( 'ShowMissingArticle', array( $this ) );
# Show delete and move logs
- LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '',
+ LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle(), '',
array( 'lim' => 10,
'conds' => array( "log_action != 'revision'" ),
'showIfEmpty' => false,
# Show error message
$oldid = $this->getOldID();
if ( $oldid ) {
- $text = wfMsgNoTrans( 'missing-article',
- $this->getTitle()->getPrefixedText(),
- wfMsgNoTrans( 'missingarticle-rev', $oldid ) );
+ $text = wfMsgNoTrans( 'missing-revision', $oldid );
} elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
// Use the default message text
$text = $this->getTitle()->getDefaultMessageText();
$lnk = $current
? wfMsgHtml( 'currentrevisionlink' )
- : Linker::link(
+ : Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'currentrevisionlink' ),
array(),
- $extraParams,
- array( 'known', 'noclasses' )
+ $extraParams
);
$curdiff = $current
? wfMsgHtml( 'diff' )
- : Linker::link(
+ : Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
'diff' => 'cur',
'oldid' => $oldid
- ) + $extraParams,
- array( 'known', 'noclasses' )
+ ) + $extraParams
);
$prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ;
$prevlink = $prev
- ? Linker::link(
+ ? Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'previousrevision' ),
array(),
array(
'direction' => 'prev',
'oldid' => $oldid
- ) + $extraParams,
- array( 'known', 'noclasses' )
+ ) + $extraParams
)
: wfMsgHtml( 'previousrevision' );
$prevdiff = $prev
- ? Linker::link(
+ ? Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
'diff' => 'prev',
'oldid' => $oldid
- ) + $extraParams,
- array( 'known', 'noclasses' )
+ ) + $extraParams
)
: wfMsgHtml( 'diff' );
$nextlink = $current
? wfMsgHtml( 'nextrevision' )
- : Linker::link(
+ : Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'nextrevision' ),
array(),
array(
'direction' => 'next',
'oldid' => $oldid
- ) + $extraParams,
- array( 'known', 'noclasses' )
+ ) + $extraParams
);
$nextdiff = $current
? wfMsgHtml( 'diff' )
- : Linker::link(
+ : Linker::linkKnown(
$this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
'diff' => 'next',
'oldid' => $oldid
- ) + $extraParams,
- array( 'known', 'noclasses' )
+ ) + $extraParams
);
$cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
LogEventsList::showLogExtract(
$outputPage,
'delete',
- $title->getPrefixedText()
+ $title
);
return;
// @todo FIXME: i18n issue/patchwork message
$this->getContext()->getOutput()->addHTML( '<strong class="mw-delete-warning-revisions">' .
wfMsgExt( 'historywarning', array( 'parseinline' ), $this->getContext()->getLanguage()->formatNum( $revisions ) ) .
- wfMsgHtml( 'word-separator' ) . Linker::link( $title,
+ wfMsgHtml( 'word-separator' ) . Linker::linkKnown( $title,
wfMsgHtml( 'history' ),
array( 'rel' => 'archives' ),
array( 'action' => 'history' ) ) .
$outputPage->addHTML( $form );
$outputPage->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
LogEventsList::showLogExtract( $outputPage, 'delete',
- $this->getTitle()->getPrefixedText()
+ $this->getTitle()
);
}
LogEventsList::showLogExtract(
$outputPage,
'delete',
- $this->getTitle()->getPrefixedText()
+ $this->getTitle()
);
} else {
$outputPage->addHTML( $error );
'UnlistedSpecialPage' => 'includes/SpecialPage.php',
'UploadSourceAdapter' => 'includes/Import.php',
'UppercaseCollation' => 'includes/Collation.php',
+ 'Uri' => 'includes/Uri.php',
'User' => 'includes/User.php',
'UserArray' => 'includes/UserArray.php',
'UserArrayFromResult' => 'includes/UserArray.php',
'UserBlockedError' => 'includes/Exception.php',
+ 'UserNotLoggedIn' => 'includes/Exception.php',
+ 'UserCache' => 'includes/cache/UserCache.php',
'UserMailer' => 'includes/UserMailer.php',
'UserRightsProxy' => 'includes/UserRightsProxy.php',
'ViewCountUpdate' => 'includes/ViewCountUpdate.php',
'ApiResult' => 'includes/api/ApiResult.php',
'ApiRollback' => 'includes/api/ApiRollback.php',
'ApiRsd' => 'includes/api/ApiRsd.php',
+ 'ApiSetNotificationTimestamp' => 'includes/api/ApiSetNotificationTimestamp.php',
'ApiTokens' => 'includes/api/ApiTokens.php',
'ApiUnblock' => 'includes/api/ApiUnblock.php',
'ApiUndelete' => 'includes/api/ApiUndelete.php',
'ExternalUser_MediaWiki' => 'includes/extauth/MediaWiki.php',
'ExternalUser_vB' => 'includes/extauth/vB.php',
+ # includes/filebackend
+ 'FileBackendGroup' => 'includes/filebackend/FileBackendGroup.php',
+ 'FileBackend' => 'includes/filebackend/FileBackend.php',
+ 'FileBackendStore' => 'includes/filebackend/FileBackendStore.php',
+ 'FileBackendStoreShardListIterator' => 'includes/filebackend/FileBackendStore.php',
+ 'FileBackendStoreShardDirIterator' => 'includes/filebackend/FileBackendStore.php',
+ 'FileBackendStoreShardFileIterator' => 'includes/filebackend/FileBackendStore.php',
+ 'FileBackendMultiWrite' => 'includes/filebackend/FileBackendMultiWrite.php',
+ 'FileBackendStoreOpHandle' => 'includes/filebackend/FileBackendStore.php',
+ 'FSFile' => 'includes/filebackend/FSFile.php',
+ 'FSFileBackend' => 'includes/filebackend/FSFileBackend.php',
+ 'FSFileBackendList' => 'includes/filebackend/FSFileBackend.php',
+ 'FSFileBackendDirList' => 'includes/filebackend/FSFileBackend.php',
+ 'FSFileBackendFileList' => 'includes/filebackend/FSFileBackend.php',
+ 'FSFileOpHandle' => 'includes/filebackend/FSFileBackend.php',
+ 'SwiftFileBackend' => 'includes/filebackend/SwiftFileBackend.php',
+ 'SwiftFileBackendList' => 'includes/filebackend/SwiftFileBackend.php',
+ 'SwiftFileBackendDirList' => 'includes/filebackend/SwiftFileBackend.php',
+ 'SwiftFileBackendFileList' => 'includes/filebackend/SwiftFileBackend.php',
+ 'SwiftFileOpHandle' => 'includes/filebackend/SwiftFileBackend.php',
+ 'TempFSFile' => 'includes/filebackend/TempFSFile.php',
+ 'FileJournal' => 'includes/filebackend/filejournal/FileJournal.php',
+ 'DBFileJournal' => 'includes/filebackend/filejournal/DBFileJournal.php',
+ 'NullFileJournal' => 'includes/filebackend/filejournal/FileJournal.php',
+ 'LockManagerGroup' => 'includes/filebackend/lockmanager/LockManagerGroup.php',
+ 'LockManager' => 'includes/filebackend/lockmanager/LockManager.php',
+ 'ScopedLock' => 'includes/filebackend/lockmanager/LockManager.php',
+ 'FSLockManager' => 'includes/filebackend/lockmanager/FSLockManager.php',
+ 'DBLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php',
+ 'LSLockManager' => 'includes/filebackend/lockmanager/LSLockManager.php',
+ 'MemcLockManager' => 'includes/filebackend/lockmanager/MemcLockManager.php',
+ 'QuorumLockManager' => 'includes/filebackend/lockmanager/LockManager.php',
+ 'MySqlLockManager'=> 'includes/filebackend/lockmanager/DBLockManager.php',
+ 'NullLockManager' => 'includes/filebackend/lockmanager/LockManager.php',
+ 'FileOp' => 'includes/filebackend/FileOp.php',
+ 'FileOpBatch' => 'includes/filebackend/FileOpBatch.php',
+ 'StoreFileOp' => 'includes/filebackend/FileOp.php',
+ 'CopyFileOp' => 'includes/filebackend/FileOp.php',
+ 'MoveFileOp' => 'includes/filebackend/FileOp.php',
+ 'DeleteFileOp' => 'includes/filebackend/FileOp.php',
+ 'ConcatenateFileOp' => 'includes/filebackend/FileOp.php',
+ 'CreateFileOp' => 'includes/filebackend/FileOp.php',
+ 'NullFileOp' => 'includes/filebackend/FileOp.php',
+
# includes/filerepo
'FileRepo' => 'includes/filerepo/FileRepo.php',
'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
'LocalFileRestoreBatch' => 'includes/filerepo/file/LocalFile.php',
'OldLocalFile' => 'includes/filerepo/file/OldLocalFile.php',
'UnregisteredLocalFile' => 'includes/filerepo/file/UnregisteredLocalFile.php',
- 'FSFile' => 'includes/filerepo/backend/FSFile.php',
- 'TempFSFile' => 'includes/filerepo/backend/TempFSFile.php',
-
- # includes/filerepo/backend
- 'FileBackendGroup' => 'includes/filerepo/backend/FileBackendGroup.php',
- 'FileBackend' => 'includes/filerepo/backend/FileBackend.php',
- 'FileBackendStore' => 'includes/filerepo/backend/FileBackendStore.php',
- 'FileBackendStoreShardListIterator' => 'includes/filerepo/backend/FileBackendStore.php',
- 'FileBackendStoreShardDirIterator' => 'includes/filerepo/backend/FileBackendStore.php',
- 'FileBackendStoreShardFileIterator' => 'includes/filerepo/backend/FileBackendStore.php',
- 'FileBackendMultiWrite' => 'includes/filerepo/backend/FileBackendMultiWrite.php',
- 'FileBackendStoreOpHandle' => 'includes/filerepo/backend/FileBackendStore.php',
- 'FSFileBackend' => 'includes/filerepo/backend/FSFileBackend.php',
- 'FSFileBackendList' => 'includes/filerepo/backend/FSFileBackend.php',
- 'FSFileBackendDirList' => 'includes/filerepo/backend/FSFileBackend.php',
- 'FSFileBackendFileList' => 'includes/filerepo/backend/FSFileBackend.php',
- 'FSFileOpHandle' => 'includes/filerepo/backend/FSFileBackend.php',
- 'SwiftFileBackend' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'SwiftFileBackendList' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'SwiftFileBackendDirList' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'SwiftFileBackendFileList' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'SwiftFileOpHandle' => 'includes/filerepo/backend/SwiftFileBackend.php',
- 'FileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
- 'DBFileJournal' => 'includes/filerepo/backend/filejournal/DBFileJournal.php',
- 'NullFileJournal' => 'includes/filerepo/backend/filejournal/FileJournal.php',
- 'LockManagerGroup' => 'includes/filerepo/backend/lockmanager/LockManagerGroup.php',
- 'LockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
- 'ScopedLock' => 'includes/filerepo/backend/lockmanager/LockManager.php',
- 'FSLockManager' => 'includes/filerepo/backend/lockmanager/FSLockManager.php',
- 'DBLockManager' => 'includes/filerepo/backend/lockmanager/DBLockManager.php',
- 'LSLockManager' => 'includes/filerepo/backend/lockmanager/LSLockManager.php',
- 'MemcLockManager' => 'includes/filerepo/backend/lockmanager/MemcLockManager.php',
- 'QuorumLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
- 'MySqlLockManager'=> 'includes/filerepo/backend/lockmanager/DBLockManager.php',
- 'NullLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
- 'FileOp' => 'includes/filerepo/backend/FileOp.php',
- 'FileOpBatch' => 'includes/filerepo/backend/FileOpBatch.php',
- 'StoreFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'CopyFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'MoveFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'DeleteFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'ConcatenateFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'CreateFileOp' => 'includes/filerepo/backend/FileOp.php',
- 'NullFileOp' => 'includes/filerepo/backend/FileOp.php',
# includes/installer
'CliInstaller' => 'includes/installer/CliInstaller.php',
'CSSJanus' => 'includes/libs/CSSJanus.php',
'CSSJanus_Tokenizer' => 'includes/libs/CSSJanus.php',
'CSSMin' => 'includes/libs/CSSMin.php',
+ 'GenericArrayObject' => 'includes/libs/GenericArrayObject.php',
'HttpStatus' => 'includes/libs/HttpStatus.php',
'IEContentAnalyzer' => 'includes/libs/IEContentAnalyzer.php',
'IEUrlExtension' => 'includes/libs/IEUrlExtension.php',
'MultiWriteBagOStuff' => 'includes/objectcache/MultiWriteBagOStuff.php',
'MWMemcached' => 'includes/objectcache/MemcachedClient.php',
'ObjectCache' => 'includes/objectcache/ObjectCache.php',
+ 'ObjectCacheSessionHandler' => 'includes/objectcache/ObjectCacheSessionHandler.php',
+ 'RedisBagOStuff' => 'includes/objectcache/RedisBagOStuff.php',
'SqlBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
'WinCacheBagOStuff' => 'includes/objectcache/WinCacheBagOStuff.php',
'XCacheBagOStuff' => 'includes/objectcache/XCacheBagOStuff.php',
# includes/parser
- 'CacheTime' => 'includes/parser/ParserOutput.php',
+ 'CacheTime' => 'includes/parser/CacheTime.php',
'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php',
'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
'CoreTagHooks' => 'includes/parser/CoreTagHooks.php',
'TestFileIterator' => 'tests/testHelpers.inc',
'TestRecorder' => 'tests/testHelpers.inc',
+ # tests/phpunit/includes
+ 'GenericArrayObjectTest' => 'tests/phpunit/includes/libs/GenericArrayObjectTest.php',
+
# tests/phpunit/includes/db
'ORMRowTest' => 'tests/phpunit/includes/db/ORMRowTest.php',
* @param $rc RecentChange
*/
public function insertRollback( &$s, &$rc ) {
- if( !$rc->mAttribs['rc_new'] && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+ if( $rc->mAttribs['rc_type'] != RC_NEW && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
$page = $rc->getTitle();
/** Check for rollback and edit permissions, disallow special pages, and only
* show a link on the top-most revision */
if ( !$rc->mAttribs['rc_patrolled'] ) {
if ( $this->getUser()->useRCPatrol() ) {
$unpatrolled = true;
- } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_new'] ) {
+ } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
$unpatrolled = true;
}
}
# M, N, b and ! (minor, new, bot and unpatrolled)
$s .= $this->recentChangesFlags(
array(
- 'newpage' => $rc->mAttribs['rc_new'],
+ 'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
'minor' => $rc->mAttribs['rc_minor'],
'unpatrolled' => $unpatrolled,
'bot' => $rc->mAttribs['rc_bot']
$allLogs = true;
foreach( $block as $rcObj ) {
$oldid = $rcObj->mAttribs['rc_last_oldid'];
- if( $rcObj->mAttribs['rc_new'] ) {
+ if( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
$isnew = true;
}
// If all log actions to this page were hidden, then don't
$r .= '<tr><td></td><td class="mw-enhanced-rc">';
$r .= $this->recentChangesFlags( array(
- 'newpage' => $rcObj->mAttribs['rc_new'],
+ 'newpage' => $type == RC_NEW,
'minor' => $rcObj->mAttribs['rc_minor'],
'unpatrolled' => $rcObj->unpatrolled,
'bot' => $rcObj->mAttribs['rc_bot'],
/** File to log database errors to */
$wgDBerrorLog = false;
+
/**
- * Override wiki timezone to UTC for wgDBerrorLog
+ * Timezone to use in the error log.
+ * Defaults to the wiki timezone ($wgLocalTimezone).
+ *
+ * A list of useable timezones can found at:
+ * http://php.net/manual/en/timezones.php
+ *
+ * @par Examples:
+ * @code
+ * $wgLocaltimezone = 'UTC';
+ * $wgLocaltimezone = 'GMT';
+ * $wgLocaltimezone = 'PST8PDT';
+ * $wgLocaltimezone = 'Europe/Sweden';
+ * $wgLocaltimezone = 'CET';
+ * @endcode
+ *
* @since 1.20
*/
-$wgDBerrorLogInUTC = false;
+$wgDBerrorLogTZ = false;
/** When to give an error message */
$wgDBClusterTimeout = 10;
*/
$wgParserCacheType = CACHE_ANYTHING;
+/**
+ * The cache type for storing session data. Used if $wgSessionsInObjectCache is true.
+ *
+ * For available types see $wgMainCacheType.
+ */
+$wgSessionCacheType = CACHE_ANYTHING;
+
/**
* The cache type for storing language conversion tables,
* which are used when parsing certain text and interface messages.
$wgDBAhandler = 'db3';
/**
- * Store sessions in MemCached. This can be useful to improve performance, or to
- * avoid the locking behaviour of PHP's default session handler, which tends to
- * prevent multiple requests for the same user from acting concurrently.
+ * Deprecated alias for $wgSessionsInObjectCache.
+ *
+ * @deprecated Use $wgSessionsInObjectCache
*/
$wgSessionsInMemcached = false;
+/**
+ * Store sessions in an object cache, configured by $wgSessionCacheType. This
+ * can be useful to improve performance, or to avoid the locking behaviour of
+ * PHP's default session handler, which tends to prevent multiple requests for
+ * the same user from acting concurrently.
+ */
+$wgSessionsInObjectCache = false;
+
+/**
+ * The expiry time to use for session storage when $wgSessionsInObjectCache is
+ * enabled, in seconds.
+ */
+$wgObjectCacheSessionExpiry = 3600;
+
/**
* This is used for setting php's session.save_handler. In practice, you will
* almost never need to change this ever. Other options might be 'user' or
/**
* Read/write timeout for MemCached server communication, in microseconds.
*/
-$wgMemCachedTimeout = 100000;
+$wgMemCachedTimeout = 500000;
/**
* Set this to true to make a local copy of the message cache, for use in
* Timezones can be translated by editing MediaWiki messages of type
* timezone-nameinlowercase like timezone-utc.
*
+ * A list of useable timezones can found at:
+ * http://php.net/manual/en/timezones.php
+ *
* @par Examples:
* @code
+ * $wgLocaltimezone = 'UTC';
* $wgLocaltimezone = 'GMT';
* $wgLocaltimezone = 'PST8PDT';
* $wgLocaltimezone = 'Europe/Sweden';
*/
$wgSend404Code = true;
+
+/**
+ * The $wgShowRollbackEditCount variable is used to show how many edits will be
+ * rollback. The numeric value of the varible are the limit up to are counted.
+ * If the value is false or 0, the edits are not counted.
+ */
+$wgShowRollbackEditCount = 10;
+
/** @} */ # End of output format settings }
/*************************************************************************//**
$wgDBtestuser = ''; //db user that has permission to create and drop the test databases only
$wgDBtestpassword = '';
+/**
+ * Whether the user must enter their password to change their e-mail address
+ */
+$wgRequirePasswordforEmailChange = true;
+
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
*
* This difers from Article::getContent() that when a missing revision is
* encountered the result will be an empty string and not the
- * 'missing-article' message.
+ * 'missing-revision' message.
*
* @since 1.19
* @return string
// Something went wrong
$wgOut->wrapWikiMsg( "<div class='errorbox'>\n$1\n</div>\n",
- array( 'missing-article', $this->mTitle->getPrefixedText(),
- wfMsgNoTrans( 'missingarticle-rev', $this->oldid ) ) );
+ array( 'missing-revision', $this->oldid ) );
}
}
}
/**
* Return the requested URL and point to file and line number from which the
- * exception occured
+ * exception occurred
*
* @return string
*/
* Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
*
+ * @since 1.7
* @ingroup Exception
*/
class FatalError extends MWException {
/**
* An error page which can definitely be safely rendered using the OutputPage.
*
+ * @since 1.7
* @ingroup Exception
*/
class ErrorPageError extends MWException {
* Similar to ErrorPage, but emit a 400 HTTP error code to let mobile
* browser it is not really a valid content.
*
+ * @since 1.19
* @ingroup Exception
*/
class BadTitleError extends ErrorPageError {
* Show an error when a user tries to do something they do not have the necessary
* permissions for.
*
+ * @since 1.18
* @ingroup Exception
*/
class PermissionsError extends ErrorPageError {
* Show an error when the wiki is locked/read-only and the user tries to do
* something that requires write access.
*
+ * @since 1.18
* @ingroup Exception
*/
class ReadOnlyError extends ErrorPageError {
/**
* Show an error when the user hits a rate limit.
*
+ * @since 1.18
* @ingroup Exception
*/
class ThrottledError extends ErrorPageError {
/**
* Show an error when the user tries to do something whilst blocked.
*
+ * @since 1.18
* @ingroup Exception
*/
class UserBlockedError extends ErrorPageError {
* This is essentially an ErrorPageError exception which by default use the
* 'exception-nologin' as a title and 'exception-nologin-text' for the message.
* @see bug 37627
+ * @since 1.20
*
* @par Example:
* @code
* Show an error that looks like an HTTP server error.
* Replacement for wfHttpError().
*
+ * @since 1.19
* @ingroup Exception
*/
class HttpError extends MWException {
$cgi = '';
foreach ( $array1 as $key => $value ) {
- if ( !is_null($value) && $value !== false ) {
+ if ( $value !== false ) {
if ( $cgi != '' ) {
$cgi .= '&';
}
} else {
if ( is_object( $value ) ) {
$value = $value->__toString();
+ } elseif( !is_null( $value ) ) {
+ $cgi .= urlencode( $key ) . '=' . urlencode( $value );
+ } else {
+ $cgi .= urlencode( $key );
}
- $cgi .= urlencode( $key ) . '=' . urlencode( $value );
}
}
}
continue;
}
if ( strpos( $bit, '=' ) === false ) {
- // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
- $key = $bit;
- $value = '';
+ // Pieces like &qwerty become 'qwerty' => null
+ $key = urldecode( $bit );
+ $value = null;
} else {
list( $key, $value ) = explode( '=', $bit );
+ $key = urldecode( $key );
+ $value = urldecode( $value );
}
- $key = urldecode( $key );
- $value = urldecode( $value );
+
if ( strpos( $key, '[' ) !== false ) {
$keys = array_reverse( explode( '[', $key ) );
$key = array_pop( $keys );
* Append a query string to an existing URL, which may or may not already
* have query string parameters already. If so, they will be combined.
*
+ * @deprecated
* @param $url String
* @param $query Mixed: string or associative array
* @return string
*/
function wfAppendQuery( $url, $query ) {
- if ( is_array( $query ) ) {
- $query = wfArrayToCgi( $query );
- }
- if( $query != '' ) {
- if( false === strpos( $url, '?' ) ) {
- $url .= '?';
- } else {
- $url .= '&';
- }
- $url .= $query;
- }
- return $url;
+ $obj = new Uri( $url );
+ $obj->extendQuery( $query );
+ return $obj->toString();
}
/**
* @todo Need to integrate this into wfExpandUrl (bug 32168)
*
* @since 1.19
+ * @deprecated
* @param $urlParts Array URL parts, as output from wfParseUrl
* @return string URL assembled from its component parts
*/
function wfAssembleUrl( $urlParts ) {
- $result = '';
-
- if ( isset( $urlParts['delimiter'] ) ) {
- if ( isset( $urlParts['scheme'] ) ) {
- $result .= $urlParts['scheme'];
- }
-
- $result .= $urlParts['delimiter'];
- }
-
- if ( isset( $urlParts['host'] ) ) {
- if ( isset( $urlParts['user'] ) ) {
- $result .= $urlParts['user'];
- if ( isset( $urlParts['pass'] ) ) {
- $result .= ':' . $urlParts['pass'];
- }
- $result .= '@';
- }
-
- $result .= $urlParts['host'];
-
- if ( isset( $urlParts['port'] ) ) {
- $result .= ':' . $urlParts['port'];
- }
- }
-
- if ( isset( $urlParts['path'] ) ) {
- $result .= $urlParts['path'];
- }
-
- if ( isset( $urlParts['query'] ) ) {
- $result .= '?' . $urlParts['query'];
- }
-
- if ( isset( $urlParts['fragment'] ) ) {
- $result .= '#' . $urlParts['fragment'];
- }
-
- return $result;
+ $obj = new Uri( $urlParts );
+ return $obj->toString();
}
/**
* 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly
* 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2))
*
+ * @deprecated
* @param $url String: a URL to parse
* @return Array: bits of the URL in an associative array, per PHP docs
*/
function wfParseUrl( $url ) {
- global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
-
- // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
- // way to handle them is to just prepend 'http:' and strip the protocol out later
- $wasRelative = substr( $url, 0, 2 ) == '//';
- if ( $wasRelative ) {
- $url = "http:$url";
- }
- wfSuppressWarnings();
- $bits = parse_url( $url );
- wfRestoreWarnings();
- // parse_url() returns an array without scheme for some invalid URLs, e.g.
- // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
- if ( !$bits || !isset( $bits['scheme'] ) ) {
- return false;
- }
-
- // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
- if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
- $bits['delimiter'] = '://';
- } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
- $bits['delimiter'] = ':';
- // parse_url detects for news: and mailto: the host part of an url as path
- // We have to correct this wrong detection
- if ( isset( $bits['path'] ) ) {
- $bits['host'] = $bits['path'];
- $bits['path'] = '';
- }
- } else {
- return false;
- }
-
- /* Provide an empty host for eg. file:/// urls (see bug 28627) */
- if ( !isset( $bits['host'] ) ) {
- $bits['host'] = '';
-
- /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
- if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
- $bits['path'] = '/' . $bits['path'];
- }
- }
-
- // If the URL was protocol-relative, fix scheme and delimiter
- if ( $wasRelative ) {
- $bits['scheme'] = '';
- $bits['delimiter'] = '//';
- }
- return $bits;
+ $obj = new Uri( $url );
+ return $obj->getComponents();
}
/**
* @param $text String: database error message.
*/
function wfLogDBError( $text ) {
- global $wgDBerrorLog, $wgDBerrorLogInUTC;
+ global $wgDBerrorLog, $wgDBerrorLogTZ;
+ static $logDBErrorTimeZoneObject = null;
+
if ( $wgDBerrorLog ) {
$host = wfHostname();
$wiki = wfWikiID();
- if( $wgDBerrorLogInUTC ) {
- $wikiTimezone = date_default_timezone_get();
- date_default_timezone_set( 'UTC' );
- }
- $date = date( 'D M j G:i:s T Y' );
- if( $wgDBerrorLogInUTC ) {
- // Restore timezone
- date_default_timezone_set( $wikiTimezone );
+ if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) {
+ $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ );
}
+ $d = date_create( "now", $logDBErrorTimeZoneObject );
+
+ $date = $d->format( 'D M j G:i:s T Y' );
+
$text = "$date\t$host\t$wiki\t$text";
wfErrorLog( $text, $wgDBerrorLog );
}
* Use wfMsgForContent() instead if the message should NOT
* change depending on the user preferences.
*
+ * @deprecated since 1.18
+ *
* @param $key String: lookup key for the message, usually
* defined in languages/Language.php
*
/**
* Same as above except doesn't transform the message
*
+ * @deprecated since 1.18
+ *
* @param $key String
* @return String
*/
* customize potentially hundreds of messages in
* order to, e.g., fix a link in every possible language.
*
+ * @deprecated since 1.18
+ *
* @param $key String: lookup key for the message, usually
* defined in languages/Language.php
* @return String
/**
* Same as above except doesn't transform the message
*
+ * @deprecated since 1.18
+ *
* @param $key String
* @return String
*/
/**
* Really get a message
*
+ * @deprecated since 1.18
+ *
* @param $key String: key to get.
* @param $args
* @param $useDB Boolean
/**
* Fetch a message string value, but don't replace any keys yet.
*
+ * @deprecated since 1.18
+ *
* @param $key String
* @param $useDB Bool
* @param $langCode String: Code of the language to get the message for, or
/**
* Replace message parameter keys on the given formatted output.
*
+ * @deprecated since 1.18
+ *
* @param $message String
* @param $args Array
* @return string
* to pre-escape them if you really do want plaintext, or just wrap
* the whole thing in htmlspecialchars().
*
+ * @deprecated since 1.18
+ *
* @param $key String
* @param string ... parameters
* @return string
* to pre-escape them if you really do want plaintext, or just wrap
* the whole thing in htmlspecialchars().
*
+ * @deprecated since 1.18
+ *
* @param $key String
* @param string ... parameters
* @return string
/**
* Returns message in the requested format
+ *
+ * @deprecated since 1.18
+ *
* @param $key String: key of the message
* @param $options Array: processing rules. Can take the following options:
* <i>parse</i>: parses wikitext to HTML
* looked up didn't exist but a XHTML string, this function checks for the
* nonexistance of messages by checking the MessageCache::get() result directly.
*
+ * @deprecated since 1.18
+ *
* @param $key String: the message key looked up
* @return Boolean True if the message *doesn't* exist.
*/
* @param $sessionId Bool
*/
function wfSetupSession( $sessionId = false ) {
- global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain,
+ global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain,
$wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler;
- if( $wgSessionsInMemcached ) {
- if ( !defined( 'MW_COMPILED' ) ) {
- global $IP;
- require_once( "$IP/includes/cache/MemcachedSessions.php" );
- }
- session_set_save_handler( 'memsess_open', 'memsess_close', 'memsess_read',
- 'memsess_write', 'memsess_destroy', 'memsess_gc' );
-
- // It's necessary to register a shutdown function to call session_write_close(),
- // because by the time the request shutdown function for the session module is
- // called, $wgMemc has already been destroyed. Shutdown functions registered
- // this way are called before object destruction.
- register_shutdown_function( 'memsess_write_close' );
+ if( $wgSessionsInObjectCache || $wgSessionsInMemcached ) {
+ ObjectCacheSessionHandler::install();
} elseif( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
# Only set this if $wgSessionHandler isn't null and session.save_handler
# hasn't already been set to the desired value (that causes errors)
/**
* Replace all invalid characters with -
+ * Additional characters can be defined in $wgIllegalFileChars (see bug 20489)
+ * By default, $wgIllegalFileChars = ':'
*
* @param $name Mixed: filename to process
* @return String
*/
function wfStripIllegalFilenameChars( $name ) {
global $wgIllegalFileChars;
+ $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
$name = wfBaseName( $name );
$name = preg_replace(
- "/[^" . Title::legalChars() . "]" .
- ( $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '' ) .
- "/",
+ "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
'-',
$name
);
* object, and typically implement at least getInputHTML, which generates
* the HTML for the input field to be placed in the table.
*
+ * You can find extensive documentation on the www.mediawiki.org wiki:
+ * - http://www.mediawiki.org/wiki/HTMLForm
+ * - http://www.mediawiki.org/wiki/HTMLForm/tutorial
+ *
* The constructor input is an associative array of $fieldname => $info,
* where $info is an Associative Array with any of the following:
*
* (eg one without the "wp" prefix), specify it here and
* it will be used without modification.
*
+ * Since 1.20, you can chain mutators to ease the form generation:
+ * @par Example:
+ * @code
+ * $form = new HTMLForm( $someFields );
+ * $form->setMethod( 'get' )
+ * ->setWrapperLegendMsg( 'message-key' )
+ * ->suppressReset()
+ * ->prepareForm()
+ * ->displayForm();
+ * @endcode
+ * Note that you will have prepareForm and displayForm at the end. Other
+ * methods call done after that would simply not be part of the form :(
+ *
* TODO: Document 'section' / 'subsection' stuff
*/
class HTMLForm extends ContextSource {
* @param $format String the name of the format to use, must be one of
* $this->availableDisplayFormats
* @since 1.20
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function setDisplayFormat( $format ) {
if ( !in_array( $format, $this->availableDisplayFormats ) ) {
throw new MWException ( 'Display format must be one of ' . print_r( $this->availableDisplayFormats, true ) );
}
$this->displayFormat = $format;
+ return $this;
}
/**
}
/**
- * Prepare form for submission
+ * Prepare form for submission.
+ *
+ * @attention When doing method chaining, that should be the very last
+ * method call before displayForm().
+ *
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function prepareForm() {
# Check if we have the info we need
# Load data from the request.
$this->loadData();
+ return $this;
}
/**
* the output from HTMLForm::filterDataForSubmit, and must
* return Bool true on success, Bool false if no submission
* was attempted, or String HTML output to display on error.
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setSubmitCallback( $cb ) {
$this->mSubmitCallback = $cb;
+ return $this;
}
/**
* Set a message to display on a validation error.
* @param $msg Mixed String or Array of valid inputs to wfMsgExt()
* (so each entry can be either a String or Array)
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setValidationErrorMessage( $msg ) {
$this->mValidationErrorMessage = $msg;
+ return $this;
}
/**
* Set the introductory message, overwriting any existing message.
* @param $msg String complete text of message to display
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setIntro( $msg ) {
$this->setPreText( $msg );
+ return $this;
}
/**
* Set the introductory message, overwriting any existing message.
* @since 1.19
* @param $msg String complete text of message to display
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
- function setPreText( $msg ) { $this->mPre = $msg; }
+ function setPreText( $msg ) {
+ $this->mPre = $msg;
+ return $this;
+ }
/**
* Add introductory text.
* @param $msg String complete text of message to display
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
- function addPreText( $msg ) { $this->mPre .= $msg; }
+ function addPreText( $msg ) {
+ $this->mPre .= $msg;
+ return $this;
+ }
/**
* Add header text, inside the form.
* @param $msg String complete text of message to display
* @param $section string The section to add the header to
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function addHeaderText( $msg, $section = null ) {
if ( is_null( $section ) ) {
}
$this->mSectionHeaders[$section] .= $msg;
}
+ return $this;
}
/**
* @since 1.19
* @param $msg String complete text of message to display
* @param $section The section to add the header to
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setHeaderText( $msg, $section = null ) {
if ( is_null( $section ) ) {
} else {
$this->mSectionHeaders[$section] = $msg;
}
+ return $this;
}
/**
* Add footer text, inside the form.
* @param $msg String complete text of message to display
* @param $section string The section to add the footer text to
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function addFooterText( $msg, $section = null ) {
if ( is_null( $section ) ) {
}
$this->mSectionFooters[$section] .= $msg;
}
+ return $this;
}
/**
* @since 1.19
* @param $msg String complete text of message to display
* @param $section string The section to add the footer text to
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setFooterText( $msg, $section = null ) {
if ( is_null( $section ) ) {
} else {
$this->mSectionFooters[$section] = $msg;
}
+ return $this;
}
/**
* Add text to the end of the display.
* @param $msg String complete text of message to display
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
- function addPostText( $msg ) { $this->mPost .= $msg; }
+ function addPostText( $msg ) {
+ $this->mPost .= $msg;
+ return $this;
+ }
/**
* Set text at the end of the display.
* @param $msg String complete text of message to display
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
- function setPostText( $msg ) { $this->mPost = $msg; }
+ function setPostText( $msg ) {
+ $this->mPost = $msg;
+ return $this;
+ }
/**
* Add a hidden field to the output
* @param $name String field name. This will be used exactly as entered
* @param $value String field value
* @param $attribs Array
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function addHiddenField( $name, $value, $attribs = array() ) {
$attribs += array( 'name' => $name );
$this->mHiddenFields[] = array( $value, $attribs );
+ return $this;
}
+ /**
+ * Add a button to the form
+ * @param $name String field name.
+ * @param $value String field value
+ * @param $id String DOM id for the button (default: null)
+ * @param $attribs Array
+ * @return HTMLForm $this for chaining calls (since 1.20)
+ */
public function addButton( $name, $value, $id = null, $attribs = null ) {
$this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' );
+ return $this;
}
/**
* Display the form (sending to $wgOut), with an appropriate error
* message or stack of messages, and any validation errors, etc.
+ *
+ * @attention You should call prepareForm() before calling this function.
+ * Moreover, when doing method chaining this should be the very last method
+ * call just after prepareForm().
+ *
* @param $submitResult Mixed output from HTMLForm::trySubmit()
+ * @return Nothing, should be last call
*/
function displayForm( $submitResult ) {
$this->getOutput()->addHTML( $this->getHTML( $submitResult ) );
/**
* Set the text for the submit button
* @param $t String plaintext.
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setSubmitText( $t ) {
$this->mSubmitText = $t;
+ return $this;
}
/**
* Set the text for the submit button to a message
* @since 1.19
* @param $msg String message key
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function setSubmitTextMsg( $msg ) {
$this->setSubmitText( $this->msg( $msg )->text() );
+ return $this;
}
/**
: wfMsg( 'htmlform-submit' );
}
+ /**
+ * @param $name String Submit button name
+ * @return HTMLForm $this for chaining calls (since 1.20)
+ */
public function setSubmitName( $name ) {
$this->mSubmitName = $name;
+ return $this;
}
+ /**
+ * @param $name String Tooltip for the submit button
+ * @return HTMLForm $this for chaining calls (since 1.20)
+ */
public function setSubmitTooltip( $name ) {
$this->mSubmitTooltip = $name;
+ return $this;
}
/**
* Set the id for the submit button.
* @param $t String.
* @todo FIXME: Integrity of $t is *not* validated
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setSubmitID( $t ) {
$this->mSubmitID = $t;
+ return $this;
}
+ /**
+ * @param $id String DOM id for the form
+ * @return HTMLForm $this for chaining calls (since 1.20)
+ */
public function setId( $id ) {
$this->mId = $id;
+ return $this;
}
/**
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this text as its "<legend>" element.
* @param $legend String HTML to go inside the "<legend>" element.
* Will be escaped
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
- public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; }
+ public function setWrapperLegend( $legend ) {
+ $this->mWrapperLegend = $legend;
+ return $this;
+ }
/**
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this message as its "<legend>" element.
* @since 1.19
* @param $msg String message key
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function setWrapperLegendMsg( $msg ) {
$this->setWrapperLegend( $this->msg( $msg )->escaped() );
+ return $this;
}
/**
* @todo currently only used for the "<fieldset>" legend on forms
* with multiple sections; should be used elsewhre?
* @param $p String
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setMessagePrefix( $p ) {
$this->mMessagePrefix = $p;
+ return $this;
}
/**
* Set the title for form submission
* @param $t Title of page the form is on/should be posted to
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function setTitle( $t ) {
$this->mTitle = $t;
+ return $this;
}
/**
/**
* Set the method used to submit the form
* @param $method String
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function setMethod( $method = 'post' ) {
$this->mMethod = $method;
+ return $this;
}
public function getMethod() {
* Stop a reset button being shown for this form
* @param $suppressReset Bool set to false to re-enable the
* button again
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
function suppressReset( $suppressReset = true ) {
$this->mShowReset = !$suppressReset;
+ return $this;
}
/**
* @since 1.19
*
* @param string|bool $action
+ * @return HTMLForm $this for chaining calls (since 1.20)
*/
public function setAction( $action ) {
$this->mAction = $action;
+ return $this;
}
}
* @return Mixed Bool true on success, or String error to display.
*/
function validate( $value, $alldata ) {
- if ( isset( $this->mParams['required'] ) && $value === '' ) {
+ if ( isset( $this->mParams['required'] ) && $this->mParams['required'] !== false && $value === '' ) {
return wfMsgExt( 'htmlform-required', 'parseinline' );
}
return $p;
}
- if ( isset( $this->mParams['required'] ) && $value[1] === '' ) {
+ if ( isset( $this->mParams['required'] ) && $this->mParams['required'] !== false && $value[1] === '' ) {
return wfMsgExt( 'htmlform-required', 'parseinline' );
}
* @param $url String: url to use. If protocol-relative, will be expanded to an http:// URL
* @param $options Array: (optional) extra params to pass (see Http::request())
*/
- function __construct( $url, $options = array() ) {
+ protected function __construct( $url, $options = array() ) {
global $wgHTTPTimeout;
$this->url = wfExpandUrl( $url, PROTO_HTTP );
$link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
$ul .= Html::rawElement(
'li',
- array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
+ array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
$link2
) . "\n";
}
}
$out->addHTML( Html::rawElement(
'li',
- array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
+ array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
$liContents
) . "\n"
);
* @return string
*/
public function beginImageHistoryList( $navLinks = '' ) {
- return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) . "\n"
+ return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() ) . "\n"
. "<div id=\"mw-imagepage-section-filehistory\">\n"
- . $this->getOutput()->parse( wfMsgNoTrans( 'filehist-help' ) )
+ . $this->msg( 'filehist-help' )->parseAsBlock()
. $navLinks . "\n"
. Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
. '<tr><td></td>'
. ( $this->current->isLocal() && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<td></td>' : '' )
- . '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
- . ( $this->showThumb ? '<th>' . wfMsgHtml( 'filehist-thumb' ) . '</th>' : '' )
- . '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
+ . '<th>' . $this->msg( 'filehist-datetime' )->escaped() . '</th>'
+ . ( $this->showThumb ? '<th>' . $this->msg( 'filehist-thumb' )->escaped() . '</th>' : '' )
+ . '<th>' . $this->msg( 'filehist-dimensions' )->escaped() . '</th>'
+ . '<th>' . $this->msg( 'filehist-user' )->escaped() . '</th>'
+ . '<th>' . $this->msg( 'filehist-comment' )->escaped() . '</th>'
. "</tr>\n";
}
}
$row .= Linker::linkKnown(
$this->title,
- wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
+ $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(),
array(), $q
);
}
$row .= '<br />';
}
// If file is top revision or locked from this user, don't link
- if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED ) ) {
+ if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
$del = Linker::revDeleteLinkDisabled( $canHide );
} else {
list( $ts, ) = explode( '!', $img, 2 );
// Reversion link/current indicator
$row .= '<td>';
if ( $iscur ) {
- $row .= wfMsgHtml( 'filehist-current' );
- } elseif ( $local && $this->title->quickUserCan( 'edit' )
- && $this->title->quickUserCan( 'upload' )
+ $row .= $this->msg( 'filehist-current' )->escaped();
+ } elseif ( $local && $this->title->quickUserCan( 'edit', $user )
+ && $this->title->quickUserCan( 'upload', $user )
) {
if ( $file->isDeleted( File::DELETED_FILE ) ) {
- $row .= wfMsgHtml( 'filehist-revert' );
+ $row .= $this->msg( 'filehist-revert' )->escaped();
} else {
$row .= Linker::linkKnown(
$this->title,
- wfMsgHtml( 'filehist-revert' ),
+ $this->msg( 'filehist-revert' )->escaped(),
array(),
array(
'action' => 'revert',
$selected = "class='filehistory-selected'";
}
$row .= "<td $selected style='white-space: nowrap;'>";
- if ( !$file->userCan( File::DELETED_FILE ) ) {
+ if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
# Don't link to unviewable files
- $row .= '<span class="history-deleted">' . $lang->timeanddate( $timestamp, true ) . '</span>';
+ $row .= '<span class="history-deleted">' . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
} elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
if ( $local ) {
$this->preventClickjacking();
# Make a link to review the image
$url = Linker::linkKnown(
$revdel,
- $lang->timeanddate( $timestamp, true ),
+ $lang->userTimeAndDate( $timestamp, $user ),
array(),
array(
'target' => $this->title->getPrefixedText(),
)
);
} else {
- $url = $lang->timeanddate( $timestamp, true );
+ $url = $lang->userTimeAndDate( $timestamp, $user );
}
$row .= '<span class="history-deleted">' . $url . '</span>';
} else {
$url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
- $row .= Xml::element( 'a', array( 'href' => $url ), $lang->timeanddate( $timestamp, true ) );
+ $row .= Xml::element( 'a', array( 'href' => $url ), $lang->userTimeAndDate( $timestamp, $user ) );
}
$row .= "</td>";
// Image dimensions + size
$row .= '<td>';
$row .= htmlspecialchars( $file->getDimensionsString() );
- $row .= $this->getContext()->msg( 'word-separator' )->plain();
+ $row .= $this->msg( 'word-separator' )->plain();
$row .= '<span style="white-space: nowrap;">';
- $row .= $this->getContext()->msg( 'parentheses' )->rawParams( Linker::formatSize( $file->getSize() ) )->plain();
+ $row .= $this->msg( 'parentheses' )->rawParams( Linker::formatSize( $file->getSize() ) )->plain();
$row .= '</span>';
$row .= '</td>';
$row .= '<td>';
// Hide deleted usernames
if ( $file->isDeleted( File::DELETED_USER ) ) {
- $row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ $row .= '<span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
} else {
if ( $local ) {
$row .= Linker::userLink( $userId, $userText );
- $row .= $this->getContext()->msg( 'word-separator' )->plain();
+ $row .= $this->msg( 'word-separator' )->plain();
$row .= '<span style="white-space: nowrap;">';
$row .= Linker::userToolLinks( $userId, $userText );
$row .= '</span>';
// Don't show deleted descriptions
if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
- $row .= '<td><span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></td>';
+ $row .= '<td><span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span></td>';
} else {
$row .= '<td dir="' . $wgContLang->getDir() . '">' . Linker::formatComment( $description, $this->title ) . '</td>';
}
*/
protected function getThumbForLine( $file ) {
$lang = $this->getLanguage();
- if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) {
+ $user = $this->getUser();
+ if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE,$user )
+ && !$file->isDeleted( File::DELETED_FILE ) )
+ {
$params = array(
'width' => '120',
'height' => '120',
$thumbnail = $file->transform( $params );
$options = array(
- 'alt' => wfMsg( 'filehist-thumbtext',
- $lang->timeanddate( $timestamp, true ),
- $lang->date( $timestamp, true ),
- $lang->time( $timestamp, true ) ),
+ 'alt' => $this->msg( 'filehist-thumbtext',
+ $lang->userTimeAndDate( $timestamp, $user ),
+ $lang->userDate( $timestamp, $user ),
+ $lang->userTime( $timestamp, $user ) )->text(),
'file-link' => true,
);
if ( !$thumbnail ) {
- return wfMsgHtml( 'filehist-nothumb' );
+ return $this->msg( 'filehist-nothumb' )->escaped();
}
return $thumbnail->toHtml( $options );
} else {
- return wfMsgHtml( 'filehist-nothumb' );
+ return $this->msg( 'filehist-nothumb' )->escaped();
}
}
wfProfileOut( __METHOD__ );
return "<!-- ERROR -->$html";
}
+
+ if( is_string( $query ) ) {
+ // some functions withing core using this still hand over query strings
+ wfDeprecated( __METHOD__ . ' with parameter $query as string (should be array)', '1.20' );
+ $query = wfCgiToArray( $query );
+ }
$options = (array)$options;
$dummy = new DummyLinker; // dummy linker instance for bc on the hooks
* Given parameters derived from [[Image:Foo|options...]], generate the
* HTML that that syntax inserts in the page.
*
- * @param $title Title object
+ * @param $parser Parser object
+ * @param $title Title object of the file (not the currently viewed page)
* @param $file File object, or false if it doesn't exist
* @param $frameParams Array: associative array of parameters external to the media handler.
* Boolean parameters are indicated by presence or absence, the value is arbitrary and
* @param $time String: timestamp of the file, set as false for current
* @param $query String: query params for desc url
* @param $widthOption: Used by the parser to remember the user preference thumbnailsize
+ * @since 1.20
* @return String: HTML for an image, with links, wrappers, etc.
*/
- public static function makeImageLink2( Title $title, $file, $frameParams = array(),
+ public static function makeImageLink( /*Parser*/ $parser, Title $title, $file, $frameParams = array(),
$handlerParams = array(), $time = false, $query = "", $widthOption = null )
{
$res = null;
}
if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
- global $wgContLang;
- # Create a thumbnail. Alignment depends on language
- # writing direction, # right aligned for left-to-right-
- # languages ("Western languages"), left-aligned
- # for right-to-left-languages ("Semitic languages")
+ # Create a thumbnail. Alignment depends on the writing direction of
+ # the page content language (right-aligned for LTR languages,
+ # left-aligned for RTL languages)
#
- # If thumbnail width has not been provided, it is set
+ # If a thumbnail width has not been provided, it is set
# to the default user option as specified in Language*.php
if ( $fp['align'] == '' ) {
- $fp['align'] = $wgContLang->alignEnd();
+ if( $parser instanceof Parser ) {
+ $fp['align'] = $parser->getTargetLanguage()->alignEnd();
+ } else {
+ # backwards compatibility, remove with makeImageLink2()
+ global $wgContLang;
+ $fp['align'] = $wgContLang->alignEnd();
+ }
}
return $prefix . self::makeThumbLink2( $title, $file, $fp, $hp, $time, $query ) . $postfix;
}
return str_replace( "\n", ' ', $prefix . $s . $postfix );
}
+ /**
+ * See makeImageLink()
+ * When this function is removed, remove if( $parser instanceof Parser ) check there too
+ * @deprecated since 1.20
+ */
+ public static function makeImageLink2( Title $title, $file, $frameParams = array(),
+ $handlerParams = array(), $time = false, $query = "", $widthOption = null ) {
+ return self::makeImageLink( null, $title, $file, $frameParams,
+ $handlerParams, $time, $query, $widthOption );
+ }
+
/**
* Get the link parameters for MediaTransformOutput::toHtml() from given
* frame parameters supplied by the Parser.
* @return String: HTML fragment
*/
public static function buildRollbackLink( $rev, IContextSource $context = null ) {
+ global $wgShowRollbackEditCount, $wgMiserMode;
+
+ // To config which pages are effected by miser mode
+ $disableRollbackEditCountSpecialPage = array( 'Recentchanges', 'Watchlist' );
+
if ( $context === null ) {
$context = RequestContext::getMain();
}
$query['bot'] = '1';
$query['hidediff'] = '1'; // bug 15999
}
- return self::link(
- $title,
- $context->msg( 'rollbacklink' )->escaped(),
- array( 'title' => $context->msg( 'tooltip-rollback' )->text() ),
- $query,
- array( 'known', 'noclasses' )
- );
+
+ $disableRollbackEditCount = false;
+ if( $wgMiserMode ) {
+ foreach( $disableRollbackEditCountSpecialPage as $specialPage ) {
+ if( $context->getTitle()->isSpecial( $specialPage ) ) {
+ $disableRollbackEditCount = true;
+ break;
+ }
+ }
+ }
+
+ if( !$disableRollbackEditCount && is_int( $wgShowRollbackEditCount ) && $wgShowRollbackEditCount > 0 ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ // Up to the value of $wgShowRollbackEditCount revisions are counted
+ $res = $dbr->select( 'revision',
+ array( 'rev_id', 'rev_user_text' ),
+ // $rev->getPage() returns null sometimes
+ array( 'rev_page' => $rev->getTitle()->getArticleID() ),
+ __METHOD__,
+ array( 'USE INDEX' => 'page_timestamp',
+ 'ORDER BY' => 'rev_timestamp DESC',
+ 'LIMIT' => $wgShowRollbackEditCount + 1 )
+ );
+
+ $editCount = 0;
+ while( $row = $dbr->fetchObject( $res ) ) {
+ if( $rev->getUserText() != $row->rev_user_text ) {
+ break;
+ }
+ $editCount++;
+ }
+
+ if( $editCount > $wgShowRollbackEditCount ) {
+ $editCount_output = $context->msg( 'rollbacklinkcount-morethan' )->numParams( $wgShowRollbackEditCount )->parse();
+ } else {
+ $editCount_output = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
+ }
+
+ return self::link(
+ $title,
+ $editCount_output,
+ array( 'title' => $context->msg( 'tooltip-rollback' )->text() ),
+ $query,
+ array( 'known', 'noclasses' )
+ );
+ } else {
+ return self::link(
+ $title,
+ $context->msg( 'rollbacklink' )->escaped(),
+ array( 'title' => $context->msg( 'tooltip-rollback' )->text() ),
+ $query,
+ array( 'known', 'noclasses' )
+ );
+ }
}
/**
* Initialises this object with an ID
*
* @param $id
+ * @throws MWException
*/
function load( $id ) {
global $wgContLang;
var $baseRegex, $regex;
var $matches;
+ /**
+ * @param $names array
+ */
function __construct( $names = array() ) {
$this->names = $names;
}
return $newRegex;
}
+ /**
+ * @since 1.20
+ * @return array
+ */
+ public function getNames() {
+ return $this->names;
+ }
+
/**
* Parse a match array from preg_match
* Returns array(magic word ID, parameter value)
*
* @param $m array
*
+ * @throws MWException
* @return array
*/
function parseMatch( $m ) {
$regexes = $this->getVariableStartToEndRegex();
foreach ( $regexes as $regex ) {
if ( $regex !== '' ) {
- $m = false;
+ $m = array();
if ( preg_match( $regex, $text, $m ) ) {
return $this->parseMatch( $m );
}
*
* First implemented with MediaWiki 1.17, the Message class is intented to
* replace the old wfMsg* functions that over time grew unusable.
- * @see https://www.mediawiki.org/wiki/New_messages_API for equivalences
+ * @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences
* between old and new functions.
*
* You should use the wfMessage() global function which acts as a wrapper for
* Request the message in any language that is supported.
* As a side effect interface message status is unconditionally
* turned off.
+ * @since 1.17
* @param $lang Mixed: language code or Language object.
* @return Message: $this
*/
/**
* Request the message in the wiki's content language,
* unless it is disabled for this message.
+ * @since 1.17
* @see $wgForceUIMsgAsContentMsg
* @return Message: $this
*/
/**
* Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader,
* such as Accept-Encoding or Cookie
- *
+ *
* @return String
*/
public function getVaryHeader() {
*
* showErrorPage( 'titlemsg', 'pagetextmsg', array( 'param1', 'param2' ) );
* showErrorPage( 'titlemsg', $messageObject );
+ * showErrorPage( $titleMessageObj, $messageObject );
*
- * @param $title String: message key for page title
+ * @param $title Mixed: message key (string) for page title, or a Message object
* @param $msg Mixed: message key (string) for page text, or a Message object
* @param $params Array: message parameters; ignored if $msg is a Message object
*/
public function showErrorPage( $title, $msg, $params = array() ) {
- $this->prepareErrorPage( $this->msg( $title ), $this->msg( 'errorpagetitle' ) );
+ if( !$title instanceof Message ) {
+ $title = $this->msg( $title );
+ }
+
+ $this->prepareErrorPage( $title, $this->msg( 'errorpagetitle' ) );
if ( $msg instanceof Message ){
$this->addHTML( $msg->parse() );
* Add a "return to" link pointing to a specified title
*
* @param $title Title to link
- * @param $query String query string
+ * @param $query Array query string parameters
* @param $text String text of the link (input is not escaped)
*/
public function addReturnTo( $title, $query = array(), $text = null ) {
$titleObj = Title::newMainPage();
}
- $this->addReturnTo( $titleObj, $returntoquery );
+ $this->addReturnTo( $titleObj, wfCgiToArray( $returntoquery ) );
}
/**
protected $mLastShown, $mFirstShown, $mPastTheEndIndex, $mDefaultQuery, $mNavigationBar;
+ /**
+ * Whether to include the offset in the query
+ */
+ protected $mIncludeOffset = false;
+
/**
* Result object for the query. Warning: seek before use.
*
# Use consistent behavior for the limit options
$this->mDefaultLimit = intval( $this->getUser()->getOption( 'rclimit' ) );
- list( $this->mLimit, /* $offset */ ) = $this->mRequest->getLimitOffset();
+ if ( !$this->mLimit ) {
+ // Don't override if a subclass calls $this->setLimit() in its constructor.
+ list( $this->mLimit, /* $offset */ ) = $this->mRequest->getLimitOffset();
+ }
$this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
$this->mDb = wfGetDB( DB_SLAVE );
/**
* Set the limit from an other source than the request
*
+ * Verifies limit is between 1 and 5000
+ *
* @param $limit Int|String
*/
function setLimit( $limit ) {
- $this->mLimit = $limit;
+ $limit = (int) $limit;
+ // WebRequest::getLimitOffset() puts a cap of 5000, so do same here.
+ if ( $limit > 5000 ) {
+ $limit = 5000;
+ }
+ if ( $limit > 0 ) {
+ $this->mLimit = $limit;
+ }
+ }
+
+ /**
+ * Set whether a row matching exactly the offset should be also included
+ * in the result or not. By default this is not the case, but when the
+ * offset is user-supplied this might be wanted.
+ *
+ * @param $include bool
+ */
+ public function setIncludeOffset( $include ) {
+ $this->mIncludeOffset = $include;
}
/**
$sortColumns = array_merge( array( $this->mIndexField ), $this->mExtraSortFields );
if ( $descending ) {
$options['ORDER BY'] = $sortColumns;
- $operator = '>';
+ $operator = $this->mIncludeOffset ? '>=' : '>';
} else {
$orderBy = array();
foreach ( $sortColumns as $col ) {
$orderBy[] = $col . ' DESC';
}
$options['ORDER BY'] = $orderBy;
- $operator = '<';
+ $operator = $this->mIncludeOffset ? '<=' : '<';
}
if ( $offset != '' ) {
$conds[] = $this->mIndexField . $operator . $this->mDb->addQuotes( $offset );
// TODO: Use doQuery()
if ( !$this->isCached() ) {
- $res = $this->reallyDoQuery( $this->limit, $this->offset );
+ # select one extra row for navigation
+ $res = $this->reallyDoQuery( $this->limit + 1, $this->offset );
} else {
- # Get the cached result
- $res = $this->fetchFromCache( $this->limit, $this->offset );
+ # Get the cached result, select one extra row for navigation
+ $res = $this->fetchFromCache( $this->limit + 1, $this->offset );
if ( !$this->listoutput ) {
# Fetch the timestamp of this update
$this->numRows, $this->offset + 1 )->parseAsBlock() );
# Disable the "next" link when we reach the end
$paging = $this->getLanguage()->viewPrevNext( $this->getTitle( $par ), $this->offset,
- $this->limit, $this->linkParameters(), ( $this->numRows < $this->limit ) );
+ $this->limit, $this->linkParameters(), ( $this->numRows <= $this->limit ) );
$out->addHTML( '<p>' . $paging . '</p>' );
} else {
# No results to show, so don't bother with "showing X of Y" etc.
$this->getSkin(),
$dbr, # Should use a ResultWrapper for this
$res,
- $this->numRows,
+ min( $this->numRows, $this->limit ), # do not format the one extra row, if exist
$this->offset );
# Repeat the paging links at the bottom
*/
var $mTitle = false;
+ /**
+ * @var User
+ */
+ private $mPerformer = false;
+
/**
* @var Title
*/
return $this->mMovedToTitle;
}
+ /**
+ * Get the User object of the person who performed this change.
+ *
+ * @return User
+ */
+ public function getPerformer() {
+ if ( $this->mPerformer === false ) {
+ if ( $this->mAttribs['rc_user'] ) {
+ $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
+ } else {
+ $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
+ }
+ }
+ return $this->mPerformer;
+ }
+
/**
* Writes the data in this object to the database
* @param $noudp bool
*/
public function save( $noudp = false ) {
- global $wgLocalInterwiki, $wgPutIPinRC, $wgContLang;
+ global $wgLocalInterwiki, $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
$dbw = wfGetDB( DB_MASTER );
if( !is_array($this->mExtra) ) {
}
# E-mail notifications
- global $wgUseEnotif, $wgShowUpdatedMarker, $wgUser;
if( $wgUseEnotif || $wgShowUpdatedMarker ) {
- // Users
- if( $this->mAttribs['rc_user'] ) {
- $editor = ($wgUser->getId() == $this->mAttribs['rc_user']) ?
- $wgUser : User::newFromID( $this->mAttribs['rc_user'] );
- // Anons
- } else {
- $editor = ($wgUser->getName() == $this->mAttribs['rc_user_text']) ?
- $wgUser : User::newFromName( $this->mAttribs['rc_user_text'], false );
- }
- $title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
+ $editor = $this->getPerformer();
+ $title = $this->getTitle();
if ( wfRunHooks( 'AbortEmailNotification', array($editor, $title) ) ) {
# @todo FIXME: This would be better as an extension hook
$enotif = new EmailNotification();
- $status = $enotif->notifyOnPageChange( $editor, $title,
+ $enotif->notifyOnPageChange( $editor, $title,
$this->mAttribs['rc_timestamp'],
$this->mAttribs['rc_comment'],
$this->mAttribs['rc_minor'],
public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
$lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0 ) {
$rc = new RecentChange;
+ $rc->mTitle = $title;
+ $rc->mPerformer = $user;
$rc->mAttribs = array(
'rc_timestamp' => $timestamp,
'rc_cur_time' => $timestamp,
public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
$ip='', $size=0, $newId=0, $patrol=0 ) {
$rc = new RecentChange;
+ $rc->mTitle = $title;
+ $rc->mPerformer = $user;
$rc->mAttribs = array(
'rc_timestamp' => $timestamp,
'rc_cur_time' => $timestamp,
global $wgRequest;
$rc = new RecentChange;
+ $rc->mTitle = $target;
+ $rc->mPerformer = $user;
$rc->mAttribs = array(
'rc_timestamp' => $timestamp,
'rc_cur_time' => $timestamp,
} else {
$comment = self::cleanupForIRC( $this->mAttribs['rc_comment'] );
$flag = '';
- if ( !$this->mAttribs['rc_patrolled'] && ( $wgUseRCPatrol || $this->mAttribs['rc_new'] && $wgUseNPPatrol ) ) {
+ if ( !$this->mAttribs['rc_patrolled'] && ( $wgUseRCPatrol || $this->mAttribs['rc_type'] == RC_NEW && $wgUseNPPatrol ) ) {
$flag .= '!';
}
- $flag .= ( $this->mAttribs['rc_new'] ? "N" : "" ) . ( $this->mAttribs['rc_minor'] ? "M" : "" ) . ( $this->mAttribs['rc_bot'] ? "B" : "" );
+ $flag .= ( $this->mAttribs['rc_type'] == RC_NEW ? "N" : "" ) . ( $this->mAttribs['rc_minor'] ? "M" : "" ) . ( $this->mAttribs['rc_bot'] ? "B" : "" );
}
if ( $wgRC2UDPInterwikiPrefix === true && $wgLocalInterwiki !== false ) {
protected $mTitle;
protected $mCurrent;
+ // Revision deletion constants
const DELETED_TEXT = 1;
const DELETED_COMMENT = 2;
const DELETED_USER = 4;
const DELETED_RESTRICTED = 8;
- // Convenience field
- const SUPPRESSED_USER = 12;
+ const SUPPRESSED_USER = 12; // convenience
+
// Audience options for accessors
const FOR_PUBLIC = 1;
const FOR_THIS_USER = 2;
* Returns null if no such revision can be found.
*
* $flags include:
- * IDBAccessObject::LATEST_READ : Select the data from the master
- * IDBAccessObject::LOCKING_READ : Select & lock the data from the master
- * IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+ * Revision::READ_LATEST : Select the data from the master
+ * Revision::READ_LOCKING : Select & lock the data from the master
*
* @param $id Integer
* @param $flags Integer (optional)
* to that title, will return null.
*
* $flags include:
- * IDBAccessObject::LATEST_READ : Select the data from the master
- * IDBAccessObject::LOCKING_READ : Select & lock the data from the master
- * IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+ * Revision::READ_LATEST : Select the data from the master
+ * Revision::READ_LOCKING : Select & lock the data from the master
*
* @param $title Title
* @param $id Integer (optional)
* @param $flags Integer Bitfield (optional)
* @return Revision or null
*/
- public static function newFromTitle( $title, $id = 0, $flags = 0 ) {
+ public static function newFromTitle( $title, $id = 0, $flags = null ) {
$conds = array(
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey()
if ( $id ) {
// Use the specified ID
$conds['rev_id'] = $id;
- } elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
- // Get the latest revision ID from the master
- $dbw = wfGetDB( DB_MASTER );
- $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
- if ( $latest === false ) {
- return null; // page does not exist
- }
- $conds['rev_id'] = $latest;
} else {
// Use a join to get the latest revision
$conds[] = 'rev_id=page_latest';
+ // Callers assume this will be up-to-date
+ $flags = is_int( $flags ) ? $flags : self::READ_LATEST; // b/c
}
- return self::newFromConds( $conds, $flags );
+ return self::newFromConds( $conds, (int)$flags );
}
/**
* Returns null if no such revision can be found.
*
* $flags include:
- * IDBAccessObject::LATEST_READ : Select the data from the master
- * IDBAccessObject::LOCKING_READ : Select & lock the data from the master
- * IDBAccessObject::AVOID_MASTER : Avoid master queries; data may be stale
+ * Revision::READ_LATEST : Select the data from the master
+ * Revision::READ_LOCKING : Select & lock the data from the master
*
* @param $revId Integer
* @param $pageId Integer (optional)
* @param $flags Integer Bitfield (optional)
* @return Revision or null
*/
- public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
+ public static function newFromPageId( $pageId, $revId = 0, $flags = null ) {
$conds = array( 'page_id' => $pageId );
if ( $revId ) {
$conds['rev_id'] = $revId;
- } elseif ( !( $flags & self::AVOID_MASTER ) && wfGetLB()->getServerCount() > 1 ) {
- // Get the latest revision ID from the master
- $dbw = wfGetDB( DB_MASTER );
- $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
- if ( $latest === false ) {
- return null; // page does not exist
- }
- $conds['rev_id'] = $latest;
} else {
+ // Use a join to get the latest revision
$conds[] = 'rev_id = page_latest';
+ // Callers assume this will be up-to-date
+ $flags = is_int( $flags ) ? $flags : self::READ_LATEST; // b/c
}
- return self::newFromConds( $conds, $flags );
+ return self::newFromConds( $conds, (int)$flags );
}
/**
* @return Revision or null
*/
private static function newFromConds( $conditions, $flags = 0 ) {
- $db = wfGetDB( ( $flags & self::LATEST_READ ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
$rev = self::loadFromConds( $db, $conditions, $flags );
if ( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
- if ( !( $flags & self::LATEST_READ ) && !( $flags & self::AVOID_MASTER ) ) {
+ if ( !( $flags & self::READ_LATEST ) ) {
$dbw = wfGetDB( DB_MASTER );
$rev = self::loadFromConds( $dbw, $conditions, $flags );
}
self::selectUserFields()
);
$options = array( 'LIMIT' => 1 );
- if ( $flags & self::FOR_UPDATE ) {
+ if ( $flags & self::READ_LOCKING ) {
$options[] = 'FOR UPDATE';
}
return $db->select(
# Can't stub this one, it sets up $_GET and $_REQUEST in its constructor
$wgRequest = new WebRequest;
- $debug = "\n\nStart request {$_SERVER['REQUEST_METHOD']} {$wgRequest->getRequestURL()}\n";
+ $debug = "\n\nStart request {$wgRequest->getMethod()} {$wgRequest->getRequestURL()}\n";
if ( $wgDebugPrintHttpHeaders ) {
$debug .= "HTTP HEADERS:\n";
}
return $wgValidSkinNames;
}
-
+
/**
* Fetch the skinname messages for available skins.
* @return array of strings
$ntl = '';
if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
- $userTitle = $this->getUser()->getUserPage();
- $userTalkTitle = $userTitle->getTalkPage();
+ $userTalkTitle = $this->getUser()->getTalkPage();
if ( !$userTalkTitle->equals( $out->getTitle() ) ) {
+ $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
+ $nofAuthors = 0;
+ if ( $lastSeenRev !== null ) {
+ $plural = true; // Default if we have a last seen revision: if unknown, use plural
+ $latestRev = Revision::newFromTitle ($userTalkTitle);
+ if ( $latestRev !== null ) {
+ // Singular if only 1 unseen revision, plural if several unseen revisions.\r
+ $plural = $latestRev->getParentId() !== $lastSeenRev->getId();\r
+ $nofAuthors = $userTalkTitle->countAuthorsBetween( $lastSeenRev, $latestRev, 10, 'include_new' );
+ }
+ } else {
+ // Singular if no revision -> diff link will show latest change only in any case
+ $plural = false;
+ }
+ $plural = $plural ? 2 : 1;
+ // 2 signifies "more than one revision". We don't know how many, and even if we did,
+ // the number of revisions or authors is not necessarily the same as the number of
+ // "messages".
$newMessagesLink = Linker::linkKnown(
$userTalkTitle,
- $this->msg( 'newmessageslink' )->escaped(),
+ $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
array(),
array( 'redirect' => 'no' )
);
$newMessagesDiffLink = Linker::linkKnown(
$userTalkTitle,
- $this->msg( 'newmessagesdifflink' )->escaped(),
+ $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
array(),
- array( 'diff' => 'cur' )
+ $lastSeenRev !== null
+ ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
+ : array( 'diff' => 'cur' )
);
- $ntl = $this->msg(
- 'youhavenewmessages',
- $newMessagesLink,
- $newMessagesDiffLink
- )->text();
+ if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
+ $ntl = $this->msg(
+ 'youhavenewmessagesfromusers',
+ $newMessagesLink,
+ $newMessagesDiffLink
+ )->numParams( $nofAuthors );
+ } else {
+ // $nofAuthors === 11 signifies "11 or more" ("more than 10")
+ $ntl = $this->msg(
+ $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
+ $newMessagesLink,
+ $newMessagesDiffLink
+ );
+ }
+ $ntl = $ntl->text();
# Disable Squid cache
$out->setSquidMaxage( 0 );
}
if ( $wgOut->isArticleRelated() ) {
if ( $title->getNamespace() == NS_FILE ) {
- $name = $title->getDBkey();
$image = wfFindFile( $title );
if ( $image ) {
- $link = htmlspecialchars( $image->getURL() );
- $style = Linker::getInternalLinkAttributes( $link, $name );
- $s[] = "<a href=\"{$link}\"{$style}>{$name}</a>";
+ $href = $image->getURL();
+ $s[] = Html::element( 'a', array( 'href' => $href,
+ 'title' => $href ), $title->getText() );
+
}
}
}
}
if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
$toolbox['print'] = $this->data['nav_urls']['print'];
+ $toolbox['print']['id'] = 't-print';
$toolbox['print']['rel'] = 'alternate';
$toolbox['print']['msg'] = 'printableversion';
}
* @param $special SpecialPage
* @param $subPage string|null
*/
- wfRunHooks( 'SpecialPageBeforeExecute', array( &$this, $subPage ) );
+ wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) );
$this->beforeExecute( $subPage );
$this->execute( $subPage );
* @param $special SpecialPage
* @param $subPage string|null
*/
- wfRunHooks( 'SpecialPageAfterExecute', array( &$this, $subPage ) );
+ wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) );
}
/**
/**
* Returns true if the title is inside the specified namespace.
- *
+ *
* Please make use of this instead of comparing to getNamespace()
* This function is much more resistant to changes we may make
* to namespaces than code that makes direct comparisons.
$title_protection['pt_create_perm'] = 'protect'; // B/C
}
if( $title_protection['pt_create_perm'] == '' ||
- !$user->isAllowed( $title_protection['pt_create_perm'] ) )
+ !$user->isAllowed( $title_protection['pt_create_perm'] ) )
{
$errors[] = array( 'titleprotected', User::whoIs( $title_protection['pt_user'] ), $title_protection['pt_reason'] );
}
}
/**
- * Get the number of authors between the given revision IDs.
+ * Get the number of authors between the given revisions or revision IDs.
* Used for diffs and other things that really need it.
*
- * @param $old int|Revision Old revision or rev ID (first before range)
- * @param $new int|Revision New revision or rev ID (first after range)
- * @param $limit Int Maximum number of authors
- * @return Int Number of revision authors between these revisions.
- */
- public function countAuthorsBetween( $old, $new, $limit ) {
+ * @param $old int|Revision Old revision or rev ID (first before range by default)
+ * @param $new int|Revision New revision or rev ID (first after range by default)
+ * @param $limit int Maximum number of authors
+ * @param $options string|array (Optional): Single option, or an array of options:
+ * 'include_old' Include $old in the range; $new is excluded.
+ * 'include_new' Include $new in the range; $old is excluded.
+ * 'include_both' Include both $old and $new in the range.
+ * Unknown option values are ignored.
+ * @return int Number of revision authors in the range; zero if not both revisions exist
+ */
+ public function countAuthorsBetween( $old, $new, $limit, $options = array() ) {
if ( !( $old instanceof Revision ) ) {
$old = Revision::newFromTitle( $this, (int)$old );
}
if ( !( $new instanceof Revision ) ) {
$new = Revision::newFromTitle( $this, (int)$new );
}
+ // XXX: what if Revision objects are passed in, but they don't refer to this title?
+ // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
+ // in the sanity check below?
if ( !$old || !$new ) {
return 0; // nothing to compare
}
+ $old_cmp = '>';
+ $new_cmp = '<';
+ $options = (array) $options;
+ if ( in_array( 'include_old', $options ) ) {
+ $old_cmp = '>=';
+ }
+ if ( in_array( 'include_new', $options ) ) {\r
+ $new_cmp = '<=';\r
+ }\r
+ if ( in_array( 'include_both', $options ) ) {\r
+ $old_cmp = '>=';\r
+ $new_cmp = '<=';
+ }
+ // No DB query needed if $old and $new are the same or successive revisions:
+ if ( $old->getId() === $new->getId() ) {
+ return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
+ } else if ( $old->getId() === $new->getParentId() ) {
+ if ( $old_cmp === '>' || $new_cmp === '<' ) {
+ return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
+ }
+ return ( $old->getRawUserText() === $new->getRawUserText() ) ? 1 : 2;
+ }\r
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
array(
'rev_page' => $this->getArticleID(),
- 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
- 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
+ "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
+ "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
), __METHOD__,
array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
);
--- /dev/null
+<?php
+/**
+ * Class for simple URI parsing and manipulation.
+ * Intended to simplify things that were using wfParseUrl and
+ * had to do manual concatenation for various needs.
+ * Built to match our JS mw.Uri in naming patterns.
+ * @file
+ * @author Daniel Friesen
+ * @since 1.20
+ */
+
+class Uri {
+
+ /**
+ * The parsed components of the URI
+ */
+ protected $components;
+
+ protected static $validComponents = array( 'scheme', 'delimiter', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment' );
+ protected static $componentAliases = array( 'protocol' => 'scheme', 'password' => 'pass' );
+
+ /**
+ * parse_url() work-alike, but non-broken. Differences:
+ *
+ * 1) Does not raise warnings on bad URLs (just returns false)
+ * 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly
+ * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2))
+ *
+ * @param $url String: a URL to parse
+ * @return Array: bits of the URL in an associative array, per PHP docs
+ */
+ protected static function parseUri( $url ) {
+ global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
+
+ // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
+ // way to handle them is to just prepend 'http:' and strip the protocol out later
+ $wasRelative = substr( $url, 0, 2 ) == '//';
+ if ( $wasRelative ) {
+ $url = "http:$url";
+ }
+ wfSuppressWarnings();
+ $bits = parse_url( $url );
+ wfRestoreWarnings();
+ // parse_url() returns an array without scheme for some invalid URLs, e.g.
+ // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
+ if ( !$bits ||
+ !isset( $bits['scheme'] ) && strpos( $url, "://" ) !== false ) {
+ wfDebug( __METHOD__ . ": Invalid URL: $url" );
+ return false;
+ } else {
+ $scheme = isset( $bits['scheme'] ) ? $bits['scheme'] : null;
+ }
+
+ // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
+ if ( in_array( $scheme . '://', $wgUrlProtocols ) ) {
+ $bits['delimiter'] = '://';
+ } elseif ( !is_null( $scheme ) && !in_array( $scheme . ':', $wgUrlProtocols ) ) {
+ wfDebug( __METHOD__ . ": Invalid scheme in URL: $scheme" );
+ return false;
+ } elseif( !is_null( $scheme ) ) {
+ if( !in_array( $scheme . ':', $wgUrlProtocols ) ) {
+ // For URLs that don't have a scheme, but do have a user:password, parse_url
+ // detects the user as the scheme.
+ unset( $bits['scheme'] );
+ $bits['user'] = $scheme;
+ } else {
+ $bits['delimiter'] = ':';
+ // parse_url detects for news: and mailto: the host part of an url as path
+ // We have to correct this wrong detection
+ if ( isset( $bits['path'] ) ) {
+ $bits['host'] = $bits['path'];
+ $bits['path'] = '';
+ }
+ }
+ }
+
+ /* Provide an empty host for eg. file:/// urls (see bug 28627) */
+ if ( !isset( $bits['host'] ) && $scheme == "file" ) {
+ $bits['host'] = '';
+
+ /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
+ if ( isset( $bits['path'] ) && substr( $bits['path'], 0, 1 ) !== '/' ) {
+ $bits['path'] = '/' . $bits['path'];
+ }
+ }
+
+ // If the URL was protocol-relative, fix scheme and delimiter
+ if ( $wasRelative ) {
+ $bits['scheme'] = '';
+ $bits['delimiter'] = '//';
+ }
+ return $bits;
+ }
+
+ /**
+ *
+ * @param $uri mixed URI string or array
+ */
+ public function __construct( $uri ) {
+ $this->components = array();
+ $this->setUri( $uri );
+ }
+
+ /**
+ * Set the Uri to the value of some other URI.
+ *
+ * @param $uri mixed URI string or array
+ */
+ public function setUri( $uri ) {
+ if ( is_string( $uri ) ) {
+ $parsed = self::parseUri( $uri );
+ if( $parsed === false ) {
+ return false;
+ }
+ $this->setComponents( $parsed );
+ } elseif ( is_array( $uri ) ) {
+ $this->setComponents( $uri );
+ } elseif ( $uri instanceof Uri ) {
+ $this->setComponents( $uri->getComponents() );
+ } else {
+ throw new MWException( __METHOD__ . ': $uri is not of a valid type.' );
+ }
+ }
+
+ /**
+ * Set the components of this array.
+ * Will output warnings when invalid components or aliases are found.
+ *
+ * @param $components Array The components to set on this Uri.
+ */
+ public function setComponents( array $components ) {
+ foreach ( $components as $name => $value ) {
+ if ( isset( self::$componentAliases[$name] ) ) {
+ $canonical = self::$componentAliases[$name];
+ wfDebug( __METHOD__ . ": Converting alias $name to canonical $canonical." );
+ $components[$canonical] = $value;
+ unset( $components[$name] );
+ } elseif ( !in_array( $name, self::$validComponents ) ) {
+ throw new MWException( __METHOD__ . ": $name is not a valid component." );
+ }
+ }
+
+ $this->components = $components;
+ }
+
+ /**
+ * Return the components for this Uri
+ * @return Array
+ */
+ public function getComponents() {
+ return $this->components;
+ }
+
+ /**
+ * Return the value of a specific component
+ *
+ * @param $name string The name of the component to return
+ * @param string|null
+ */
+ public function getComponent( $name ) {
+ if ( isset( self::$componentAliases[$name] ) ) {
+ // Component is an alias. Get the actual name.
+ $alias = $name;
+ $name = self::$componentAliases[$name];
+ wfDebug( __METHOD__ . ": Converting alias $alias to canonical $name." );
+ }
+
+ if( !in_array( $name, self::$validComponents ) ) {
+ // Component is invalid
+ throw new MWException( __METHOD__ . ": $name is not a valid component." );
+ } elseif( !empty( $this->components[$name] ) ) {
+ // Component is valid and has a value.
+ return $this->components[$name];
+ } else {
+ // Component is empty
+ return null;
+ }
+ }
+
+ /**
+ * Set a component for this Uri
+ * @param $name string The name of the component to set
+ * @param $value string|null The value to set
+ */
+ public function setComponent( $name, $value ) {
+ if ( isset( self::$componentAliases[$name] ) ) {
+ $alias = $name;
+ $name = self::$componentAliases[$name];
+ wfDebug( __METHOD__ . ": Converting alias $alias to canonical $name." );
+ } elseif ( !in_array( $name, self::$validComponents ) ) {
+ throw new MWException( __METHOD__ . ": $name is not a valid component." );
+ }
+ $this->components[$name] = $value;
+ }
+
+ public function getProtocol() { return $this->getComponent( 'scheme' ); }
+ public function getUser() { return $this->getComponent( 'user' ); }
+ public function getPassword() { return $this->getComponent( 'pass' ); }
+ public function getHost() { return $this->getComponent( 'host' ); }
+ public function getPort() { return $this->getComponent( 'port' ); }
+ public function getPath() { return $this->getComponent( 'path' ); }
+ public function getQueryString() { return $this->getComponent( 'query' ); }
+ public function getFragment() { return $this->getComponent( 'fragment' ); }
+
+ public function setProtocol( $scheme ) { $this->setComponent( 'scheme', $scheme ); }
+ public function setUser( $user ) { $this->setComponent( 'user', $user ); }
+ public function setPassword( $pass ) { $this->setComponent( 'pass', $pass ); }
+ public function setHost( $host ) { $this->setComponent( 'host', $host ); }
+ public function setPort( $port ) { $this->setComponent( 'port', $port ); }
+ public function setPath( $path ) { $this->setComponent( 'path', $path ); }
+ public function setFragment( $fragment ) { $this->setComponent( 'fragment', $fragment ); }
+
+ /**
+ * Gets the protocol-authority delimiter of a URI (:// or //).
+ * @return string|null
+ */
+ public function getDelimiter() {
+ $delimiter = $this->getComponent( 'delimiter' );
+ if ( $delimiter ) {
+ // A specific delimiter is set, so return it.
+ return $delimiter;
+ }
+ if ( $this->getAuthority() && $this->getProtocol() ) {
+ // If the URI has a protocol and a body (i.e., some sort of host, etc.)
+ // the default delimiter is "://", e.g., "http://test.com".
+ return '://';
+ }
+ return null;
+ }
+
+ /**
+ * Gets query portion of a URI in array format.
+ * @return string
+ */
+ public function getQuery() {
+ return wfCgiToArray( $this->getQueryString() );
+ }
+
+ /**
+ * Gets query portion of a URI.
+ * @param string|array $query
+ */
+ public function setQuery( $query ) {
+ if ( is_array( $query ) ) {
+ $query = wfArrayToCGI( $query );
+ }
+ $this->setComponent( 'query', $query );
+ }
+
+ /**
+ * Extend the query -- supply query parameters to override or add to ours
+ * @param Array|string $parameters query parameters to override or add
+ * @return Uri this URI object
+ */
+ public function extendQuery( $parameters ) {
+ if ( !is_array( $parameters ) ) {
+ $parameters = wfCgiToArray( $parameters );
+ }
+
+ $query = $this->getQuery();
+ foreach( $parameters as $key => $value ) {
+ $query[$key] = $value;
+ }
+
+ $this->setQuery( $query );
+ return $this;
+ }
+
+ /**
+ * Returns user and password portion of a URI.
+ * @return string
+ */
+ public function getUserInfo() {
+ $user = $this->getComponent( 'user' );
+ $pass = $this->getComponent( 'pass' );
+ return $pass ? "$user:$pass" : $user;
+ }
+
+ /**
+ * Gets host and port portion of a URI.
+ * @return string
+ */
+ public function getHostPort() {
+ $host = $this->getComponent( 'host' );
+ $port = $this->getComponent( 'port' );
+ return $port ? "$host:$port" : $host;
+ }
+
+ /**
+ * Returns the userInfo and host and port portion of the URI.
+ * In most real-world URLs, this is simply the hostname, but it is more general.
+ * @return string
+ */
+ public function getAuthority() {
+ $userinfo = $this->getUserInfo();
+ $hostinfo = $this->getHostPort();
+ return $userinfo ? "$userinfo@$hostinfo" : $hostinfo;
+ }
+
+ /**
+ * Returns everything after the authority section of the URI
+ * @return String
+ */
+ public function getRelativePath() {
+ $path = $this->getComponent( 'path' );
+ $query = $this->getComponent( 'query' );
+ $fragment = $this->getComponent( 'fragment' );
+
+ $retval = $path;
+ if( $query ) {
+ $retval .= "?$query";
+ }
+ if( $fragment ) {
+ $retval .= "#$fragment";
+ }
+ return $retval;
+ }
+
+ /**
+ * Gets the entire URI string. May not be precisely the same as input due to order of query arguments.
+ * @return String the URI string
+ */
+ public function toString() {
+ return $this->getComponent( 'scheme' ) . $this->getDelimiter() . $this->getAuthority() . $this->getRelativePath();
+ }
+
+ /**
+ * Gets the entire URI string. May not be precisely the same as input due to order of query arguments.
+ * @return String the URI string
+ */
+ public function __toString() {
+ return $this->toString();
+ }
+
+}
'deleterevision',
'edit',
'editinterface',
+ 'editprotected',
'editusercssjs', #deprecated
'editusercss',
'edituserjs',
'nominornewtalk',
'noratelimit',
'override-export-depth',
+ 'passwordreset',
'patrol',
+ 'patrolmarks',
'protect',
'proxyunbannable',
'purge',
'read',
'reupload',
+ 'reupload-own',
'reupload-shared',
'rollback',
'sendemail',
* @return String|bool The corresponding username
*/
public static function whoIs( $id ) {
- $dbr = wfGetDB( DB_SLAVE );
- return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), __METHOD__ );
+ return UserCache::singleton()->getProp( $id, 'name' );
}
/**
* @return String|bool The corresponding user's real name
*/
public static function whoIsReal( $id ) {
- $dbr = wfGetDB( DB_SLAVE );
- return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), __METHOD__ );
+ return UserCache::singleton()->getProp( $id, 'real_name' );
}
/**
$ipList = gethostbynamel( $host );
if( $ipList ) {
- wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
+ wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
$found = true;
break;
} else {
- wfDebug( "Requested $host, not found in $base.\n" );
+ wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base.\n" );
}
}
}
*/
public function getNewMessageLinks() {
$talks = array();
- if( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) )
+ if( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) ) {
return $talks;
-
- if( !$this->getNewtalk() )
+ } elseif( !$this->getNewtalk() ) {
return array();
- $up = $this->getUserPage();
- $utp = $up->getTalkPage();
- return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL() ) );
+ }
+ $utp = $this->getTalkPage();
+ $dbr = wfGetDB( DB_SLAVE );
+ // Get the "last viewed rev" timestamp from the oldest message notification
+ $timestamp = $dbr->selectField( 'user_newtalk',
+ 'MIN(user_last_timestamp)',
+ $this->isAnon() ? array( 'user_ip' => $this->getName() ) : array( 'user_id' => $this->getID() ),
+ __METHOD__ );
+ $rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
+ return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ) );
}
/**
* Add or update the new messages flag
* @param $field String 'user_ip' for anonymous users, 'user_id' otherwise
* @param $id String|Int User's IP address for anonymous users, User ID otherwise
+ * @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null.
* @return Bool True if successful, false otherwise
*/
- protected function updateNewtalk( $field, $id ) {
+ protected function updateNewtalk( $field, $id, $curRev = null ) {
+ // Get timestamp of the talk page revision prior to the current one
+ $prevRev = $curRev ? $curRev->getPrevious() : false;
+ $ts = $prevRev ? $prevRev->getTimestamp() : null;
+ // Mark the user as having new messages since this revision
$dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'user_newtalk',
- array( $field => $id ),
+ array( $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ),
__METHOD__,
'IGNORE' );
if ( $dbw->affectedRows() ) {
/**
* Update the 'You have new messages!' status.
* @param $val Bool Whether the user has new messages
+ * @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null or !$val.
*/
- public function setNewtalk( $val ) {
+ public function setNewtalk( $val, $curRev = null ) {
if( wfReadOnly() ) {
return;
}
global $wgMemc;
if( $val ) {
- $changed = $this->updateNewtalk( $field, $id );
+ $changed = $this->updateNewtalk( $field, $id, $curRev );
} else {
$changed = $this->deleteNewtalk( $field, $id );
}
$force = 'force';
}
- $wi = WatchedItem::fromUserTitle( $this, $title );
- $wi->resetNotificationTimestamp( $force );
+ $this->getWatchedItem( $title )->resetNotificationTimestamp( $force );
}
/**
if ( !preg_match( '!^https?://!', $url ) ) {
$url = 'http://unused' . $url;
}
+ wfSuppressWarnings();
$a = parse_url( $url );
+ wfRestoreWarnings();
if( $a ) {
$path = isset( $a['path'] ) ? $a['path'] : '';
public function getCheck( $name ) {
# Checkboxes and buttons are only present when clicked
# Presence connotes truth, abscense false
- $val = $this->getVal( $name, null );
- return isset( $val );
+ return $this->getVal( $name, null ) !== null;
}
/**
return $_GET;
}
+ /**
+ * Get the HTTP method used for this request.
+ *
+ * @return String
+ */
+ public function getMethod() {
+ return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+ }
+
/**
* Returns true if the present request was reached by a POST operation,
* false otherwise (GET, HEAD, or command-line).
* @return Boolean
*/
public function wasPosted() {
- return isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] == 'POST';
+ return $this->getMethod() == 'POST';
}
/**
}
}
+ public function getMethod() {
+ return $this->wasPosted ? 'POST' : 'GET';
+ }
+
/**
* @return bool
*/
}
$this->context = $context;
- $this->context->setTitle( $this->parseTitle() );
}
/**
$wiki = WikiMap::getWiki( $wikiID );
if ( $wiki ) {
- return $wiki->getUrl( $page );
+ return $wiki->getFullUrl( $page );
}
return false;
*
* @internal documentation reviewed 15 Mar 2010
*/
-class WikiPage extends Page {
+class WikiPage extends Page implements IDBAccessObject {
// Constants for $mDataLoadedFrom and related
- /**
- * Data has not been loaded yet (or the object was cleared)
- */
- const DATA_NOT_LOADED = 0;
-
- /**
- * Data has been loaded from a slave database
- */
- const DATA_FROM_SLAVE = 1;
-
- /**
- * Data has been loaded from the master database
- */
- const DATA_FROM_MASTER = 2;
-
- /**
- * Data has been loaded from the master database using FOR UPDATE
- */
- const DATA_FOR_UPDATE = 3;
-
/**
* @var Title
*/
/**@}}*/
/**
- * @var int; one of the DATA_* constants
+ * @var int; one of the READ_* constants
*/
- protected $mDataLoadedFrom = self::DATA_NOT_LOADED;
+ protected $mDataLoadedFrom = self::READ_NONE;
/**
* @var Title
*
* @param $id Int article ID to load
* @param $from string|int one of the following values:
- * - "fromdb" or self::DATA_FROM_SLAVE to select from a slave database
- * - "fromdbmaster" or self::DATA_FROM_MASTER to select from the master database
+ * - "fromdb" or WikiPage::READ_NORMAL to select from a slave database
+ * - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database
*
* @return WikiPage|null
*/
public static function newFromID( $id, $from = 'fromdb' ) {
$from = self::convertSelectType( $from );
- $db = wfGetDB( $from === self::DATA_FROM_MASTER ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
$row = $db->selectRow( 'page', self::selectFields(), array( 'page_id' => $id ), __METHOD__ );
if ( !$row ) {
return null;
* @param $row object: database row containing at least fields returned
* by selectFields().
* @param $from string|int: source of $data:
- * - "fromdb" or self::DATA_FROM_SLAVE: from a slave DB
- * - "fromdbmaster" or self::DATA_FROM_MASTER: from the master DB
- * - "forupdate" or self::DATA_FOR_UPDATE: from the master DB using SELECT FOR UPDATE
+ * - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
+ * - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
+ * - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
* @return WikiPage
*/
public static function newFromRow( $row, $from = 'fromdb' ) {
}
/**
- * Convert 'fromdb', 'fromdbmaster' and 'forupdate' to DATA_* constants.
+ * Convert 'fromdb', 'fromdbmaster' and 'forupdate' to READ_* constants.
*
* @param $type object|string|int
* @return mixed
private static function convertSelectType( $type ) {
switch ( $type ) {
case 'fromdb':
- return self::DATA_FROM_SLAVE;
+ return self::READ_NORMAL;
case 'fromdbmaster':
- return self::DATA_FROM_MASTER;
+ return self::READ_LATEST;
case 'forupdate':
- return self::DATA_FOR_UPDATE;
+ return self::READ_LOCKING;
default:
// It may already be an integer or whatever else
return $type;
*/
public function clear() {
$this->mDataLoaded = false;
- $this->mDataLoadedFrom = self::DATA_NOT_LOADED;
+ $this->mDataLoadedFrom = self::READ_NONE;
$this->clearCacheFields();
}
*
* @param $from object|string|int One of the following:
* - A DB query result object
- * - "fromdb" or self::DATA_FROM_SLAVE to get from a slave DB
- * - "fromdbmaster" or self::DATA_FROM_MASTER to get from the master DB
- * - "forupdate" or self::DATA_FOR_UPDATE to get from the master DB using SELECT FOR UPDATE
+ * - "fromdb" or WikiPage::READ_NORMAL to get from a slave DB
+ * - "fromdbmaster" or WikiPage::READ_LATEST to get from the master DB
+ * - "forupdate" or WikiPage::READ_LOCKING to get from the master DB using SELECT FOR UPDATE
*
* @return void
*/
return;
}
- if ( $from === self::DATA_FOR_UPDATE ) {
+ if ( $from === self::READ_LOCKING ) {
$data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle, array( 'FOR UPDATE' ) );
- } elseif ( $from === self::DATA_FROM_MASTER ) {
+ } elseif ( $from === self::READ_LATEST ) {
$data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
- } elseif ( $from === self::DATA_FROM_SLAVE ) {
+ } elseif ( $from === self::READ_NORMAL ) {
$data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
# Use a "last rev inserted" timestamp key to dimish the issue of slave lag.
# Note that DB also stores the master position in the session and checks it.
$touched = $this->getCachedLastEditTime();
if ( $touched ) { // key set
if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
- $from = self::DATA_FROM_MASTER;
+ $from = self::READ_LATEST;
$data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
}
}
} else {
// No idea from where the caller got this data, assume slave database.
$data = $from;
- $from = self::DATA_FROM_SLAVE;
+ $from = self::READ_NORMAL;
}
$this->loadFromRow( $data, $from );
* @param $data object: database row containing at least fields returned
* by selectFields()
* @param $from string|int One of the following:
- * - "fromdb" or self::DATA_FROM_SLAVE if the data comes from a slave DB
- * - "fromdbmaster" or self::DATA_FROM_MASTER if the data comes from the master DB
- * - "forupdate" or self::DATA_FOR_UPDATE if the data comes from from
+ * - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
+ * - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
+ * - "forupdate" or WikiPage::READ_LOCKING if the data comes from from
* the master DB using SELECT FOR UPDATE
*/
public function loadFromRow( $data, $from ) {
// also gets the revision row FOR UPDATE; otherwise, it may not find it since a page row
// UPDATE and revision row INSERT by S2 may have happened after the first S1 SELECT.
// http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read.
- $flags = ( $this->mDataLoadedFrom == self::DATA_FOR_UPDATE ) ? Revision::LOCKING_READ : 0;
+ $flags = ( $this->mDataLoadedFrom == self::READ_LOCKING ) ? Revision::READ_LOCKING : 0;
$revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
if ( $revision ) { // sanity
$this->setLastEdit( $revision );
wfDebug( __METHOD__ . ": invalid username\n" );
} elseif ( User::isIP( $shortTitle ) ) {
// An anonymous user
- $other->setNewtalk( true );
+ $other->setNewtalk( true, $revision );
} elseif ( $other->isLoggedIn() ) {
- $other->setNewtalk( true );
+ $other->setNewtalk( true, $revision );
} else {
wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
}
* Deletes the article with database consistency, writes logs, purges caches
*
* @param $reason string delete reason for deletion log
- * @param $suppress int bitfield
- * Revision::DELETED_TEXT
- * Revision::DELETED_COMMENT
- * Revision::DELETED_USER
- * Revision::DELETED_RESTRICTED
+ * @param $suppress boolean suppress all revisions and log the deletion in
+ * the suppression log instead of the deletion log
* @param $id int article ID
* @param $commit boolean defaults to true, triggers transaction end
* @param &$error Array of errors to append to
* Back-end article deletion
* Deletes the article with database consistency, writes logs, purges caches
*
+ * @since 1.19
+ *
* @param $reason string delete reason for deletion log
- * @param $suppress int bitfield
- * Revision::DELETED_TEXT
- * Revision::DELETED_COMMENT
- * Revision::DELETED_USER
- * Revision::DELETED_RESTRICTED
- * @param $id int article ID
+ * @param $suppress boolean suppress all revisions and log the deletion in
+ * the suppression log instead of the deletion log
* @param $commit boolean defaults to true, triggers transaction end
* @param &$error Array of errors to append to
* @param $user User The deleting user
$title = $this->getTitle()->getSubjectPage();
- $pageInfo = self::pageCountInfo( $title );
- $talkInfo = self::pageCountInfo( $title->getTalkPage() );
+ $userCanViewUnwatchedPages = $this->getUser()->isAllowed( 'unwatchedpages' );
- return Html::rawElement( 'table', array( 'class' => 'wikitable mw-page-info' ),
+ $pageInfo = self::pageCountInfo( $title, $userCanViewUnwatchedPages, $wgDisableCounters );
+ $talkInfo = self::pageCountInfo( $title->getTalkPage(), $userCanViewUnwatchedPages, $wgDisableCounters );
+
+ $lang = $this->getLanguage();
+
+ $content =
Html::rawElement( 'tr', array(),
Html::element( 'th', array(), '' ) .
- Html::element( 'th', array(), $this->msg( 'pageinfo-subjectpage' )->text() ) .
- Html::element( 'th', array(), $this->msg( 'pageinfo-talkpage' )->text() )
+ Html::element( 'th', array(), $this->msg( 'pageinfo-subjectpage' )->text() ) .
+ Html::element( 'th', array(), $this->msg( 'pageinfo-talkpage' )->text() )
) .
Html::rawElement( 'tr', array(),
Html::element( 'th', array( 'colspan' => 3 ), $this->msg( 'pageinfo-header-edits' )->text() )
) .
Html::rawElement( 'tr', array(),
Html::element( 'td', array(), $this->msg( 'pageinfo-edits' )->text() ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $pageInfo['edits'] ) ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $talkInfo['edits'] ) )
+ Html::element( 'td', array(), $lang->formatNum( $pageInfo['edits'] ) ) .
+ Html::element( 'td', array(), $lang->formatNum( $talkInfo['edits'] ) )
) .
Html::rawElement( 'tr', array(),
Html::element( 'td', array(), $this->msg( 'pageinfo-authors' )->text() ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $pageInfo['authors'] ) ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $talkInfo['authors'] ) )
+ Html::element( 'td', array(), $lang->formatNum( $pageInfo['authors'] ) ) .
+ Html::element( 'td', array(), $lang->formatNum( $talkInfo['authors'] ) )
+ );
+
+ if ( $userCanViewUnwatchedPages ) {
+ $content .= Html::rawElement( 'tr', array(),
+ Html::element( 'th', array( 'colspan' => 3 ), $this->msg( 'pageinfo-header-watchlist' )->text() )
) .
- ( !$this->getUser()->isAllowed( 'unwatchedpages' ) ? '' :
- Html::rawElement( 'tr', array(),
- Html::element( 'th', array( 'colspan' => 3 ), $this->msg( 'pageinfo-header-watchlist' )->text() )
- ) .
Html::rawElement( 'tr', array(),
Html::element( 'td', array(), $this->msg( 'pageinfo-watchers' )->text() ) .
- Html::element( 'td', array( 'colspan' => 2 ), $this->getLanguage()->formatNum( $pageInfo['watchers'] ) )
- )
- ).
- ( $wgDisableCounters ? '' :
- Html::rawElement( 'tr', array(),
- Html::element( 'th', array( 'colspan' => 3 ), $this->msg( 'pageinfo-header-views' )->text() )
- ) .
+ Html::element( 'td', array( 'colspan' => 2 ), $lang->formatNum( $pageInfo['watchers'] ) )
+ );
+ }
+
+ if ( !$wgDisableCounters ) {
+ $content .= Html::rawElement( 'tr', array(),
+ Html::element( 'th', array( 'colspan' => 3 ), $this->msg( 'pageinfo-header-views' )->text() )
+ ) .
Html::rawElement( 'tr', array(),
Html::element( 'td', array(), $this->msg( 'pageinfo-views' )->text() ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $pageInfo['views'] ) ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( $talkInfo['views'] ) )
+ Html::element( 'td', array(), $lang->formatNum( $pageInfo['views'] ) ) .
+ Html::element( 'td', array(), $lang->formatNum( $talkInfo['views'] ) )
) .
Html::rawElement( 'tr', array(),
Html::element( 'td', array(), $this->msg( 'pageinfo-viewsperedit' )->text() ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( sprintf( '%.2f', $pageInfo['edits'] ? $pageInfo['views'] / $pageInfo['edits'] : 0 ) ) ) .
- Html::element( 'td', array(), $this->getLanguage()->formatNum( sprintf( '%.2f', $talkInfo['edits'] ? $talkInfo['views'] / $talkInfo['edits'] : 0 ) ) )
- )
- )
- );
+ Html::element( 'td', array(), $lang->formatNum( sprintf( '%.2f', $pageInfo['edits'] ? $pageInfo['views'] / $pageInfo['edits'] : 0 ) ) ) .
+ Html::element( 'td', array(), $lang->formatNum( sprintf( '%.2f', $talkInfo['edits'] ? $talkInfo['views'] / $talkInfo['edits'] : 0 ) ) )
+ );
+ }
+ return Html::rawElement( 'table', array( 'class' => 'wikitable mw-page-info' ), $content );
}
/**
* on a given page. If page does not exist, returns false.
*
* @param $title Title object
- * @return mixed array or boolean false
+ * @param $canViewUnwatched bool
+ * @param $disableCounter bool
+ * @return array
*/
- public static function pageCountInfo( $title ) {
+ public static function pageCountInfo( $title, $canViewUnwatched, $disableCounter ) {
wfProfileIn( __METHOD__ );
$id = $title->getArticleID();
$dbr = wfGetDB( DB_SLAVE );
- $watchers = (int)$dbr->selectField(
- 'watchlist',
- 'COUNT(*)',
- array(
- 'wl_namespace' => $title->getNamespace(),
- 'wl_title' => $title->getDBkey(),
- ),
- __METHOD__
- );
+ $result = array();
+ if ( $canViewUnwatched ) {
+ $watchers = (int)$dbr->selectField(
+ 'watchlist',
+ 'COUNT(*)',
+ array(
+ 'wl_namespace' => $title->getNamespace(),
+ 'wl_title' => $title->getDBkey(),
+ ),
+ __METHOD__
+ );
+ $result['watchers'] = $watchers;
+ }
$edits = (int)$dbr->selectField(
'revision',
array( 'rev_page' => $id ),
__METHOD__
);
+ $result['edits'] = $edits;
$authors = (int)$dbr->selectField(
'revision',
array( 'rev_page' => $id ),
__METHOD__
);
- $result = array( 'watchers' => $watchers, 'edits' => $edits,
- 'authors' => $authors );
+ $result['authors'] = $authors;
- global $wgDisableCounters;
- if ( !$wgDisableCounters ) {
+ if ( !$disableCounter ) {
$views = (int)$dbr->selectField(
'page',
'page_counter',
$type = isset( $paramSettings[self::PARAM_TYPE] ) ? $paramSettings[self::PARAM_TYPE] : null;
if ( isset( $type ) ) {
- if ( isset( $paramSettings[self::PARAM_ISMULTI] ) && $paramSettings[self::PARAM_ISMULTI] ) {
+ $hintPipeSeparated = true;
+ $multi = isset( $paramSettings[self::PARAM_ISMULTI] ) ? $paramSettings[self::PARAM_ISMULTI] : false;
+ if ( $multi ) {
$prompt = 'Values (separate with \'|\'): ';
} else {
$prompt = 'One value: ';
if ( is_array( $type ) ) {
$choices = array();
- $nothingPrompt = false;
+ $nothingPrompt = '';
foreach ( $type as $t ) {
if ( $t === '' ) {
$nothingPrompt = 'Can be empty, or ';
$desc .= $paramPrefix . $nothingPrompt . $prompt;
$choicesstring = implode( ', ', $choices );
$desc .= wordwrap( $choicesstring, 100, $descWordwrap );
+ $hintPipeSeparated = false;
} else {
switch ( $type ) {
case 'namespace':
$desc .= $paramPrefix . $prompt;
$desc .= wordwrap( implode( ', ', MWNamespace::getValidNamespaces() ),
100, $descWordwrap );
+ $hintPipeSeparated = false;
break;
case 'limit':
$desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]}";
$desc .= ' allowed';
break;
case 'integer':
+ $s = $multi ? 's' : '';
$hasMin = isset( $paramSettings[self::PARAM_MIN] );
$hasMax = isset( $paramSettings[self::PARAM_MAX] );
if ( $hasMin || $hasMax ) {
if ( !$hasMax ) {
- $intRangeStr = "The value must be no less than {$paramSettings[self::PARAM_MIN]}";
+ $intRangeStr = "The value$s must be no less than {$paramSettings[self::PARAM_MIN]}";
} elseif ( !$hasMin ) {
- $intRangeStr = "The value must be no more than {$paramSettings[self::PARAM_MAX]}";
+ $intRangeStr = "The value$s must be no more than {$paramSettings[self::PARAM_MAX]}";
} else {
- $intRangeStr = "The value must be between {$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}";
+ $intRangeStr = "The value$s must be between {$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}";
}
$desc .= $paramPrefix . $intRangeStr;
}
}
- if ( isset( $paramSettings[self::PARAM_ISMULTI] ) && $paramSettings[self::PARAM_ISMULTI] ) {
- $isArray = is_array( $type );
+ if ( $multi ) {
+ if ( $hintPipeSeparated ) {
+ $desc .= $paramPrefix . "Separate values with '|'";
+ }
+ $isArray = is_array( $type );
if ( !$isArray
|| $isArray && count( $type ) > self::LIMIT_SML1 ) {
$desc .= $paramPrefix . "Maximum number of values " .
'nodeleteablefile' => array( 'code' => 'nodeleteablefile', 'info' => 'No such old version of the file' ),
'fileexists-forbidden' => array( 'code' => 'fileexists-forbidden', 'info' => 'A file with name "$1" already exists, and cannot be overwritten.' ),
'fileexists-shared-forbidden' => array( 'code' => 'fileexists-shared-forbidden', 'info' => 'A file with name "$1" already exists in the shared file repository, and cannot be overwritten.' ),
+ 'filerevert-badversion' => array( 'code' => 'filerevert-badversion', 'info' => 'There is no previous local version of this file with the provided timestamp.' ),
// ApiEditPage messages
'noimageredirect-anon' => array( 'code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects" ),
'edit-already-exists' => array( 'code' => 'edit-already-exists', 'info' => "It seems the page you tried to create already exist" ),
// uploadMsgs
- 'invalid-session-key' => array( 'code' => 'invalid-session-key', 'info' => 'Not a valid session key' ),
+ 'invalid-file-key' => array( 'code' => 'invalid-file-key', 'info' => 'Not a valid file key' ),
'nouploadmodule' => array( 'code' => 'nouploadmodule', 'info' => 'No upload module set' ),
'uploaddisabled' => array( 'code' => 'uploaddisabled', 'info' => 'Uploads are not enabled. Make sure $wgEnableUploads is set to true in LocalSettings.php and the PHP ini setting file_uploads is true' ),
'copyuploaddisabled' => array( 'code' => 'copyuploaddisabled', 'info' => 'Uploads by URL is not enabled. Make sure $wgAllowCopyUploads is set to true in LocalSettings.php.' ),
}
/**
- * Returns whether this module requires a Token to execute
+ * Returns whether this module requires a token to execute
+ * It is used to show possible errors in action=paraminfo
+ * see bug 25248
* @return bool
*/
public function needsToken() {
}
/**
- * Returns the token salt if there is one, '' if the module doesn't require a salt, else false if the module doesn't need a token
- * @return bool|string
+ * Returns the token salt if there is one,
+ * '' if the module doesn't require a salt,
+ * else false if the module doesn't need a token
+ * You have also to override needsToken()
+ * Value is passed to User::getEditToken
+ * @return bool|string|array
*/
public function getTokenSalt() {
return false;
if ( !$oldfile->exists() || !$oldfile->isLocal() || $oldfile->getRedirected() ) {
return array( array( 'nodeleteablefile' ) );
}
- } else {
- $oldfile = false;
}
if ( is_null( $reason ) ) { // Log and RC don't like null reasons
* @param $user User The user to check.
*/
protected function checkPermissions( $user ) {
+ $title = $this->file->getTitle();
$permissionErrors = array_merge(
- $this->file->getTitle()->getUserPermissionsErrors( 'edit' , $user ),
- $this->file->getTitle()->getUserPermissionsErrors( 'upload' , $user )
+ $title->getUserPermissionsErrors( 'edit' , $user ),
+ $title->getUserPermissionsErrors( 'upload' , $user )
);
if ( $permissionErrors ) {
if ( is_null( $title ) ) {
$this->dieUsageMsg( array( 'invalidtitle', $this->params['filename'] ) );
}
+ $localRepo = RepoGroup::singleton()->getLocalRepo();
+
// Check if the file really exists
- $this->file = wfLocalFile( $title );
+ $this->file = $localRepo->newFile( $title );
if ( !$this->file->exists() ) {
$this->dieUsageMsg( 'notanarticle' );
}
// Check if the archivename is valid for this file
$this->archiveName = $this->params['archivename'];
- $oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $this->archiveName );
+ $oldFile = $localRepo->newFromArchiveName( $title, $this->archiveName );
if ( !$oldFile->exists() ) {
$this->dieUsageMsg( 'filerevert-badversion' );
}
*/
public static function setResult( $result, $feed, $feedItems ) {
// Store output in the Result data.
- // This way we can check during execution if any error has occured
+ // This way we can check during execution if any error has occurred
// Disable size checking for this because we can't continue
// cleanly; size checking would cause more problems than it'd
// solve
}
$feed->outFooter();
} else {
- // Error has occured, print something useful
+ // Error has occurred, print something useful
ApiBase::dieDebug( __METHOD__, 'Invalid feed class/item' );
}
}
// Write modules
'purge' => 'ApiPurge',
+ 'setnotificationtimestamp' => 'ApiSetNotificationTimestamp',
'rollback' => 'ApiRollback',
'delete' => 'ApiDelete',
'undelete' => 'ApiUndelete',
// Reset and print just the error message
ob_clean();
- // If the error occured during printing, do a printer->profileOut()
+ // If the error occurred during printing, do a printer->profileOut()
$this->mPrinter->safeProfileOut();
$this->printResult( true );
}
*/
protected function handleCORS() {
global $wgCrossSiteAJAXdomains, $wgCrossSiteAJAXdomainExceptions;
- $response = $this->getRequest()->response();
+
$originParam = $this->getParameter( 'origin' ); // defaults to null
if ( $originParam === null ) {
// No origin parameter, nothing to do
return true;
}
+
+ $request = $this->getRequest();
+ $response = $request->response();
// Origin: header is a space-separated list of origins, check all of them
- $originHeader = isset( $_SERVER['HTTP_ORIGIN'] ) ? $_SERVER['HTTP_ORIGIN'] : '';
- $origins = explode( ' ', $originHeader );
+ $originHeader = $request->getHeader( 'Origin' );
+ if ( $originHeader === false ) {
+ $origins = array();
+ } else {
+ $origins = explode( ' ', $originHeader );
+ }
if ( !in_array( $originParam, $origins ) ) {
// origin parameter set but incorrect
// Send a 403 response
return array(
'token' => 'An options token previously obtained through the action=tokens',
'reset' => 'Resets all preferences to the site defaults',
- 'change' => 'Pipe-separated list of changes, formatted name=value (e.g. skin=vector), value cannot contain pipe characters',
+ 'change' => 'List of changes, formatted name=value (e.g. skin=vector), value cannot contain pipe characters',
'optionname' => 'A name of a option which should have an optionvalue set',
'optionvalue' => 'A value of the option specified by the optionname, can contain pipe characters',
);
public function getPossibleErrors() {
return array_merge( parent::getPossibleErrors(), array(
- array( 'notloggedin' ),
- array( 'nochanges' ),
+ array( 'code' => 'notloggedin', 'info' => 'Anonymous users cannot change preferences' ),
+ array( 'code' => 'nochanges', 'info' => 'No changes were requested' ),
) );
}
$result_array['properties'] = $this->formatProperties( $p_result->getProperties() );
}
+ if ( $params['generatexml'] ) {
+ $wgParser->startExternalParse( $titleObj, $popts, OT_PREPROCESS );
+ $dom = $wgParser->preprocessToDom( $this->text );
+ if ( is_callable( array( $dom, 'saveXML' ) ) ) {
+ $xml = $dom->saveXML();
+ } else {
+ $xml = $dom->__toString();
+ }
+ $result_array['parsetree'] = array();
+ $result->setContent( $result_array['parsetree'], $xml );
+ }
+
$result_mapping = array(
'redirects' => 'r',
'langlinks' => 'll',
'uselang' => null,
'section' => null,
'disablepp' => false,
+ 'generatexml' => false,
);
}
'uselang' => 'Which language to parse the request in',
'section' => 'Only retrieve the content of this section number',
'disablepp' => 'Disable the PP Report from the parser output',
+ 'generatexml' => 'Generate XML parse tree',
);
}
'title' => "Title of the page you want to (un)protect. Cannot be used together with {$p}pageid",
'pageid' => "ID of the page you want to (un)protect. Cannot be used together with {$p}title",
'token' => 'A protect token previously retrieved through prop=info',
- 'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
+ 'protections' => 'List of protection levels, formatted action=group (e.g. edit=sysop)',
'expiry' => array( 'Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.',
'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.' ),
'reason' => 'Reason for (un)protecting',
$psModule = new ApiPageSet( $this );
return array_merge(
parent::getPossibleErrors(),
- array( array( 'cantpurge' ), ),
$psModule->getPossibleErrors()
);
}
$this->mListModuleNames = array_keys( $this->mQueryListModules );
$this->mMetaModuleNames = array_keys( $this->mQueryMetaModules );
- $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' );
- $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' );
+ $this->makeGeneratorList( $this->mQueryPropModules );
+ $this->makeGeneratorList( $this->mQueryListModules );
}
/**
// Make sure the internal object is empty
// (just in case a sub-module decides to optimize during instantiation)
$this->mPageSet = null;
- $this->mAllowedGenerators = array(); // Will be repopulated
$querySeparator = str_repeat( '--- ', 12 );
$moduleSeparator = str_repeat( '*** ', 14 );
$msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' );
$msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n";
- // Perform the base call last because the $this->mAllowedGenerators
- // will be updated inside makeHelpMsgHelper()
// Use parent to make default message for the query module
$msg = parent::makeHelpMsg() . $msg;
$msg .= $msg2;
}
if ( $module instanceof ApiQueryGeneratorBase ) {
- $this->mAllowedGenerators[] = $moduleName;
$msg .= "Generator:\n This module may be used as a generator\n";
}
$moduleDescriptions[] = $msg;
return implode( "\n", $moduleDescriptions );
}
+ /**
+ * Adds any classes that are a subclass of ApiQueryGeneratorBase
+ * to the allowed generator list
+ * @param $moduleList array()
+ */
+ private function makeGeneratorList( $moduleList ) {
+ foreach( $moduleList as $moduleName => $moduleClass ) {
+ if ( is_subclass_of( $moduleClass, 'ApiQueryGeneratorBase' ) ) {
+ $this->mAllowedGenerators[] = $moduleName;
+ }
+ }
+ }
+
/**
* Override to add extra parameters from PageSet
* @return string
'start' => 'The timestamp to start enumerating from',
'end' => 'The timestamp to stop enumerating at',
'dir' => $this->getDirectionDescription( $p ),
- 'ids' => 'Pipe-separated list of block IDs to list (optional)',
- 'users' => 'Pipe-separated list of users to search for (optional)',
+ 'ids' => 'List of block IDs to list (optional)',
+ 'users' => 'List of users to search for (optional)',
'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.',
'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted' ),
'limit' => 'The maximum amount of blocks to list',
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
+ return array_merge( parent::getPossibleErrors(),
$this->getRequireOnlyOneParameterErrorMessages( array( 'users', 'ip' ) ),
- array( 'code' => 'cidrtoobroad', 'info' => 'CIDR ranges broader than /16 are not accepted' ),
- array( 'code' => 'param_user', 'info' => 'User parameter may not be empty' ),
- array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
- array( 'show' ),
- ) );
+ array(
+ array( 'code' => 'cidrtoobroad', 'info' => 'CIDR ranges broader than /16 are not accepted' ),
+ array( 'code' => 'param_user', 'info' => 'User parameter may not be empty' ),
+ array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
+ array( 'show' ),
+ )
+ );
}
public function getExamples() {
}
}
- $files = RepoGroup::singleton()->findFiles( array_keys( $images ) );
+ $filesToFind = array_keys( $images );
+ if( $params['localonly'] ) {
+ $files = RepoGroup::singleton()->getLocalRepo()->findFiles( $filesToFind );
+ } else {
+ $files = RepoGroup::singleton()->findFiles( $filesToFind );
+ }
$fit = true;
$count = 0;
}
// find all files with the hashes, result format is: array( hash => array( dup1, dup2 ), hash1 => ... )
- $filesBySha1s = RepoGroup::singleton()->findBySha1s( array_unique( array_values( $sha1s ) ) );
+ $filesToFindBySha1s = array_unique( array_values( $sha1s ) );
+ if( $params['localonly'] ) {
+ $filesBySha1s = RepoGroup::singleton()->getLocalRepo()->findBySha1s( $filesToFindBySha1s );
+ } else {
+ $filesBySha1s = RepoGroup::singleton()->findBySha1s( $filesToFindBySha1s );
+ }
// iterate over $images to handle continue param correct
foreach( $images as $image => $pageId ) {
'descending'
)
),
+ 'localonly' => false,
);
}
'limit' => 'How many duplicate files to return',
'continue' => 'When more results are available, use this to continue',
'dir' => 'The direction in which to list',
+ 'localonly' => 'Look only for files in the local repository',
);
}
}
$result = $this->getResult();
- $images = RepoGroup::singleton()->findFiles( $titles );
+ //search only inside the local repo
+ if( $params['localonly'] ) {
+ $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles );
+ } else {
+ $images = RepoGroup::singleton()->findFiles( $titles );
+ }
foreach ( $images as $img ) {
// Skip redirects
if ( $img->getOriginalTitle()->isRedirect() ) {
ApiBase::PARAM_TYPE => 'string',
),
'continue' => null,
+ 'localonly' => false,
);
}
'end' => 'Timestamp to stop listing at',
'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
"Defaults to '1' for backwards compatibility" ),
- 'continue' => 'If the query response includes a continue value, use it here to get another page of results'
+ 'continue' => 'If the query response includes a continue value, use it here to get another page of results',
+ 'localonly' => 'Look only for files in the local repository',
);
}
private $fld_protection = false, $fld_talkid = false,
$fld_subjectid = false, $fld_url = false,
- $fld_readable = false, $fld_watched = false,
+ $fld_readable = false, $fld_watched = false, $fld_notificationtimestamp = false,
$fld_preload = false, $fld_displaytitle = false;
private $params, $titles, $missing, $everything, $pageCounter;
private $pageRestrictions, $pageIsRedir, $pageIsNew, $pageTouched,
$pageLatest, $pageLength;
- private $protections, $watched, $talkids, $subjectids, $displaytitles;
+ private $protections, $watched, $notificationtimestamps, $talkids, $subjectids, $displaytitles;
private $tokenFunctions;
$prop = array_flip( $this->params['prop'] );
$this->fld_protection = isset( $prop['protection'] );
$this->fld_watched = isset( $prop['watched'] );
+ $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] );
$this->fld_talkid = isset( $prop['talkid'] );
$this->fld_subjectid = isset( $prop['subjectid'] );
$this->fld_url = isset( $prop['url'] );
$this->getProtectionInfo();
}
- if ( $this->fld_watched ) {
+ if ( $this->fld_watched || $this->fld_notificationtimestamp ) {
$this->getWatchedInfo();
}
$pageInfo['watched'] = '';
}
+ if ( $this->fld_notificationtimestamp ) {
+ $pageInfo['notificationtimestamp'] = '';
+ if ( isset( $this->notificationtimestamps[$ns][$dbkey] ) ) {
+ $pageInfo['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $this->notificationtimestamps[$ns][$dbkey] );
+ }
+ }
+
if ( $this->fld_talkid && isset( $this->talkids[$ns][$dbkey] ) ) {
$pageInfo['talkid'] = $this->talkids[$ns][$dbkey];
}
/**
* Get information about watched status and put it in $this->watched
+ * and $this->notificationtimestamps
*/
private function getWatchedInfo() {
$user = $this->getUser();
}
$this->watched = array();
+ $this->notificationtimestamps = array();
$db = $this->getDB();
$lb = new LinkBatch( $this->everything );
$this->resetQueryParams();
$this->addTables( array( 'watchlist' ) );
$this->addFields( array( 'wl_title', 'wl_namespace' ) );
+ $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp );
$this->addWhere( array(
$lb->constructSet( 'wl', $db ),
'wl_user' => $user->getID()
$res = $this->select( __METHOD__ );
foreach ( $res as $row ) {
- $this->watched[$row->wl_namespace][$row->wl_title] = true;
+ if ( $this->fld_watched ) {
+ $this->watched[$row->wl_namespace][$row->wl_title] = true;
+ }
+ if ( $this->fld_notificationtimestamp ) {
+ $this->notificationtimestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
+ }
}
}
'protection',
'talkid',
'watched', # private
+ 'notificationtimestamp', # private
'subjectid',
'url',
'readable', # private
return array(
'prop' => array(
'Which additional properties to get:',
- ' protection - List the protection level of each page',
- ' talkid - The page ID of the talk page for each non-talk page',
- ' watched - List the watched status of each page',
- ' subjectid - The page ID of the parent page for each talk page',
- ' url - Gives a full URL to the page, and also an edit URL',
- ' readable - Whether the user can read this page',
- ' preload - Gives the text returned by EditFormPreloadText',
- ' displaytitle - Gives the way the page title is actually displayed',
+ ' protection - List the protection level of each page',
+ ' talkid - The page ID of the talk page for each non-talk page',
+ ' watched - List the watched status of each page',
+ ' notificationtimestamp - The watchlist notification timestamp of each page',
+ ' subjectid - The page ID of the parent page for each talk page',
+ ' url - Gives a full URL to the page, and also an edit URL',
+ ' readable - Whether the user can read this page',
+ ' preload - Gives the text returned by EditFormPreloadText',
+ ' displaytitle - Gives the way the page title is actually displayed',
),
'token' => 'Request a token to perform a data-modifying action on a page',
'continue' => 'When more results are available, use this to continue',
'watched' => array(
'watched' => 'boolean'
),
+ 'notificationtimestamp' => array(
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
'talkid' => array(
'talkid' => array(
ApiBase::PROP_TYPE => 'integer',
$this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
$this->addFieldsIf( 'rc_user', $this->fld_user );
$this->addFieldsIf( 'rc_user_text', $this->fld_user || $this->fld_userid );
- $this->addFieldsIf( array( 'rc_minor', 'rc_new', 'rc_bot' ) , $this->fld_flags );
+ $this->addFieldsIf( array( 'rc_minor', 'rc_type', 'rc_bot' ) , $this->fld_flags );
$this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
$this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
$this->addFieldsIf( array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), $this->fld_loginfo );
if ( $row->rc_bot ) {
$vals['bot'] = '';
}
- if ( $row->rc_new ) {
+ if ( $row->rc_type == RC_NEW ) {
$vals['new'] = '';
}
if ( $row->rc_minor ) {
$this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' );
}
+ // Continuing effectively uses startid. But we can't use rvstartid
+ // directly, because there is no way to tell the client to ''not''
+ // send rvstart if it sent it in the original query. So instead we
+ // send the continuation startid as rvcontinue, and ignore both
+ // rvstart and rvstartid when that is supplied.
+ if ( !is_null( $params['continue'] ) ) {
+ $params['startid'] = $params['continue'];
+ unset( $params['start'] );
+ }
+
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
// the same result. This way users may request revisions starting at a given time,
// but to page through results use the rev_id returned after each page.
if ( !$enumRevMode ) {
ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report
}
- $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) );
+ $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
break;
}
$fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' );
if ( !$fit ) {
if ( $enumRevMode ) {
- $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) );
+ $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
} elseif ( $revCount > 0 ) {
$this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
} else {
* @return array
*/
public static function getAutoGroups( $user ) {
+ // FIXME this logic is duplicated from User::getEffectiveGroups(), centralize this
$groups = array();
$groups[] = '*';
if ( !$user->isAnon() ) {
$groups[] = 'user';
+ $groups = array_merge( $groups, Autopromote::getAutopromoteGroups( $user ) );
}
- return array_merge( $groups, Autopromote::getAutopromoteGroups( $user ) );
+ return $groups;
}
public function getCacheMode( $params ) {
'rc_last_oldid',
) );
- $this->addFieldsIf( array( 'rc_new', 'rc_minor', 'rc_bot' ), $this->fld_flags );
+ $this->addFieldsIf( array( 'rc_type', 'rc_minor', 'rc_bot' ), $this->fld_flags );
$this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
$this->addFieldsIf( 'rc_user_text', $this->fld_user );
$this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
}
if ( $this->fld_flags ) {
- if ( $row->rc_new ) {
+ if ( $row->rc_type == RC_NEW ) {
$vals['new'] = '';
}
if ( $row->rc_minor ) {
--- /dev/null
+<?php
+
+/**
+ * API for MediaWiki 1.14+
+ *
+ * Created on Jun 18, 2012
+ *
+ * Copyright © 2012 Brad Jorsch
+ *
+ * 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
+ */
+
+/**
+ * API interface for setting the wl_notificationtimestamp field
+ * @ingroup API
+ */
+class ApiSetNotificationTimestamp extends ApiBase {
+
+ public function __construct( $main, $action ) {
+ parent::__construct( $main, $action );
+ }
+
+ public function execute() {
+ $user = $this->getUser();
+
+ if ( $user->isAnon() ) {
+ $this->dieUsage( 'Anonymous users cannot use watchlist change notifications', 'notloggedin' );
+ }
+
+ $params = $this->extractRequestParams();
+ $this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' );
+
+ $pageSet = new ApiPageSet( $this );
+ $args = array_merge( array( $params, 'entirewatchlist' ), array_keys( $pageSet->getAllowedParams() ) );
+ call_user_func_array( array( $this, 'requireOnlyOneParameter' ), $args );
+
+ $dbw = $this->getDB( DB_MASTER );
+
+ $timestamp = null;
+ if ( isset( $params['timestamp'] ) ) {
+ $timestamp = $dbw->timestamp( $params['timestamp'] );
+ }
+
+ if ( !$params['entirewatchlist'] ) {
+ $pageSet->execute();
+ }
+
+ if ( isset( $params['torevid'] ) ) {
+ if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
+ $this->dieUsage( 'torevid may only be used with a single page', 'multpages' );
+ }
+ $title = reset( $pageSet->getGoodTitles() );
+ $timestamp = Revision::getTimestampFromId( $title, $params['torevid'] );
+ if ( $timestamp ) {
+ $timestamp = $dbw->timestamp( $timestamp );
+ } else {
+ $timestamp = null;
+ }
+ } elseif ( isset( $params['newerthanrevid'] ) ) {
+ if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
+ $this->dieUsage( 'newerthanrevid may only be used with a single page', 'multpages' );
+ }
+ $title = reset( $pageSet->getGoodTitles() );
+ $revid = $title->getNextRevisionID( $params['newerthanrevid'] );
+ if ( $revid ) {
+ $timestamp = $dbw->timestamp( Revision::getTimestampFromId( $title, $revid ) );
+ } else {
+ $timestamp = null;
+ }
+ }
+
+ $apiResult = $this->getResult();
+ $result = array();
+ if ( $params['entirewatchlist'] ) {
+ // Entire watchlist mode: Just update the thing and return a success indicator
+ $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
+ array( 'wl_user' => $user->getID() ),
+ __METHOD__
+ );
+
+ $result['notificationtimestamp'] = ( is_null( $timestamp ) ? '' : wfTimestamp( TS_ISO_8601, $timestamp ) );
+ } else {
+ // First, log the invalid titles
+ foreach( $pageSet->getInvalidTitles() as $title ) {
+ $r = array();
+ $r['title'] = $title;
+ $r['invalid'] = '';
+ $result[] = $r;
+ }
+ foreach( $pageSet->getMissingPageIDs() as $p ) {
+ $page = array();
+ $page['pageid'] = $p;
+ $page['missing'] = '';
+ $page['notwatched'] = '';
+ $result[] = $page;
+ }
+ foreach( $pageSet->getMissingRevisionIDs() as $r ) {
+ $rev = array();
+ $rev['revid'] = $r;
+ $rev['missing'] = '';
+ $rev['notwatched'] = '';
+ $result[] = $rev;
+ }
+
+ // Now process the valid titles
+ $lb = new LinkBatch( $pageSet->getTitles() );
+ $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
+ array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
+ __METHOD__
+ );
+
+ // Query the results of our update
+ $timestamps = array();
+ $res = $dbw->select( 'watchlist', array( 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ),
+ array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
+ __METHOD__
+ );
+ foreach ( $res as $row ) {
+ $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
+ }
+
+ // Now, put the valid titles into the result
+ foreach ( $pageSet->getTitles() as $title ) {
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+ $r = array(
+ 'ns' => intval( $ns ),
+ 'title' => $title->getPrefixedText(),
+ );
+ if ( !$title->exists() ) {
+ $r['missing'] = '';
+ }
+ if ( isset( $timestamps[$ns] ) && array_key_exists( $dbkey, $timestamps[$ns] ) ) {
+ $r['notificationtimestamp'] = '';
+ if ( $timestamps[$ns][$dbkey] !== null ) {
+ $r['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $timestamps[$ns][$dbkey] );
+ }
+ } else {
+ $r['notwatched'] = '';
+ }
+ $result[] = $r;
+ }
+
+ $apiResult->setIndexedTagName( $result, 'page' );
+ }
+ $apiResult->addValue( null, $this->getModuleName(), $result );
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function isWriteMode() {
+ return true;
+ }
+
+ public function needsToken() {
+ return true;
+ }
+
+ public function getTokenSalt() {
+ return '';
+ }
+
+ public function getAllowedParams() {
+ $psModule = new ApiPageSet( $this );
+ return $psModule->getAllowedParams() + array(
+ 'entirewatchlist' => array(
+ ApiBase::PARAM_TYPE => 'boolean'
+ ),
+ 'token' => null,
+ 'timestamp' => array(
+ ApiBase::PARAM_TYPE => 'timestamp'
+ ),
+ 'torevid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
+ 'newerthanrevid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
+ );
+ }
+
+ public function getParamDescription() {
+ $psModule = new ApiPageSet( $this );
+ return $psModule->getParamDescription() + array(
+ 'entirewatchlist' => 'Work on all watched pages',
+ 'timestamp' => 'Timestamp to which to set the notification timestamp',
+ 'torevid' => 'Revision to set the notification timestamp to (one page only)',
+ 'newerthanrevid' => 'Revision to set the notification timestamp newer than (one page only)',
+ 'token' => 'A token previously acquired via prop=info',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => true,
+ ApiBase::PROP_ROOT => array(
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ '' => array(
+ 'ns' => array(
+ ApiBase::PROP_TYPE => 'namespace',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'title' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'pageid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'revid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'invalid' => 'boolean',
+ 'missing' => 'boolean',
+ 'notwatched' => 'boolean',
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
+ public function getDescription() {
+ return array( 'Update the notification timestamp for watched pages.',
+ 'This affects the highlighting of changed pages in the watchlist and history,',
+ 'and the sending of email when the "E-mail me when a page on my watchlist is',
+ 'changed" preference is enabled.'
+ );
+ }
+
+ public function getPossibleErrors() {
+ $psModule = new ApiPageSet( $this );
+ return array_merge(
+ parent::getPossibleErrors(),
+ $psModule->getPossibleErrors(),
+ $this->getRequireMaxOneParameterErrorMessages( array( 'timestamp', 'torevid', 'newerthanrevid' ) ),
+ $this->getRequireOnlyOneParameterErrorMessages( array_merge( array( 'entirewatchlist' ), array_keys( $psModule->getAllowedParams() ) ) ),
+ array(
+ array( 'code' => 'notloggedin', 'info' => 'Anonymous users cannot use watchlist change notifications' ),
+ array( 'code' => 'multpages', 'info' => 'torevid may only be used with a single page' ),
+ array( 'code' => 'multpages', 'info' => 'newerthanrevid may only be used with a single page' ),
+ )
+ );
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=setnotificationtimestamp&entirewatchlist=&token=ABC123' => 'Reset the notification status for the entire watchlist',
+ 'api.php?action=setnotificationtimestamp&titles=Main_page&token=ABC123' => 'Reset the notification status for "Main page"',
+ 'api.php?action=setnotificationtimestamp&titles=Main_page×tamp=2012-01-01T00:00:00Z&token=ABC123' => 'Set the notification timestamp for "Main page" so all edits since 1 January 2012 are unviewed',
+ );
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:SetNotificationTimestamp';
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
*/
private function getContextResult(){
$warnings = $this->getApiWarnings();
- if ( $warnings ) {
+ if ( $warnings && !$this->mParams['ignorewarnings'] ) {
// Get warnings formated in result array format
return $this->getWarningsResult( $warnings );
} elseif ( $this->mParams['chunk'] ) {
// Add chunk, and get result
- return $this->getChunkResult();
+ return $this->getChunkResult( $warnings );
} elseif ( $this->mParams['stash'] ) {
// Stash the file and get stash result
- return $this->getStashResult();
+ return $this->getStashResult( $warnings );
}
// This is the most common case -- a normal upload with no warnings
// performUpload will return a formatted properly for the API with status
- return $this->performUpload();
+ return $this->performUpload( $warnings );
}
/**
* Get Stash Result, throws an expetion if the file could not be stashed.
+ * @param $warnings array Array of Api upload warnings
* @return array
*/
- private function getStashResult(){
+ private function getStashResult( $warnings ){
$result = array ();
// Some uploads can request they be stashed, so as not to publish them immediately.
// In this case, a failure to stash ought to be fatal
$result['result'] = 'Success';
$result['filekey'] = $this->performStash();
$result['sessionkey'] = $result['filekey']; // backwards compatibility
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
} catch ( MWException $e ) {
$this->dieUsage( $e->getMessage(), 'stashfailed' );
}
}
/**
* Get Warnings Result
- * @param $warnings Array of Api upload warnings
+ * @param $warnings array Array of Api upload warnings
* @return array
*/
private function getWarningsResult( $warnings ){
}
/**
* Get the result of a chunk upload.
+ * @param $warnings array Array of Api upload warnings
* @return array
*/
- private function getChunkResult(){
+ private function getChunkResult( $warnings ){
$result = array();
$result['result'] = 'Continue';
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
$request = $this->getMain()->getRequest();
$chunkPath = $request->getFileTempname( 'chunk' );
$chunkSize = $request->getUpload( 'chunk' )->getSize();
break;
case UploadBase::FILETYPE_BADTYPE:
- $this->dieUsage( 'This type of file is banned', 'filetype-banned',
- 0, array(
- 'filetype' => $verification['finalExt'],
- 'allowed' => $wgFileExtensions
- ) );
+ $extradata = array(
+ 'filetype' => $verification['finalExt'],
+ 'allowed' => $wgFileExtensions
+ );
+ $this->getResult()->setIndexedTagName( $extradata['allowed'], 'ext' );
+
+ $msg = "Filetype not permitted: ";
+ if ( isset( $verification['blacklistedExt'] ) ) {
+ $msg .= join( ', ', $verification['blacklistedExt'] );
+ $extradata['blacklisted'] = array_values( $verification['blacklistedExt'] );
+ $this->getResult()->setIndexedTagName( $extradata['blacklisted'], 'ext' );
+ } else {
+ $msg .= $verification['finalExt'];
+ }
+ $this->dieUsage( $msg, 'filetype-banned', 0, $extradata );
break;
case UploadBase::VERIFICATION_ERROR:
$this->getResult()->setIndexedTagName( $verification['details'], 'detail' );
/**
- * Check warnings if ignorewarnings is not set.
+ * Check warnings.
* Returns a suitable array for inclusion into API results if there were warnings
* Returns the empty array if there were no warnings
*
* @return array
*/
protected function getApiWarnings() {
- $warnings = array();
+ $warnings = $this->mUpload->checkWarnings();
- if ( !$this->mParams['ignorewarnings'] ) {
- $warnings = $this->mUpload->checkWarnings();
- }
return $this->transformWarnings( $warnings );
}
* Perform the actual upload. Returns a suitable result array on success;
* dies on failure.
*
+ * @param $warnings array Array of Api upload warnings
* @return array
*/
- protected function performUpload() {
+ protected function performUpload( $warnings ) {
// Use comment as initial page text by default
if ( is_null( $this->mParams['text'] ) ) {
$this->mParams['text'] = $this->mParams['comment'];
$result['result'] = 'Success';
$result['filename'] = $file->getName();
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
return $result;
}
+++ /dev/null
-<?php
-/**
- * Session storage in object cache.
- *
- * This file gets included if $wgSessionsInMemcache is set in the config.
- * It redirects session handling functions to store their data in memcached
- * instead of the local filesystem. Depending on circumstances, it may also
- * be necessary to change the cookie settings to work across hostnames.
- * See: http://www.php.net/manual/en/function.session-set-save-handler.php
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Get a cache key for the given session id.
- *
- * @param $id String: session id
- * @return String: cache key
- */
-function memsess_key( $id ) {
- return wfMemcKey( 'session', $id );
-}
-
-/**
- * Callback when opening a session.
- * NOP: $wgMemc should be set up already.
- *
- * @param $save_path String: path used to store session files, unused
- * @param $session_name String: session name
- * @return Boolean: success
- */
-function memsess_open( $save_path, $session_name ) {
- return true;
-}
-
-/**
- * Callback when closing a session.
- * NOP.
- *
- * @return Boolean: success
- */
-function memsess_close() {
- return true;
-}
-
-/**
- * Callback when reading session data.
- *
- * @param $id String: session id
- * @return Mixed: session data
- */
-function memsess_read( $id ) {
- global $wgMemc;
- $data = $wgMemc->get( memsess_key( $id ) );
- if( ! $data ) return '';
- return $data;
-}
-
-/**
- * Callback when writing session data.
- *
- * @param $id String: session id
- * @param $data Mixed: session data
- * @return Boolean: success
- */
-function memsess_write( $id, $data ) {
- global $wgMemc;
- $wgMemc->set( memsess_key( $id ), $data, 3600 );
- return true;
-}
-
-/**
- * Callback to destroy a session when calling session_destroy().
- *
- * @param $id String: session id
- * @return Boolean: success
- */
-function memsess_destroy( $id ) {
- global $wgMemc;
-
- $wgMemc->delete( memsess_key( $id ) );
- return true;
-}
-
-/**
- * Callback to execute garbage collection.
- * NOP: Memcached performs garbage collection.
- *
- * @param $maxlifetime Integer: maximum session life time
- * @return Boolean: success
- */
-function memsess_gc( $maxlifetime ) {
- return true;
-}
-
-function memsess_write_close() {
- session_write_close();
-}
-
--- /dev/null
+<?php
+/**
+ * Caches current user names and other info based on user IDs.
+ *
+ * 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 Cache
+ */
+
+/**
+ * @since 1.20
+ */
+class UserCache {
+ protected $cache = array(); // (uid => property => value)
+ protected $typesCached = array(); // (uid => cache type => 1)
+
+ /**
+ * @return UserCache
+ */
+ public static function singleton() {
+ static $instance = null;
+ if ( $instance === null ) {
+ $instance = new self();
+ }
+ return $instance;
+ }
+
+ protected function __construct() {}
+
+ /**
+ * Get a property of a user based on their user ID
+ *
+ * @param $userId integer User ID
+ * @param $prop string User property
+ * @return mixed The property or false if the user does not exist
+ */
+ public function getProp( $userId, $prop ) {
+ if ( !isset( $this->cache[$userId][$prop] ) ) {
+ wfDebug( __METHOD__ . ": querying DB for prop '$prop' for user ID '$userId'.\n" );
+ $this->doQuery( array( $userId ) ); // cache miss
+ }
+ return isset( $this->cache[$userId][$prop] )
+ ? $this->cache[$userId][$prop]
+ : false; // user does not exist?
+ }
+
+ /**
+ * Preloads user names for given list of users.
+ * @param $userIds Array List of user IDs
+ * @param $options Array Option flags; include 'userpage' and 'usertalk'
+ * @param $caller String: the calling method
+ */
+ public function doQuery( array $userIds, $options = array(), $caller = '' ) {
+ wfProfileIn( __METHOD__ );
+
+ $usersToCheck = array();
+ $usersToQuery = array();
+
+ foreach ( $userIds as $userId ) {
+ $userId = (int)$userId;
+ if ( $userId <= 0 ) {
+ continue; // skip anons
+ }
+ if ( isset( $this->cache[$userId]['name'] ) ) {
+ $usersToCheck[$userId] = $this->cache[$userId]['name']; // already have name
+ } else {
+ $usersToQuery[] = $userId; // we need to get the name
+ }
+ }
+
+ // Lookup basic info for users not yet loaded...
+ if ( count( $usersToQuery ) ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $table = array( 'user' );
+ $conds = array( 'user_id' => $usersToQuery );
+ $fields = array( 'user_name', 'user_real_name', 'user_registration', 'user_id' );
+
+ $comment = __METHOD__;
+ if ( strval( $caller ) !== '' ) {
+ $comment .= "/$caller";
+ }
+
+ $res = $dbr->select( $table, $fields, $conds, $comment );
+ foreach ( $res as $row ) { // load each user into cache
+ $userId = (int)$row->user_id;
+ $this->cache[$userId]['name'] = $row->user_name;
+ $this->cache[$userId]['real_name'] = $row->user_real_name;
+ $this->cache[$userId]['registration'] = $row->user_registration;
+ $usersToCheck[$userId] = $row->user_name;
+ }
+ }
+
+ $lb = new LinkBatch();
+ foreach ( $usersToCheck as $userId => $name ) {
+ if ( $this->queryNeeded( $userId, 'userpage', $options ) ) {
+ $lb->add( NS_USER, str_replace( ' ', '_', $row->user_name ) );
+ $this->typesCached[$userId]['userpage'] = 1;
+ }
+ if ( $this->queryNeeded( $userId, 'usertalk', $options ) ) {
+ $lb->add( NS_USER_TALK, str_replace( ' ', '_', $row->user_name ) );
+ $this->typesCached[$userId]['usertalk'] = 1;
+ }
+ }
+ $lb->execute();
+
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Check if a cache type is in $options and was not loaded for this user
+ *
+ * @param $uid integer user ID
+ * @param $type string Cache type
+ * @param $options Array Requested cache types
+ * @return bool
+ */
+ protected function queryNeeded( $uid, $type, array $options ) {
+ return ( in_array( $type, $options ) && !isset( $this->typesCached[$uid][$type] ) );
+ }
+}
*/
/**
- * Interface for database access objects
+ * Interface for database access objects.
+ *
+ * Classes using this support a set of constants in a bitfield argument to their data loading
+ * functions. In general, objects should assume READ_NORMAL if no flags are explicitly given,
+ * though certain objects may assume READ_LATEST for common use case or legacy reasons.
+ *
+ * There are three types of reads:
+ * - READ_NORMAL : Potentially cached read of data (e.g. from a slave or stale replica)
+ * - READ_LATEST : Up-to-date read as of transaction start (e.g. from master or a quorum read)
+ * - READ_LOCKING : Up-to-date read as of now, that locks the records for the transaction
+ *
+ * Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
+ * In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
+ * often good enough. If UPDATE race condition checks are required on a row and expensive code
+ * must run after the row is fetched to determine the UPDATE, it may help to do something like:
+ * - a) Read the current row
+ * - b) Determine the new row (expensive, so we don't want to hold locks now)
+ * - c) Re-read the current row with READ_LOCKING; if it changed then bail out
+ * - d) otherwise, do the updates
*/
interface IDBAccessObject {
- const LATEST_READ = 1; // read from the master
- const FOR_UPDATE = 2; // lock the rows read
- const LOCKING_READ = 3; // LATEST_READ | FOR_UPDATE
- const AVOID_MASTER = 4; // avoiding checking the master
+ // Constants for object loading bitfield flags (higher => higher QoS)
+ const READ_LATEST = 1; // read from the master
+ const READ_LOCKING = 3; // READ_LATEST and "FOR UPDATE"
+
+ // Convenience constant for callers to explicitly request slave data
+ const READ_NORMAL = 0; // read from the slave
+
+ // Convenience constant for tracking how data was loaded (higher => higher QoS)
+ const READ_NONE = -1; // not loaded yet (or the object was cleared)
}
return true;
}
- /**
- * Returns true if this database requires that SELECT DISTINCT queries require that all
- ORDER BY expressions occur in the SELECT list per the SQL92 standard
- *
- * @return bool
- */
- function standardSelectDistinct() {
- return true;
- }
-
/**
* Returns true if this database can do a native search on IP columns
* e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
throw new MWException( 'Database serialization may cause problems, since the connection is not restored on wakeup.' );
}
- /**
- * Same as new DatabaseMysql( ... ), kept for backward compatibility
- * @deprecated since 1.17
- *
- * @param $server
- * @param $user
- * @param $password
- * @param $dbName
- * @param $flags int
- * @return DatabaseMysql
- */
- static function newFromParams( $server, $user, $password, $dbName, $flags = 0 ) {
- wfDeprecated( __METHOD__, '1.17' );
- return new DatabaseMysql( $server, $user, $password, $dbName, $flags );
- }
-
- /**
- * Same as new factory( ... ), kept for backward compatibility
- * @deprecated since 1.18
- * @see Database::factory()
- * @return DatabaseBase
- */
- public final static function newFromType( $dbType, $p = array() ) {
- wfDeprecated( __METHOD__, '1.18' );
- if ( isset( $p['tableprefix'] ) ) {
- $p['tablePrefix'] = $p['tableprefix'];
- }
- return self::factory( $dbType, $p );
- }
-
/**
* Given a DB type, construct the name of the appropriate child class of
* DatabaseBase. This is designed to replace all of the manual stuff like:
* & = filename; reads the file and inserts as a blob
* (we don't use this though...)
*
- * This function should not be used directly by new code outside of the
- * database classes. The query wrapper functions (select() etc.) should be
- * used instead.
- *
* @param $sql string
* @param $func string
*
* @return array
*/
- function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
+ protected function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
/* MySQL doesn't support prepared statements (yet), so just
pack up the query for reference. We'll manually replace
the bits later. */
* Free a prepared query, generated by prepare().
* @param $prepared
*/
- function freePrepared( $prepared ) {
+ protected function freePrepared( $prepared ) {
/* No-op by default */
}
}
/**
- * Prepare & execute an SQL statement, quoting and inserting arguments
- * in the appropriate places.
- *
- * This function should not be used directly by new code outside of the
- * database classes. The query wrapper functions (select() etc.) should be
- * used instead.
+ * For faking prepared SQL statements on DBs that don't support it directly.
*
- * @param $query String
- * @param $args ...
- *
- * @return ResultWrapper
- */
- function safeQuery( $query, $args = null ) {
- $prepared = $this->prepare( $query, 'DatabaseBase::safeQuery' );
-
- if ( !is_array( $args ) ) {
- # Pull the var args
- $args = func_get_args();
- array_shift( $args );
- }
-
- $retval = $this->execute( $prepared, $args );
- $this->freePrepared( $prepared );
-
- return $retval;
- }
-
- /**
- * For faking prepared SQL statements on DBs that don't support
- * it directly.
* @param $preparedQuery String: a 'preparable' SQL statement
* @param $args Array of arguments to fill it with
* @return string executable SQL
* @param $matches Array
* @return String
*/
- function fillPreparedArg( $matches ) {
+ protected function fillPreparedArg( $matches ) {
switch( $matches[1] ) {
case '\\?': return '?';
case '\\!': return '!';
*
* @param $res Mixed: A SQL result
*/
- function freeResult( $res ) {
- }
-
- /**
- * Simple UPDATE wrapper.
- * Usually throws a DBQueryError on failure.
- * If errors are explicitly ignored, returns success
- *
- * This function exists for historical reasons, DatabaseBase::update() has a more standard
- * calling convention and feature set
- *
- * @param $table string
- * @param $var
- * @param $value
- * @param $cond
- * @param $fname string
- *
- * @return bool
- */
- function set( $table, $var, $value, $cond, $fname = 'DatabaseBase::set' ) {
- $table = $this->tableName( $table );
- $sql = "UPDATE $table SET $var = '" .
- $this->strencode( $value ) . "' WHERE ($cond)";
-
- return (bool)$this->query( $sql, $fname );
- }
+ public function freeResult( $res ) {}
/**
* A SELECT wrapper which returns a single field from a single result row.
/**
* The equivalent of DatabaseBase::select() except that the constructed SQL
- * is returned, instead of being immediately executed.
+ * is returned, instead of being immediately executed. This can be useful for
+ * doing UNION queries, where the SQL text of each query is needed. In general,
+ * however, callers outside of Database classes should just use select().
*
* @param $table string|array Table name
* @param $vars string|array Field names
$fname = 'DatabaseBase::estimateRowCount', $options = array() )
{
$rows = 0;
- $res = $this->select ( $table, 'COUNT(*) AS rowcount', $conds, $fname, $options );
+ $res = $this->select( $table, 'COUNT(*) AS rowcount', $conds, $fname, $options );
if ( $res ) {
$row = $this->fetchRow( $res );
* @param $options array
* @return string
*/
- function makeInsertOptions( $options ) {
+ protected function makeInsertOptions( $options ) {
return implode( ' ', $options );
}
* @param $options Array: The options passed to DatabaseBase::update
* @return string
*/
- function makeUpdateOptions( $options ) {
+ protected function makeUpdateOptions( $options ) {
if ( !is_array( $options ) ) {
$options = array( $options );
}
}
/**
- * Bitwise operations
+ * Return aggregated value alias
+ *
+ * @param $valuedata
+ * @param $valuename string
+ *
+ * @return string
*/
+ public function aggregateValue( $valuedata, $valuename = 'value' ) {
+ return $valuename;
+ }
/**
* @param $field
return "($fieldLeft | $fieldRight)";
}
+ /**
+ * Build a concatenation list to feed into a SQL query
+ * @param $stringList Array: list of raw SQL expressions; caller is responsible for any quoting
+ * @return String
+ */
+ public function buildConcat( $stringList ) {
+ return 'CONCAT(' . implode( ',', $stringList ) . ')';
+ }
+
/**
* Change the current database
*
# Quote the $database and $table and apply the prefix if not quoted.
if ( isset( $database ) ) {
- $database = ( $format == 'quoted' || $this->isQuotedIdentifier( $database ) ? $database : $this->addIdentifierQuotes( $database ) );
+ if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
+ $database = $this->addIdentifierQuotes( $database );
+ }
}
$table = "{$prefix}{$table}";
*
* @return string
*/
- function indexName( $index ) {
+ protected function indexName( $index ) {
// Backwards-compatibility hack
$renamed = array(
'ar_usertext_timestamp' => 'usertext_timestamp',
return $name[0] == '"' && substr( $name, -1, 1 ) == '"';
}
- /**
- * Backwards compatibility, identifier quoting originated in DatabasePostgres
- * which used quote_ident which does not follow our naming conventions
- * was renamed to addIdentifierQuotes.
- * @deprecated since 1.18 use addIdentifierQuotes
- *
- * @param $s string
- *
- * @return string
- */
- function quote_ident( $s ) {
- wfDeprecated( __METHOD__, '1.18' );
- return $this->addIdentifierQuotes( $s );
- }
-
- /**
- * Escape string for safe LIKE usage.
- * WARNING: you should almost never use this function directly,
- * instead use buildLike() that escapes everything automatically
- * @deprecated since 1.17, warnings in 1.17, removed in ???
- *
- * @param $s string
- *
- * @return string
- */
- public function escapeLike( $s ) {
- wfDeprecated( __METHOD__, '1.17' );
- return $this->escapeLikeInternal( $s );
- }
-
/**
* @param $s string
* @return string
* If the result of the query is not ordered, then the rows to be returned
* are theoretically arbitrary.
*
- * $sql is expected to be a SELECT, if that makes a difference. For
- * UPDATE, limitResultForUpdate should be used.
+ * $sql is expected to be a SELECT, if that makes a difference.
*
* The version provided by default works in MySQL and SQLite. It will very
* likely need to be overridden for most other DBMSes.
if ( !is_numeric( $limit ) ) {
throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
}
-
return "$sql LIMIT "
- . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
- . "{$limit} ";
- }
-
- /**
- * @param $sql
- * @param $num
- * @return string
- */
- function limitResultForUpdate( $sql, $num ) {
- return $this->limitResult( $sql, $num, 0 );
+ . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
+ . "{$limit} ";
}
/**
}
}
- /**
- * Return aggregated value alias
- *
- * @param $valuedata
- * @param $valuename string
- *
- * @return string
- */
- function aggregateValue ( $valuedata, $valuename = 'value' ) {
- return $valuename;
- }
-
/**
* Ping the server and try to reconnect if it there is no connection
*
return $b;
}
- /**
- * Override database's default connection timeout
- *
- * @param $timeout Integer in seconds
- * @return void
- * @deprecated since 1.19; use setSessionOptions()
- */
- public function setTimeout( $timeout ) {
- wfDeprecated( __METHOD__, '1.19' );
- $this->setSessionOptions( array( 'connTimeout' => $timeout ) );
- }
-
/**
* Override database's default behavior. $options include:
* 'connTimeout' : Set the connection timeout value in seconds.
return $this->indexName( $matches[1] );
}
- /**
- * Build a concatenation list to feed into a SQL query
- * @param $stringList Array: list of raw SQL expressions; caller is responsible for any quoting
- * @return String
- */
- function buildConcat( $stringList ) {
- return 'CONCAT(' . implode( ',', $stringList ) . ')';
- }
-
/**
* Check to see if a named lock is available. This is non-blocking.
*
*/
public function __construct( $db, $result, $num_rows, $sql, $columns ){
$this->db = $db;
-
+
if( $result instanceof ResultWrapper ){
$this->result = $result->result;
}
else{
$this->result = $result;
}
-
+
$this->num_rows = $num_rows;
$this->current_pos = 0;
if ( $this->num_rows > 0 ) {
// Make a lower-case list of the column names
// By default, DB2 column names are capitalized
// while MySQL column names are lowercase
-
+
// Is there a reasonable maximum value for $i?
// Setting to 2048 to prevent an infinite loop
for( $i = 0; $i < 2048; $i++ ) {
else {
return false;
}
-
+
$this->columns[$i] = strtolower( $name );
}
}
-
+
$this->sql = $sql;
}
* @return mixed Object on success, false on failure.
*/
public function fetchObject() {
- if ( $this->result
- && $this->num_rows > 0
- && $this->current_pos >= 0
- && $this->current_pos < $this->num_rows )
+ if ( $this->result
+ && $this->num_rows > 0
+ && $this->current_pos >= 0
+ && $this->current_pos < $this->num_rows )
{
$row = $this->fetchRow();
$ret = new stdClass();
-
+
foreach ( $row as $k => $v ) {
$lc = $this->columns[$k];
$ret->$lc = $v;
* @throws DBUnexpectedError
*/
public function fetchRow(){
- if ( $this->result
- && $this->num_rows > 0
- && $this->current_pos >= 0
+ if ( $this->result
+ && $this->num_rows > 0
+ && $this->current_pos >= 0
&& $this->current_pos < $this->num_rows )
{
if ( $this->loadedLines <= $this->current_pos ) {
if ( $this->loadedLines > $this->current_pos ){
return $this->resultSet[$this->current_pos++];
}
-
+
}
return false;
}
return 'ibm_db2';
}
- /**
+ /**
* Returns the database connection object
* @return Object
*/
$res2 = parent::select( $table, $vars2, $conds, $fname, $options2,
$join_conds );
-
+
$obj = $this->fetchObject( $res2 );
$this->mNumRows = $obj->num_rows;
-
+
return new ResultWrapper( $this, new IBM_DB2Result( $this, $res, $obj->num_rows, $vars, $sql ) );
}
######################################
# Unimplemented and not applicable
######################################
- /**
- * Not implemented
- * @return string $sql
- */
- public function limitResultForUpdate( $sql, $num ) {
- $this->installPrint( 'Not implemented for DB2: limitResultForUpdate()' );
- return $sql;
- }
/**
* Only useful with fake prepare like in base Database class
return $res;
}
- /**
- * Prepare & execute an SQL statement, quoting and inserting arguments
- * in the appropriate places.
- * @param $query String
- * @param $args ...
- * @return Resource
- */
- public function safeQuery( $query, $args = null ) {
- // copied verbatim from Database.php
- $prepared = $this->prepare( $query, 'DB2::safeQuery' );
- if( !is_array( $args ) ) {
- # Pull the var args
- $args = func_get_args();
- array_shift( $args );
- }
- $retval = $this->execute( $prepared, $args );
- $this->freePrepared( $prepared );
- return $retval;
- }
-
/**
* For faking prepared SQL statements on DBs that don't support
* it directly.
return $sql;
}
- // MSSQL does support this, but documentation is too thin to make a generalized
- // function for this. Apparently UPDATE TOP (N) works, but the sort order
- // may not be what we're expecting so the top n results may be a random selection.
- // TODO: Implement properly.
- function limitResultForUpdate( $sql, $num ) {
- return $sql;
- }
-
function timestamp( $ts = 0 ) {
return wfTimestamp( TS_ISO_8601, $ts );
}
return '[http://www.mysql.com/ MySQL]';
}
- /**
- * @return bool
- */
- function standardSelectDistinct() {
- return false;
- }
-
/**
* @param $options array
*/
}
}
- /* Not even sure why this is used in the main codebase... */
- function limitResultForUpdate( $sql, $num ) {
- return $sql;
- }
-
/* defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; */
function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
$fname = 'DatabaseOracle::sourceStream', $inputCallback = false ) {
return pg_field_type( $res, $index );
}
- /* Not even sure why this is used in the main codebase... */
- function limitResultForUpdate( $sql, $num ) {
- return $sql;
- }
-
/**
* @param $b
* @return Blob
}
$cachedResult = false;
$table = 'dummy_search_test';
-
+
$db = new DatabaseSqliteStandalone( ':memory:' );
if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
/**
* @param $res ResultWrapper
- * @param $n
+ * @param $n
* @return bool
*/
function fieldName( $res, $n ) {
$this->mTrxLevel = 0;
}
- /**
- * @param $sql
- * @param $num
- * @return string
- */
- function limitResultForUpdate( $sql, $num ) {
- return $this->limitResult( $sql, $num );
- }
-
/**
* @param $s string
* @return string
}
return $this->query( $sql, $fname );
}
-
-
+
+
/**
* List all tables on the database
*
'name',
"type='table'"
);
-
+
$endArray = array();
-
- foreach( $result as $table ) {
+
+ foreach( $result as $table ) {
$vars = get_object_vars($table);
$table = array_pop( $vars );
-
+
if( !$prefix || strpos( $table, $prefix ) === 0 ) {
if ( strpos( $table, 'sqlite_' ) !== 0 ) {
$endArray[] = $table;
}
-
+
}
}
-
+
return $endArray;
}
public function delete( array $conditions, $functionName = null ) {
return wfGetDB( DB_MASTER )->delete(
$this->getName(),
- $this->getPrefixedValues( $conditions ),
+ $conditions === array() ? '*' : $this->getPrefixedValues( $conditions ),
$functionName
) !== false; // DatabaseBase::delete does not always return true for success as documented...
}
public function updateSummaryFields( $summaryFields = null, array $conditions = array() ) {
$this->setReadDb( DB_MASTER );
- foreach ( $this->select( null, $conditions ) as /* IORMRow */ $item ) {
+ /**
+ * @var IORMRow $item
+ */
+ foreach ( $this->select( null, $conditions ) as $item ) {
$item->loadSummaryFields( $summaryFields );
$item->setSummaryMode( true );
$item->save();
'debugLog' => self::$debug,
'queries' => self::$query,
'request' => array(
- 'method' => $_SERVER['REQUEST_METHOD'],
+ 'method' => $request->getMethod(),
'url' => $request->getRequestURL(),
'headers' => $request->getAllHeaders(),
'params' => $request->getValues(),
}
}
+ private function showMissingRevision() {
+ $out = $this->getOutput();
+
+ $missing = array();
+ if ( $this->mOldRev === null ) {
+ $missing[] = $this->deletedIdMarker( $this->mOldid );
+ }
+ if ( $this->mNewRev === null ) {
+ $missing[] = $this->deletedIdMarker( $this->mNewid );
+ }
+
+ $out->setPageTitle( $this->msg( 'errorpagetitle' ) );
+ $out->addWikiMsg( 'difference-missing-revision',
+ $this->getLanguage()->listToText( $missing ), count( $missing ) );
+ }
+
function showDiffPage( $diffOnly = false ) {
wfProfileIn( __METHOD__ );
$out->setRobotPolicy( 'noindex,nofollow' );
if ( !$this->loadRevisionData() ) {
- // Sounds like a deleted revision... Let's see what we can do.
- $t = $this->getTitle()->getPrefixedText();
- $d = $this->msg( 'missingarticle-diff',
- $this->deletedIdMarker( $this->mOldid ),
- $this->deletedIdMarker( $this->mNewid ) )->escaped();
- $out->setPageTitle( $this->msg( 'errorpagetitle' ) );
- $out->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", "<span class='plainlinks'>$d</span>" );
+ $this->showMissingRevision();
wfProfileOut( __METHOD__ );
return;
}
function showDiff( $otitle, $ntitle, $notice = '' ) {
$diff = $this->getDiff( $otitle, $ntitle, $notice );
if ( $diff === false ) {
- $this->getOutput()->addWikiMsg( 'missing-article', "<nowiki>(fixme, bug)</nowiki>", '' );
+ $this->showMissingRevision();
return false;
} else {
$this->showDiffStyle();
}
$msg = $this->msg( $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold' )->escaped();
- $header .= ' (' . Linker::linkKnown( $title, $msg, array(), $editQuery ) . ')';
+ $header .= ' ' . $this->msg( 'parentheses' )->rawParams(
+ Linker::linkKnown( $title, $msg, array(), $editQuery ) )->plain();
if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
$header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header );
}
// Load the new revision object
$this->mNewRev = $this->mNewid
? Revision::newFromId( $this->mNewid )
- : Revision::newFromTitle( $this->getTitle(), false, Revision::AVOID_MASTER );
+ : Revision::newFromTitle( $this->getTitle(), false, Revision::READ_NORMAL );
if ( !$this->mNewRev instanceof Revision ) {
return false;
--- /dev/null
+<?php
+/**
+ * Non-directory file on the file system.
+ *
+ * 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 FileBackend
+ */
+
+/**
+ * Class representing a non-directory file on the file system
+ *
+ * @ingroup FileBackend
+ */
+class FSFile {
+ protected $path; // path to file
+
+ /**
+ * Sets up the file object
+ *
+ * @param $path string Path to temporary file on local disk
+ * @throws MWException
+ */
+ public function __construct( $path ) {
+ if ( FileBackend::isStoragePath( $path ) ) {
+ throw new MWException( __METHOD__ . " given storage path `$path`." );
+ }
+ $this->path = $path;
+ }
+
+ /**
+ * Returns the file system path
+ *
+ * @return String
+ */
+ public function getPath() {
+ return $this->path;
+ }
+
+ /**
+ * Checks if the file exists
+ *
+ * @return bool
+ */
+ public function exists() {
+ return is_file( $this->path );
+ }
+
+ /**
+ * Get the file size in bytes
+ *
+ * @return int|bool
+ */
+ public function getSize() {
+ return filesize( $this->path );
+ }
+
+ /**
+ * Get the file's last-modified timestamp
+ *
+ * @return string|bool TS_MW timestamp or false on failure
+ */
+ public function getTimestamp() {
+ wfSuppressWarnings();
+ $timestamp = filemtime( $this->path );
+ wfRestoreWarnings();
+ if ( $timestamp !== false ) {
+ $timestamp = wfTimestamp( TS_MW, $timestamp );
+ }
+ return $timestamp;
+ }
+
+ /**
+ * Guess the MIME type from the file contents alone
+ *
+ * @return string
+ */
+ public function getMimeType() {
+ return MimeMagic::singleton()->guessMimeType( $this->path, false );
+ }
+
+ /**
+ * Get an associative array containing information about
+ * a file with the given storage path.
+ *
+ * @param $ext Mixed: the file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension.
+ *
+ * @return array
+ */
+ public function getProps( $ext = true ) {
+ wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__.": Getting file info for $this->path\n" );
+
+ $info = self::placeholderProps();
+ $info['fileExists'] = $this->exists();
+
+ if ( $info['fileExists'] ) {
+ $magic = MimeMagic::singleton();
+
+ # get the file extension
+ if ( $ext === true ) {
+ $ext = self::extensionFromPath( $this->path );
+ }
+
+ # mime type according to file contents
+ $info['file-mime'] = $this->getMimeType();
+ # logical mime type
+ $info['mime'] = $magic->improveTypeFromExtension( $info['file-mime'], $ext );
+
+ list( $info['major_mime'], $info['minor_mime'] ) = File::splitMime( $info['mime'] );
+ $info['media_type'] = $magic->getMediaType( $this->path, $info['mime'] );
+
+ # Get size in bytes
+ $info['size'] = $this->getSize();
+
+ # Height, width and metadata
+ $handler = MediaHandler::getHandler( $info['mime'] );
+ if ( $handler ) {
+ $tempImage = (object)array();
+ $info['metadata'] = $handler->getMetadata( $tempImage, $this->path );
+ $gis = $handler->getImageSize( $tempImage, $this->path, $info['metadata'] );
+ if ( is_array( $gis ) ) {
+ $info = $this->extractImageSizeInfo( $gis ) + $info;
+ }
+ }
+ $info['sha1'] = $this->getSha1Base36();
+
+ wfDebug(__METHOD__.": $this->path loaded, {$info['size']} bytes, {$info['mime']}.\n");
+ } else {
+ wfDebug(__METHOD__.": $this->path NOT FOUND!\n");
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $info;
+ }
+
+ /**
+ * Placeholder file properties to use for files that don't exist
+ *
+ * @return Array
+ */
+ public static function placeholderProps() {
+ $info = array();
+ $info['fileExists'] = false;
+ $info['mime'] = null;
+ $info['media_type'] = MEDIATYPE_UNKNOWN;
+ $info['metadata'] = '';
+ $info['sha1'] = '';
+ $info['width'] = 0;
+ $info['height'] = 0;
+ $info['bits'] = 0;
+ return $info;
+ }
+
+ /**
+ * Exract image size information
+ *
+ * @param $gis array
+ * @return Array
+ */
+ protected function extractImageSizeInfo( array $gis ) {
+ $info = array();
+ # NOTE: $gis[2] contains a code for the image type. This is no longer used.
+ $info['width'] = $gis[0];
+ $info['height'] = $gis[1];
+ if ( isset( $gis['bits'] ) ) {
+ $info['bits'] = $gis['bits'];
+ } else {
+ $info['bits'] = 0;
+ }
+ return $info;
+ }
+
+ /**
+ * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
+ * encoding, zero padded to 31 digits.
+ *
+ * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
+ * fairly neatly.
+ *
+ * @return bool|string False on failure
+ */
+ public function getSha1Base36() {
+ wfProfileIn( __METHOD__ );
+
+ wfSuppressWarnings();
+ $hash = sha1_file( $this->path );
+ wfRestoreWarnings();
+ if ( $hash !== false ) {
+ $hash = wfBaseConvert( $hash, 16, 36, 31 );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $hash;
+ }
+
+ /**
+ * Get the final file extension from a file system path
+ *
+ * @param $path string
+ * @return string
+ */
+ public static function extensionFromPath( $path ) {
+ $i = strrpos( $path, '.' );
+ return strtolower( $i ? substr( $path, $i + 1 ) : '' );
+ }
+
+ /**
+ * Get an associative array containing information about a file in the local filesystem.
+ *
+ * @param $path String: absolute local filesystem path
+ * @param $ext Mixed: the file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension.
+ *
+ * @return array
+ */
+ public static function getPropsFromPath( $path, $ext = true ) {
+ $fsFile = new self( $path );
+ return $fsFile->getProps( $ext );
+ }
+
+ /**
+ * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
+ * encoding, zero padded to 31 digits.
+ *
+ * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
+ * fairly neatly.
+ *
+ * @param $path string
+ *
+ * @return bool|string False on failure
+ */
+ public static function getSha1Base36FromPath( $path ) {
+ $fsFile = new self( $path );
+ return $fsFile->getSha1Base36();
+ }
+}
--- /dev/null
+<?php
+/**
+ * File system based backend.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Class for a file system (FS) based file backend.
+ *
+ * All "containers" each map to a directory under the backend's base directory.
+ * For backwards-compatibility, some container paths can be set to custom paths.
+ * The wiki ID will not be used in any custom paths, so this should be avoided.
+ *
+ * Having directories with thousands of files will diminish performance.
+ * Sharding can be accomplished by using FileRepo-style hash paths.
+ *
+ * Status messages should avoid mentioning the internal FS paths.
+ * PHP warnings are assumed to be logged rather than output.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+class FSFileBackend extends FileBackendStore {
+ protected $basePath; // string; directory holding the container directories
+ /** @var Array Map of container names to root paths */
+ protected $containerPaths = array(); // for custom container paths
+ protected $fileMode; // integer; file permission mode
+
+ protected $hadWarningErrors = array();
+
+ /**
+ * @see FileBackendStore::__construct()
+ * Additional $config params include:
+ * - basePath : File system directory that holds containers.
+ * - containerPaths : Map of container names to custom file system directories.
+ * This should only be used for backwards-compatibility.
+ * - fileMode : Octal UNIX file permissions to use on files stored.
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+
+ // Remove any possible trailing slash from directories
+ if ( isset( $config['basePath'] ) ) {
+ $this->basePath = rtrim( $config['basePath'], '/' ); // remove trailing slash
+ } else {
+ $this->basePath = null; // none; containers must have explicit paths
+ }
+
+ if ( isset( $config['containerPaths'] ) ) {
+ $this->containerPaths = (array)$config['containerPaths'];
+ foreach ( $this->containerPaths as &$path ) {
+ $path = rtrim( $path, '/' ); // remove trailing slash
+ }
+ }
+
+ $this->fileMode = isset( $config['fileMode'] )
+ ? $config['fileMode']
+ : 0644;
+ }
+
+ /**
+ * @see FileBackendStore::resolveContainerPath()
+ * @param $container string
+ * @param $relStoragePath string
+ * @return null|string
+ */
+ protected function resolveContainerPath( $container, $relStoragePath ) {
+ // Check that container has a root directory
+ if ( isset( $this->containerPaths[$container] ) || isset( $this->basePath ) ) {
+ // Check for sane relative paths (assume the base paths are OK)
+ if ( $this->isLegalRelPath( $relStoragePath ) ) {
+ return $relStoragePath;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sanity check a relative file system path for validity
+ *
+ * @param $path string Normalized relative path
+ * @return bool
+ */
+ protected function isLegalRelPath( $path ) {
+ // Check for file names longer than 255 chars
+ if ( preg_match( '![^/]{256}!', $path ) ) { // ext3/NTFS
+ return false;
+ }
+ if ( wfIsWindows() ) { // NTFS
+ return !preg_match( '![:*?"<>|]!', $path );
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Given the short (unresolved) and full (resolved) name of
+ * a container, return the file system path of the container.
+ *
+ * @param $shortCont string
+ * @param $fullCont string
+ * @return string|null
+ */
+ protected function containerFSRoot( $shortCont, $fullCont ) {
+ if ( isset( $this->containerPaths[$shortCont] ) ) {
+ return $this->containerPaths[$shortCont];
+ } elseif ( isset( $this->basePath ) ) {
+ return "{$this->basePath}/{$fullCont}";
+ }
+ return null; // no container base path defined
+ }
+
+ /**
+ * Get the absolute file system path for a storage path
+ *
+ * @param $storagePath string Storage path
+ * @return string|null
+ */
+ protected function resolveToFSPath( $storagePath ) {
+ list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
+ if ( $relPath === null ) {
+ return null; // invalid
+ }
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $storagePath );
+ $fsPath = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ if ( $relPath != '' ) {
+ $fsPath .= "/{$relPath}";
+ }
+ return $fsPath;
+ }
+
+ /**
+ * @see FileBackendStore::isPathUsableInternal()
+ * @return bool
+ */
+ public function isPathUsableInternal( $storagePath ) {
+ $fsPath = $this->resolveToFSPath( $storagePath );
+ if ( $fsPath === null ) {
+ return false; // invalid
+ }
+ $parentDir = dirname( $fsPath );
+
+ if ( file_exists( $fsPath ) ) {
+ $ok = is_file( $fsPath ) && is_writable( $fsPath );
+ } else {
+ $ok = is_dir( $parentDir ) && is_writable( $parentDir );
+ }
+
+ return $ok;
+ }
+
+ /**
+ * @see FileBackendStore::doStoreInternal()
+ * @return Status
+ */
+ protected function doStoreInternal( array $params ) {
+ $status = Status::newGood();
+
+ $dest = $this->resolveToFSPath( $params['dst'] );
+ if ( $dest === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ if ( file_exists( $dest ) ) {
+ if ( !empty( $params['overwrite'] ) ) {
+ $ok = unlink( $dest );
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-delete', $params['dst'] );
+ return $status;
+ }
+ } else {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ }
+
+ if ( !empty( $params['async'] ) ) { // deferred
+ $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
+ wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
+ ) );
+ $status->value = new FSFileOpHandle( $this, $params, 'Store', $cmd, $dest );
+ } else { // immediate write
+ $ok = copy( $params['src'], $dest );
+ // In some cases (at least over NFS), copy() returns true when it fails
+ if ( !$ok || ( filesize( $params['src'] ) !== filesize( $dest ) ) ) {
+ if ( $ok ) { // PHP bug
+ unlink( $dest ); // remove broken file
+ trigger_error( __METHOD__ . ": copy() failed but returned true." );
+ }
+ $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
+ return $status;
+ }
+ $this->chmod( $dest );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FSFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseStore( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doCopyInternal()
+ * @return Status
+ */
+ protected function doCopyInternal( array $params ) {
+ $status = Status::newGood();
+
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ $dest = $this->resolveToFSPath( $params['dst'] );
+ if ( $dest === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ if ( file_exists( $dest ) ) {
+ if ( !empty( $params['overwrite'] ) ) {
+ $ok = unlink( $dest );
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-delete', $params['dst'] );
+ return $status;
+ }
+ } else {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ }
+
+ if ( !empty( $params['async'] ) ) { // deferred
+ $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
+ wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
+ ) );
+ $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd, $dest );
+ } else { // immediate write
+ $ok = copy( $source, $dest );
+ // In some cases (at least over NFS), copy() returns true when it fails
+ if ( !$ok || ( filesize( $source ) !== filesize( $dest ) ) ) {
+ if ( $ok ) { // PHP bug
+ unlink( $dest ); // remove broken file
+ trigger_error( __METHOD__ . ": copy() failed but returned true." );
+ }
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ return $status;
+ }
+ $this->chmod( $dest );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FSFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseCopy( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doMoveInternal()
+ * @return Status
+ */
+ protected function doMoveInternal( array $params ) {
+ $status = Status::newGood();
+
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ $dest = $this->resolveToFSPath( $params['dst'] );
+ if ( $dest === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ if ( file_exists( $dest ) ) {
+ if ( !empty( $params['overwrite'] ) ) {
+ // Windows does not support moving over existing files
+ if ( wfIsWindows() ) {
+ $ok = unlink( $dest );
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-delete', $params['dst'] );
+ return $status;
+ }
+ }
+ } else {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ }
+
+ if ( !empty( $params['async'] ) ) { // deferred
+ $cmd = implode( ' ', array( wfIsWindows() ? 'MOVE' : 'mv',
+ wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
+ wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
+ ) );
+ $status->value = new FSFileOpHandle( $this, $params, 'Move', $cmd );
+ } else { // immediate write
+ $ok = rename( $source, $dest );
+ clearstatcache(); // file no longer at source
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ return $status;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FSFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseMove( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doDeleteInternal()
+ * @return Status
+ */
+ protected function doDeleteInternal( array $params ) {
+ $status = Status::newGood();
+
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ if ( !is_file( $source ) ) {
+ if ( empty( $params['ignoreMissingSource'] ) ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ }
+ return $status; // do nothing; either OK or bad status
+ }
+
+ if ( !empty( $params['async'] ) ) { // deferred
+ $cmd = implode( ' ', array( wfIsWindows() ? 'DEL' : 'unlink',
+ wfEscapeShellArg( $this->cleanPathSlashes( $source ) )
+ ) );
+ $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
+ } else { // immediate write
+ $ok = unlink( $source );
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ return $status;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FSFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseDelete( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doCreateInternal()
+ * @return Status
+ */
+ protected function doCreateInternal( array $params ) {
+ $status = Status::newGood();
+
+ $dest = $this->resolveToFSPath( $params['dst'] );
+ if ( $dest === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ if ( file_exists( $dest ) ) {
+ if ( !empty( $params['overwrite'] ) ) {
+ $ok = unlink( $dest );
+ if ( !$ok ) {
+ $status->fatal( 'backend-fail-delete', $params['dst'] );
+ return $status;
+ }
+ } else {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ }
+
+ if ( !empty( $params['async'] ) ) { // deferred
+ $tempFile = TempFSFile::factory( 'create_', 'tmp' );
+ if ( !$tempFile ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ return $status;
+ }
+ $bytes = file_put_contents( $tempFile->getPath(), $params['content'] );
+ if ( $bytes === false ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ return $status;
+ }
+ $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+ wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
+ wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
+ ) );
+ $status->value = new FSFileOpHandle( $this, $params, 'Create', $cmd, $dest );
+ $tempFile->bind( $status->value );
+ } else { // immediate write
+ $bytes = file_put_contents( $dest, $params['content'] );
+ if ( $bytes === false ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ return $status;
+ }
+ $this->chmod( $dest );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FSFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseCreate( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doPrepareInternal()
+ * @return Status
+ */
+ protected function doPrepareInternal( $fullCont, $dirRel, array $params ) {
+ $status = Status::newGood();
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ $existed = is_dir( $dir ); // already there?
+ if ( !wfMkdirParents( $dir ) ) { // make directory and its parents
+ $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
+ } elseif ( !is_writable( $dir ) ) {
+ $status->fatal( 'directoryreadonlyerror', $params['dir'] );
+ } elseif ( !is_readable( $dir ) ) {
+ $status->fatal( 'directorynotreadableerror', $params['dir'] );
+ }
+ if ( is_dir( $dir ) && !$existed ) {
+ // Respect any 'noAccess' or 'noListing' flags...
+ $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doSecureInternal()
+ * @return Status
+ */
+ protected function doSecureInternal( $fullCont, $dirRel, array $params ) {
+ $status = Status::newGood();
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ // Seed new directories with a blank index.html, to prevent crawling...
+ if ( !empty( $params['noListing'] ) && !file_exists( "{$dir}/index.html" ) ) {
+ $bytes = file_put_contents( "{$dir}/index.html", $this->indexHtmlPrivate() );
+ if ( $bytes === false ) {
+ $status->fatal( 'backend-fail-create', $params['dir'] . '/index.html' );
+ return $status;
+ }
+ }
+ // Add a .htaccess file to the root of the container...
+ if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
+ $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
+ if ( $bytes === false ) {
+ $storeDir = "mwstore://{$this->name}/{$shortCont}";
+ $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
+ return $status;
+ }
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doPublishInternal()
+ * @return Status
+ */
+ protected function doPublishInternal( $fullCont, $dirRel, array $params ) {
+ $status = Status::newGood();
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ // Unseed new directories with a blank index.html, to allow crawling...
+ if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
+ $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
+ if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
+ $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
+ return $status;
+ }
+ }
+ // Remove the .htaccess file from the root of the container...
+ if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
+ $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
+ if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
+ $storeDir = "mwstore://{$this->name}/{$shortCont}";
+ $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
+ return $status;
+ }
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doCleanInternal()
+ * @return Status
+ */
+ protected function doCleanInternal( $fullCont, $dirRel, array $params ) {
+ $status = Status::newGood();
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ wfSuppressWarnings();
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir ); // remove directory if empty
+ }
+ wfRestoreWarnings();
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doFileExists()
+ * @return array|bool|null
+ */
+ protected function doGetFileStat( array $params ) {
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ return false; // invalid storage path
+ }
+
+ $this->trapWarnings(); // don't trust 'false' if there were errors
+ $stat = is_file( $source ) ? stat( $source ) : false; // regular files only
+ $hadError = $this->untrapWarnings();
+
+ if ( $stat ) {
+ return array(
+ 'mtime' => wfTimestamp( TS_MW, $stat['mtime'] ),
+ 'size' => $stat['size']
+ );
+ } elseif ( !$hadError ) {
+ return false; // file does not exist
+ } else {
+ return null; // failure
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doClearCache()
+ */
+ protected function doClearCache( array $paths = null ) {
+ clearstatcache(); // clear the PHP file stat cache
+ }
+
+ /**
+ * @see FileBackendStore::doDirectoryExists()
+ * @return bool|null
+ */
+ protected function doDirectoryExists( $fullCont, $dirRel, array $params ) {
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+
+ $this->trapWarnings(); // don't trust 'false' if there were errors
+ $exists = is_dir( $dir );
+ $hadError = $this->untrapWarnings();
+
+ return $hadError ? null : $exists;
+ }
+
+ /**
+ * @see FileBackendStore::getDirectoryListInternal()
+ * @return Array|null
+ */
+ public function getDirectoryListInternal( $fullCont, $dirRel, array $params ) {
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ $exists = is_dir( $dir );
+ if ( !$exists ) {
+ wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
+ return array(); // nothing under this dir
+ } elseif ( !is_readable( $dir ) ) {
+ wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
+ return null; // bad permissions?
+ }
+ return new FSFileBackendDirList( $dir, $params );
+ }
+
+ /**
+ * @see FileBackendStore::getFileListInternal()
+ * @return array|FSFileBackendFileList|null
+ */
+ public function getFileListInternal( $fullCont, $dirRel, array $params ) {
+ list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
+ $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
+ $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
+ $exists = is_dir( $dir );
+ if ( !$exists ) {
+ wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
+ return array(); // nothing under this dir
+ } elseif ( !is_readable( $dir ) ) {
+ wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
+ return null; // bad permissions?
+ }
+ return new FSFileBackendFileList( $dir, $params );
+ }
+
+ /**
+ * @see FileBackendStore::getLocalReference()
+ * @return FSFile|null
+ */
+ public function getLocalReference( array $params ) {
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ return null;
+ }
+ return new FSFile( $source );
+ }
+
+ /**
+ * @see FileBackendStore::getLocalCopy()
+ * @return null|TempFSFile
+ */
+ public function getLocalCopy( array $params ) {
+ $source = $this->resolveToFSPath( $params['src'] );
+ if ( $source === null ) {
+ return null;
+ }
+
+ // Create a new temporary file with the same extension...
+ $ext = FileBackend::extensionFromPath( $params['src'] );
+ $tmpFile = TempFSFile::factory( wfBaseName( $source ) . '_', $ext );
+ if ( !$tmpFile ) {
+ return null;
+ }
+ $tmpPath = $tmpFile->getPath();
+
+ // Copy the source file over the temp file
+ $ok = copy( $source, $tmpPath );
+ if ( !$ok ) {
+ return null;
+ }
+
+ $this->chmod( $tmpPath );
+
+ return $tmpFile;
+ }
+
+ /**
+ * @see FileBackendStore::directoriesAreVirtual()
+ * @return bool
+ */
+ protected function directoriesAreVirtual() {
+ return false;
+ }
+
+ /**
+ * @see FileBackendStore::doExecuteOpHandlesInternal()
+ * @return Array List of corresponding Status objects
+ */
+ protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
+ $statuses = array();
+
+ $pipes = array();
+ foreach ( $fileOpHandles as $index => $fileOpHandle ) {
+ $pipes[$index] = popen( "{$fileOpHandle->cmd} 2>&1", 'r' );
+ }
+
+ $errs = array();
+ foreach ( $pipes as $index => $pipe ) {
+ // Result will be empty on success in *NIX. On Windows,
+ // it may be something like " 1 file(s) [copied|moved].".
+ $errs[$index] = stream_get_contents( $pipe );
+ fclose( $pipe );
+ }
+
+ foreach ( $fileOpHandles as $index => $fileOpHandle ) {
+ $status = Status::newGood();
+ $function = '_getResponse' . $fileOpHandle->call;
+ $this->$function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
+ $statuses[$index] = $status;
+ if ( $status->isOK() && $fileOpHandle->chmodPath ) {
+ $this->chmod( $fileOpHandle->chmodPath );
+ }
+ }
+
+ clearstatcache(); // files changed
+ return $statuses;
+ }
+
+ /**
+ * Chmod a file, suppressing the warnings
+ *
+ * @param $path string Absolute file system path
+ * @return bool Success
+ */
+ protected function chmod( $path ) {
+ wfSuppressWarnings();
+ $ok = chmod( $path, $this->fileMode );
+ wfRestoreWarnings();
+
+ return $ok;
+ }
+
+ /**
+ * Return the text of an index.html file to hide directory listings
+ *
+ * @return string
+ */
+ protected function indexHtmlPrivate() {
+ return '';
+ }
+
+ /**
+ * Return the text of a .htaccess file to make a directory private
+ *
+ * @return string
+ */
+ protected function htaccessPrivate() {
+ return "Deny from all\n";
+ }
+
+ /**
+ * Clean up directory separators for the given OS
+ *
+ * @param $path string FS path
+ * @return string
+ */
+ protected function cleanPathSlashes( $path ) {
+ return wfIsWindows() ? strtr( $path, '/', '\\' ) : $path;
+ }
+
+ /**
+ * Listen for E_WARNING errors and track whether any happen
+ *
+ * @return bool
+ */
+ protected function trapWarnings() {
+ $this->hadWarningErrors[] = false; // push to stack
+ set_error_handler( array( $this, 'handleWarning' ), E_WARNING );
+ return false; // invoke normal PHP error handler
+ }
+
+ /**
+ * Stop listening for E_WARNING errors and return true if any happened
+ *
+ * @return bool
+ */
+ protected function untrapWarnings() {
+ restore_error_handler(); // restore previous handler
+ return array_pop( $this->hadWarningErrors ); // pop from stack
+ }
+
+ /**
+ * @return bool
+ */
+ private function handleWarning() {
+ $this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
+ return true; // suppress from PHP handler
+ }
+}
+
+/**
+ * @see FileBackendStoreOpHandle
+ */
+class FSFileOpHandle extends FileBackendStoreOpHandle {
+ public $cmd; // string; shell command
+ public $chmodPath; // string; file to chmod
+
+ /**
+ * @param $backend
+ * @param $params array
+ * @param $call
+ * @param $cmd
+ * @param $chmodPath null
+ */
+ public function __construct( $backend, array $params, $call, $cmd, $chmodPath = null ) {
+ $this->backend = $backend;
+ $this->params = $params;
+ $this->call = $call;
+ $this->cmd = $cmd;
+ $this->chmodPath = $chmodPath;
+ }
+}
+
+/**
+ * Wrapper around RecursiveDirectoryIterator/DirectoryIterator that
+ * catches exception or does any custom behavoir that we may want.
+ * Do not use this class from places outside FSFileBackend.
+ *
+ * @ingroup FileBackend
+ */
+abstract class FSFileBackendList implements Iterator {
+ /** @var Iterator */
+ protected $iter;
+ protected $suffixStart; // integer
+ protected $pos = 0; // integer
+ /** @var Array */
+ protected $params = array();
+
+ /**
+ * @param $dir string file system directory
+ * @param $params array
+ */
+ public function __construct( $dir, array $params ) {
+ $dir = realpath( $dir ); // normalize
+ $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/"
+ $this->params = $params;
+
+ try {
+ $this->iter = $this->initIterator( $dir );
+ } catch ( UnexpectedValueException $e ) {
+ $this->iter = null; // bad permissions? deleted?
+ }
+ }
+
+ /**
+ * Return an appropriate iterator object to wrap
+ *
+ * @param $dir string file system directory
+ * @return Iterator
+ */
+ protected function initIterator( $dir ) {
+ if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
+ # Get an iterator that will get direct sub-nodes
+ return new DirectoryIterator( $dir );
+ } else { // recursive
+ # Get an iterator that will return leaf nodes (non-directories)
+ # RecursiveDirectoryIterator extends FilesystemIterator.
+ # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
+ $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
+ return new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator( $dir, $flags ),
+ RecursiveIteratorIterator::CHILD_FIRST // include dirs
+ );
+ }
+ }
+
+ /**
+ * @see Iterator::key()
+ * @return integer
+ */
+ public function key() {
+ return $this->pos;
+ }
+
+ /**
+ * @see Iterator::current()
+ * @return string|bool String or false
+ */
+ public function current() {
+ return $this->getRelPath( $this->iter->current()->getPathname() );
+ }
+
+ /**
+ * @see Iterator::next()
+ * @return void
+ */
+ public function next() {
+ try {
+ $this->iter->next();
+ $this->filterViaNext();
+ } catch ( UnexpectedValueException $e ) {
+ $this->iter = null;
+ }
+ ++$this->pos;
+ }
+
+ /**
+ * @see Iterator::rewind()
+ * @return void
+ */
+ public function rewind() {
+ $this->pos = 0;
+ try {
+ $this->iter->rewind();
+ $this->filterViaNext();
+ } catch ( UnexpectedValueException $e ) {
+ $this->iter = null;
+ }
+ }
+
+ /**
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ return $this->iter && $this->iter->valid();
+ }
+
+ /**
+ * Filter out items by advancing to the next ones
+ */
+ protected function filterViaNext() {}
+
+ /**
+ * Return only the relative path and normalize slashes to FileBackend-style.
+ * Uses the "real path" since the suffix is based upon that.
+ *
+ * @param $path string
+ * @return string
+ */
+ protected function getRelPath( $path ) {
+ return strtr( substr( realpath( $path ), $this->suffixStart ), '\\', '/' );
+ }
+}
+
+class FSFileBackendDirList extends FSFileBackendList {
+ protected function filterViaNext() {
+ while ( $this->iter->valid() ) {
+ if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
+ $this->iter->next(); // skip non-directories and dot files
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+class FSFileBackendFileList extends FSFileBackendList {
+ protected function filterViaNext() {
+ while ( $this->iter->valid() ) {
+ if ( !$this->iter->current()->isFile() ) {
+ $this->iter->next(); // skip non-files and dot files
+ } else {
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * @defgroup FileBackend File backend
+ * @ingroup FileRepo
+ *
+ * File backend is used to interact with file storage systems,
+ * such as the local file system, NFS, or cloud storage systems.
+ */
+
+/**
+ * Base class for all file backends.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Base class for all file backend classes (including multi-write backends).
+ *
+ * This class defines the methods as abstract that subclasses must implement.
+ * Outside callers can assume that all backends will have these functions.
+ *
+ * All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
+ * The "<path>" portion is a relative path that uses UNIX file system (FS)
+ * notation, though any particular backend may not actually be using a local
+ * filesystem.
+ * Therefore, the relative paths are only virtual.
+ *
+ * Backend contents are stored under wiki-specific container names by default.
+ * For legacy reasons, this has no effect for the FS backend class, and per-wiki
+ * segregation must be done by setting the container paths appropriately.
+ *
+ * FS-based backends are somewhat more restrictive due to the existence of real
+ * directory files; a regular file cannot have the same name as a directory. Other
+ * backends with virtual directories may not have this limitation. Callers should
+ * store files in such a way that no files and directories are under the same path.
+ *
+ * Methods should avoid throwing exceptions at all costs.
+ * As a corollary, external dependencies should be kept to a minimum.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+abstract class FileBackend {
+ protected $name; // string; unique backend name
+ protected $wikiId; // string; unique wiki name
+ protected $readOnly; // string; read-only explanation message
+ protected $parallelize; // string; when to do operations in parallel
+ protected $concurrency; // integer; how many operations can be done in parallel
+
+ /** @var LockManager */
+ protected $lockManager;
+ /** @var FileJournal */
+ protected $fileJournal;
+
+ /**
+ * Create a new backend instance from configuration.
+ * This should only be called from within FileBackendGroup.
+ *
+ * $config includes:
+ * - name : The unique name of this backend.
+ * This should consist of alphanumberic, '-', and '_' characters.
+ * This name should not be changed after use.
+ * - wikiId : Prefix to container names that is unique to this wiki.
+ * It should only consist of alphanumberic, '-', and '_' characters.
+ * - lockManager : Registered name of a file lock manager to use.
+ * - fileJournal : File journal configuration; see FileJournal::factory().
+ * Journals simply log changes to files stored in the backend.
+ * - readOnly : Write operations are disallowed if this is a non-empty string.
+ * It should be an explanation for the backend being read-only.
+ * - parallelize : When to do file operations in parallel (when possible).
+ * Allowed values are "implicit", "explicit" and "off".
+ * - concurrency : How many file operations can be done in parallel.
+ *
+ * @param $config Array
+ * @throws MWException
+ */
+ public function __construct( array $config ) {
+ $this->name = $config['name'];
+ if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
+ throw new MWException( "Backend name `{$this->name}` is invalid." );
+ }
+ $this->wikiId = isset( $config['wikiId'] )
+ ? $config['wikiId']
+ : wfWikiID(); // e.g. "my_wiki-en_"
+ $this->lockManager = ( $config['lockManager'] instanceof LockManager )
+ ? $config['lockManager']
+ : LockManagerGroup::singleton()->get( $config['lockManager'] );
+ $this->fileJournal = isset( $config['fileJournal'] )
+ ? ( ( $config['fileJournal'] instanceof FileJournal )
+ ? $config['fileJournal']
+ : FileJournal::factory( $config['fileJournal'], $this->name ) )
+ : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
+ $this->readOnly = isset( $config['readOnly'] )
+ ? (string)$config['readOnly']
+ : '';
+ $this->parallelize = isset( $config['parallelize'] )
+ ? (string)$config['parallelize']
+ : 'off';
+ $this->concurrency = isset( $config['concurrency'] )
+ ? (int)$config['concurrency']
+ : 50;
+ }
+
+ /**
+ * Get the unique backend name.
+ * We may have multiple different backends of the same type.
+ * For example, we can have two Swift backends using different proxies.
+ *
+ * @return string
+ */
+ final public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Check if this backend is read-only
+ *
+ * @return bool
+ */
+ final public function isReadOnly() {
+ return ( $this->readOnly != '' );
+ }
+
+ /**
+ * Get an explanatory message if this backend is read-only
+ *
+ * @return string|bool Returns false if the backend is not read-only
+ */
+ final public function getReadOnlyReason() {
+ return ( $this->readOnly != '' ) ? $this->readOnly : false;
+ }
+
+ /**
+ * This is the main entry point into the backend for write operations.
+ * Callers supply an ordered list of operations to perform as a transaction.
+ * Files will be locked, the stat cache cleared, and then the operations attempted.
+ * If any serious errors occur, all attempted operations will be rolled back.
+ *
+ * $ops is an array of arrays. The outer array holds a list of operations.
+ * Each inner array is a set of key value pairs that specify an operation.
+ *
+ * Supported operations and their parameters. The supported actions are:
+ * - create
+ * - store
+ * - copy
+ * - move
+ * - delete
+ * - null
+ *
+ * a) Create a new file in storage with the contents of a string
+ * @code
+ * array(
+ * 'op' => 'create',
+ * 'dst' => <storage path>,
+ * 'content' => <string of new file contents>,
+ * 'overwrite' => <boolean>,
+ * 'overwriteSame' => <boolean>
+ * );
+ * @endcode
+ *
+ * b) Copy a file system file into storage
+ * @code
+ * array(
+ * 'op' => 'store',
+ * 'src' => <file system path>,
+ * 'dst' => <storage path>,
+ * 'overwrite' => <boolean>,
+ * 'overwriteSame' => <boolean>
+ * )
+ * @endcode
+ *
+ * c) Copy a file within storage
+ * @code
+ * array(
+ * 'op' => 'copy',
+ * 'src' => <storage path>,
+ * 'dst' => <storage path>,
+ * 'overwrite' => <boolean>,
+ * 'overwriteSame' => <boolean>
+ * )
+ * @endcode
+ *
+ * d) Move a file within storage
+ * @code
+ * array(
+ * 'op' => 'move',
+ * 'src' => <storage path>,
+ * 'dst' => <storage path>,
+ * 'overwrite' => <boolean>,
+ * 'overwriteSame' => <boolean>
+ * )
+ * @endcode
+ *
+ * e) Delete a file within storage
+ * @code
+ * array(
+ * 'op' => 'delete',
+ * 'src' => <storage path>,
+ * 'ignoreMissingSource' => <boolean>
+ * )
+ * @endcode
+ *
+ * f) Do nothing (no-op)
+ * @code
+ * array(
+ * 'op' => 'null',
+ * )
+ * @endcode
+ *
+ * Boolean flags for operations (operation-specific):
+ * - ignoreMissingSource : The operation will simply succeed and do
+ * nothing if the source file does not exist.
+ * - overwrite : Any destination file will be overwritten.
+ * - overwriteSame : An error will not be given if a file already
+ * exists at the destination that has the same
+ * contents as the new contents to be written there.
+ *
+ * $opts is an associative of boolean flags, including:
+ * - force : Operation precondition errors no longer trigger an abort.
+ * Any remaining operations are still attempted. Unexpected
+ * failures may still cause remaning operations to be aborted.
+ * - nonLocking : No locks are acquired for the operations.
+ * This can increase performance for non-critical writes.
+ * This has no effect unless the 'force' flag is set.
+ * - allowStale : Don't require the latest available data.
+ * This can increase performance for non-critical writes.
+ * This has no effect unless the 'force' flag is set.
+ * - nonJournaled : Don't log this operation batch in the file journal.
+ * This limits the ability of recovery scripts.
+ * - parallelize : Try to do operations in parallel when possible.
+ * - bypassReadOnly : Allow writes in read-only mode (since 1.20).
+ *
+ * @remarks Remarks on locking:
+ * File system paths given to operations should refer to files that are
+ * already locked or otherwise safe from modification from other processes.
+ * Normally these files will be new temp files, which should be adequate.
+ *
+ * @par Return value:
+ *
+ * This returns a Status, which contains all warnings and fatals that occurred
+ * during the operation. The 'failCount', 'successCount', and 'success' members
+ * will reflect each operation attempted.
+ *
+ * The status will be "OK" unless:
+ * - a) unexpected operation errors occurred (network partitions, disk full...)
+ * - b) significant operation errors occurred and 'force' was not set
+ *
+ * @param $ops Array List of operations to execute in order
+ * @param $opts Array Batch operation options
+ * @return Status
+ */
+ final public function doOperations( array $ops, array $opts = array() ) {
+ if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ if ( empty( $opts['force'] ) ) { // sanity
+ unset( $opts['nonLocking'] );
+ unset( $opts['allowStale'] );
+ }
+ $opts['concurrency'] = 1; // off
+ if ( $this->parallelize === 'implicit' ) {
+ if ( !isset( $opts['parallelize'] ) || $opts['parallelize'] ) {
+ $opts['concurrency'] = $this->concurrency;
+ }
+ } elseif ( $this->parallelize === 'explicit' ) {
+ if ( !empty( $opts['parallelize'] ) ) {
+ $opts['concurrency'] = $this->concurrency;
+ }
+ }
+ return $this->doOperationsInternal( $ops, $opts );
+ }
+
+ /**
+ * @see FileBackend::doOperations()
+ */
+ abstract protected function doOperationsInternal( array $ops, array $opts );
+
+ /**
+ * Same as doOperations() except it takes a single operation.
+ * If you are doing a batch of operations that should either
+ * all succeed or all fail, then use that function instead.
+ *
+ * @see FileBackend::doOperations()
+ *
+ * @param $op Array Operation
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function doOperation( array $op, array $opts = array() ) {
+ return $this->doOperations( array( $op ), $opts );
+ }
+
+ /**
+ * Performs a single create operation.
+ * This sets $params['op'] to 'create' and passes it to doOperation().
+ *
+ * @see FileBackend::doOperation()
+ *
+ * @param $params Array Operation parameters
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function create( array $params, array $opts = array() ) {
+ return $this->doOperation( array( 'op' => 'create' ) + $params, $opts );
+ }
+
+ /**
+ * Performs a single store operation.
+ * This sets $params['op'] to 'store' and passes it to doOperation().
+ *
+ * @see FileBackend::doOperation()
+ *
+ * @param $params Array Operation parameters
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function store( array $params, array $opts = array() ) {
+ return $this->doOperation( array( 'op' => 'store' ) + $params, $opts );
+ }
+
+ /**
+ * Performs a single copy operation.
+ * This sets $params['op'] to 'copy' and passes it to doOperation().
+ *
+ * @see FileBackend::doOperation()
+ *
+ * @param $params Array Operation parameters
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function copy( array $params, array $opts = array() ) {
+ return $this->doOperation( array( 'op' => 'copy' ) + $params, $opts );
+ }
+
+ /**
+ * Performs a single move operation.
+ * This sets $params['op'] to 'move' and passes it to doOperation().
+ *
+ * @see FileBackend::doOperation()
+ *
+ * @param $params Array Operation parameters
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function move( array $params, array $opts = array() ) {
+ return $this->doOperation( array( 'op' => 'move' ) + $params, $opts );
+ }
+
+ /**
+ * Performs a single delete operation.
+ * This sets $params['op'] to 'delete' and passes it to doOperation().
+ *
+ * @see FileBackend::doOperation()
+ *
+ * @param $params Array Operation parameters
+ * @param $opts Array Operation options
+ * @return Status
+ */
+ final public function delete( array $params, array $opts = array() ) {
+ return $this->doOperation( array( 'op' => 'delete' ) + $params, $opts );
+ }
+
+ /**
+ * Perform a set of independent file operations on some files.
+ *
+ * This does no locking, nor journaling, and possibly no stat calls.
+ * Any destination files that already exist will be overwritten.
+ * This should *only* be used on non-original files, like cache files.
+ *
+ * Supported operations and their parameters:
+ * - create
+ * - store
+ * - copy
+ * - move
+ * - delete
+ * - null
+ *
+ * a) Create a new file in storage with the contents of a string
+ * @code
+ * array(
+ * 'op' => 'create',
+ * 'dst' => <storage path>,
+ * 'content' => <string of new file contents>
+ * )
+ * @endcode
+ * b) Copy a file system file into storage
+ * @code
+ * array(
+ * 'op' => 'store',
+ * 'src' => <file system path>,
+ * 'dst' => <storage path>
+ * )
+ * @endcode
+ * c) Copy a file within storage
+ * @code
+ * array(
+ * 'op' => 'copy',
+ * 'src' => <storage path>,
+ * 'dst' => <storage path>
+ * )
+ * @endcode
+ * d) Move a file within storage
+ * @code
+ * array(
+ * 'op' => 'move',
+ * 'src' => <storage path>,
+ * 'dst' => <storage path>
+ * )
+ * @endcode
+ * e) Delete a file within storage
+ * @code
+ * array(
+ * 'op' => 'delete',
+ * 'src' => <storage path>,
+ * 'ignoreMissingSource' => <boolean>
+ * )
+ * @endcode
+ * f) Do nothing (no-op)
+ * @code
+ * array(
+ * 'op' => 'null',
+ * )
+ * @endcode
+ *
+ * @par Boolean flags for operations (operation-specific):
+ * - ignoreMissingSource : The operation will simply succeed and do
+ * nothing if the source file does not exist.
+ *
+ * $opts is an associative of boolean flags, including:
+ * - bypassReadOnly : Allow writes in read-only mode (since 1.20)
+ *
+ * @par Return value:
+ * This returns a Status, which contains all warnings and fatals that occurred
+ * during the operation. The 'failCount', 'successCount', and 'success' members
+ * will reflect each operation attempted for the given files. The status will be
+ * considered "OK" as long as no fatal errors occurred.
+ *
+ * @param $ops Array Set of operations to execute
+ * @param $opts Array Batch operation options
+ * @return Status
+ * @since 1.20
+ */
+ final public function doQuickOperations( array $ops, array $opts = array() ) {
+ if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ foreach ( $ops as &$op ) {
+ $op['overwrite'] = true; // avoids RTTs in key/value stores
+ }
+ return $this->doQuickOperationsInternal( $ops );
+ }
+
+ /**
+ * @see FileBackend::doQuickOperations()
+ * @since 1.20
+ */
+ abstract protected function doQuickOperationsInternal( array $ops );
+
+ /**
+ * Same as doQuickOperations() except it takes a single operation.
+ * If you are doing a batch of operations, then use that function instead.
+ *
+ * @see FileBackend::doQuickOperations()
+ *
+ * @param $op Array Operation
+ * @return Status
+ * @since 1.20
+ */
+ final public function doQuickOperation( array $op ) {
+ return $this->doQuickOperations( array( $op ) );
+ }
+
+ /**
+ * Performs a single quick create operation.
+ * This sets $params['op'] to 'create' and passes it to doQuickOperation().
+ *
+ * @see FileBackend::doQuickOperation()
+ *
+ * @param $params Array Operation parameters
+ * @return Status
+ * @since 1.20
+ */
+ final public function quickCreate( array $params ) {
+ return $this->doQuickOperation( array( 'op' => 'create' ) + $params );
+ }
+
+ /**
+ * Performs a single quick store operation.
+ * This sets $params['op'] to 'store' and passes it to doQuickOperation().
+ *
+ * @see FileBackend::doQuickOperation()
+ *
+ * @param $params Array Operation parameters
+ * @return Status
+ * @since 1.20
+ */
+ final public function quickStore( array $params ) {
+ return $this->doQuickOperation( array( 'op' => 'store' ) + $params );
+ }
+
+ /**
+ * Performs a single quick copy operation.
+ * This sets $params['op'] to 'copy' and passes it to doQuickOperation().
+ *
+ * @see FileBackend::doQuickOperation()
+ *
+ * @param $params Array Operation parameters
+ * @return Status
+ * @since 1.20
+ */
+ final public function quickCopy( array $params ) {
+ return $this->doQuickOperation( array( 'op' => 'copy' ) + $params );
+ }
+
+ /**
+ * Performs a single quick move operation.
+ * This sets $params['op'] to 'move' and passes it to doQuickOperation().
+ *
+ * @see FileBackend::doQuickOperation()
+ *
+ * @param $params Array Operation parameters
+ * @return Status
+ * @since 1.20
+ */
+ final public function quickMove( array $params ) {
+ return $this->doQuickOperation( array( 'op' => 'move' ) + $params );
+ }
+
+ /**
+ * Performs a single quick delete operation.
+ * This sets $params['op'] to 'delete' and passes it to doQuickOperation().
+ *
+ * @see FileBackend::doQuickOperation()
+ *
+ * @param $params Array Operation parameters
+ * @return Status
+ * @since 1.20
+ */
+ final public function quickDelete( array $params ) {
+ return $this->doQuickOperation( array( 'op' => 'delete' ) + $params );
+ }
+
+ /**
+ * Concatenate a list of storage files into a single file system file.
+ * The target path should refer to a file that is already locked or
+ * otherwise safe from modification from other processes. Normally,
+ * the file will be a new temp file, which should be adequate.
+ *
+ * @param $params Array Operation parameters
+ * $params include:
+ * - srcs : ordered source storage paths (e.g. chunk1, chunk2, ...)
+ * - dst : file system path to 0-byte temp file
+ * @return Status
+ */
+ abstract public function concatenate( array $params );
+
+ /**
+ * Prepare a storage directory for usage.
+ * This will create any required containers and parent directories.
+ * Backends using key/value stores only need to create the container.
+ *
+ * The 'noAccess' and 'noListing' parameters works the same as in secure(),
+ * except they are only applied *if* the directory/container had to be created.
+ * These flags should always be set for directories that have private files.
+ *
+ * @param $params Array
+ * $params include:
+ * - dir : storage directory
+ * - noAccess : try to deny file access (since 1.20)
+ * - noListing : try to deny file listing (since 1.20)
+ * - bypassReadOnly : allow writes in read-only mode (since 1.20)
+ * @return Status
+ */
+ final public function prepare( array $params ) {
+ if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ return $this->doPrepare( $params );
+ }
+
+ /**
+ * @see FileBackend::prepare()
+ */
+ abstract protected function doPrepare( array $params );
+
+ /**
+ * Take measures to block web access to a storage directory and
+ * the container it belongs to. FS backends might add .htaccess
+ * files whereas key/value store backends might revoke container
+ * access to the storage user representing end-users in web requests.
+ * This is not guaranteed to actually do anything.
+ *
+ * @param $params Array
+ * $params include:
+ * - dir : storage directory
+ * - noAccess : try to deny file access
+ * - noListing : try to deny file listing
+ * - bypassReadOnly : allow writes in read-only mode (since 1.20)
+ * @return Status
+ */
+ final public function secure( array $params ) {
+ if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ return $this->doSecure( $params );
+ }
+
+ /**
+ * @see FileBackend::secure()
+ */
+ abstract protected function doSecure( array $params );
+
+ /**
+ * Remove measures to block web access to a storage directory and
+ * the container it belongs to. FS backends might remove .htaccess
+ * files whereas key/value store backends might grant container
+ * access to the storage user representing end-users in web requests.
+ * This essentially can undo the result of secure() calls.
+ *
+ * @param $params Array
+ * $params include:
+ * - dir : storage directory
+ * - access : try to allow file access
+ * - listing : try to allow file listing
+ * - bypassReadOnly : allow writes in read-only mode (since 1.20)
+ * @return Status
+ * @since 1.20
+ */
+ final public function publish( array $params ) {
+ if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ return $this->doPublish( $params );
+ }
+
+ /**
+ * @see FileBackend::publish()
+ */
+ abstract protected function doPublish( array $params );
+
+ /**
+ * Delete a storage directory if it is empty.
+ * Backends using key/value stores may do nothing unless the directory
+ * is that of an empty container, in which case it should be deleted.
+ *
+ * @param $params Array
+ * $params include:
+ * - dir : storage directory
+ * - recursive : recursively delete empty subdirectories first (since 1.20)
+ * - bypassReadOnly : allow writes in read-only mode (since 1.20)
+ * @return Status
+ */
+ final public function clean( array $params ) {
+ if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
+ return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
+ }
+ return $this->doClean( $params );
+ }
+
+ /**
+ * @see FileBackend::clean()
+ */
+ abstract protected function doClean( array $params );
+
+ /**
+ * Check if a file exists at a storage path in the backend.
+ * This returns false if only a directory exists at the path.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return bool|null Returns null on failure
+ */
+ abstract public function fileExists( array $params );
+
+ /**
+ * Get the last-modified timestamp of the file at a storage path.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return string|bool TS_MW timestamp or false on failure
+ */
+ abstract public function getFileTimestamp( array $params );
+
+ /**
+ * Get the contents of a file at a storage path in the backend.
+ * This should be avoided for potentially large files.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return string|bool Returns false on failure
+ */
+ abstract public function getFileContents( array $params );
+
+ /**
+ * Get the size (bytes) of a file at a storage path in the backend.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return integer|bool Returns false on failure
+ */
+ abstract public function getFileSize( array $params );
+
+ /**
+ * Get quick information about a file at a storage path in the backend.
+ * If the file does not exist, then this returns false.
+ * Otherwise, the result is an associative array that includes:
+ * - mtime : the last-modified timestamp (TS_MW)
+ * - size : the file size (bytes)
+ * Additional values may be included for internal use only.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return Array|bool|null Returns null on failure
+ */
+ abstract public function getFileStat( array $params );
+
+ /**
+ * Get a SHA-1 hash of the file at a storage path in the backend.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return string|bool Hash string or false on failure
+ */
+ abstract public function getFileSha1Base36( array $params );
+
+ /**
+ * Get the properties of the file at a storage path in the backend.
+ * Returns FSFile::placeholderProps() on failure.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return Array
+ */
+ abstract public function getFileProps( array $params );
+
+ /**
+ * Stream the file at a storage path in the backend.
+ * If the file does not exists, a 404 error will be given.
+ * Appropriate HTTP headers (Status, Content-Type, Content-Length)
+ * must be sent if streaming began, while none should be sent otherwise.
+ * Implementations should flush the output buffer before sending data.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - headers : additional HTTP headers to send on success
+ * - latest : use the latest available data
+ * @return Status
+ */
+ abstract public function streamFile( array $params );
+
+ /**
+ * Returns a file system file, identical to the file at a storage path.
+ * The file returned is either:
+ * - a) A local copy of the file at a storage path in the backend.
+ * The temporary copy will have the same extension as the source.
+ * - b) An original of the file at a storage path in the backend.
+ * Temporary files may be purged when the file object falls out of scope.
+ *
+ * Write operations should *never* be done on this file as some backends
+ * may do internal tracking or may be instances of FileBackendMultiWrite.
+ * In that later case, there are copies of the file that must stay in sync.
+ * Additionally, further calls to this function may return the same file.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return FSFile|null Returns null on failure
+ */
+ abstract public function getLocalReference( array $params );
+
+ /**
+ * Get a local copy on disk of the file at a storage path in the backend.
+ * The temporary copy will have the same file extension as the source.
+ * Temporary files may be purged when the file object falls out of scope.
+ *
+ * @param $params Array
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return TempFSFile|null Returns null on failure
+ */
+ abstract public function getLocalCopy( array $params );
+
+ /**
+ * Check if a directory exists at a given storage path.
+ * Backends using key/value stores will check if the path is a
+ * virtual directory, meaning there are files under the given directory.
+ *
+ * Storage backends with eventual consistency might return stale data.
+ *
+ * @param $params array
+ * $params include:
+ * - dir : storage directory
+ * @return bool|null Returns null on failure
+ * @since 1.20
+ */
+ abstract public function directoryExists( array $params );
+
+ /**
+ * Get an iterator to list *all* directories under a storage directory.
+ * If the directory is of the form "mwstore://backend/container",
+ * then all directories in the container should be listed.
+ * If the directory is of form "mwstore://backend/container/dir",
+ * then all directories directly under that directory should be listed.
+ * Results should be storage directories relative to the given directory.
+ *
+ * Storage backends with eventual consistency might return stale data.
+ *
+ * @param $params array
+ * $params include:
+ * - dir : storage directory
+ * - topOnly : only return direct child dirs of the directory
+ * @return Traversable|Array|null Returns null on failure
+ * @since 1.20
+ */
+ abstract public function getDirectoryList( array $params );
+
+ /**
+ * Same as FileBackend::getDirectoryList() except only lists
+ * directories that are immediately under the given directory.
+ *
+ * Storage backends with eventual consistency might return stale data.
+ *
+ * @param $params array
+ * $params include:
+ * - dir : storage directory
+ * @return Traversable|Array|null Returns null on failure
+ * @since 1.20
+ */
+ final public function getTopDirectoryList( array $params ) {
+ return $this->getDirectoryList( array( 'topOnly' => true ) + $params );
+ }
+
+ /**
+ * Get an iterator to list *all* stored files under a storage directory.
+ * If the directory is of the form "mwstore://backend/container",
+ * then all files in the container should be listed.
+ * If the directory is of form "mwstore://backend/container/dir",
+ * then all files under that directory should be listed.
+ * Results should be storage paths relative to the given directory.
+ *
+ * Storage backends with eventual consistency might return stale data.
+ *
+ * @param $params array
+ * $params include:
+ * - dir : storage directory
+ * - topOnly : only return direct child files of the directory (since 1.20)
+ * @return Traversable|Array|null Returns null on failure
+ */
+ abstract public function getFileList( array $params );
+
+ /**
+ * Same as FileBackend::getFileList() except only lists
+ * files that are immediately under the given directory.
+ *
+ * Storage backends with eventual consistency might return stale data.
+ *
+ * @param $params array
+ * $params include:
+ * - dir : storage directory
+ * @return Traversable|Array|null Returns null on failure
+ * @since 1.20
+ */
+ final public function getTopFileList( array $params ) {
+ return $this->getFileList( array( 'topOnly' => true ) + $params );
+ }
+
+ /**
+ * Invalidate any in-process file existence and property cache.
+ * If $paths is given, then only the cache for those files will be cleared.
+ *
+ * @param $paths Array Storage paths (optional)
+ * @return void
+ */
+ public function clearCache( array $paths = null ) {}
+
+ /**
+ * Lock the files at the given storage paths in the backend.
+ * This will either lock all the files or none (on failure).
+ *
+ * Callers should consider using getScopedFileLocks() instead.
+ *
+ * @param $paths Array Storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @return Status
+ */
+ final public function lockFiles( array $paths, $type ) {
+ return $this->lockManager->lock( $paths, $type );
+ }
+
+ /**
+ * Unlock the files at the given storage paths in the backend.
+ *
+ * @param $paths Array Storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @return Status
+ */
+ final public function unlockFiles( array $paths, $type ) {
+ return $this->lockManager->unlock( $paths, $type );
+ }
+
+ /**
+ * Lock the files at the given storage paths in the backend.
+ * This will either lock all the files or none (on failure).
+ * On failure, the status object will be updated with errors.
+ *
+ * Once the return value goes out scope, the locks will be released and
+ * the status updated. Unlock fatals will not change the status "OK" value.
+ *
+ * @param $paths Array Storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @param $status Status Status to update on lock/unlock
+ * @return ScopedLock|null Returns null on failure
+ */
+ final public function getScopedFileLocks( array $paths, $type, Status $status ) {
+ return ScopedLock::factory( $this->lockManager, $paths, $type, $status );
+ }
+
+ /**
+ * Get an array of scoped locks needed for a batch of file operations.
+ *
+ * Normally, FileBackend::doOperations() handles locking, unless
+ * the 'nonLocking' param is passed in. This function is useful if you
+ * want the files to be locked for a broader scope than just when the
+ * files are changing. For example, if you need to update DB metadata,
+ * you may want to keep the files locked until finished.
+ *
+ * @see FileBackend::doOperations()
+ *
+ * @param $ops Array List of file operations to FileBackend::doOperations()
+ * @param $status Status Status to update on lock/unlock
+ * @return Array List of ScopedFileLocks or null values
+ * @since 1.20
+ */
+ abstract public function getScopedLocksForOps( array $ops, Status $status );
+
+ /**
+ * Get the root storage path of this backend.
+ * All container paths are "subdirectories" of this path.
+ *
+ * @return string Storage path
+ * @since 1.20
+ */
+ final public function getRootStoragePath() {
+ return "mwstore://{$this->name}";
+ }
+
+ /**
+ * Get the file journal object for this backend
+ *
+ * @return FileJournal
+ */
+ final public function getJournal() {
+ return $this->fileJournal;
+ }
+
+ /**
+ * Check if a given path is a "mwstore://" path.
+ * This does not do any further validation or any existence checks.
+ *
+ * @param $path string
+ * @return bool
+ */
+ final public static function isStoragePath( $path ) {
+ return ( strpos( $path, 'mwstore://' ) === 0 );
+ }
+
+ /**
+ * Split a storage path into a backend name, a container name,
+ * and a relative file path. The relative path may be the empty string.
+ * This does not do any path normalization or traversal checks.
+ *
+ * @param $storagePath string
+ * @return Array (backend, container, rel object) or (null, null, null)
+ */
+ final public static function splitStoragePath( $storagePath ) {
+ if ( self::isStoragePath( $storagePath ) ) {
+ // Remove the "mwstore://" prefix and split the path
+ $parts = explode( '/', substr( $storagePath, 10 ), 3 );
+ if ( count( $parts ) >= 2 && $parts[0] != '' && $parts[1] != '' ) {
+ if ( count( $parts ) == 3 ) {
+ return $parts; // e.g. "backend/container/path"
+ } else {
+ return array( $parts[0], $parts[1], '' ); // e.g. "backend/container"
+ }
+ }
+ }
+ return array( null, null, null );
+ }
+
+ /**
+ * Normalize a storage path by cleaning up directory separators.
+ * Returns null if the path is not of the format of a valid storage path.
+ *
+ * @param $storagePath string
+ * @return string|null
+ */
+ final public static function normalizeStoragePath( $storagePath ) {
+ list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
+ if ( $relPath !== null ) { // must be for this backend
+ $relPath = self::normalizeContainerPath( $relPath );
+ if ( $relPath !== null ) {
+ return ( $relPath != '' )
+ ? "mwstore://{$backend}/{$container}/{$relPath}"
+ : "mwstore://{$backend}/{$container}";
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the parent storage directory of a storage path.
+ * This returns a path like "mwstore://backend/container",
+ * "mwstore://backend/container/...", or null if there is no parent.
+ *
+ * @param $storagePath string
+ * @return string|null
+ */
+ final public static function parentStoragePath( $storagePath ) {
+ $storagePath = dirname( $storagePath );
+ list( $b, $cont, $rel ) = self::splitStoragePath( $storagePath );
+ return ( $rel === null ) ? null : $storagePath;
+ }
+
+ /**
+ * Get the final extension from a storage or FS path
+ *
+ * @param $path string
+ * @return string
+ */
+ final public static function extensionFromPath( $path ) {
+ $i = strrpos( $path, '.' );
+ return strtolower( $i ? substr( $path, $i + 1 ) : '' );
+ }
+
+ /**
+ * Check if a relative path has no directory traversals
+ *
+ * @param $path string
+ * @return bool
+ * @since 1.20
+ */
+ final public static function isPathTraversalFree( $path ) {
+ return ( self::normalizeContainerPath( $path ) !== null );
+ }
+
+ /**
+ * Validate and normalize a relative storage path.
+ * Null is returned if the path involves directory traversal.
+ * Traversal is insecure for FS backends and broken for others.
+ *
+ * This uses the same traversal protection as Title::secureAndSplit().
+ *
+ * @param $path string Storage path relative to a container
+ * @return string|null
+ */
+ final protected static function normalizeContainerPath( $path ) {
+ // Normalize directory separators
+ $path = strtr( $path, '\\', '/' );
+ // Collapse any consecutive directory separators
+ $path = preg_replace( '![/]{2,}!', '/', $path );
+ // Remove any leading directory separator
+ $path = ltrim( $path, '/' );
+ // Use the same traversal protection as Title::secureAndSplit()
+ if ( strpos( $path, '.' ) !== false ) {
+ if (
+ $path === '.' ||
+ $path === '..' ||
+ strpos( $path, './' ) === 0 ||
+ strpos( $path, '../' ) === 0 ||
+ strpos( $path, '/./' ) !== false ||
+ strpos( $path, '/../' ) !== false
+ ) {
+ return null;
+ }
+ }
+ return $path;
+ }
+}
--- /dev/null
+<?php
+/**
+ * File backend registration handling.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * Class to handle file backend registration
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+class FileBackendGroup {
+ /**
+ * @var FileBackendGroup
+ */
+ protected static $instance = null;
+
+ /** @var Array (name => ('class' => string, 'config' => array, 'instance' => object)) */
+ protected $backends = array();
+
+ protected function __construct() {}
+
+ /**
+ * @return FileBackendGroup
+ */
+ public static function singleton() {
+ if ( self::$instance == null ) {
+ self::$instance = new self();
+ self::$instance->initFromGlobals();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Destroy the singleton instance
+ *
+ * @return void
+ */
+ public static function destroySingleton() {
+ self::$instance = null;
+ }
+
+ /**
+ * Register file backends from the global variables
+ *
+ * @return void
+ */
+ protected function initFromGlobals() {
+ global $wgLocalFileRepo, $wgForeignFileRepos, $wgFileBackends;
+
+ // Register explicitly defined backends
+ $this->register( $wgFileBackends );
+
+ $autoBackends = array();
+ // Automatically create b/c backends for file repos...
+ $repos = array_merge( $wgForeignFileRepos, array( $wgLocalFileRepo ) );
+ foreach ( $repos as $info ) {
+ $backendName = $info['backend'];
+ if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
+ continue; // already defined (or set to the object for some reason)
+ }
+ $repoName = $info['name'];
+ // Local vars that used to be FSRepo members...
+ $directory = $info['directory'];
+ $deletedDir = isset( $info['deletedDir'] )
+ ? $info['deletedDir']
+ : false; // deletion disabled
+ $thumbDir = isset( $info['thumbDir'] )
+ ? $info['thumbDir']
+ : "{$directory}/thumb";
+ $fileMode = isset( $info['fileMode'] )
+ ? $info['fileMode']
+ : 0644;
+ // Get the FS backend configuration
+ $autoBackends[] = array(
+ 'name' => $backendName,
+ 'class' => 'FSFileBackend',
+ 'lockManager' => 'fsLockManager',
+ 'containerPaths' => array(
+ "{$repoName}-public" => "{$directory}",
+ "{$repoName}-thumb" => $thumbDir,
+ "{$repoName}-deleted" => $deletedDir,
+ "{$repoName}-temp" => "{$directory}/temp"
+ ),
+ 'fileMode' => $fileMode,
+ );
+ }
+
+ // Register implicitly defined backends
+ $this->register( $autoBackends );
+ }
+
+ /**
+ * Register an array of file backend configurations
+ *
+ * @param $configs Array
+ * @return void
+ * @throws MWException
+ */
+ protected function register( array $configs ) {
+ foreach ( $configs as $config ) {
+ if ( !isset( $config['name'] ) ) {
+ throw new MWException( "Cannot register a backend with no name." );
+ }
+ $name = $config['name'];
+ if ( !isset( $config['class'] ) ) {
+ throw new MWException( "Cannot register backend `{$name}` with no class." );
+ }
+ $class = $config['class'];
+
+ unset( $config['class'] ); // backend won't need this
+ $this->backends[$name] = array(
+ 'class' => $class,
+ 'config' => $config,
+ 'instance' => null
+ );
+ }
+ }
+
+ /**
+ * Get the backend object with a given name
+ *
+ * @param $name string
+ * @return FileBackend
+ * @throws MWException
+ */
+ public function get( $name ) {
+ if ( !isset( $this->backends[$name] ) ) {
+ throw new MWException( "No backend defined with the name `$name`." );
+ }
+ // Lazy-load the actual backend instance
+ if ( !isset( $this->backends[$name]['instance'] ) ) {
+ $class = $this->backends[$name]['class'];
+ $config = $this->backends[$name]['config'];
+ $this->backends[$name]['instance'] = new $class( $config );
+ }
+ return $this->backends[$name]['instance'];
+ }
+
+ /**
+ * Get the config array for a backend object with a given name
+ *
+ * @param $name string
+ * @return Array
+ * @throws MWException
+ */
+ public function config( $name ) {
+ if ( !isset( $this->backends[$name] ) ) {
+ throw new MWException( "No backend defined with the name `$name`." );
+ }
+ $class = $this->backends[$name]['class'];
+ return array( 'class' => $class ) + $this->backends[$name]['config'];
+ }
+
+ /**
+ * Get an appropriate backend object from a storage path
+ *
+ * @param $storagePath string
+ * @return FileBackend|null Backend or null on failure
+ */
+ public function backendFromPath( $storagePath ) {
+ list( $backend, $c, $p ) = FileBackend::splitStoragePath( $storagePath );
+ if ( $backend !== null && isset( $this->backends[$backend] ) ) {
+ return $this->get( $backend );
+ }
+ return null;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Proxy backend that mirrors writes to several internal backends.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Proxy backend that mirrors writes to several internal backends.
+ *
+ * This class defines a multi-write backend. Multiple backends can be
+ * registered to this proxy backend and it will act as a single backend.
+ * Use this when all access to those backends is through this proxy backend.
+ * At least one of the backends must be declared the "master" backend.
+ *
+ * Only use this class when transitioning from one storage system to another.
+ *
+ * Read operations are only done on the 'master' backend for consistency.
+ * Write operations are performed on all backends, in the order defined.
+ * If an operation fails on one backend it will be rolled back from the others.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+class FileBackendMultiWrite extends FileBackend {
+ /** @var Array Prioritized list of FileBackendStore objects */
+ protected $backends = array(); // array of (backend index => backends)
+ protected $masterIndex = -1; // integer; index of master backend
+ protected $syncChecks = 0; // integer bitfield
+
+ /* Possible internal backend consistency checks */
+ const CHECK_SIZE = 1;
+ const CHECK_TIME = 2;
+ const CHECK_SHA1 = 4;
+
+ /**
+ * Construct a proxy backend that consists of several internal backends.
+ * Locking, journaling, and read-only checks are handled by the proxy backend.
+ *
+ * Additional $config params include:
+ * - backends : Array of backend config and multi-backend settings.
+ * Each value is the config used in the constructor of a
+ * FileBackendStore class, but with these additional settings:
+ * - class : The name of the backend class
+ * - isMultiMaster : This must be set for one backend.
+ * - template: : If given a backend name, this will use
+ * the config of that backend as a template.
+ * Values specified here take precedence.
+ * - syncChecks : Integer bitfield of internal backend sync checks to perform.
+ * Possible bits include the FileBackendMultiWrite::CHECK_* constants.
+ * There are constants for SIZE, TIME, and SHA1.
+ * The checks are done before allowing any file operations.
+ * @param $config Array
+ * @throws MWException
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+ $namesUsed = array();
+ // Construct backends here rather than via registration
+ // to keep these backends hidden from outside the proxy.
+ foreach ( $config['backends'] as $index => $config ) {
+ if ( isset( $config['template'] ) ) {
+ // Config is just a modified version of a registered backend's.
+ // This should only be used when that config is used only by this backend.
+ $config = $config + FileBackendGroup::singleton()->config( $config['template'] );
+ }
+ $name = $config['name'];
+ if ( isset( $namesUsed[$name] ) ) { // don't break FileOp predicates
+ throw new MWException( "Two or more backends defined with the name $name." );
+ }
+ $namesUsed[$name] = 1;
+ // Alter certain sub-backend settings for sanity
+ unset( $config['readOnly'] ); // use proxy backend setting
+ unset( $config['fileJournal'] ); // use proxy backend journal
+ $config['wikiId'] = $this->wikiId; // use the proxy backend wiki ID
+ $config['lockManager'] = 'nullLockManager'; // lock under proxy backend
+ if ( !empty( $config['isMultiMaster'] ) ) {
+ if ( $this->masterIndex >= 0 ) {
+ throw new MWException( 'More than one master backend defined.' );
+ }
+ $this->masterIndex = $index; // this is the "master"
+ $config['fileJournal'] = $this->fileJournal; // log under proxy backend
+ }
+ // Create sub-backend object
+ if ( !isset( $config['class'] ) ) {
+ throw new MWException( 'No class given for a backend config.' );
+ }
+ $class = $config['class'];
+ $this->backends[$index] = new $class( $config );
+ }
+ if ( $this->masterIndex < 0 ) { // need backends and must have a master
+ throw new MWException( 'No master backend defined.' );
+ }
+ $this->syncChecks = isset( $config['syncChecks'] )
+ ? $config['syncChecks']
+ : self::CHECK_SIZE;
+ }
+
+ /**
+ * @see FileBackend::doOperationsInternal()
+ * @return Status
+ */
+ final protected function doOperationsInternal( array $ops, array $opts ) {
+ $status = Status::newGood();
+
+ $mbe = $this->backends[$this->masterIndex]; // convenience
+
+ // Get the paths to lock from the master backend
+ $realOps = $this->substOpBatchPaths( $ops, $mbe );
+ $paths = $mbe->getPathsToLockForOpsInternal( $mbe->getOperationsInternal( $realOps ) );
+ // Get the paths under the proxy backend's name
+ $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
+ $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
+ // Try to lock those files for the scope of this function...
+ if ( empty( $opts['nonLocking'] ) ) {
+ // Try to lock those files for the scope of this function...
+ $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
+ $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+ if ( !$status->isOK() ) {
+ return $status; // abort
+ }
+ }
+ // Clear any cache entries (after locks acquired)
+ $this->clearCache();
+ // Do a consistency check to see if the backends agree
+ $status->merge( $this->consistencyCheck( $this->fileStoragePathsForOps( $ops ) ) );
+ if ( !$status->isOK() ) {
+ return $status; // abort
+ }
+ // Actually attempt the operation batch on the master backend...
+ $masterStatus = $mbe->doOperations( $realOps, $opts );
+ $status->merge( $masterStatus );
+ // Propagate the operations to the clone backends...
+ foreach ( $this->backends as $index => $backend ) {
+ if ( $index !== $this->masterIndex ) { // not done already
+ $realOps = $this->substOpBatchPaths( $ops, $backend );
+ $status->merge( $backend->doOperations( $realOps, $opts ) );
+ }
+ }
+ // Make 'success', 'successCount', and 'failCount' fields reflect
+ // the overall operation, rather than all the batches for each backend.
+ // Do this by only using success values from the master backend's batch.
+ $status->success = $masterStatus->success;
+ $status->successCount = $masterStatus->successCount;
+ $status->failCount = $masterStatus->failCount;
+
+ return $status;
+ }
+
+ /**
+ * Check that a set of files are consistent across all internal backends
+ *
+ * @param $paths Array
+ * @return Status
+ */
+ public function consistencyCheck( array $paths ) {
+ $status = Status::newGood();
+ if ( $this->syncChecks == 0 || count( $this->backends ) <= 1 ) {
+ return $status; // skip checks
+ }
+
+ $mBackend = $this->backends[$this->masterIndex];
+ foreach ( array_unique( $paths ) as $path ) {
+ $params = array( 'src' => $path, 'latest' => true );
+ $mParams = $this->substOpPaths( $params, $mBackend );
+ // Stat the file on the 'master' backend
+ $mStat = $mBackend->getFileStat( $mParams );
+ if ( $this->syncChecks & self::CHECK_SHA1 ) {
+ $mSha1 = $mBackend->getFileSha1( $mParams );
+ } else {
+ $mSha1 = false;
+ }
+ $mUsable = $mBackend->isPathUsableInternal( $mParams['src'] );
+ // Check of all clone backends agree with the master...
+ foreach ( $this->backends as $index => $cBackend ) {
+ if ( $index === $this->masterIndex ) {
+ continue; // master
+ }
+ $cParams = $this->substOpPaths( $params, $cBackend );
+ $cStat = $cBackend->getFileStat( $cParams );
+ if ( $mStat ) { // file is in master
+ if ( !$cStat ) { // file should exist
+ $status->fatal( 'backend-fail-synced', $path );
+ continue;
+ }
+ if ( $this->syncChecks & self::CHECK_SIZE ) {
+ if ( $cStat['size'] != $mStat['size'] ) { // wrong size
+ $status->fatal( 'backend-fail-synced', $path );
+ continue;
+ }
+ }
+ if ( $this->syncChecks & self::CHECK_TIME ) {
+ $mTs = wfTimestamp( TS_UNIX, $mStat['mtime'] );
+ $cTs = wfTimestamp( TS_UNIX, $cStat['mtime'] );
+ if ( abs( $mTs - $cTs ) > 30 ) { // outdated file somewhere
+ $status->fatal( 'backend-fail-synced', $path );
+ continue;
+ }
+ }
+ if ( $this->syncChecks & self::CHECK_SHA1 ) {
+ if ( $cBackend->getFileSha1( $cParams ) !== $mSha1 ) { // wrong SHA1
+ $status->fatal( 'backend-fail-synced', $path );
+ continue;
+ }
+ }
+ } else { // file is not in master
+ if ( $cStat ) { // file should not exist
+ $status->fatal( 'backend-fail-synced', $path );
+ }
+ }
+ if ( $mUsable !== $cBackend->isPathUsableInternal( $cParams['src'] ) ) {
+ $status->fatal( 'backend-fail-synced', $path );
+ }
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Get a list of file storage paths to read or write for a list of operations
+ *
+ * @param $ops Array Same format as doOperations()
+ * @return Array List of storage paths to files (does not include directories)
+ */
+ protected function fileStoragePathsForOps( array $ops ) {
+ $paths = array();
+ foreach ( $ops as $op ) {
+ if ( isset( $op['src'] ) ) {
+ $paths[] = $op['src'];
+ }
+ if ( isset( $op['srcs'] ) ) {
+ $paths = array_merge( $paths, $op['srcs'] );
+ }
+ if ( isset( $op['dst'] ) ) {
+ $paths[] = $op['dst'];
+ }
+ }
+ return array_unique( $paths );
+ }
+
+ /**
+ * Substitute the backend name in storage path parameters
+ * for a set of operations with that of a given internal backend.
+ *
+ * @param $ops Array List of file operation arrays
+ * @param $backend FileBackendStore
+ * @return Array
+ */
+ protected function substOpBatchPaths( array $ops, FileBackendStore $backend ) {
+ $newOps = array(); // operations
+ foreach ( $ops as $op ) {
+ $newOp = $op; // operation
+ foreach ( array( 'src', 'srcs', 'dst', 'dir' ) as $par ) {
+ if ( isset( $newOp[$par] ) ) { // string or array
+ $newOp[$par] = $this->substPaths( $newOp[$par], $backend );
+ }
+ }
+ $newOps[] = $newOp;
+ }
+ return $newOps;
+ }
+
+ /**
+ * Same as substOpBatchPaths() but for a single operation
+ *
+ * @param $ops array File operation array
+ * @param $backend FileBackendStore
+ * @return Array
+ */
+ protected function substOpPaths( array $ops, FileBackendStore $backend ) {
+ $newOps = $this->substOpBatchPaths( array( $ops ), $backend );
+ return $newOps[0];
+ }
+
+ /**
+ * Substitute the backend of storage paths with an internal backend's name
+ *
+ * @param $paths Array|string List of paths or single string path
+ * @param $backend FileBackendStore
+ * @return Array|string
+ */
+ protected function substPaths( $paths, FileBackendStore $backend ) {
+ return preg_replace(
+ '!^mwstore://' . preg_quote( $this->name ) . '/!',
+ StringUtils::escapeRegexReplacement( "mwstore://{$backend->getName()}/" ),
+ $paths // string or array
+ );
+ }
+
+ /**
+ * Substitute the backend of internal storage paths with the proxy backend's name
+ *
+ * @param $paths Array|string List of paths or single string path
+ * @return Array|string
+ */
+ protected function unsubstPaths( $paths ) {
+ return preg_replace(
+ '!^mwstore://([^/]+)!',
+ StringUtils::escapeRegexReplacement( "mwstore://{$this->name}" ),
+ $paths // string or array
+ );
+ }
+
+ /**
+ * @see FileBackend::doQuickOperationsInternal()
+ * @return Status
+ */
+ protected function doQuickOperationsInternal( array $ops ) {
+ $status = Status::newGood();
+ // Do the operations on the master backend; setting Status fields...
+ $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
+ $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
+ $status->merge( $masterStatus );
+ // Propagate the operations to the clone backends...
+ foreach ( $this->backends as $index => $backend ) {
+ if ( $index !== $this->masterIndex ) { // not done already
+ $realOps = $this->substOpBatchPaths( $ops, $backend );
+ $status->merge( $backend->doQuickOperations( $realOps ) );
+ }
+ }
+ // Make 'success', 'successCount', and 'failCount' fields reflect
+ // the overall operation, rather than all the batches for each backend.
+ // Do this by only using success values from the master backend's batch.
+ $status->success = $masterStatus->success;
+ $status->successCount = $masterStatus->successCount;
+ $status->failCount = $masterStatus->failCount;
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doPrepare()
+ * @return Status
+ */
+ protected function doPrepare( array $params ) {
+ $status = Status::newGood();
+ foreach ( $this->backends as $backend ) {
+ $realParams = $this->substOpPaths( $params, $backend );
+ $status->merge( $backend->doPrepare( $realParams ) );
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doSecure()
+ * @param $params array
+ * @return Status
+ */
+ protected function doSecure( array $params ) {
+ $status = Status::newGood();
+ foreach ( $this->backends as $backend ) {
+ $realParams = $this->substOpPaths( $params, $backend );
+ $status->merge( $backend->doSecure( $realParams ) );
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doPublish()
+ * @param $params array
+ * @return Status
+ */
+ protected function doPublish( array $params ) {
+ $status = Status::newGood();
+ foreach ( $this->backends as $backend ) {
+ $realParams = $this->substOpPaths( $params, $backend );
+ $status->merge( $backend->doPublish( $realParams ) );
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doClean()
+ * @param $params array
+ * @return Status
+ */
+ protected function doClean( array $params ) {
+ $status = Status::newGood();
+ foreach ( $this->backends as $backend ) {
+ $realParams = $this->substOpPaths( $params, $backend );
+ $status->merge( $backend->doClean( $realParams ) );
+ }
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::concatenate()
+ * @param $params array
+ * @return Status
+ */
+ public function concatenate( array $params ) {
+ // We are writing to an FS file, so we don't need to do this per-backend
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->concatenate( $realParams );
+ }
+
+ /**
+ * @see FileBackend::fileExists()
+ * @param $params array
+ */
+ public function fileExists( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->fileExists( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileTimestamp()
+ * @param $params array
+ * @return bool|string
+ */
+ public function getFileTimestamp( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileTimestamp( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileSize()
+ * @param $params array
+ * @return bool|int
+ */
+ public function getFileSize( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileSize( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileStat()
+ * @param $params array
+ * @return Array|bool|null
+ */
+ public function getFileStat( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileStat( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileContents()
+ * @param $params array
+ * @return bool|string
+ */
+ public function getFileContents( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileContents( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileSha1Base36()
+ * @param $params array
+ * @return bool|string
+ */
+ public function getFileSha1Base36( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileSha1Base36( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileProps()
+ * @param $params array
+ * @return Array
+ */
+ public function getFileProps( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileProps( $realParams );
+ }
+
+ /**
+ * @see FileBackend::streamFile()
+ * @param $params array
+ * @return \Status
+ */
+ public function streamFile( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->streamFile( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getLocalReference()
+ * @param $params array
+ * @return FSFile|null
+ */
+ public function getLocalReference( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getLocalReference( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getLocalCopy()
+ * @param $params array
+ * @return null|TempFSFile
+ */
+ public function getLocalCopy( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getLocalCopy( $realParams );
+ }
+
+ /**
+ * @see FileBackend::directoryExists()
+ * @param $params array
+ * @return bool|null
+ */
+ public function directoryExists( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->directoryExists( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getSubdirectoryList()
+ * @param $params array
+ * @return Array|null|Traversable
+ */
+ public function getDirectoryList( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getDirectoryList( $realParams );
+ }
+
+ /**
+ * @see FileBackend::getFileList()
+ * @param $params array
+ * @return Array|null|\Traversable
+ */
+ public function getFileList( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileList( $realParams );
+ }
+
+ /**
+ * @see FileBackend::clearCache()
+ */
+ public function clearCache( array $paths = null ) {
+ foreach ( $this->backends as $backend ) {
+ $realPaths = is_array( $paths ) ? $this->substPaths( $paths, $backend ) : null;
+ $backend->clearCache( $realPaths );
+ }
+ }
+
+ /**
+ * @see FileBackend::getScopedLocksForOps()
+ */
+ public function getScopedLocksForOps( array $ops, Status $status ) {
+ $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $ops );
+ // Get the paths to lock from the master backend
+ $paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
+ // Get the paths under the proxy backend's name
+ $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
+ $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
+ return array(
+ $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
+ $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Base class for all backends using particular storage medium.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Base class for all backends using particular storage medium.
+ *
+ * This class defines the methods as abstract that subclasses must implement.
+ * Outside callers should *not* use functions with "Internal" in the name.
+ *
+ * The FileBackend operations are implemented using basic functions
+ * such as storeInternal(), copyInternal(), deleteInternal() and the like.
+ * This class is also responsible for path resolution and sanitization.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+abstract class FileBackendStore extends FileBackend {
+ /** @var BagOStuff */
+ protected $memCache;
+ /** @var ProcessCacheLRU */
+ protected $cheapCache; // Map of paths to small (RAM/disk) cache items
+ /** @var ProcessCacheLRU */
+ protected $expensiveCache; // Map of paths to large (RAM/disk) cache items
+
+ /** @var Array Map of container names to sharding settings */
+ protected $shardViaHashLevels = array(); // (container name => config array)
+
+ protected $maxFileSize = 4294967296; // integer bytes (4GiB)
+
+ /**
+ * @see FileBackend::__construct()
+ *
+ * @param $config Array
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+ $this->memCache = new EmptyBagOStuff(); // disabled by default
+ $this->cheapCache = new ProcessCacheLRU( 300 );
+ $this->expensiveCache = new ProcessCacheLRU( 5 );
+ }
+
+ /**
+ * Get the maximum allowable file size given backend
+ * medium restrictions and basic performance constraints.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * @return integer Bytes
+ */
+ final public function maxFileSizeInternal() {
+ return $this->maxFileSize;
+ }
+
+ /**
+ * Check if a file can be created at a given storage path.
+ * FS backends should check if the parent directory exists and the file is writable.
+ * Backends using key/value stores should check if the container exists.
+ *
+ * @param $storagePath string
+ * @return bool
+ */
+ abstract public function isPathUsableInternal( $storagePath );
+
+ /**
+ * Create a file in the backend with the given contents.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * $params include:
+ * - content : the raw file contents
+ * - dst : destination storage path
+ * - overwrite : overwrite any file that exists at the destination
+ * - async : Status will be returned immediately if supported.
+ * If the status is OK, then its value field will be
+ * set to a FileBackendStoreOpHandle object.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function createInternal( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
+ $status = Status::newFatal( 'backend-fail-maxsize',
+ $params['dst'], $this->maxFileSizeInternal() );
+ } else {
+ $status = $this->doCreateInternal( $params );
+ $this->clearCache( array( $params['dst'] ) );
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::createInternal()
+ */
+ abstract protected function doCreateInternal( array $params );
+
+ /**
+ * Store a file into the backend from a file on disk.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * $params include:
+ * - src : source path on disk
+ * - dst : destination storage path
+ * - overwrite : overwrite any file that exists at the destination
+ * - async : Status will be returned immediately if supported.
+ * If the status is OK, then its value field will be
+ * set to a FileBackendStoreOpHandle object.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function storeInternal( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
+ $status = Status::newFatal( 'backend-fail-maxsize',
+ $params['dst'], $this->maxFileSizeInternal() );
+ } else {
+ $status = $this->doStoreInternal( $params );
+ $this->clearCache( array( $params['dst'] ) );
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::storeInternal()
+ */
+ abstract protected function doStoreInternal( array $params );
+
+ /**
+ * Copy a file from one storage path to another in the backend.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * $params include:
+ * - src : source storage path
+ * - dst : destination storage path
+ * - overwrite : overwrite any file that exists at the destination
+ * - async : Status will be returned immediately if supported.
+ * If the status is OK, then its value field will be
+ * set to a FileBackendStoreOpHandle object.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function copyInternal( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = $this->doCopyInternal( $params );
+ $this->clearCache( array( $params['dst'] ) );
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::copyInternal()
+ */
+ abstract protected function doCopyInternal( array $params );
+
+ /**
+ * Delete a file at the storage path.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * $params include:
+ * - src : source storage path
+ * - ignoreMissingSource : do nothing if the source file does not exist
+ * - async : Status will be returned immediately if supported.
+ * If the status is OK, then its value field will be
+ * set to a FileBackendStoreOpHandle object.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function deleteInternal( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = $this->doDeleteInternal( $params );
+ $this->clearCache( array( $params['src'] ) );
+ $this->deleteFileCache( $params['src'] ); // persistent cache
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::deleteInternal()
+ */
+ abstract protected function doDeleteInternal( array $params );
+
+ /**
+ * Move a file from one storage path to another in the backend.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * $params include:
+ * - src : source storage path
+ * - dst : destination storage path
+ * - overwrite : overwrite any file that exists at the destination
+ * - async : Status will be returned immediately if supported.
+ * If the status is OK, then its value field will be
+ * set to a FileBackendStoreOpHandle object.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function moveInternal( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = $this->doMoveInternal( $params );
+ $this->clearCache( array( $params['src'], $params['dst'] ) );
+ $this->deleteFileCache( $params['src'] ); // persistent cache
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::moveInternal()
+ * @return Status
+ */
+ protected function doMoveInternal( array $params ) {
+ unset( $params['async'] ); // two steps, won't work here :)
+ // Copy source to dest
+ $status = $this->copyInternal( $params );
+ if ( $status->isOK() ) {
+ // Delete source (only fails due to races or medium going down)
+ $status->merge( $this->deleteInternal( array( 'src' => $params['src'] ) ) );
+ $status->setResult( true, $status->value ); // ignore delete() errors
+ }
+ return $status;
+ }
+
+ /**
+ * No-op file operation that does nothing.
+ * Do not call this function from places outside FileBackend and FileOp.
+ *
+ * @param $params Array
+ * @return Status
+ */
+ final public function nullInternal( array $params ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileBackend::concatenate()
+ * @return Status
+ */
+ final public function concatenate( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ // Try to lock the source files for the scope of this function
+ $scopeLockS = $this->getScopedFileLocks( $params['srcs'], LockManager::LOCK_UW, $status );
+ if ( $status->isOK() ) {
+ // Actually do the concatenation
+ $status->merge( $this->doConcatenate( $params ) );
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::concatenate()
+ * @return Status
+ */
+ protected function doConcatenate( array $params ) {
+ $status = Status::newGood();
+ $tmpPath = $params['dst']; // convenience
+
+ // Check that the specified temp file is valid...
+ wfSuppressWarnings();
+ $ok = ( is_file( $tmpPath ) && !filesize( $tmpPath ) );
+ wfRestoreWarnings();
+ if ( !$ok ) { // not present or not empty
+ $status->fatal( 'backend-fail-opentemp', $tmpPath );
+ return $status;
+ }
+
+ // Build up the temp file using the source chunks (in order)...
+ $tmpHandle = fopen( $tmpPath, 'ab' );
+ if ( $tmpHandle === false ) {
+ $status->fatal( 'backend-fail-opentemp', $tmpPath );
+ return $status;
+ }
+ foreach ( $params['srcs'] as $virtualSource ) {
+ // Get a local FS version of the chunk
+ $tmpFile = $this->getLocalReference( array( 'src' => $virtualSource ) );
+ if ( !$tmpFile ) {
+ $status->fatal( 'backend-fail-read', $virtualSource );
+ return $status;
+ }
+ // Get a handle to the local FS version
+ $sourceHandle = fopen( $tmpFile->getPath(), 'r' );
+ if ( $sourceHandle === false ) {
+ fclose( $tmpHandle );
+ $status->fatal( 'backend-fail-read', $virtualSource );
+ return $status;
+ }
+ // Append chunk to file (pass chunk size to avoid magic quotes)
+ if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
+ fclose( $sourceHandle );
+ fclose( $tmpHandle );
+ $status->fatal( 'backend-fail-writetemp', $tmpPath );
+ return $status;
+ }
+ fclose( $sourceHandle );
+ }
+ if ( !fclose( $tmpHandle ) ) {
+ $status->fatal( 'backend-fail-closetemp', $tmpPath );
+ return $status;
+ }
+
+ clearstatcache(); // temp file changed
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doPrepare()
+ * @return Status
+ */
+ final protected function doPrepare( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+
+ $status = Status::newGood();
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // invalid storage path
+ }
+
+ if ( $shard !== null ) { // confined to a single container/shard
+ $status->merge( $this->doPrepareInternal( $fullCont, $dir, $params ) );
+ } else { // directory is on several shards
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+ $status->merge( $this->doPrepareInternal( "{$fullCont}{$suffix}", $dir, $params ) );
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doPrepare()
+ * @return Status
+ */
+ protected function doPrepareInternal( $container, $dir, array $params ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileBackend::doSecure()
+ * @return Status
+ */
+ final protected function doSecure( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // invalid storage path
+ }
+
+ if ( $shard !== null ) { // confined to a single container/shard
+ $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
+ } else { // directory is on several shards
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+ $status->merge( $this->doSecureInternal( "{$fullCont}{$suffix}", $dir, $params ) );
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doSecure()
+ * @return Status
+ */
+ protected function doSecureInternal( $container, $dir, array $params ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileBackend::doPublish()
+ * @return Status
+ */
+ final protected function doPublish( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // invalid storage path
+ }
+
+ if ( $shard !== null ) { // confined to a single container/shard
+ $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
+ } else { // directory is on several shards
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+ $status->merge( $this->doPublishInternal( "{$fullCont}{$suffix}", $dir, $params ) );
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doPublish()
+ * @return Status
+ */
+ protected function doPublishInternal( $container, $dir, array $params ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileBackend::doClean()
+ * @return Status
+ */
+ final protected function doClean( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ // Recursive: first delete all empty subdirs recursively
+ if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
+ $subDirsRel = $this->getTopDirectoryList( array( 'dir' => $params['dir'] ) );
+ if ( $subDirsRel !== null ) { // no errors
+ foreach ( $subDirsRel as $subDirRel ) {
+ $subDir = $params['dir'] . "/{$subDirRel}"; // full path
+ $status->merge( $this->doClean( array( 'dir' => $subDir ) + $params ) );
+ }
+ }
+ }
+
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // invalid storage path
+ }
+
+ // Attempt to lock this directory...
+ $filesLockEx = array( $params['dir'] );
+ $scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
+ if ( !$status->isOK() ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // abort
+ }
+
+ if ( $shard !== null ) { // confined to a single container/shard
+ $status->merge( $this->doCleanInternal( $fullCont, $dir, $params ) );
+ $this->deleteContainerCache( $fullCont ); // purge cache
+ } else { // directory is on several shards
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+ $status->merge( $this->doCleanInternal( "{$fullCont}{$suffix}", $dir, $params ) );
+ $this->deleteContainerCache( "{$fullCont}{$suffix}" ); // purge cache
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doClean()
+ * @return Status
+ */
+ protected function doCleanInternal( $container, $dir, array $params ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileBackend::fileExists()
+ * @return bool|null
+ */
+ final public function fileExists( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $stat = $this->getFileStat( $params );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return ( $stat === null ) ? null : (bool)$stat; // null => failure
+ }
+
+ /**
+ * @see FileBackend::getFileTimestamp()
+ * @return bool
+ */
+ final public function getFileTimestamp( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $stat = $this->getFileStat( $params );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat ? $stat['mtime'] : false;
+ }
+
+ /**
+ * @see FileBackend::getFileSize()
+ * @return bool
+ */
+ final public function getFileSize( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $stat = $this->getFileStat( $params );
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat ? $stat['size'] : false;
+ }
+
+ /**
+ * @see FileBackend::getFileStat()
+ * @return bool
+ */
+ final public function getFileStat( array $params ) {
+ $path = self::normalizeStoragePath( $params['src'] );
+ if ( $path === null ) {
+ return false; // invalid storage path
+ }
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $latest = !empty( $params['latest'] ); // use latest data?
+ if ( !$this->cheapCache->has( $path, 'stat' ) ) {
+ $this->primeFileCache( array( $path ) ); // check persistent cache
+ }
+ if ( $this->cheapCache->has( $path, 'stat' ) ) {
+ $stat = $this->cheapCache->get( $path, 'stat' );
+ // If we want the latest data, check that this cached
+ // value was in fact fetched with the latest available data.
+ if ( !$latest || $stat['latest'] ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat;
+ }
+ }
+ wfProfileIn( __METHOD__ . '-miss' );
+ wfProfileIn( __METHOD__ . '-miss-' . $this->name );
+ $stat = $this->doGetFileStat( $params );
+ wfProfileOut( __METHOD__ . '-miss-' . $this->name );
+ wfProfileOut( __METHOD__ . '-miss' );
+ if ( is_array( $stat ) ) { // don't cache negatives
+ $stat['latest'] = $latest;
+ $this->cheapCache->set( $path, 'stat', $stat );
+ $this->setFileCache( $path, $stat ); // update persistent cache
+ if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
+ $this->cheapCache->set( $path, 'sha1',
+ array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
+ }
+ } else {
+ wfDebug( __METHOD__ . ": File $path does not exist.\n" );
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat;
+ }
+
+ /**
+ * @see FileBackendStore::getFileStat()
+ */
+ abstract protected function doGetFileStat( array $params );
+
+ /**
+ * @see FileBackend::getFileContents()
+ * @return bool|string
+ */
+ public function getFileContents( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $tmpFile = $this->getLocalReference( $params );
+ if ( !$tmpFile ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ wfSuppressWarnings();
+ $data = file_get_contents( $tmpFile->getPath() );
+ wfRestoreWarnings();
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $data;
+ }
+
+ /**
+ * @see FileBackend::getFileSha1Base36()
+ * @return bool|string
+ */
+ final public function getFileSha1Base36( array $params ) {
+ $path = self::normalizeStoragePath( $params['src'] );
+ if ( $path === null ) {
+ return false; // invalid storage path
+ }
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $latest = !empty( $params['latest'] ); // use latest data?
+ if ( $this->cheapCache->has( $path, 'sha1' ) ) {
+ $stat = $this->cheapCache->get( $path, 'sha1' );
+ // If we want the latest data, check that this cached
+ // value was in fact fetched with the latest available data.
+ if ( !$latest || $stat['latest'] ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat['hash'];
+ }
+ }
+ wfProfileIn( __METHOD__ . '-miss' );
+ wfProfileIn( __METHOD__ . '-miss-' . $this->name );
+ $hash = $this->doGetFileSha1Base36( $params );
+ wfProfileOut( __METHOD__ . '-miss-' . $this->name );
+ wfProfileOut( __METHOD__ . '-miss' );
+ if ( $hash ) { // don't cache negatives
+ $this->cheapCache->set( $path, 'sha1',
+ array( 'hash' => $hash, 'latest' => $latest ) );
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $hash;
+ }
+
+ /**
+ * @see FileBackendStore::getFileSha1Base36()
+ * @return bool|string
+ */
+ protected function doGetFileSha1Base36( array $params ) {
+ $fsFile = $this->getLocalReference( $params );
+ if ( !$fsFile ) {
+ return false;
+ } else {
+ return $fsFile->getSha1Base36();
+ }
+ }
+
+ /**
+ * @see FileBackend::getFileProps()
+ * @return Array
+ */
+ final public function getFileProps( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $fsFile = $this->getLocalReference( $params );
+ $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $props;
+ }
+
+ /**
+ * @see FileBackend::getLocalReference()
+ * @return TempFSFile|null
+ */
+ public function getLocalReference( array $params ) {
+ $path = self::normalizeStoragePath( $params['src'] );
+ if ( $path === null ) {
+ return null; // invalid storage path
+ }
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $latest = !empty( $params['latest'] ); // use latest data?
+ if ( $this->expensiveCache->has( $path, 'localRef' ) ) {
+ $val = $this->expensiveCache->get( $path, 'localRef' );
+ // If we want the latest data, check that this cached
+ // value was in fact fetched with the latest available data.
+ if ( !$latest || $val['latest'] ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $val['object'];
+ }
+ }
+ $tmpFile = $this->getLocalCopy( $params );
+ if ( $tmpFile ) { // don't cache negatives
+ $this->expensiveCache->set( $path, 'localRef',
+ array( 'object' => $tmpFile, 'latest' => $latest ) );
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $tmpFile;
+ }
+
+ /**
+ * @see FileBackend::streamFile()
+ * @return Status
+ */
+ final public function streamFile( array $params ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ $info = $this->getFileStat( $params );
+ if ( !$info ) { // let StreamFile handle the 404
+ $status->fatal( 'backend-fail-notexists', $params['src'] );
+ }
+
+ // Set output buffer and HTTP headers for stream
+ $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : array();
+ $res = StreamFile::prepareForStream( $params['src'], $info, $extraHeaders );
+ if ( $res == StreamFile::NOT_MODIFIED ) {
+ // do nothing; client cache is up to date
+ } elseif ( $res == StreamFile::READY_STREAM ) {
+ wfProfileIn( __METHOD__ . '-send' );
+ wfProfileIn( __METHOD__ . '-send-' . $this->name );
+ $status = $this->doStreamFile( $params );
+ wfProfileOut( __METHOD__ . '-send-' . $this->name );
+ wfProfileOut( __METHOD__ . '-send' );
+ } else {
+ $status->fatal( 'backend-fail-stream', $params['src'] );
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::streamFile()
+ * @return Status
+ */
+ protected function doStreamFile( array $params ) {
+ $status = Status::newGood();
+
+ $fsFile = $this->getLocalReference( $params );
+ if ( !$fsFile ) {
+ $status->fatal( 'backend-fail-stream', $params['src'] );
+ } elseif ( !readfile( $fsFile->getPath() ) ) {
+ $status->fatal( 'backend-fail-stream', $params['src'] );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::directoryExists()
+ * @return bool|null
+ */
+ final public function directoryExists( array $params ) {
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) {
+ return false; // invalid storage path
+ }
+ if ( $shard !== null ) { // confined to a single container/shard
+ return $this->doDirectoryExists( $fullCont, $dir, $params );
+ } else { // directory is on several shards
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ $res = false; // response
+ foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
+ $exists = $this->doDirectoryExists( "{$fullCont}{$suffix}", $dir, $params );
+ if ( $exists ) {
+ $res = true;
+ break; // found one!
+ } elseif ( $exists === null ) { // error?
+ $res = null; // if we don't find anything, it is indeterminate
+ }
+ }
+ return $res;
+ }
+ }
+
+ /**
+ * @see FileBackendStore::directoryExists()
+ *
+ * @param $container string Resolved container name
+ * @param $dir string Resolved path relative to container
+ * @param $params Array
+ * @return bool|null
+ */
+ abstract protected function doDirectoryExists( $container, $dir, array $params );
+
+ /**
+ * @see FileBackend::getDirectoryList()
+ * @return Traversable|Array|null Returns null on failure
+ */
+ final public function getDirectoryList( array $params ) {
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) { // invalid storage path
+ return null;
+ }
+ if ( $shard !== null ) {
+ // File listing is confined to a single container/shard
+ return $this->getDirectoryListInternal( $fullCont, $dir, $params );
+ } else {
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ // File listing spans multiple containers/shards
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ return new FileBackendStoreShardDirIterator( $this,
+ $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
+ }
+ }
+
+ /**
+ * Do not call this function from places outside FileBackend
+ *
+ * @see FileBackendStore::getDirectoryList()
+ *
+ * @param $container string Resolved container name
+ * @param $dir string Resolved path relative to container
+ * @param $params Array
+ * @return Traversable|Array|null Returns null on failure
+ */
+ abstract public function getDirectoryListInternal( $container, $dir, array $params );
+
+ /**
+ * @see FileBackend::getFileList()
+ * @return Traversable|Array|null Returns null on failure
+ */
+ final public function getFileList( array $params ) {
+ list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
+ if ( $dir === null ) { // invalid storage path
+ return null;
+ }
+ if ( $shard !== null ) {
+ // File listing is confined to a single container/shard
+ return $this->getFileListInternal( $fullCont, $dir, $params );
+ } else {
+ wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
+ // File listing spans multiple containers/shards
+ list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
+ return new FileBackendStoreShardFileIterator( $this,
+ $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
+ }
+ }
+
+ /**
+ * Do not call this function from places outside FileBackend
+ *
+ * @see FileBackendStore::getFileList()
+ *
+ * @param $container string Resolved container name
+ * @param $dir string Resolved path relative to container
+ * @param $params Array
+ * @return Traversable|Array|null Returns null on failure
+ */
+ abstract public function getFileListInternal( $container, $dir, array $params );
+
+ /**
+ * Return a list of FileOp objects from a list of operations.
+ * Do not call this function from places outside FileBackend.
+ *
+ * The result must have the same number of items as the input.
+ * An exception is thrown if an unsupported operation is requested.
+ *
+ * @param $ops Array Same format as doOperations()
+ * @return Array List of FileOp objects
+ * @throws MWException
+ */
+ final public function getOperationsInternal( array $ops ) {
+ $supportedOps = array(
+ 'store' => 'StoreFileOp',
+ 'copy' => 'CopyFileOp',
+ 'move' => 'MoveFileOp',
+ 'delete' => 'DeleteFileOp',
+ 'create' => 'CreateFileOp',
+ 'null' => 'NullFileOp'
+ );
+
+ $performOps = array(); // array of FileOp objects
+ // Build up ordered array of FileOps...
+ foreach ( $ops as $operation ) {
+ $opName = $operation['op'];
+ if ( isset( $supportedOps[$opName] ) ) {
+ $class = $supportedOps[$opName];
+ // Get params for this operation
+ $params = $operation;
+ // Append the FileOp class
+ $performOps[] = new $class( $this, $params );
+ } else {
+ throw new MWException( "Operation '$opName' is not supported." );
+ }
+ }
+
+ return $performOps;
+ }
+
+ /**
+ * Get a list of storage paths to lock for a list of operations
+ * Returns an array with 'sh' (shared) and 'ex' (exclusive) keys,
+ * each corresponding to a list of storage paths to be locked.
+ *
+ * @param $performOps Array List of FileOp objects
+ * @return Array ('sh' => list of paths, 'ex' => list of paths)
+ */
+ final public function getPathsToLockForOpsInternal( array $performOps ) {
+ // Build up a list of files to lock...
+ $paths = array( 'sh' => array(), 'ex' => array() );
+ foreach ( $performOps as $fileOp ) {
+ $paths['sh'] = array_merge( $paths['sh'], $fileOp->storagePathsRead() );
+ $paths['ex'] = array_merge( $paths['ex'], $fileOp->storagePathsChanged() );
+ }
+ // Optimization: if doing an EX lock anyway, don't also set an SH one
+ $paths['sh'] = array_diff( $paths['sh'], $paths['ex'] );
+ // Get a shared lock on the parent directory of each path changed
+ $paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
+
+ return $paths;
+ }
+
+ /**
+ * @see FileBackend::getScopedLocksForOps()
+ * @return Array
+ */
+ public function getScopedLocksForOps( array $ops, Status $status ) {
+ $paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
+ return array(
+ $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
+ $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
+ );
+ }
+
+ /**
+ * @see FileBackend::doOperationsInternal()
+ * @return Status
+ */
+ final protected function doOperationsInternal( array $ops, array $opts ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ // Build up a list of FileOps...
+ $performOps = $this->getOperationsInternal( $ops );
+
+ // Acquire any locks as needed...
+ if ( empty( $opts['nonLocking'] ) ) {
+ // Build up a list of files to lock...
+ $paths = $this->getPathsToLockForOpsInternal( $performOps );
+ // Try to lock those files for the scope of this function...
+ $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
+ $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+ if ( !$status->isOK() ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status; // abort
+ }
+ }
+
+ // Clear any file cache entries (after locks acquired)
+ $this->clearCache();
+
+ // Load from the persistent file and container caches
+ $this->primeFileCache( $performOps );
+ $this->primeContainerCache( $performOps );
+
+ // Actually attempt the operation batch...
+ $subStatus = FileOpBatch::attempt( $performOps, $opts, $this->fileJournal );
+
+ // Merge errors into status fields
+ $status->merge( $subStatus );
+ $status->success = $subStatus->success; // not done in merge()
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * @see FileBackend::doQuickOperationsInternal()
+ * @return Status
+ * @throws MWException
+ */
+ final protected function doQuickOperationsInternal( array $ops ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'null' );
+ $async = ( $this->parallelize === 'implicit' );
+ $maxConcurrency = $this->concurrency; // throttle
+
+ $statuses = array(); // array of (index => Status)
+ $fileOpHandles = array(); // list of (index => handle) arrays
+ $curFileOpHandles = array(); // current handle batch
+ // Perform the sync-only ops and build up op handles for the async ops...
+ foreach ( $ops as $index => $params ) {
+ if ( !in_array( $params['op'], $supportedOps ) ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ throw new MWException( "Operation '{$params['op']}' is not supported." );
+ }
+ $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
+ $subStatus = $this->$method( array( 'async' => $async ) + $params );
+ if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
+ if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
+ $fileOpHandles[] = $curFileOpHandles; // push this batch
+ $curFileOpHandles = array();
+ }
+ $curFileOpHandles[$index] = $subStatus->value; // keep index
+ } else { // error or completed
+ $statuses[$index] = $subStatus; // keep index
+ }
+ }
+ if ( count( $curFileOpHandles ) ) {
+ $fileOpHandles[] = $curFileOpHandles; // last batch
+ }
+ // Do all the async ops that can be done concurrently...
+ foreach ( $fileOpHandles as $fileHandleBatch ) {
+ $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
+ }
+ // Marshall and merge all the responses...
+ foreach ( $statuses as $index => $subStatus ) {
+ $status->merge( $subStatus );
+ if ( $subStatus->isOK() ) {
+ $status->success[$index] = true;
+ ++$status->successCount;
+ } else {
+ $status->success[$index] = false;
+ ++$status->failCount;
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * Execute a list of FileBackendStoreOpHandle handles in parallel.
+ * The resulting Status object fields will correspond
+ * to the order in which the handles where given.
+ *
+ * @param $handles Array List of FileBackendStoreOpHandle objects
+ * @return Array Map of Status objects
+ * @throws MWException
+ */
+ final public function executeOpHandlesInternal( array $fileOpHandles ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ foreach ( $fileOpHandles as $fileOpHandle ) {
+ if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
+ throw new MWException( "Given a non-FileBackendStoreOpHandle object." );
+ } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
+ throw new MWException( "Given a FileBackendStoreOpHandle for the wrong backend." );
+ }
+ }
+ $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
+ foreach ( $fileOpHandles as $fileOpHandle ) {
+ $fileOpHandle->closeResources();
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $res;
+ }
+
+ /**
+ * @see FileBackendStore::executeOpHandlesInternal()
+ * @return Array List of corresponding Status objects
+ */
+ protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
+ foreach ( $fileOpHandles as $fileOpHandle ) { // OK if empty
+ throw new MWException( "This backend supports no asynchronous operations." );
+ }
+ return array();
+ }
+
+ /**
+ * @see FileBackend::clearCache()
+ */
+ final public function clearCache( array $paths = null ) {
+ if ( is_array( $paths ) ) {
+ $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
+ $paths = array_filter( $paths, 'strlen' ); // remove nulls
+ }
+ if ( $paths === null ) {
+ $this->cheapCache->clear();
+ $this->expensiveCache->clear();
+ } else {
+ foreach ( $paths as $path ) {
+ $this->cheapCache->clear( $path );
+ $this->expensiveCache->clear( $path );
+ }
+ }
+ $this->doClearCache( $paths );
+ }
+
+ /**
+ * Clears any additional stat caches for storage paths
+ *
+ * @see FileBackend::clearCache()
+ *
+ * @param $paths Array Storage paths (optional)
+ * @return void
+ */
+ protected function doClearCache( array $paths = null ) {}
+
+ /**
+ * Is this a key/value store where directories are just virtual?
+ * Virtual directories exists in so much as files exists that are
+ * prefixed with the directory path followed by a forward slash.
+ *
+ * @return bool
+ */
+ abstract protected function directoriesAreVirtual();
+
+ /**
+ * Check if a container name is valid.
+ * This checks for for length and illegal characters.
+ *
+ * @param $container string
+ * @return bool
+ */
+ final protected static function isValidContainerName( $container ) {
+ // This accounts for Swift and S3 restrictions while leaving room
+ // for things like '.xxx' (hex shard chars) or '.seg' (segments).
+ // This disallows directory separators or traversal characters.
+ // Note that matching strings URL encode to the same string;
+ // in Swift, the length restriction is *after* URL encoding.
+ return preg_match( '/^[a-z0-9][a-z0-9-_]{0,199}$/i', $container );
+ }
+
+ /**
+ * Splits a storage path into an internal container name,
+ * an internal relative file name, and a container shard suffix.
+ * Any shard suffix is already appended to the internal container name.
+ * This also checks that the storage path is valid and within this backend.
+ *
+ * If the container is sharded but a suffix could not be determined,
+ * this means that the path can only refer to a directory and can only
+ * be scanned by looking in all the container shards.
+ *
+ * @param $storagePath string
+ * @return Array (container, path, container suffix) or (null, null, null) if invalid
+ */
+ final protected function resolveStoragePath( $storagePath ) {
+ list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
+ if ( $backend === $this->name ) { // must be for this backend
+ $relPath = self::normalizeContainerPath( $relPath );
+ if ( $relPath !== null ) {
+ // Get shard for the normalized path if this container is sharded
+ $cShard = $this->getContainerShard( $container, $relPath );
+ // Validate and sanitize the relative path (backend-specific)
+ $relPath = $this->resolveContainerPath( $container, $relPath );
+ if ( $relPath !== null ) {
+ // Prepend any wiki ID prefix to the container name
+ $container = $this->fullContainerName( $container );
+ if ( self::isValidContainerName( $container ) ) {
+ // Validate and sanitize the container name (backend-specific)
+ $container = $this->resolveContainerName( "{$container}{$cShard}" );
+ if ( $container !== null ) {
+ return array( $container, $relPath, $cShard );
+ }
+ }
+ }
+ }
+ }
+ return array( null, null, null );
+ }
+
+ /**
+ * Like resolveStoragePath() except null values are returned if
+ * the container is sharded and the shard could not be determined.
+ *
+ * @see FileBackendStore::resolveStoragePath()
+ *
+ * @param $storagePath string
+ * @return Array (container, path) or (null, null) if invalid
+ */
+ final protected function resolveStoragePathReal( $storagePath ) {
+ list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
+ if ( $cShard !== null ) {
+ return array( $container, $relPath );
+ }
+ return array( null, null );
+ }
+
+ /**
+ * Get the container name shard suffix for a given path.
+ * Any empty suffix means the container is not sharded.
+ *
+ * @param $container string Container name
+ * @param $relPath string Storage path relative to the container
+ * @return string|null Returns null if shard could not be determined
+ */
+ final protected function getContainerShard( $container, $relPath ) {
+ list( $levels, $base, $repeat ) = $this->getContainerHashLevels( $container );
+ if ( $levels == 1 || $levels == 2 ) {
+ // Hash characters are either base 16 or 36
+ $char = ( $base == 36 ) ? '[0-9a-z]' : '[0-9a-f]';
+ // Get a regex that represents the shard portion of paths.
+ // The concatenation of the captures gives us the shard.
+ if ( $levels === 1 ) { // 16 or 36 shards per container
+ $hashDirRegex = '(' . $char . ')';
+ } else { // 256 or 1296 shards per container
+ if ( $repeat ) { // verbose hash dir format (e.g. "a/ab/abc")
+ $hashDirRegex = $char . '/(' . $char . '{2})';
+ } else { // short hash dir format (e.g. "a/b/c")
+ $hashDirRegex = '(' . $char . ')/(' . $char . ')';
+ }
+ }
+ // Allow certain directories to be above the hash dirs so as
+ // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
+ // They must be 2+ chars to avoid any hash directory ambiguity.
+ $m = array();
+ if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
+ return '.' . implode( '', array_slice( $m, 1 ) );
+ }
+ return null; // failed to match
+ }
+ return ''; // no sharding
+ }
+
+ /**
+ * Check if a storage path maps to a single shard.
+ * Container dirs like "a", where the container shards on "x/xy",
+ * can reside on several shards. Such paths are tricky to handle.
+ *
+ * @param $storagePath string Storage path
+ * @return bool
+ */
+ final public function isSingleShardPathInternal( $storagePath ) {
+ list( $c, $r, $shard ) = $this->resolveStoragePath( $storagePath );
+ return ( $shard !== null );
+ }
+
+ /**
+ * Get the sharding config for a container.
+ * If greater than 0, then all file storage paths within
+ * the container are required to be hashed accordingly.
+ *
+ * @param $container string
+ * @return Array (integer levels, integer base, repeat flag) or (0, 0, false)
+ */
+ final protected function getContainerHashLevels( $container ) {
+ if ( isset( $this->shardViaHashLevels[$container] ) ) {
+ $config = $this->shardViaHashLevels[$container];
+ $hashLevels = (int)$config['levels'];
+ if ( $hashLevels == 1 || $hashLevels == 2 ) {
+ $hashBase = (int)$config['base'];
+ if ( $hashBase == 16 || $hashBase == 36 ) {
+ return array( $hashLevels, $hashBase, $config['repeat'] );
+ }
+ }
+ }
+ return array( 0, 0, false ); // no sharding
+ }
+
+ /**
+ * Get a list of full container shard suffixes for a container
+ *
+ * @param $container string
+ * @return Array
+ */
+ final protected function getContainerSuffixes( $container ) {
+ $shards = array();
+ list( $digits, $base ) = $this->getContainerHashLevels( $container );
+ if ( $digits > 0 ) {
+ $numShards = pow( $base, $digits );
+ for ( $index = 0; $index < $numShards; $index++ ) {
+ $shards[] = '.' . wfBaseConvert( $index, 10, $base, $digits );
+ }
+ }
+ return $shards;
+ }
+
+ /**
+ * Get the full container name, including the wiki ID prefix
+ *
+ * @param $container string
+ * @return string
+ */
+ final protected function fullContainerName( $container ) {
+ if ( $this->wikiId != '' ) {
+ return "{$this->wikiId}-$container";
+ } else {
+ return $container;
+ }
+ }
+
+ /**
+ * Resolve a container name, checking if it's allowed by the backend.
+ * This is intended for internal use, such as encoding illegal chars.
+ * Subclasses can override this to be more restrictive.
+ *
+ * @param $container string
+ * @return string|null
+ */
+ protected function resolveContainerName( $container ) {
+ return $container;
+ }
+
+ /**
+ * Resolve a relative storage path, checking if it's allowed by the backend.
+ * This is intended for internal use, such as encoding illegal chars or perhaps
+ * getting absolute paths (e.g. FS based backends). Note that the relative path
+ * may be the empty string (e.g. the path is simply to the container).
+ *
+ * @param $container string Container name
+ * @param $relStoragePath string Storage path relative to the container
+ * @return string|null Path or null if not valid
+ */
+ protected function resolveContainerPath( $container, $relStoragePath ) {
+ return $relStoragePath;
+ }
+
+ /**
+ * Get the cache key for a container
+ *
+ * @param $container string Resolved container name
+ * @return string
+ */
+ private function containerCacheKey( $container ) {
+ return wfMemcKey( 'backend', $this->getName(), 'container', $container );
+ }
+
+ /**
+ * Set the cached info for a container
+ *
+ * @param $container string Resolved container name
+ * @param $val mixed Information to cache
+ */
+ final protected function setContainerCache( $container, $val ) {
+ $this->memCache->add( $this->containerCacheKey( $container ), $val, 14*86400 );
+ }
+
+ /**
+ * Delete the cached info for a container.
+ * The cache key is salted for a while to prevent race conditions.
+ *
+ * @param $container string Resolved container name
+ */
+ final protected function deleteContainerCache( $container ) {
+ if ( !$this->memCache->set( $this->containerCacheKey( $container ), 'PURGED', 300 ) ) {
+ trigger_error( "Unable to delete stat cache for container $container." );
+ }
+ }
+
+ /**
+ * Do a batch lookup from cache for container stats for all containers
+ * used in a list of container names, storage paths, or FileOp objects.
+ *
+ * @param $items Array
+ * @return void
+ */
+ final protected function primeContainerCache( array $items ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+
+ $paths = array(); // list of storage paths
+ $contNames = array(); // (cache key => resolved container name)
+ // Get all the paths/containers from the items...
+ foreach ( $items as $item ) {
+ if ( $item instanceof FileOp ) {
+ $paths = array_merge( $paths, $item->storagePathsRead() );
+ $paths = array_merge( $paths, $item->storagePathsChanged() );
+ } elseif ( self::isStoragePath( $item ) ) {
+ $paths[] = $item;
+ } elseif ( is_string( $item ) ) { // full container name
+ $contNames[$this->containerCacheKey( $item )] = $item;
+ }
+ }
+ // Get all the corresponding cache keys for paths...
+ foreach ( $paths as $path ) {
+ list( $fullCont, $r, $s ) = $this->resolveStoragePath( $path );
+ if ( $fullCont !== null ) { // valid path for this backend
+ $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
+ }
+ }
+
+ $contInfo = array(); // (resolved container name => cache value)
+ // Get all cache entries for these container cache keys...
+ $values = $this->memCache->getMulti( array_keys( $contNames ) );
+ foreach ( $values as $cacheKey => $val ) {
+ $contInfo[$contNames[$cacheKey]] = $val;
+ }
+
+ // Populate the container process cache for the backend...
+ $this->doPrimeContainerCache( array_filter( $contInfo, 'is_array' ) );
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Fill the backend-specific process cache given an array of
+ * resolved container names and their corresponding cached info.
+ * Only containers that actually exist should appear in the map.
+ *
+ * @param $containerInfo Array Map of resolved container names to cached info
+ * @return void
+ */
+ protected function doPrimeContainerCache( array $containerInfo ) {}
+
+ /**
+ * Get the cache key for a file path
+ *
+ * @param $path string Storage path
+ * @return string
+ */
+ private function fileCacheKey( $path ) {
+ return wfMemcKey( 'backend', $this->getName(), 'file', sha1( $path ) );
+ }
+
+ /**
+ * Set the cached stat info for a file path
+ *
+ * @param $path string Storage path
+ * @param $val mixed Information to cache
+ */
+ final protected function setFileCache( $path, $val ) {
+ $this->memCache->add( $this->fileCacheKey( $path ), $val, 7*86400 );
+ }
+
+ /**
+ * Delete the cached stat info for a file path.
+ * The cache key is salted for a while to prevent race conditions.
+ *
+ * @param $path string Storage path
+ */
+ final protected function deleteFileCache( $path ) {
+ if ( !$this->memCache->set( $this->fileCacheKey( $path ), 'PURGED', 300 ) ) {
+ trigger_error( "Unable to delete stat cache for file $path." );
+ }
+ }
+
+ /**
+ * Do a batch lookup from cache for file stats for all paths
+ * used in a list of storage paths or FileOp objects.
+ *
+ * @param $items Array List of storage paths or FileOps
+ * @return void
+ */
+ final protected function primeFileCache( array $items ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+
+ $paths = array(); // list of storage paths
+ $pathNames = array(); // (cache key => storage path)
+ // Get all the paths/containers from the items...
+ foreach ( $items as $item ) {
+ if ( $item instanceof FileOp ) {
+ $paths = array_merge( $paths, $item->storagePathsRead() );
+ $paths = array_merge( $paths, $item->storagePathsChanged() );
+ } elseif ( self::isStoragePath( $item ) ) {
+ $paths[] = $item;
+ }
+ }
+ // Get all the corresponding cache keys for paths...
+ foreach ( $paths as $path ) {
+ list( $cont, $rel, $s ) = $this->resolveStoragePath( $path );
+ if ( $rel !== null ) { // valid path for this backend
+ $pathNames[$this->fileCacheKey( $path )] = $path;
+ }
+ }
+ // Get all cache entries for these container cache keys...
+ $values = $this->memCache->getMulti( array_keys( $pathNames ) );
+ foreach ( $values as $cacheKey => $val ) {
+ if ( is_array( $val ) ) {
+ $path = $pathNames[$cacheKey];
+ $this->cheapCache->set( $path, 'stat', $val );
+ if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
+ $this->cheapCache->set( $path, 'sha1',
+ array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
+ }
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ }
+}
+
+/**
+ * FileBackendStore helper class for performing asynchronous file operations.
+ *
+ * For example, calling FileBackendStore::createInternal() with the "async"
+ * param flag may result in a Status that contains this object as a value.
+ * This class is largely backend-specific and is mostly just "magic" to be
+ * passed to FileBackendStore::executeOpHandlesInternal().
+ */
+abstract class FileBackendStoreOpHandle {
+ /** @var Array */
+ public $params = array(); // params to caller functions
+ /** @var FileBackendStore */
+ public $backend;
+ /** @var Array */
+ public $resourcesToClose = array();
+
+ public $call; // string; name that identifies the function called
+
+ /**
+ * Close all open file handles
+ *
+ * @return void
+ */
+ public function closeResources() {
+ array_map( 'fclose', $this->resourcesToClose );
+ }
+}
+
+/**
+ * FileBackendStore helper function to handle listings that span container shards.
+ * Do not use this class from places outside of FileBackendStore.
+ *
+ * @ingroup FileBackend
+ */
+abstract class FileBackendStoreShardListIterator implements Iterator {
+ /** @var FileBackendStore */
+ protected $backend;
+ /** @var Array */
+ protected $params;
+ /** @var Array */
+ protected $shardSuffixes;
+ protected $container; // string; full container name
+ protected $directory; // string; resolved relative path
+
+ /** @var Traversable */
+ protected $iter;
+ protected $curShard = 0; // integer
+ protected $pos = 0; // integer
+
+ /** @var Array */
+ protected $multiShardPaths = array(); // (rel path => 1)
+
+ /**
+ * @param $backend FileBackendStore
+ * @param $container string Full storage container name
+ * @param $dir string Storage directory relative to container
+ * @param $suffixes Array List of container shard suffixes
+ * @param $params Array
+ */
+ public function __construct(
+ FileBackendStore $backend, $container, $dir, array $suffixes, array $params
+ ) {
+ $this->backend = $backend;
+ $this->container = $container;
+ $this->directory = $dir;
+ $this->shardSuffixes = $suffixes;
+ $this->params = $params;
+ }
+
+ /**
+ * @see Iterator::key()
+ * @return integer
+ */
+ public function key() {
+ return $this->pos;
+ }
+
+ /**
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ if ( $this->iter instanceof Iterator ) {
+ return $this->iter->valid();
+ } elseif ( is_array( $this->iter ) ) {
+ return ( current( $this->iter ) !== false ); // no paths can have this value
+ }
+ return false; // some failure?
+ }
+
+ /**
+ * @see Iterator::current()
+ * @return string|bool String or false
+ */
+ public function current() {
+ return ( $this->iter instanceof Iterator )
+ ? $this->iter->current()
+ : current( $this->iter );
+ }
+
+ /**
+ * @see Iterator::next()
+ * @return void
+ */
+ public function next() {
+ ++$this->pos;
+ ( $this->iter instanceof Iterator ) ? $this->iter->next() : next( $this->iter );
+ do {
+ $continue = false; // keep scanning shards?
+ $this->filterViaNext(); // filter out duplicates
+ // Find the next non-empty shard if no elements are left
+ if ( !$this->valid() ) {
+ $this->nextShardIteratorIfNotValid();
+ $continue = $this->valid(); // re-filter unless we ran out of shards
+ }
+ } while ( $continue );
+ }
+
+ /**
+ * @see Iterator::rewind()
+ * @return void
+ */
+ public function rewind() {
+ $this->pos = 0;
+ $this->curShard = 0;
+ $this->setIteratorFromCurrentShard();
+ do {
+ $continue = false; // keep scanning shards?
+ $this->filterViaNext(); // filter out duplicates
+ // Find the next non-empty shard if no elements are left
+ if ( !$this->valid() ) {
+ $this->nextShardIteratorIfNotValid();
+ $continue = $this->valid(); // re-filter unless we ran out of shards
+ }
+ } while ( $continue );
+ }
+
+ /**
+ * Filter out duplicate items by advancing to the next ones
+ */
+ protected function filterViaNext() {
+ while ( $this->valid() ) {
+ $rel = $this->iter->current(); // path relative to given directory
+ $path = $this->params['dir'] . "/{$rel}"; // full storage path
+ if ( $this->backend->isSingleShardPathInternal( $path ) ) {
+ break; // path is only on one shard; no issue with duplicates
+ } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
+ // Don't keep listing paths that are on multiple shards
+ ( $this->iter instanceof Iterator ) ? $this->iter->next() : next( $this->iter );
+ } else {
+ $this->multiShardPaths[$rel] = 1;
+ break;
+ }
+ }
+ }
+
+ /**
+ * If the list iterator for this container shard is out of items,
+ * then move on to the next container that has items.
+ * If there are none, then it advances to the last container.
+ */
+ protected function nextShardIteratorIfNotValid() {
+ while ( !$this->valid() && ++$this->curShard < count( $this->shardSuffixes ) ) {
+ $this->setIteratorFromCurrentShard();
+ }
+ }
+
+ /**
+ * Set the list iterator to that of the current container shard
+ */
+ protected function setIteratorFromCurrentShard() {
+ $this->iter = $this->listFromShard(
+ $this->container . $this->shardSuffixes[$this->curShard],
+ $this->directory, $this->params );
+ // Start loading results so that current() works
+ if ( $this->iter ) {
+ ( $this->iter instanceof Iterator ) ? $this->iter->rewind() : reset( $this->iter );
+ }
+ }
+
+ /**
+ * Get the list for a given container shard
+ *
+ * @param $container string Resolved container name
+ * @param $dir string Resolved path relative to container
+ * @param $params Array
+ * @return Traversable|Array|null
+ */
+ abstract protected function listFromShard( $container, $dir, array $params );
+}
+
+/**
+ * Iterator for listing directories
+ */
+class FileBackendStoreShardDirIterator extends FileBackendStoreShardListIterator {
+ /**
+ * @see FileBackendStoreShardListIterator::listFromShard()
+ * @return Array|null|Traversable
+ */
+ protected function listFromShard( $container, $dir, array $params ) {
+ return $this->backend->getDirectoryListInternal( $container, $dir, $params );
+ }
+}
+
+/**
+ * Iterator for listing regular files
+ */
+class FileBackendStoreShardFileIterator extends FileBackendStoreShardListIterator {
+ /**
+ * @see FileBackendStoreShardListIterator::listFromShard()
+ * @return Array|null|Traversable
+ */
+ protected function listFromShard( $container, $dir, array $params ) {
+ return $this->backend->getFileListInternal( $container, $dir, $params );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Helper class for representing operations with transaction support.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * FileBackend helper class for representing operations.
+ * Do not use this class from places outside FileBackend.
+ *
+ * Methods called from FileOpBatch::attempt() should avoid throwing
+ * exceptions at all costs. FileOp objects should be lightweight in order
+ * to support large arrays in memory and serialization.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+abstract class FileOp {
+ /** @var Array */
+ protected $params = array();
+ /** @var FileBackendStore */
+ protected $backend;
+
+ protected $state = self::STATE_NEW; // integer
+ protected $failed = false; // boolean
+ protected $async = false; // boolean
+ protected $useLatest = true; // boolean
+ protected $batchId; // string
+
+ protected $sourceSha1; // string
+ protected $destSameAsSource; // boolean
+
+ /* Object life-cycle */
+ const STATE_NEW = 1;
+ const STATE_CHECKED = 2;
+ const STATE_ATTEMPTED = 3;
+
+ /**
+ * Build a new file operation transaction
+ *
+ * @param $backend FileBackendStore
+ * @param $params Array
+ * @throws MWException
+ */
+ final public function __construct( FileBackendStore $backend, array $params ) {
+ $this->backend = $backend;
+ list( $required, $optional ) = $this->allowedParams();
+ foreach ( $required as $name ) {
+ if ( isset( $params[$name] ) ) {
+ $this->params[$name] = $params[$name];
+ } else {
+ throw new MWException( "File operation missing parameter '$name'." );
+ }
+ }
+ foreach ( $optional as $name ) {
+ if ( isset( $params[$name] ) ) {
+ $this->params[$name] = $params[$name];
+ }
+ }
+ $this->params = $params;
+ }
+
+ /**
+ * Set the batch UUID this operation belongs to
+ *
+ * @param $batchId string
+ * @return void
+ */
+ final public function setBatchId( $batchId ) {
+ $this->batchId = $batchId;
+ }
+
+ /**
+ * Whether to allow stale data for file reads and stat checks
+ *
+ * @param $allowStale bool
+ * @return void
+ */
+ final public function allowStaleReads( $allowStale ) {
+ $this->useLatest = !$allowStale;
+ }
+
+ /**
+ * Get the value of the parameter with the given name
+ *
+ * @param $name string
+ * @return mixed Returns null if the parameter is not set
+ */
+ final public function getParam( $name ) {
+ return isset( $this->params[$name] ) ? $this->params[$name] : null;
+ }
+
+ /**
+ * Check if this operation failed precheck() or attempt()
+ *
+ * @return bool
+ */
+ final public function failed() {
+ return $this->failed;
+ }
+
+ /**
+ * Get a new empty predicates array for precheck()
+ *
+ * @return Array
+ */
+ final public static function newPredicates() {
+ return array( 'exists' => array(), 'sha1' => array() );
+ }
+
+ /**
+ * Get a new empty dependency tracking array for paths read/written to
+ *
+ * @return Array
+ */
+ final public static function newDependencies() {
+ return array( 'read' => array(), 'write' => array() );
+ }
+
+ /**
+ * Update a dependency tracking array to account for this operation
+ *
+ * @param $deps Array Prior path reads/writes; format of FileOp::newPredicates()
+ * @return Array
+ */
+ final public function applyDependencies( array $deps ) {
+ $deps['read'] += array_fill_keys( $this->storagePathsRead(), 1 );
+ $deps['write'] += array_fill_keys( $this->storagePathsChanged(), 1 );
+ return $deps;
+ }
+
+ /**
+ * Check if this operation changes files listed in $paths
+ *
+ * @param $paths Array Prior path reads/writes; format of FileOp::newPredicates()
+ * @return boolean
+ */
+ final public function dependsOn( array $deps ) {
+ foreach ( $this->storagePathsChanged() as $path ) {
+ if ( isset( $deps['read'][$path] ) || isset( $deps['write'][$path] ) ) {
+ return true; // "output" or "anti" dependency
+ }
+ }
+ foreach ( $this->storagePathsRead() as $path ) {
+ if ( isset( $deps['write'][$path] ) ) {
+ return true; // "flow" dependency
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the file journal entries for this file operation
+ *
+ * @param $oPredicates Array Pre-op info about files (format of FileOp::newPredicates)
+ * @param $nPredicates Array Post-op info about files (format of FileOp::newPredicates)
+ * @return Array
+ */
+ final public function getJournalEntries( array $oPredicates, array $nPredicates ) {
+ $nullEntries = array();
+ $updateEntries = array();
+ $deleteEntries = array();
+ $pathsUsed = array_merge( $this->storagePathsRead(), $this->storagePathsChanged() );
+ foreach ( $pathsUsed as $path ) {
+ $nullEntries[] = array( // assertion for recovery
+ 'op' => 'null',
+ 'path' => $path,
+ 'newSha1' => $this->fileSha1( $path, $oPredicates )
+ );
+ }
+ foreach ( $this->storagePathsChanged() as $path ) {
+ if ( $nPredicates['sha1'][$path] === false ) { // deleted
+ $deleteEntries[] = array(
+ 'op' => 'delete',
+ 'path' => $path,
+ 'newSha1' => ''
+ );
+ } else { // created/updated
+ $updateEntries[] = array(
+ 'op' => $this->fileExists( $path, $oPredicates ) ? 'update' : 'create',
+ 'path' => $path,
+ 'newSha1' => $nPredicates['sha1'][$path]
+ );
+ }
+ }
+ return array_merge( $nullEntries, $updateEntries, $deleteEntries );
+ }
+
+ /**
+ * Check preconditions of the operation without writing anything
+ *
+ * @param $predicates Array
+ * @return Status
+ */
+ final public function precheck( array &$predicates ) {
+ if ( $this->state !== self::STATE_NEW ) {
+ return Status::newFatal( 'fileop-fail-state', self::STATE_NEW, $this->state );
+ }
+ $this->state = self::STATE_CHECKED;
+ $status = $this->doPrecheck( $predicates );
+ if ( !$status->isOK() ) {
+ $this->failed = true;
+ }
+ return $status;
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doPrecheck( array &$predicates ) {
+ return Status::newGood();
+ }
+
+ /**
+ * Attempt the operation
+ *
+ * @return Status
+ */
+ final public function attempt() {
+ if ( $this->state !== self::STATE_CHECKED ) {
+ return Status::newFatal( 'fileop-fail-state', self::STATE_CHECKED, $this->state );
+ } elseif ( $this->failed ) { // failed precheck
+ return Status::newFatal( 'fileop-fail-attempt-precheck' );
+ }
+ $this->state = self::STATE_ATTEMPTED;
+ $status = $this->doAttempt();
+ if ( !$status->isOK() ) {
+ $this->failed = true;
+ $this->logFailure( 'attempt' );
+ }
+ return $status;
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ return Status::newGood();
+ }
+
+ /**
+ * Attempt the operation in the background
+ *
+ * @return Status
+ */
+ final public function attemptAsync() {
+ $this->async = true;
+ $result = $this->attempt();
+ $this->async = false;
+ return $result;
+ }
+
+ /**
+ * Get the file operation parameters
+ *
+ * @return Array (required params list, optional params list)
+ */
+ protected function allowedParams() {
+ return array( array(), array() );
+ }
+
+ /**
+ * Adjust params to FileBackendStore internal file calls
+ *
+ * @param $params Array
+ * @return Array (required params list, optional params list)
+ */
+ protected function setFlags( array $params ) {
+ return array( 'async' => $this->async ) + $params;
+ }
+
+ /**
+ * Get a list of storage paths read from for this operation
+ *
+ * @return Array
+ */
+ final public function storagePathsRead() {
+ return array_map( 'FileBackend::normalizeStoragePath', $this->doStoragePathsRead() );
+ }
+
+ /**
+ * @see FileOp::storagePathsRead()
+ * @return Array
+ */
+ protected function doStoragePathsRead() {
+ return array();
+ }
+
+ /**
+ * Get a list of storage paths written to for this operation
+ *
+ * @return Array
+ */
+ final public function storagePathsChanged() {
+ return array_map( 'FileBackend::normalizeStoragePath', $this->doStoragePathsChanged() );
+ }
+
+ /**
+ * @see FileOp::storagePathsChanged()
+ * @return Array
+ */
+ protected function doStoragePathsChanged() {
+ return array();
+ }
+
+ /**
+ * Check for errors with regards to the destination file already existing.
+ * This also updates the destSameAsSource and sourceSha1 member variables.
+ * A bad status will be returned if there is no chance it can be overwritten.
+ *
+ * @param $predicates Array
+ * @return Status
+ */
+ protected function precheckDestExistence( array $predicates ) {
+ $status = Status::newGood();
+ // Get hash of source file/string and the destination file
+ $this->sourceSha1 = $this->getSourceSha1Base36(); // FS file or data string
+ if ( $this->sourceSha1 === null ) { // file in storage?
+ $this->sourceSha1 = $this->fileSha1( $this->params['src'], $predicates );
+ }
+ $this->destSameAsSource = false;
+ if ( $this->fileExists( $this->params['dst'], $predicates ) ) {
+ if ( $this->getParam( 'overwrite' ) ) {
+ return $status; // OK
+ } elseif ( $this->getParam( 'overwriteSame' ) ) {
+ $dhash = $this->fileSha1( $this->params['dst'], $predicates );
+ // Check if hashes are valid and match each other...
+ if ( !strlen( $this->sourceSha1 ) || !strlen( $dhash ) ) {
+ $status->fatal( 'backend-fail-hashes' );
+ } elseif ( $this->sourceSha1 !== $dhash ) {
+ // Give an error if the files are not identical
+ $status->fatal( 'backend-fail-notsame', $this->params['dst'] );
+ } else {
+ $this->destSameAsSource = true; // OK
+ }
+ return $status; // do nothing; either OK or bad status
+ } else {
+ $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] );
+ return $status;
+ }
+ }
+ return $status;
+ }
+
+ /**
+ * precheckDestExistence() helper function to get the source file SHA-1.
+ * Subclasses should overwride this iff the source is not in storage.
+ *
+ * @return string|bool Returns false on failure
+ */
+ protected function getSourceSha1Base36() {
+ return null; // N/A
+ }
+
+ /**
+ * Check if a file will exist in storage when this operation is attempted
+ *
+ * @param $source string Storage path
+ * @param $predicates Array
+ * @return bool
+ */
+ final protected function fileExists( $source, array $predicates ) {
+ if ( isset( $predicates['exists'][$source] ) ) {
+ return $predicates['exists'][$source]; // previous op assures this
+ } else {
+ $params = array( 'src' => $source, 'latest' => $this->useLatest );
+ return $this->backend->fileExists( $params );
+ }
+ }
+
+ /**
+ * Get the SHA-1 of a file in storage when this operation is attempted
+ *
+ * @param $source string Storage path
+ * @param $predicates Array
+ * @return string|bool False on failure
+ */
+ final protected function fileSha1( $source, array $predicates ) {
+ if ( isset( $predicates['sha1'][$source] ) ) {
+ return $predicates['sha1'][$source]; // previous op assures this
+ } else {
+ $params = array( 'src' => $source, 'latest' => $this->useLatest );
+ return $this->backend->getFileSha1Base36( $params );
+ }
+ }
+
+ /**
+ * Get the backend this operation is for
+ *
+ * @return FileBackendStore
+ */
+ public function getBackend() {
+ return $this->backend;
+ }
+
+ /**
+ * Log a file operation failure and preserve any temp files
+ *
+ * @param $action string
+ * @return void
+ */
+ final public function logFailure( $action ) {
+ $params = $this->params;
+ $params['failedAction'] = $action;
+ try {
+ wfDebugLog( 'FileOperation', get_class( $this ) .
+ " failed (batch #{$this->batchId}): " . FormatJson::encode( $params ) );
+ } catch ( Exception $e ) {
+ // bad config? debug log error?
+ }
+ }
+}
+
+/**
+ * Store a file into the backend from a file on the file system.
+ * Parameters similar to FileBackendStore::storeInternal(), which include:
+ * - src : source path on file system
+ * - dst : destination storage path
+ * - overwrite : do nothing and pass if an identical file exists at destination
+ * - overwriteSame : override any existing file at destination
+ */
+class StoreFileOp extends FileOp {
+ /**
+ * @return array
+ */
+ protected function allowedParams() {
+ return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ }
+
+ /**
+ * @param $predicates array
+ * @return Status
+ */
+ protected function doPrecheck( array &$predicates ) {
+ $status = Status::newGood();
+ // Check if the source file exists on the file system
+ if ( !is_file( $this->params['src'] ) ) {
+ $status->fatal( 'backend-fail-notexists', $this->params['src'] );
+ return $status;
+ // Check if the source file is too big
+ } elseif ( filesize( $this->params['src'] ) > $this->backend->maxFileSizeInternal() ) {
+ $status->fatal( 'backend-fail-maxsize',
+ $this->params['dst'], $this->backend->maxFileSizeInternal() );
+ $status->fatal( 'backend-fail-store', $this->params['src'], $this->params['dst'] );
+ return $status;
+ // Check if a file can be placed at the destination
+ } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
+ $status->fatal( 'backend-fail-usable', $this->params['dst'] );
+ $status->fatal( 'backend-fail-store', $this->params['src'], $this->params['dst'] );
+ return $status;
+ }
+ // Check if destination file exists
+ $status->merge( $this->precheckDestExistence( $predicates ) );
+ if ( $status->isOK() ) {
+ // Update file existence predicates
+ $predicates['exists'][$this->params['dst']] = true;
+ $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
+ }
+ return $status; // safe to call attempt()
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ // Store the file at the destination
+ if ( !$this->destSameAsSource ) {
+ return $this->backend->storeInternal( $this->setFlags( $this->params ) );
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * @return bool|string
+ */
+ protected function getSourceSha1Base36() {
+ wfSuppressWarnings();
+ $hash = sha1_file( $this->params['src'] );
+ wfRestoreWarnings();
+ if ( $hash !== false ) {
+ $hash = wfBaseConvert( $hash, 16, 36, 31 );
+ }
+ return $hash;
+ }
+
+ protected function doStoragePathsChanged() {
+ return array( $this->params['dst'] );
+ }
+}
+
+/**
+ * Create a file in the backend with the given content.
+ * Parameters similar to FileBackendStore::createInternal(), which include:
+ * - content : the raw file contents
+ * - dst : destination storage path
+ * - overwrite : do nothing and pass if an identical file exists at destination
+ * - overwriteSame : override any existing file at destination
+ */
+class CreateFileOp extends FileOp {
+ protected function allowedParams() {
+ return array( array( 'content', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ }
+
+ protected function doPrecheck( array &$predicates ) {
+ $status = Status::newGood();
+ // Check if the source data is too big
+ if ( strlen( $this->getParam( 'content' ) ) > $this->backend->maxFileSizeInternal() ) {
+ $status->fatal( 'backend-fail-maxsize',
+ $this->params['dst'], $this->backend->maxFileSizeInternal() );
+ $status->fatal( 'backend-fail-create', $this->params['dst'] );
+ return $status;
+ // Check if a file can be placed at the destination
+ } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
+ $status->fatal( 'backend-fail-usable', $this->params['dst'] );
+ $status->fatal( 'backend-fail-create', $this->params['dst'] );
+ return $status;
+ }
+ // Check if destination file exists
+ $status->merge( $this->precheckDestExistence( $predicates ) );
+ if ( $status->isOK() ) {
+ // Update file existence predicates
+ $predicates['exists'][$this->params['dst']] = true;
+ $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
+ }
+ return $status; // safe to call attempt()
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ if ( !$this->destSameAsSource ) {
+ // Create the file at the destination
+ return $this->backend->createInternal( $this->setFlags( $this->params ) );
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * @return bool|String
+ */
+ protected function getSourceSha1Base36() {
+ return wfBaseConvert( sha1( $this->params['content'] ), 16, 36, 31 );
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsChanged() {
+ return array( $this->params['dst'] );
+ }
+}
+
+/**
+ * Copy a file from one storage path to another in the backend.
+ * Parameters similar to FileBackendStore::copyInternal(), which include:
+ * - src : source storage path
+ * - dst : destination storage path
+ * - overwrite : do nothing and pass if an identical file exists at destination
+ * - overwriteSame : override any existing file at destination
+ */
+class CopyFileOp extends FileOp {
+ /**
+ * @return array
+ */
+ protected function allowedParams() {
+ return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ }
+
+ /**
+ * @param $predicates array
+ * @return Status
+ */
+ protected function doPrecheck( array &$predicates ) {
+ $status = Status::newGood();
+ // Check if the source file exists
+ if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
+ $status->fatal( 'backend-fail-notexists', $this->params['src'] );
+ return $status;
+ // Check if a file can be placed at the destination
+ } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
+ $status->fatal( 'backend-fail-usable', $this->params['dst'] );
+ $status->fatal( 'backend-fail-copy', $this->params['src'], $this->params['dst'] );
+ return $status;
+ }
+ // Check if destination file exists
+ $status->merge( $this->precheckDestExistence( $predicates ) );
+ if ( $status->isOK() ) {
+ // Update file existence predicates
+ $predicates['exists'][$this->params['dst']] = true;
+ $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
+ }
+ return $status; // safe to call attempt()
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ // Do nothing if the src/dst paths are the same
+ if ( $this->params['src'] !== $this->params['dst'] ) {
+ // Copy the file into the destination
+ if ( !$this->destSameAsSource ) {
+ return $this->backend->copyInternal( $this->setFlags( $this->params ) );
+ }
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsRead() {
+ return array( $this->params['src'] );
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsChanged() {
+ return array( $this->params['dst'] );
+ }
+}
+
+/**
+ * Move a file from one storage path to another in the backend.
+ * Parameters similar to FileBackendStore::moveInternal(), which include:
+ * - src : source storage path
+ * - dst : destination storage path
+ * - overwrite : do nothing and pass if an identical file exists at destination
+ * - overwriteSame : override any existing file at destination
+ */
+class MoveFileOp extends FileOp {
+ /**
+ * @return array
+ */
+ protected function allowedParams() {
+ return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+ }
+
+ /**
+ * @param $predicates array
+ * @return Status
+ */
+ protected function doPrecheck( array &$predicates ) {
+ $status = Status::newGood();
+ // Check if the source file exists
+ if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
+ $status->fatal( 'backend-fail-notexists', $this->params['src'] );
+ return $status;
+ // Check if a file can be placed at the destination
+ } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
+ $status->fatal( 'backend-fail-usable', $this->params['dst'] );
+ $status->fatal( 'backend-fail-move', $this->params['src'], $this->params['dst'] );
+ return $status;
+ }
+ // Check if destination file exists
+ $status->merge( $this->precheckDestExistence( $predicates ) );
+ if ( $status->isOK() ) {
+ // Update file existence predicates
+ $predicates['exists'][$this->params['src']] = false;
+ $predicates['sha1'][$this->params['src']] = false;
+ $predicates['exists'][$this->params['dst']] = true;
+ $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
+ }
+ return $status; // safe to call attempt()
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ // Do nothing if the src/dst paths are the same
+ if ( $this->params['src'] !== $this->params['dst'] ) {
+ if ( !$this->destSameAsSource ) {
+ // Move the file into the destination
+ return $this->backend->moveInternal( $this->setFlags( $this->params ) );
+ } else {
+ // Just delete source as the destination needs no changes
+ $params = array( 'src' => $this->params['src'] );
+ return $this->backend->deleteInternal( $this->setFlags( $params ) );
+ }
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsRead() {
+ return array( $this->params['src'] );
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsChanged() {
+ return array( $this->params['src'], $this->params['dst'] );
+ }
+}
+
+/**
+ * Delete a file at the given storage path from the backend.
+ * Parameters similar to FileBackendStore::deleteInternal(), which include:
+ * - src : source storage path
+ * - ignoreMissingSource : don't return an error if the file does not exist
+ */
+class DeleteFileOp extends FileOp {
+ /**
+ * @return array
+ */
+ protected function allowedParams() {
+ return array( array( 'src' ), array( 'ignoreMissingSource' ) );
+ }
+
+ protected $needsDelete = true;
+
+ /**
+ * @param array $predicates
+ * @return Status
+ */
+ protected function doPrecheck( array &$predicates ) {
+ $status = Status::newGood();
+ // Check if the source file exists
+ if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
+ if ( !$this->getParam( 'ignoreMissingSource' ) ) {
+ $status->fatal( 'backend-fail-notexists', $this->params['src'] );
+ return $status;
+ }
+ $this->needsDelete = false;
+ }
+ // Update file existence predicates
+ $predicates['exists'][$this->params['src']] = false;
+ $predicates['sha1'][$this->params['src']] = false;
+ return $status; // safe to call attempt()
+ }
+
+ /**
+ * @return Status
+ */
+ protected function doAttempt() {
+ if ( $this->needsDelete ) {
+ // Delete the source file
+ return $this->backend->deleteInternal( $this->setFlags( $this->params ) );
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * @return array
+ */
+ protected function doStoragePathsChanged() {
+ return array( $this->params['src'] );
+ }
+}
+
+/**
+ * Placeholder operation that has no params and does nothing
+ */
+class NullFileOp extends FileOp {}
--- /dev/null
+<?php
+/**
+ * Helper class for representing batch file operations.
+ *
+ * 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 FileBackend
+ * @author Aaron Schulz
+ */
+
+/**
+ * Helper class for representing batch file operations.
+ * Do not use this class from places outside FileBackend.
+ *
+ * Methods should avoid throwing exceptions at all costs.
+ *
+ * @ingroup FileBackend
+ * @since 1.20
+ */
+class FileOpBatch {
+ /* Timeout related parameters */
+ const MAX_BATCH_SIZE = 1000; // integer
+
+ /**
+ * Attempt to perform a series of file operations.
+ * Callers are responsible for handling file locking.
+ *
+ * $opts is an array of options, including:
+ * - force : Errors that would normally cause a rollback do not.
+ * The remaining operations are still attempted if any fail.
+ * - allowStale : Don't require the latest available data.
+ * This can increase performance for non-critical writes.
+ * This has no effect unless the 'force' flag is set.
+ * - nonJournaled : Don't log this operation batch in the file journal.
+ * - concurrency : Try to do this many operations in parallel when possible.
+ *
+ * The resulting Status will be "OK" unless:
+ * - a) unexpected operation errors occurred (network partitions, disk full...)
+ * - b) significant operation errors occurred and 'force' was not set
+ *
+ * @param $performOps Array List of FileOp operations
+ * @param $opts Array Batch operation options
+ * @param $journal FileJournal Journal to log operations to
+ * @return Status
+ */
+ public static function attempt( array $performOps, array $opts, FileJournal $journal ) {
+ wfProfileIn( __METHOD__ );
+ $status = Status::newGood();
+
+ $n = count( $performOps );
+ if ( $n > self::MAX_BATCH_SIZE ) {
+ $status->fatal( 'backend-fail-batchsize', $n, self::MAX_BATCH_SIZE );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ $batchId = $journal->getTimestampedUUID();
+ $allowStale = !empty( $opts['allowStale'] );
+ $ignoreErrors = !empty( $opts['force'] );
+ $journaled = empty( $opts['nonJournaled'] );
+ $maxConcurrency = isset( $opts['concurrency'] ) ? $opts['concurrency'] : 1;
+
+ $entries = array(); // file journal entry list
+ $predicates = FileOp::newPredicates(); // account for previous ops in prechecks
+ $curBatch = array(); // concurrent FileOp sub-batch accumulation
+ $curBatchDeps = FileOp::newDependencies(); // paths used in FileOp sub-batch
+ $pPerformOps = array(); // ordered list of concurrent FileOp sub-batches
+ $lastBackend = null; // last op backend name
+ // Do pre-checks for each operation; abort on failure...
+ foreach ( $performOps as $index => $fileOp ) {
+ $backendName = $fileOp->getBackend()->getName();
+ $fileOp->setBatchId( $batchId ); // transaction ID
+ $fileOp->allowStaleReads( $allowStale ); // consistency level
+ // Decide if this op can be done concurrently within this sub-batch
+ // or if a new concurrent sub-batch must be started after this one...
+ if ( $fileOp->dependsOn( $curBatchDeps )
+ || count( $curBatch ) >= $maxConcurrency
+ || ( $backendName !== $lastBackend && count( $curBatch ) )
+ ) {
+ $pPerformOps[] = $curBatch; // push this batch
+ $curBatch = array(); // start a new sub-batch
+ $curBatchDeps = FileOp::newDependencies();
+ }
+ $lastBackend = $backendName;
+ $curBatch[$index] = $fileOp; // keep index
+ // Update list of affected paths in this batch
+ $curBatchDeps = $fileOp->applyDependencies( $curBatchDeps );
+ // Simulate performing the operation...
+ $oldPredicates = $predicates;
+ $subStatus = $fileOp->precheck( $predicates ); // updates $predicates
+ $status->merge( $subStatus );
+ if ( $subStatus->isOK() ) {
+ if ( $journaled ) { // journal log entries
+ $entries = array_merge( $entries,
+ $fileOp->getJournalEntries( $oldPredicates, $predicates ) );
+ }
+ } else { // operation failed?
+ $status->success[$index] = false;
+ ++$status->failCount;
+ if ( !$ignoreErrors ) {
+ wfProfileOut( __METHOD__ );
+ return $status; // abort
+ }
+ }
+ }
+ // Push the last sub-batch
+ if ( count( $curBatch ) ) {
+ $pPerformOps[] = $curBatch;
+ }
+
+ // Log the operations in the file journal...
+ if ( count( $entries ) ) {
+ $subStatus = $journal->logChangeBatch( $entries, $batchId );
+ if ( !$subStatus->isOK() ) {
+ wfProfileOut( __METHOD__ );
+ return $subStatus; // abort
+ }
+ }
+
+ if ( $ignoreErrors ) { // treat precheck() fatals as mere warnings
+ $status->setResult( true, $status->value );
+ }
+
+ // Attempt each operation (in parallel if allowed and possible)...
+ if ( count( $pPerformOps ) < count( $performOps ) ) {
+ self::runBatchParallel( $pPerformOps, $status );
+ } else {
+ self::runBatchSeries( $performOps, $status );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * Attempt a list of file operations in series.
+ * This will abort remaining ops on failure.
+ *
+ * @param $performOps Array
+ * @param $status Status
+ * @return bool Success
+ */
+ protected static function runBatchSeries( array $performOps, Status $status ) {
+ foreach ( $performOps as $index => $fileOp ) {
+ if ( $fileOp->failed() ) {
+ continue; // nothing to do
+ }
+ $subStatus = $fileOp->attempt();
+ $status->merge( $subStatus );
+ if ( $subStatus->isOK() ) {
+ $status->success[$index] = true;
+ ++$status->successCount;
+ } else {
+ $status->success[$index] = false;
+ ++$status->failCount;
+ // We can't continue (even with $ignoreErrors) as $predicates is wrong.
+ // Log the remaining ops as failed for recovery...
+ for ( $i = ($index + 1); $i < count( $performOps ); $i++ ) {
+ $performOps[$i]->logFailure( 'attempt_aborted' );
+ }
+ return false; // bail out
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attempt a list of file operations sub-batches in series.
+ *
+ * The operations *in* each sub-batch will be done in parallel.
+ * The caller is responsible for making sure the operations
+ * within any given sub-batch do not depend on each other.
+ * This will abort remaining ops on failure.
+ *
+ * @param $pPerformOps Array
+ * @param $status Status
+ * @return bool Success
+ */
+ protected static function runBatchParallel( array $pPerformOps, Status $status ) {
+ $aborted = false;
+ foreach ( $pPerformOps as $performOpsBatch ) {
+ if ( $aborted ) { // check batch op abort flag...
+ // We can't continue (even with $ignoreErrors) as $predicates is wrong.
+ // Log the remaining ops as failed for recovery...
+ foreach ( $performOpsBatch as $i => $fileOp ) {
+ $performOpsBatch[$i]->logFailure( 'attempt_aborted' );
+ }
+ continue;
+ }
+ $statuses = array();
+ $opHandles = array();
+ // Get the backend; all sub-batch ops belong to a single backend
+ $backend = reset( $performOpsBatch )->getBackend();
+ // If attemptAsync() returns synchronously, it was either an
+ // error Status or the backend just doesn't support async ops.
+ foreach ( $performOpsBatch as $i => $fileOp ) {
+ if ( !$fileOp->failed() ) { // failed => already has Status
+ $subStatus = $fileOp->attemptAsync();
+ if ( $subStatus->value instanceof FileBackendStoreOpHandle ) {
+ $opHandles[$i] = $subStatus->value; // deferred
+ } else {
+ $statuses[$i] = $subStatus; // done already
+ }
+ }
+ }
+ // Try to do all the operations concurrently...
+ $statuses = $statuses + $backend->executeOpHandlesInternal( $opHandles );
+ // Marshall and merge all the responses (blocking)...
+ foreach ( $performOpsBatch as $i => $fileOp ) {
+ if ( !$fileOp->failed() ) { // failed => already has Status
+ $subStatus = $statuses[$i];
+ $status->merge( $subStatus );
+ if ( $subStatus->isOK() ) {
+ $status->success[$i] = true;
+ ++$status->successCount;
+ } else {
+ $status->success[$i] = false;
+ ++$status->failCount;
+ $aborted = true; // set abort flag; we can't continue
+ }
+ }
+ }
+ }
+ return $status;
+ }
+}
--- /dev/null
+<?php
+/**
+ * OpenStack Swift based file backend.
+ *
+ * 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 FileBackend
+ * @author Russ Nelson
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Class for an OpenStack Swift based file backend.
+ *
+ * This requires the SwiftCloudFiles MediaWiki extension, which includes
+ * the php-cloudfiles library (https://github.com/rackspace/php-cloudfiles).
+ * php-cloudfiles requires the curl, fileinfo, and mb_string PHP extensions.
+ *
+ * Status messages should avoid mentioning the Swift account name.
+ * Likewise, error suppression should be used to avoid path disclosure.
+ *
+ * @ingroup FileBackend
+ * @since 1.19
+ */
+class SwiftFileBackend extends FileBackendStore {
+ /** @var CF_Authentication */
+ protected $auth; // Swift authentication handler
+ protected $authTTL; // integer seconds
+ protected $swiftAnonUser; // string; username to handle unauthenticated requests
+ protected $swiftUseCDN; // boolean; whether CloudFiles CDN is enabled
+ protected $swiftCDNExpiry; // integer; how long to cache things in the CDN
+ protected $swiftCDNPurgable; // boolean; whether object CDN purging is enabled
+
+ protected $maxContCacheSize = 300; // integer; max containers with entries
+
+ /** @var CF_Connection */
+ protected $conn; // Swift connection handle
+ protected $connStarted = 0; // integer UNIX timestamp
+ protected $connContainers = array(); // container object cache
+ protected $connException; // CloudFiles exception
+
+ /**
+ * @see FileBackendStore::__construct()
+ * Additional $config params include:
+ * - swiftAuthUrl : Swift authentication server URL
+ * - swiftUser : Swift user used by MediaWiki (account:username)
+ * - swiftKey : Swift authentication key for the above user
+ * - swiftAuthTTL : Swift authentication TTL (seconds)
+ * - swiftAnonUser : Swift user used for end-user requests (account:username).
+ * If set, then views of public containers are assumed to go
+ * through this user. If not set, then public containers are
+ * accessible to unauthenticated requests via ".r:*" in the ACL.
+ * - swiftUseCDN : Whether a Cloud Files Content Delivery Network is set up
+ * - swiftCDNExpiry : How long (in seconds) to store content in the CDN.
+ * If files may likely change, this should probably not exceed
+ * a few days. For example, deletions may take this long to apply.
+ * If object purging is enabled, however, this is not an issue.
+ * - swiftCDNPurgable : Whether object purge requests are allowed by the CDN.
+ * - shardViaHashLevels : Map of container names to sharding config with:
+ * - base : base of hash characters, 16 or 36
+ * - levels : the number of hash levels (and digits)
+ * - repeat : hash subdirectories are prefixed with all the
+ * parent hash directory names (e.g. "a/ab/abc")
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+ if ( !MWInit::classExists( 'CF_Constants' ) ) {
+ throw new MWException( 'SwiftCloudFiles extension not installed.' );
+ }
+ // Required settings
+ $this->auth = new CF_Authentication(
+ $config['swiftUser'],
+ $config['swiftKey'],
+ null, // account; unused
+ $config['swiftAuthUrl']
+ );
+ // Optional settings
+ $this->authTTL = isset( $config['swiftAuthTTL'] )
+ ? $config['swiftAuthTTL']
+ : 5 * 60; // some sane number
+ $this->swiftAnonUser = isset( $config['swiftAnonUser'] )
+ ? $config['swiftAnonUser']
+ : '';
+ $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
+ ? $config['shardViaHashLevels']
+ : '';
+ $this->swiftUseCDN = isset( $config['swiftUseCDN'] )
+ ? $config['swiftUseCDN']
+ : false;
+ $this->swiftCDNExpiry = isset( $config['swiftCDNExpiry'] )
+ ? $config['swiftCDNExpiry']
+ : 3600; // hour
+ $this->swiftCDNPurgable = isset( $config['swiftCDNPurgable'] )
+ ? $config['swiftCDNPurgable']
+ : true;
+ // Cache container info to mask latency
+ $this->memCache = wfGetMainCache();
+ }
+
+ /**
+ * @see FileBackendStore::resolveContainerPath()
+ * @return null
+ */
+ protected function resolveContainerPath( $container, $relStoragePath ) {
+ if ( !mb_check_encoding( $relStoragePath, 'UTF-8' ) ) { // mb_string required by CF
+ return null; // not UTF-8, makes it hard to use CF and the swift HTTP API
+ } elseif ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
+ return null; // too long for Swift
+ }
+ return $relStoragePath;
+ }
+
+ /**
+ * @see FileBackendStore::isPathUsableInternal()
+ * @return bool
+ */
+ public function isPathUsableInternal( $storagePath ) {
+ list( $container, $rel ) = $this->resolveStoragePathReal( $storagePath );
+ if ( $rel === null ) {
+ return false; // invalid
+ }
+
+ try {
+ $this->getContainer( $container );
+ return true; // container exists
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__, array( 'path' => $storagePath ) );
+ }
+
+ return false;
+ }
+
+ /**
+ * @see FileBackendStore::doCreateInternal()
+ * @return Status
+ */
+ protected function doCreateInternal( array $params ) {
+ $status = Status::newGood();
+
+ list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
+ if ( $dstRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ // (a) Check the destination container and object
+ try {
+ $dContObj = $this->getContainer( $dstCont );
+ if ( empty( $params['overwrite'] ) &&
+ $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
+ {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ return $status;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Get a SHA-1 hash of the object
+ $sha1Hash = wfBaseConvert( sha1( $params['content'] ), 16, 36, 31 );
+
+ // (c) Actually create the object
+ try {
+ // Create a fresh CF_Object with no fields preloaded.
+ // We don't want to preserve headers, metadata, and such.
+ $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ // Note: metadata keys stored as [Upper case char][[Lower case char]...]
+ $obj->metadata = array( 'Sha1base36' => $sha1Hash );
+ // Manually set the ETag (https://github.com/rackspace/php-cloudfiles/issues/59).
+ // The MD5 here will be checked within Swift against its own MD5.
+ $obj->set_etag( md5( $params['content'] ) );
+ // Use the same content type as StreamFile for security
+ $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] );
+ if ( !strlen( $obj->content_type ) ) { // special case
+ $obj->content_type = 'unknown/unknown';
+ }
+ if ( !empty( $params['async'] ) ) { // deferred
+ $handle = $obj->write_async( $params['content'] );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $handle );
+ $status->value->affectedObjects[] = $obj;
+ } else { // actually write the object in Swift
+ $obj->write( $params['content'] );
+ $this->purgeCDNCache( array( $obj ) );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( BadContentTypeException $e ) {
+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see SwiftFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseCreate( CF_Async_Op $cfOp, Status $status, array $params ) {
+ try {
+ $cfOp->getLastResponse();
+ } catch ( BadContentTypeException $e ) {
+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doStoreInternal()
+ * @return Status
+ */
+ protected function doStoreInternal( array $params ) {
+ $status = Status::newGood();
+
+ list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
+ if ( $dstRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ // (a) Check the destination container and object
+ try {
+ $dContObj = $this->getContainer( $dstCont );
+ if ( empty( $params['overwrite'] ) &&
+ $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
+ {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ return $status;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Get a SHA-1 hash of the object
+ $sha1Hash = sha1_file( $params['src'] );
+ if ( $sha1Hash === false ) { // source doesn't exist?
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ return $status;
+ }
+ $sha1Hash = wfBaseConvert( $sha1Hash, 16, 36, 31 );
+
+ // (c) Actually store the object
+ try {
+ // Create a fresh CF_Object with no fields preloaded.
+ // We don't want to preserve headers, metadata, and such.
+ $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ // Note: metadata keys stored as [Upper case char][[Lower case char]...]
+ $obj->metadata = array( 'Sha1base36' => $sha1Hash );
+ // The MD5 here will be checked within Swift against its own MD5.
+ $obj->set_etag( md5_file( $params['src'] ) );
+ // Use the same content type as StreamFile for security
+ $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] );
+ if ( !strlen( $obj->content_type ) ) { // special case
+ $obj->content_type = 'unknown/unknown';
+ }
+ if ( !empty( $params['async'] ) ) { // deferred
+ wfSuppressWarnings();
+ $fp = fopen( $params['src'], 'rb' );
+ wfRestoreWarnings();
+ if ( !$fp ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ } else {
+ $handle = $obj->write_async( $fp, filesize( $params['src'] ), true );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $handle );
+ $status->value->resourcesToClose[] = $fp;
+ $status->value->affectedObjects[] = $obj;
+ }
+ } else { // actually write the object in Swift
+ $obj->load_from_filename( $params['src'], true ); // calls $obj->write()
+ $this->purgeCDNCache( array( $obj ) );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( BadContentTypeException $e ) {
+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
+ } catch ( IOException $e ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see SwiftFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseStore( CF_Async_Op $cfOp, Status $status, array $params ) {
+ try {
+ $cfOp->getLastResponse();
+ } catch ( BadContentTypeException $e ) {
+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
+ } catch ( IOException $e ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doCopyInternal()
+ * @return Status
+ */
+ protected function doCopyInternal( array $params ) {
+ $status = Status::newGood();
+
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
+ if ( $dstRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ // (a) Check the source/destination containers and destination object
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $dContObj = $this->getContainer( $dstCont );
+ if ( empty( $params['overwrite'] ) &&
+ $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
+ {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ return $status;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Actually copy the file to the destination
+ try {
+ $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ if ( !empty( $params['async'] ) ) { // deferred
+ $handle = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $handle );
+ $status->value->affectedObjects[] = $dstObj;
+ } else { // actually write the object in Swift
+ $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel );
+ $this->purgeCDNCache( array( $dstObj ) );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( NoSuchObjectException $e ) { // source object does not exist
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see SwiftFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseCopy( CF_Async_Op $cfOp, Status $status, array $params ) {
+ try {
+ $cfOp->getLastResponse();
+ } catch ( NoSuchObjectException $e ) { // source object does not exist
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doMoveInternal()
+ * @return Status
+ */
+ protected function doMoveInternal( array $params ) {
+ $status = Status::newGood();
+
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
+ if ( $dstRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+ return $status;
+ }
+
+ // (a) Check the source/destination containers and destination object
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $dContObj = $this->getContainer( $dstCont );
+ if ( empty( $params['overwrite'] ) &&
+ $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
+ {
+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
+ return $status;
+ }
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ return $status;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Actually move the file to the destination
+ try {
+ $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+ $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+ if ( !empty( $params['async'] ) ) { // deferred
+ $handle = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $handle );
+ $status->value->affectedObjects[] = $srcObj;
+ $status->value->affectedObjects[] = $dstObj;
+ } else { // actually write the object in Swift
+ $sContObj->move_object_to( $srcRel, $dContObj, $dstRel );
+ $this->purgeCDNCache( array( $srcObj, $dstObj ) );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( NoSuchObjectException $e ) { // source object does not exist
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see SwiftFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseMove( CF_Async_Op $cfOp, Status $status, array $params ) {
+ try {
+ $cfOp->getLastResponse();
+ } catch ( NoSuchObjectException $e ) { // source object does not exist
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doDeleteInternal()
+ * @return Status
+ */
+ protected function doDeleteInternal( array $params ) {
+ $status = Status::newGood();
+
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ return $status;
+ }
+
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+ if ( !empty( $params['async'] ) ) { // deferred
+ $handle = $sContObj->delete_object_async( $srcRel );
+ $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $handle );
+ $status->value->affectedObjects[] = $srcObj;
+ } else { // actually write the object in Swift
+ $sContObj->delete_object( $srcRel );
+ $this->purgeCDNCache( array( $srcObj ) );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ } catch ( NoSuchObjectException $e ) {
+ if ( empty( $params['ignoreMissingSource'] ) ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ }
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see SwiftFileBackend::doExecuteOpHandlesInternal()
+ */
+ protected function _getResponseDelete( CF_Async_Op $cfOp, Status $status, array $params ) {
+ try {
+ $cfOp->getLastResponse();
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ } catch ( NoSuchObjectException $e ) {
+ if ( empty( $params['ignoreMissingSource'] ) ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ }
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doPrepareInternal()
+ * @return Status
+ */
+ protected function doPrepareInternal( $fullCont, $dir, array $params ) {
+ $status = Status::newGood();
+
+ // (a) Check if container already exists
+ try {
+ $contObj = $this->getContainer( $fullCont );
+ // NoSuchContainerException not thrown: container must exist
+ return $status; // already exists
+ } catch ( NoSuchContainerException $e ) {
+ // NoSuchContainerException thrown: container does not exist
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Create container as needed
+ try {
+ $contObj = $this->createContainer( $fullCont );
+ if ( !empty( $params['noAccess'] ) ) {
+ // Make container private to end-users...
+ $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
+ } else {
+ // Make container public to end-users...
+ $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
+ }
+ if ( $this->swiftUseCDN ) { // Rackspace style CDN
+ $contObj->make_public( $this->swiftCDNExpiry );
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doSecureInternal()
+ * @return Status
+ */
+ protected function doSecureInternal( $fullCont, $dir, array $params ) {
+ $status = Status::newGood();
+ if ( empty( $params['noAccess'] ) ) {
+ return $status; // nothing to do
+ }
+
+ // Restrict container from end-users...
+ try {
+ // doPrepareInternal() should have been called,
+ // so the Swift container should already exist...
+ $contObj = $this->getContainer( $fullCont ); // normally a cache hit
+ // NoSuchContainerException not thrown: container must exist
+
+ // Make container private to end-users...
+ $status->merge( $this->setContainerAccess(
+ $contObj,
+ array( $this->auth->username ), // read
+ array( $this->auth->username ) // write
+ ) );
+ if ( $this->swiftUseCDN && $contObj->is_public() ) { // Rackspace style CDN
+ $contObj->make_private();
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doPublishInternal()
+ * @return Status
+ */
+ protected function doPublishInternal( $fullCont, $dir, array $params ) {
+ $status = Status::newGood();
+
+ // Unrestrict container from end-users...
+ try {
+ // doPrepareInternal() should have been called,
+ // so the Swift container should already exist...
+ $contObj = $this->getContainer( $fullCont ); // normally a cache hit
+ // NoSuchContainerException not thrown: container must exist
+
+ // Make container public to end-users...
+ if ( $this->swiftAnonUser != '' ) {
+ $status->merge( $this->setContainerAccess(
+ $contObj,
+ array( $this->auth->username, $this->swiftAnonUser ), // read
+ array( $this->auth->username, $this->swiftAnonUser ) // write
+ ) );
+ } else {
+ $status->merge( $this->setContainerAccess(
+ $contObj,
+ array( $this->auth->username, '.r:*' ), // read
+ array( $this->auth->username ) // write
+ ) );
+ }
+ if ( $this->swiftUseCDN && !$contObj->is_public() ) { // Rackspace style CDN
+ $contObj->make_public();
+ }
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doCleanInternal()
+ * @return Status
+ */
+ protected function doCleanInternal( $fullCont, $dir, array $params ) {
+ $status = Status::newGood();
+
+ // Only containers themselves can be removed, all else is virtual
+ if ( $dir != '' ) {
+ return $status; // nothing to do
+ }
+
+ // (a) Check the container
+ try {
+ $contObj = $this->getContainer( $fullCont, true );
+ } catch ( NoSuchContainerException $e ) {
+ return $status; // ok, nothing to do
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ // (b) Delete the container if empty
+ if ( $contObj->object_count == 0 ) {
+ try {
+ $this->deleteContainer( $fullCont );
+ } catch ( NoSuchContainerException $e ) {
+ return $status; // race?
+ } catch ( NonEmptyContainerException $e ) {
+ return $status; // race? consistency delay?
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::doFileExists()
+ * @return array|bool|null
+ */
+ protected function doGetFileStat( array $params ) {
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ return false; // invalid storage path
+ }
+
+ $stat = false;
+ try {
+ $contObj = $this->getContainer( $srcCont );
+ $srcObj = $contObj->get_object( $srcRel, $this->headersFromParams( $params ) );
+ $this->addMissingMetadata( $srcObj, $params['src'] );
+ $stat = array(
+ // Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
+ 'mtime' => wfTimestamp( TS_MW, $srcObj->last_modified ),
+ 'size' => (int)$srcObj->content_length,
+ 'sha1' => $srcObj->metadata['Sha1base36']
+ );
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( NoSuchObjectException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $stat = null;
+ $this->handleException( $e, null, __METHOD__, $params );
+ }
+
+ return $stat;
+ }
+
+ /**
+ * Fill in any missing object metadata and save it to Swift
+ *
+ * @param $obj CF_Object
+ * @param $path string Storage path to object
+ * @return bool Success
+ * @throws Exception cloudfiles exceptions
+ */
+ protected function addMissingMetadata( CF_Object $obj, $path ) {
+ if ( isset( $obj->metadata['Sha1base36'] ) ) {
+ return true; // nothing to do
+ }
+ $status = Status::newGood();
+ $scopeLockS = $this->getScopedFileLocks( array( $path ), LockManager::LOCK_UW, $status );
+ if ( $status->isOK() ) {
+ # Do not stat the file in getLocalCopy() to avoid infinite loops
+ $tmpFile = $this->getLocalCopy( array( 'src' => $path, 'latest' => 1, 'nostat' => 1 ) );
+ if ( $tmpFile ) {
+ $hash = $tmpFile->getSha1Base36();
+ if ( $hash !== false ) {
+ $obj->metadata['Sha1base36'] = $hash;
+ $obj->sync_metadata(); // save to Swift
+ return true; // success
+ }
+ }
+ }
+ $obj->metadata['Sha1base36'] = false;
+ return false; // failed
+ }
+
+ /**
+ * @see FileBackend::getFileContents()
+ * @return bool|null|string
+ */
+ public function getFileContents( array $params ) {
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ return false; // invalid storage path
+ }
+
+ if ( !$this->fileExists( $params ) ) {
+ return null;
+ }
+
+ $data = false;
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+ $data = $obj->read( $this->headersFromParams( $params ) );
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__, $params );
+ }
+
+ return $data;
+ }
+
+ /**
+ * @see FileBackendStore::doDirectoryExists()
+ * @return bool|null
+ */
+ protected function doDirectoryExists( $fullCont, $dir, array $params ) {
+ try {
+ $container = $this->getContainer( $fullCont );
+ $prefix = ( $dir == '' ) ? null : "{$dir}/";
+ return ( count( $container->list_objects( 1, null, $prefix ) ) > 0 );
+ } catch ( NoSuchContainerException $e ) {
+ return false;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__,
+ array( 'cont' => $fullCont, 'dir' => $dir ) );
+ }
+
+ return null; // error
+ }
+
+ /**
+ * @see FileBackendStore::getDirectoryListInternal()
+ * @return SwiftFileBackendDirList
+ */
+ public function getDirectoryListInternal( $fullCont, $dir, array $params ) {
+ return new SwiftFileBackendDirList( $this, $fullCont, $dir, $params );
+ }
+
+ /**
+ * @see FileBackendStore::getFileListInternal()
+ * @return SwiftFileBackendFileList
+ */
+ public function getFileListInternal( $fullCont, $dir, array $params ) {
+ return new SwiftFileBackendFileList( $this, $fullCont, $dir, $params );
+ }
+
+ /**
+ * Do not call this function outside of SwiftFileBackendFileList
+ *
+ * @param $fullCont string Resolved container name
+ * @param $dir string Resolved storage directory with no trailing slash
+ * @param $after string|null Storage path of file to list items after
+ * @param $limit integer Max number of items to list
+ * @param $params Array Includes flag for 'topOnly'
+ * @return Array List of relative paths of dirs directly under $dir
+ */
+ public function getDirListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
+ $dirs = array();
+ if ( $after === INF ) {
+ return $dirs; // nothing more
+ }
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+
+ try {
+ $container = $this->getContainer( $fullCont );
+ $prefix = ( $dir == '' ) ? null : "{$dir}/";
+ // Non-recursive: only list dirs right under $dir
+ if ( !empty( $params['topOnly'] ) ) {
+ $objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
+ foreach ( $objects as $object ) { // files and dirs
+ if ( substr( $object, -1 ) === '/' ) {
+ $dirs[] = $object; // directories end in '/'
+ }
+ }
+ // Recursive: list all dirs under $dir and its subdirs
+ } else {
+ // Get directory from last item of prior page
+ $lastDir = $this->getParentDir( $after ); // must be first page
+ $objects = $container->list_objects( $limit, $after, $prefix );
+ foreach ( $objects as $object ) { // files
+ $objectDir = $this->getParentDir( $object ); // directory of object
+ if ( $objectDir !== false ) { // file has a parent dir
+ // Swift stores paths in UTF-8, using binary sorting.
+ // See function "create_container_table" in common/db.py.
+ // If a directory is not "greater" than the last one,
+ // then it was already listed by the calling iterator.
+ if ( $objectDir > $lastDir ) {
+ $pDir = $objectDir;
+ do { // add dir and all its parent dirs
+ $dirs[] = "{$pDir}/";
+ $pDir = $this->getParentDir( $pDir );
+ } while ( $pDir !== false // sanity
+ && $pDir > $lastDir // not done already
+ && strlen( $pDir ) > strlen( $dir ) // within $dir
+ );
+ }
+ $lastDir = $objectDir;
+ }
+ }
+ }
+ if ( count( $objects ) < $limit ) {
+ $after = INF; // avoid a second RTT
+ } else {
+ $after = end( $objects ); // update last item
+ }
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__,
+ array( 'cont' => $fullCont, 'dir' => $dir ) );
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ return $dirs;
+ }
+
+ protected function getParentDir( $path ) {
+ return ( strpos( $path, '/' ) !== false ) ? dirname( $path ) : false;
+ }
+
+ /**
+ * Do not call this function outside of SwiftFileBackendFileList
+ *
+ * @param $fullCont string Resolved container name
+ * @param $dir string Resolved storage directory with no trailing slash
+ * @param $after string|null Storage path of file to list items after
+ * @param $limit integer Max number of items to list
+ * @param $params Array Includes flag for 'topOnly'
+ * @return Array List of relative paths of files under $dir
+ */
+ public function getFileListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
+ $files = array();
+ if ( $after === INF ) {
+ return $files; // nothing more
+ }
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+
+ try {
+ $container = $this->getContainer( $fullCont );
+ $prefix = ( $dir == '' ) ? null : "{$dir}/";
+ // Non-recursive: only list files right under $dir
+ if ( !empty( $params['topOnly'] ) ) { // files and dirs
+ $objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
+ foreach ( $objects as $object ) {
+ if ( substr( $object, -1 ) !== '/' ) {
+ $files[] = $object; // directories end in '/'
+ }
+ }
+ // Recursive: list all files under $dir and its subdirs
+ } else { // files
+ $objects = $container->list_objects( $limit, $after, $prefix );
+ $files = $objects;
+ }
+ if ( count( $objects ) < $limit ) {
+ $after = INF; // avoid a second RTT
+ } else {
+ $after = end( $objects ); // update last item
+ }
+ } catch ( NoSuchContainerException $e ) {
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__,
+ array( 'cont' => $fullCont, 'dir' => $dir ) );
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ return $files;
+ }
+
+ /**
+ * @see FileBackendStore::doGetFileSha1base36()
+ * @return bool
+ */
+ protected function doGetFileSha1base36( array $params ) {
+ $stat = $this->getFileStat( $params );
+ if ( $stat ) {
+ return $stat['sha1'];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @see FileBackendStore::doStreamFile()
+ * @return Status
+ */
+ protected function doStreamFile( array $params ) {
+ $status = Status::newGood();
+
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+ }
+
+ try {
+ $cont = $this->getContainer( $srcCont );
+ } catch ( NoSuchContainerException $e ) {
+ $status->fatal( 'backend-fail-stream', $params['src'] );
+ return $status;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ return $status;
+ }
+
+ try {
+ $output = fopen( 'php://output', 'wb' );
+ $obj = new CF_Object( $cont, $srcRel, false, false ); // skip HEAD
+ $obj->stream( $output, $this->headersFromParams( $params ) );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status, __METHOD__, $params );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileBackendStore::getLocalCopy()
+ * @return null|TempFSFile
+ */
+ public function getLocalCopy( array $params ) {
+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+ if ( $srcRel === null ) {
+ return null;
+ }
+
+ # Check the recursion guard to avoid loops when filling metadata
+ if ( empty( $params['nostat'] ) && !$this->fileExists( $params ) ) {
+ return null;
+ }
+
+ $tmpFile = null;
+ try {
+ $sContObj = $this->getContainer( $srcCont );
+ $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+ // Get source file extension
+ $ext = FileBackend::extensionFromPath( $srcRel );
+ // Create a new temporary file...
+ $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext );
+ if ( $tmpFile ) {
+ $handle = fopen( $tmpFile->getPath(), 'wb' );
+ if ( $handle ) {
+ $obj->stream( $handle, $this->headersFromParams( $params ) );
+ fclose( $handle );
+ } else {
+ $tmpFile = null; // couldn't open temp file
+ }
+ }
+ } catch ( NoSuchContainerException $e ) {
+ $tmpFile = null;
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $tmpFile = null;
+ $this->handleException( $e, null, __METHOD__, $params );
+ }
+
+ return $tmpFile;
+ }
+
+ /**
+ * @see FileBackendStore::directoriesAreVirtual()
+ * @return bool
+ */
+ protected function directoriesAreVirtual() {
+ return true;
+ }
+
+ /**
+ * Get headers to send to Swift when reading a file based
+ * on a FileBackend params array, e.g. that of getLocalCopy().
+ * $params is currently only checked for a 'latest' flag.
+ *
+ * @param $params Array
+ * @return Array
+ */
+ protected function headersFromParams( array $params ) {
+ $hdrs = array();
+ if ( !empty( $params['latest'] ) ) {
+ $hdrs[] = 'X-Newest: true';
+ }
+ return $hdrs;
+ }
+
+ /**
+ * @see FileBackendStore::doExecuteOpHandlesInternal()
+ * @return Array List of corresponding Status objects
+ */
+ protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
+ $statuses = array();
+
+ $cfOps = array(); // list of CF_Async_Op objects
+ foreach ( $fileOpHandles as $index => $fileOpHandle ) {
+ $cfOps[$index] = $fileOpHandle->cfOp;
+ }
+ $batch = new CF_Async_Op_Batch( $cfOps );
+
+ $cfOps = $batch->execute();
+ foreach ( $cfOps as $index => $cfOp ) {
+ $status = Status::newGood();
+ try { // catch exceptions; update status
+ $function = '_getResponse' . $fileOpHandles[$index]->call;
+ $this->$function( $cfOp, $status, $fileOpHandles[$index]->params );
+ $this->purgeCDNCache( $fileOpHandles[$index]->affectedObjects );
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, $status,
+ __CLASS__ . ":$function", $fileOpHandles[$index]->params );
+ }
+ $statuses[$index] = $status;
+ }
+
+ return $statuses;
+ }
+
+ /**
+ * Set read/write permissions for a Swift container.
+ *
+ * $readGrps is a list of the possible criteria for a request to have
+ * access to read a container. Each item is one of the following formats:
+ * - account:user : Grants access if the request is by the given user
+ * - .r:<regex> : Grants access if the request is from a referrer host that
+ * matches the expression and the request is not for a listing.
+ * Setting this to '*' effectively makes a container public.
+ * - .rlistings:<regex> : Grants access if the request is from a referrer host that
+ * matches the expression and the request for a listing.
+ *
+ * $writeGrps is a list of the possible criteria for a request to have
+ * access to write to a container. Each item is of the following format:
+ * - account:user : Grants access if the request is by the given user
+ *
+ * @see http://swift.openstack.org/misc.html#acls
+ *
+ * In general, we don't allow listings to end-users. It's not useful, isn't well-defined
+ * (lists are truncated to 10000 item with no way to page), and is just a performance risk.
+ *
+ * @param $contObj CF_Container Swift container
+ * @param $readGrps Array List of read access routes
+ * @param $writeGrps Array List of write access routes
+ * @return Status
+ */
+ protected function setContainerAccess(
+ CF_Container $contObj, array $readGrps, array $writeGrps
+ ) {
+ $creds = $contObj->cfs_auth->export_credentials();
+
+ $url = $creds['storage_url'] . '/' . rawurlencode( $contObj->name );
+
+ // Note: 10 second timeout consistent with php-cloudfiles
+ $req = MWHttpRequest::factory( $url, array( 'method' => 'POST', 'timeout' => 10 ) );
+ $req->setHeader( 'X-Auth-Token', $creds['auth_token'] );
+ $req->setHeader( 'X-Container-Read', implode( ',', $readGrps ) );
+ $req->setHeader( 'X-Container-Write', implode( ',', $writeGrps ) );
+
+ return $req->execute(); // should return 204
+ }
+
+ /**
+ * Purge the CDN cache of affected objects if CDN caching is enabled.
+ * This is for Rackspace/Akamai CDNs.
+ *
+ * @param $objects Array List of CF_Object items
+ * @return void
+ */
+ public function purgeCDNCache( array $objects ) {
+ if ( $this->swiftUseCDN && $this->swiftCDNPurgable ) {
+ foreach ( $objects as $object ) {
+ try {
+ $object->purge_from_cdn();
+ } catch ( CDNNotEnabledException $e ) {
+ // CDN not enabled; nothing to see here
+ } catch ( CloudFilesException $e ) {
+ $this->handleException( $e, null, __METHOD__,
+ array( 'cont' => $object->container->name, 'obj' => $object->name ) );
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a connection to the Swift proxy
+ *
+ * @return CF_Connection|bool False on failure
+ * @throws CloudFilesException
+ */
+ protected function getConnection() {
+ if ( $this->connException instanceof Exception ) {
+ throw $this->connException; // failed last attempt
+ }
+ // Session keys expire after a while, so we renew them periodically
+ if ( $this->conn && ( time() - $this->connStarted ) > $this->authTTL ) {
+ $this->conn->close(); // close active cURL connections
+ $this->conn = null;
+ }
+ // Authenticate with proxy and get a session key...
+ if ( !$this->conn ) {
+ $this->connStarted = 0;
+ $this->connContainers = array();
+ try {
+ $this->auth->authenticate();
+ $this->conn = new CF_Connection( $this->auth );
+ $this->connStarted = time();
+ } catch ( CloudFilesException $e ) {
+ $this->connException = $e; // don't keep re-trying
+ throw $e; // throw it back
+ }
+ }
+ return $this->conn;
+ }
+
+ /**
+ * @see FileBackendStore::doClearCache()
+ */
+ protected function doClearCache( array $paths = null ) {
+ $this->connContainers = array(); // clear container object cache
+ }
+
+ /**
+ * Get a Swift container object, possibly from process cache.
+ * Use $reCache if the file count or byte count is needed.
+ *
+ * @param $container string Container name
+ * @param $bypassCache bool Bypass all caches and load from Swift
+ * @return CF_Container
+ * @throws CloudFilesException
+ */
+ protected function getContainer( $container, $bypassCache = false ) {
+ $conn = $this->getConnection(); // Swift proxy connection
+ if ( $bypassCache ) { // purge cache
+ unset( $this->connContainers[$container] );
+ } elseif ( !isset( $this->connContainers[$container] ) ) {
+ $this->primeContainerCache( array( $container ) ); // check persistent cache
+ }
+ if ( !isset( $this->connContainers[$container] ) ) {
+ $contObj = $conn->get_container( $container );
+ // NoSuchContainerException not thrown: container must exist
+ if ( count( $this->connContainers ) >= $this->maxContCacheSize ) { // trim cache?
+ reset( $this->connContainers );
+ unset( $this->connContainers[key( $this->connContainers )] );
+ }
+ $this->connContainers[$container] = $contObj; // cache it
+ if ( !$bypassCache ) {
+ $this->setContainerCache( $container, // update persistent cache
+ array( 'bytes' => $contObj->bytes_used, 'count' => $contObj->object_count )
+ );
+ }
+ }
+ return $this->connContainers[$container];
+ }
+
+ /**
+ * Create a Swift container
+ *
+ * @param $container string Container name
+ * @return CF_Container
+ * @throws CloudFilesException
+ */
+ protected function createContainer( $container ) {
+ $conn = $this->getConnection(); // Swift proxy connection
+ $contObj = $conn->create_container( $container );
+ $this->connContainers[$container] = $contObj; // cache it
+ return $contObj;
+ }
+
+ /**
+ * Delete a Swift container
+ *
+ * @param $container string Container name
+ * @return void
+ * @throws CloudFilesException
+ */
+ protected function deleteContainer( $container ) {
+ $conn = $this->getConnection(); // Swift proxy connection
+ unset( $this->connContainers[$container] ); // purge cache
+ $conn->delete_container( $container );
+ }
+
+ /**
+ * @see FileBackendStore::doPrimeContainerCache()
+ * @return void
+ */
+ protected function doPrimeContainerCache( array $containerInfo ) {
+ try {
+ $conn = $this->getConnection(); // Swift proxy connection
+ foreach ( $containerInfo as $container => $info ) {
+ $this->connContainers[$container] = new CF_Container(
+ $conn->cfs_auth,
+ $conn->cfs_http,
+ $container,
+ $info['count'],
+ $info['bytes']
+ );
+ }
+ } catch ( CloudFilesException $e ) { // some other exception?
+ $this->handleException( $e, null, __METHOD__, array() );
+ }
+ }
+
+ /**
+ * Log an unexpected exception for this backend.
+ * This also sets the Status object to have a fatal error.
+ *
+ * @param $e Exception
+ * @param $status Status|null
+ * @param $func string
+ * @param $params Array
+ * @return void
+ */
+ protected function handleException( Exception $e, $status, $func, array $params ) {
+ if ( $status instanceof Status ) {
+ if ( $e instanceof AuthenticationException ) {
+ $status->fatal( 'backend-fail-connect', $this->name );
+ } else {
+ $status->fatal( 'backend-fail-internal', $this->name );
+ }
+ }
+ if ( $e->getMessage() ) {
+ trigger_error( "$func: " . $e->getMessage(), E_USER_WARNING );
+ }
+ wfDebugLog( 'SwiftBackend',
+ get_class( $e ) . " in '{$func}' (given '" . FormatJson::encode( $params ) . "')" .
+ ( $e->getMessage() ? ": {$e->getMessage()}" : "" )
+ );
+ }
+}
+
+/**
+ * @see FileBackendStoreOpHandle
+ */
+class SwiftFileOpHandle extends FileBackendStoreOpHandle {
+ /** @var CF_Async_Op */
+ public $cfOp;
+ /** @var Array */
+ public $affectedObjects = array();
+
+ public function __construct( $backend, array $params, $call, CF_Async_Op $cfOp ) {
+ $this->backend = $backend;
+ $this->params = $params;
+ $this->call = $call;
+ $this->cfOp = $cfOp;
+ }
+}
+
+/**
+ * SwiftFileBackend helper class to page through listings.
+ * Swift also has a listing limit of 10,000 objects for sanity.
+ * Do not use this class from places outside SwiftFileBackend.
+ *
+ * @ingroup FileBackend
+ */
+abstract class SwiftFileBackendList implements Iterator {
+ /** @var Array */
+ protected $bufferIter = array();
+ protected $bufferAfter = null; // string; list items *after* this path
+ protected $pos = 0; // integer
+ /** @var Array */
+ protected $params = array();
+
+ /** @var SwiftFileBackend */
+ protected $backend;
+ protected $container; // string; container name
+ protected $dir; // string; storage directory
+ protected $suffixStart; // integer
+
+ const PAGE_SIZE = 9000; // file listing buffer size
+
+ /**
+ * @param $backend SwiftFileBackend
+ * @param $fullCont string Resolved container name
+ * @param $dir string Resolved directory relative to container
+ * @param $params Array
+ */
+ public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) {
+ $this->backend = $backend;
+ $this->container = $fullCont;
+ $this->dir = $dir;
+ if ( substr( $this->dir, -1 ) === '/' ) {
+ $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
+ }
+ if ( $this->dir == '' ) { // whole container
+ $this->suffixStart = 0;
+ } else { // dir within container
+ $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/"
+ }
+ $this->params = $params;
+ }
+
+ /**
+ * @see Iterator::key()
+ * @return integer
+ */
+ public function key() {
+ return $this->pos;
+ }
+
+ /**
+ * @see Iterator::next()
+ * @return void
+ */
+ public function next() {
+ // Advance to the next file in the page
+ next( $this->bufferIter );
+ ++$this->pos;
+ // Check if there are no files left in this page and
+ // advance to the next page if this page was not empty.
+ if ( !$this->valid() && count( $this->bufferIter ) ) {
+ $this->bufferIter = $this->pageFromList(
+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
+ ); // updates $this->bufferAfter
+ }
+ }
+
+ /**
+ * @see Iterator::rewind()
+ * @return void
+ */
+ public function rewind() {
+ $this->pos = 0;
+ $this->bufferAfter = null;
+ $this->bufferIter = $this->pageFromList(
+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
+ ); // updates $this->bufferAfter
+ }
+
+ /**
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ if ( $this->bufferIter === null ) {
+ return false; // some failure?
+ } else {
+ return ( current( $this->bufferIter ) !== false ); // no paths can have this value
+ }
+ }
+
+ /**
+ * Get the given list portion (page)
+ *
+ * @param $container string Resolved container name
+ * @param $dir string Resolved path relative to container
+ * @param $after string|null
+ * @param $limit integer
+ * @param $params Array
+ * @return Traversable|Array|null Returns null on failure
+ */
+ abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params );
+}
+
+/**
+ * Iterator for listing directories
+ */
+class SwiftFileBackendDirList extends SwiftFileBackendList {
+ /**
+ * @see Iterator::current()
+ * @return string|bool String (relative path) or false
+ */
+ public function current() {
+ return substr( current( $this->bufferIter ), $this->suffixStart, -1 );
+ }
+
+ /**
+ * @see SwiftFileBackendList::pageFromList()
+ * @return Array|null
+ */
+ protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
+ return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params );
+ }
+}
+
+/**
+ * Iterator for listing regular files
+ */
+class SwiftFileBackendFileList extends SwiftFileBackendList {
+ /**
+ * @see Iterator::current()
+ * @return string|bool String (relative path) or false
+ */
+ public function current() {
+ return substr( current( $this->bufferIter ), $this->suffixStart );
+ }
+
+ /**
+ * @see SwiftFileBackendList::pageFromList()
+ * @return Array|null
+ */
+ protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
+ return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Location holder of files stored temporarily
+ *
+ * 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 FileBackend
+ */
+
+/**
+ * This class is used to hold the location and do limited manipulation
+ * of files stored temporarily (this will be whatever wfTempDir() returns)
+ *
+ * @ingroup FileBackend
+ */
+class TempFSFile extends FSFile {
+ protected $canDelete = false; // bool; garbage collect the temp file
+
+ /** @var Array of active temp files to purge on shutdown */
+ protected static $instances = array();
+
+ /**
+ * Make a new temporary file on the file system.
+ * Temporary files may be purged when the file object falls out of scope.
+ *
+ * @param $prefix string
+ * @param $extension string
+ * @return TempFSFile|null
+ */
+ public static function factory( $prefix, $extension = '' ) {
+ $base = wfTempDir() . '/' . $prefix . dechex( mt_rand( 0, 99999999 ) );
+ $ext = ( $extension != '' ) ? ".{$extension}" : "";
+ for ( $attempt = 1; true; $attempt++ ) {
+ $path = "{$base}-{$attempt}{$ext}";
+ wfSuppressWarnings();
+ $newFileHandle = fopen( $path, 'x' );
+ wfRestoreWarnings();
+ if ( $newFileHandle ) {
+ fclose( $newFileHandle );
+ break; // got it
+ }
+ if ( $attempt >= 15 ) {
+ return null; // give up
+ }
+ }
+ $tmpFile = new self( $path );
+ $tmpFile->canDelete = true; // safely instantiated
+ return $tmpFile;
+ }
+
+ /**
+ * Purge this file off the file system
+ *
+ * @return bool Success
+ */
+ public function purge() {
+ $this->canDelete = false; // done
+ wfSuppressWarnings();
+ $ok = unlink( $this->path );
+ wfRestoreWarnings();
+ return $ok;
+ }
+
+ /**
+ * Clean up the temporary file only after an object goes out of scope
+ *
+ * @param $object Object
+ * @return void
+ */
+ public function bind( $object ) {
+ if ( is_object( $object ) ) {
+ $object->tempFSFileReferences[] = $this;
+ }
+ }
+
+ /**
+ * Set flag to not clean up after the temporary file
+ *
+ * @return void
+ */
+ public function preserve() {
+ $this->canDelete = false;
+ }
+
+ /**
+ * Cleans up after the temporary file by deleting it
+ */
+ function __destruct() {
+ if ( $this->canDelete ) {
+ wfSuppressWarnings();
+ unlink( $this->path );
+ wfRestoreWarnings();
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Version of FileJournal that logs to a DB table.
+ *
+ * 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 FileJournal
+ * @author Aaron Schulz
+ */
+
+/**
+ * Version of FileJournal that logs to a DB table
+ * @since 1.20
+ */
+class DBFileJournal extends FileJournal {
+ protected $wiki = false; // string; wiki DB name
+
+ /**
+ * Construct a new instance from configuration.
+ * $config includes:
+ * 'wiki' : wiki name to use for LoadBalancer
+ *
+ * @param $config Array
+ */
+ protected function __construct( array $config ) {
+ parent::__construct( $config );
+
+ $this->wiki = $config['wiki'];
+ }
+
+ /**
+ * @see FileJournal::logChangeBatch()
+ * @return Status
+ */
+ protected function doLogChangeBatch( array $entries, $batchId ) {
+ $status = Status::newGood();
+
+ try {
+ $dbw = $this->getMasterDB();
+ } catch ( DBError $e ) {
+ $status->fatal( 'filejournal-fail-dbconnect', $this->backend );
+ return $status;
+ }
+
+ $now = wfTimestamp( TS_UNIX );
+
+ $data = array();
+ foreach ( $entries as $entry ) {
+ $data[] = array(
+ 'fj_batch_uuid' => $batchId,
+ 'fj_backend' => $this->backend,
+ 'fj_op' => $entry['op'],
+ 'fj_path' => $entry['path'],
+ 'fj_new_sha1' => $entry['newSha1'],
+ 'fj_timestamp' => $dbw->timestamp( $now )
+ );
+ }
+
+ try {
+ $dbw->begin();
+ $dbw->insert( 'filejournal', $data, __METHOD__ );
+ $dbw->commit();
+ } catch ( DBError $e ) {
+ $status->fatal( 'filejournal-fail-dbquery', $this->backend );
+ return $status;
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see FileJournal::doGetChangeEntries()
+ * @return Array
+ * @throws DBError
+ */
+ protected function doGetChangeEntries( $start, $limit ) {
+ $dbw = $this->getMasterDB();
+
+ $res = $dbw->select( 'filejournal', '*',
+ array(
+ 'fj_backend' => $this->backend,
+ 'fj_id >= ' . $dbw->addQuotes( (int)$start ) ), // $start may be 0
+ __METHOD__,
+ array_merge( array( 'ORDER BY' => 'fj_id ASC' ),
+ $limit ? array( 'LIMIT' => $limit ) : array() )
+ );
+
+ $entries = array();
+ foreach ( $res as $row ) {
+ $item = array();
+ foreach ( (array)$row as $key => $value ) {
+ $item[substr( $key, 3 )] = $value; // "fj_op" => "op"
+ }
+ $entries[] = $item;
+ }
+
+ return $entries;
+ }
+
+ /**
+ * @see FileJournal::purgeOldLogs()
+ * @return Status
+ * @throws DBError
+ */
+ protected function doPurgeOldLogs() {
+ $status = Status::newGood();
+ if ( $this->ttlDays <= 0 ) {
+ return $status; // nothing to do
+ }
+
+ $dbw = $this->getMasterDB();
+ $dbCutoff = $dbw->timestamp( time() - 86400 * $this->ttlDays );
+
+ $dbw->begin();
+ $dbw->delete( 'filejournal',
+ array( 'fj_timestamp < ' . $dbw->addQuotes( $dbCutoff ) ),
+ __METHOD__
+ );
+ $dbw->commit();
+
+ return $status;
+ }
+
+ /**
+ * Get a master connection to the logging DB
+ *
+ * @return DatabaseBase
+ * @throws DBError
+ */
+ protected function getMasterDB() {
+ $lb = wfGetLBFactory()->newMainLB();
+ return $lb->getConnection( DB_MASTER, array(), $this->wiki );
+ }
+}
--- /dev/null
+<?php
+/**
+ * @defgroup FileJournal File journal
+ * @ingroup FileBackend
+ */
+
+/**
+ * File operation journaling.
+ *
+ * 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 FileJournal
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Class for handling file operation journaling.
+ *
+ * Subclasses should avoid throwing exceptions at all costs.
+ *
+ * @ingroup FileJournal
+ * @since 1.20
+ */
+abstract class FileJournal {
+ protected $backend; // string
+ protected $ttlDays; // integer
+
+ /**
+ * Construct a new instance from configuration.
+ * $config includes:
+ * 'ttlDays' : days to keep log entries around (false means "forever")
+ *
+ * @param $config Array
+ */
+ protected function __construct( array $config ) {
+ $this->ttlDays = isset( $config['ttlDays'] ) ? $config['ttlDays'] : false;
+ }
+
+ /**
+ * Create an appropriate FileJournal object from config
+ *
+ * @param $config Array
+ * @param $backend string A registered file backend name
+ * @throws MWException
+ * @return FileJournal
+ */
+ final public static function factory( array $config, $backend ) {
+ $class = $config['class'];
+ $jrn = new $class( $config );
+ if ( !$jrn instanceof self ) {
+ throw new MWException( "Class given is not an instance of FileJournal." );
+ }
+ $jrn->backend = $backend;
+ return $jrn;
+ }
+
+ /**
+ * Get a statistically unique ID string
+ *
+ * @return string <9 char TS_MW timestamp in base 36><22 random base 36 chars>
+ */
+ final public function getTimestampedUUID() {
+ $s = '';
+ for ( $i = 0; $i < 5; $i++ ) {
+ $s .= mt_rand( 0, 2147483647 );
+ }
+ $s = wfBaseConvert( sha1( $s ), 16, 36, 31 );
+ return substr( wfBaseConvert( wfTimestamp( TS_MW ), 10, 36, 9 ) . $s, 0, 31 );
+ }
+
+ /**
+ * Log changes made by a batch file operation.
+ * $entries is an array of log entries, each of which contains:
+ * op : Basic operation name (create, store, copy, delete)
+ * path : The storage path of the file
+ * newSha1 : The final base 36 SHA-1 of the file
+ * Note that 'false' should be used as the SHA-1 for non-existing files.
+ *
+ * @param $entries Array List of file operations (each an array of parameters)
+ * @param $batchId string UUID string that identifies the operation batch
+ * @return Status
+ */
+ final public function logChangeBatch( array $entries, $batchId ) {
+ if ( !count( $entries ) ) {
+ return Status::newGood();
+ }
+ return $this->doLogChangeBatch( $entries, $batchId );
+ }
+
+ /**
+ * @see FileJournal::logChangeBatch()
+ *
+ * @param $entries Array List of file operations (each an array of parameters)
+ * @param $batchId string UUID string that identifies the operation batch
+ * @return Status
+ */
+ abstract protected function doLogChangeBatch( array $entries, $batchId );
+
+ /**
+ * Get an array of file change log entries.
+ * A starting change ID and/or limit can be specified.
+ *
+ * The result as a list of associative arrays, each having:
+ * id : unique, monotonic, ID for this change
+ * batch_uuid : UUID for an operation batch
+ * backend : the backend name
+ * op : primitive operation (create,update,delete)
+ * path : affected storage path
+ * path_sha1 : base 36 sha1 of the affected storage path
+ * timestamp : TS_MW timestamp of the batch change
+
+ * Also, $next is updated to the ID of the next entry.
+ *
+ * @param $start integer Starting change ID or null
+ * @param $limit integer Maximum number of items to return
+ * @param &$next string
+ * @return Array
+ */
+ final public function getChangeEntries( $start = null, $limit = 0, &$next = null ) {
+ $entries = $this->doGetChangeEntries( $start, $limit ? $limit + 1 : 0 );
+ if ( $limit && count( $entries ) > $limit ) {
+ $last = array_pop( $entries ); // remove the extra entry
+ $next = $last['id']; // update for next call
+ } else {
+ $next = null; // end of list
+ }
+ return $entries;
+ }
+
+ /**
+ * @see FileJournal::getChangeEntries()
+ * @return Array
+ */
+ abstract protected function doGetChangeEntries( $start, $limit );
+
+ /**
+ * Purge any old log entries
+ *
+ * @return Status
+ */
+ final public function purgeOldLogs() {
+ return $this->doPurgeOldLogs();
+ }
+
+ /**
+ * @see FileJournal::purgeOldLogs()
+ * @return Status
+ */
+ abstract protected function doPurgeOldLogs();
+}
+
+/**
+ * Simple version of FileJournal that does nothing
+ * @since 1.20
+ */
+class NullFileJournal extends FileJournal {
+ /**
+ * @see FileJournal::logChangeBatch()
+ * @param $entries array
+ * @param $batchId string
+ * @return Status
+ */
+ protected function doLogChangeBatch( array $entries, $batchId ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see FileJournal::doGetChangeEntries()
+ * @return Array
+ */
+ protected function doGetChangeEntries( $start, $limit ) {
+ return array();
+ }
+
+ /**
+ * @see FileJournal::purgeOldLogs()
+ * @return Status
+ */
+ protected function doPurgeOldLogs() {
+ return Status::newGood();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Version of LockManager based on using DB table locks.
+ *
+ * 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 LockManager
+ */
+
+/**
+ * Version of LockManager based on using DB table locks.
+ * This is meant for multi-wiki systems that may share files.
+ * All locks are blocking, so it might be useful to set a small
+ * lock-wait timeout via server config to curtail deadlocks.
+ *
+ * All lock requests for a resource, identified by a hash string, will map
+ * to one bucket. Each bucket maps to one or several peer DBs, each on their
+ * own server, all having the filelocks.sql tables (with row-level locking).
+ * A majority of peer DBs must agree for a lock to be acquired.
+ *
+ * Caching is used to avoid hitting servers that are down.
+ *
+ * @ingroup LockManager
+ * @since 1.19
+ */
+class DBLockManager extends QuorumLockManager {
+ /** @var Array Map of DB names to server config */
+ protected $dbServers; // (DB name => server config array)
+ /** @var BagOStuff */
+ protected $statusCache;
+
+ protected $lockExpiry; // integer number of seconds
+ protected $safeDelay; // integer number of seconds
+
+ protected $session = 0; // random integer
+ /** @var Array Map Database connections (DB name => Database) */
+ protected $conns = array();
+
+ /**
+ * Construct a new instance from configuration.
+ *
+ * $config paramaters include:
+ * 'dbServers' : Associative array of DB names to server configuration.
+ * Configuration is an associative array that includes:
+ * 'host' - DB server name
+ * 'dbname' - DB name
+ * 'type' - DB type (mysql,postgres,...)
+ * 'user' - DB user
+ * 'password' - DB user password
+ * 'tablePrefix' - DB table prefix
+ * 'flags' - DB flags (see DatabaseBase)
+ * 'dbsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
+ * each having an odd-numbered list of DB names (peers) as values.
+ * Any DB named 'localDBMaster' will automatically use the DB master
+ * settings for this wiki (without the need for a dbServers entry).
+ * 'lockExpiry' : Lock timeout (seconds) for dropped connections. [optional]
+ * This tells the DB server how long to wait before assuming
+ * connection failure and releasing all the locks for a session.
+ *
+ * @param Array $config
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+
+ $this->dbServers = isset( $config['dbServers'] )
+ ? $config['dbServers']
+ : array(); // likely just using 'localDBMaster'
+ // Sanitize srvsByBucket config to prevent PHP errors
+ $this->srvsByBucket = array_filter( $config['dbsByBucket'], 'is_array' );
+ $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
+
+ if ( isset( $config['lockExpiry'] ) ) {
+ $this->lockExpiry = $config['lockExpiry'];
+ } else {
+ $met = ini_get( 'max_execution_time' );
+ $this->lockExpiry = $met ? $met : 60; // use some sane amount if 0
+ }
+ $this->safeDelay = ( $this->lockExpiry <= 0 )
+ ? 60 // pick a safe-ish number to match DB timeout default
+ : $this->lockExpiry; // cover worst case
+
+ foreach ( $this->srvsByBucket as $bucket ) {
+ if ( count( $bucket ) > 1 ) { // multiple peers
+ // Tracks peers that couldn't be queried recently to avoid lengthy
+ // connection timeouts. This is useless if each bucket has one peer.
+ try {
+ $this->statusCache = ObjectCache::newAccelerator( array() );
+ } catch ( MWException $e ) {
+ trigger_error( __CLASS__ .
+ " using multiple DB peers without apc, xcache, or wincache." );
+ }
+ break;
+ }
+ }
+
+ $this->session = wfRandomString( 31 );
+ }
+
+ /**
+ * Get a connection to a lock DB and acquire locks on $paths.
+ * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
+ *
+ * @see QuorumLockManager::getLocksOnServer()
+ * @return Status
+ */
+ protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ if ( $type == self::LOCK_EX ) { // writer locks
+ try {
+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ # Build up values for INSERT clause
+ $data = array();
+ foreach ( $keys as $key ) {
+ $data[] = array( 'fle_key' => $key );
+ }
+ # Wait on any existing writers and block new ones if we get in
+ $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+ $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
+ } catch ( DBError $e ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::freeLocksOnServer()
+ * @return Status
+ */
+ protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
+ return Status::newGood(); // not supported
+ }
+
+ /**
+ * @see QuorumLockManager::releaseAllLocks()
+ * @return Status
+ */
+ protected function releaseAllLocks() {
+ $status = Status::newGood();
+
+ foreach ( $this->conns as $lockDb => $db ) {
+ if ( $db->trxLevel() ) { // in transaction
+ try {
+ $db->rollback( __METHOD__ ); // finish transaction and kill any rows
+ } catch ( DBError $e ) {
+ $status->fatal( 'lockmanager-fail-db-release', $lockDb );
+ }
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::isServerUp()
+ * @return bool
+ */
+ protected function isServerUp( $lockSrv ) {
+ if ( !$this->cacheCheckFailures( $lockSrv ) ) {
+ return false; // recent failure to connect
+ }
+ try {
+ $this->getConnection( $lockSrv );
+ } catch ( DBError $e ) {
+ $this->cacheRecordFailure( $lockSrv );
+ return false; // failed to connect
+ }
+ return true;
+ }
+
+ /**
+ * Get (or reuse) a connection to a lock DB
+ *
+ * @param $lockDb string
+ * @return DatabaseBase
+ * @throws DBError
+ */
+ protected function getConnection( $lockDb ) {
+ if ( !isset( $this->conns[$lockDb] ) ) {
+ $db = null;
+ if ( $lockDb === 'localDBMaster' ) {
+ $lb = wfGetLBFactory()->newMainLB();
+ $db = $lb->getConnection( DB_MASTER );
+ } elseif ( isset( $this->dbServers[$lockDb] ) ) {
+ $config = $this->dbServers[$lockDb];
+ $db = DatabaseBase::factory( $config['type'], $config );
+ }
+ if ( !$db ) {
+ return null; // config error?
+ }
+ $this->conns[$lockDb] = $db;
+ $this->conns[$lockDb]->clearFlag( DBO_TRX );
+ # If the connection drops, try to avoid letting the DB rollback
+ # and release the locks before the file operations are finished.
+ # This won't handle the case of DB server restarts however.
+ $options = array();
+ if ( $this->lockExpiry > 0 ) {
+ $options['connTimeout'] = $this->lockExpiry;
+ }
+ $this->conns[$lockDb]->setSessionOptions( $options );
+ $this->initConnection( $lockDb, $this->conns[$lockDb] );
+ }
+ if ( !$this->conns[$lockDb]->trxLevel() ) {
+ $this->conns[$lockDb]->begin( __METHOD__ ); // start transaction
+ }
+ return $this->conns[$lockDb];
+ }
+
+ /**
+ * Do additional initialization for new lock DB connection
+ *
+ * @param $lockDb string
+ * @param $db DatabaseBase
+ * @return void
+ * @throws DBError
+ */
+ protected function initConnection( $lockDb, DatabaseBase $db ) {}
+
+ /**
+ * Checks if the DB has not recently had connection/query errors.
+ * This just avoids wasting time on doomed connection attempts.
+ *
+ * @param $lockDb string
+ * @return bool
+ */
+ protected function cacheCheckFailures( $lockDb ) {
+ return ( $this->statusCache && $this->safeDelay > 0 )
+ ? !$this->statusCache->get( $this->getMissKey( $lockDb ) )
+ : true;
+ }
+
+ /**
+ * Log a lock request failure to the cache
+ *
+ * @param $lockDb string
+ * @return bool Success
+ */
+ protected function cacheRecordFailure( $lockDb ) {
+ return ( $this->statusCache && $this->safeDelay > 0 )
+ ? $this->statusCache->set( $this->getMissKey( $lockDb ), 1, $this->safeDelay )
+ : true;
+ }
+
+ /**
+ * Get a cache key for recent query misses for a DB
+ *
+ * @param $lockDb string
+ * @return string
+ */
+ protected function getMissKey( $lockDb ) {
+ $lockDb = ( $lockDb === 'localDBMaster' ) ? wfWikiID() : $lockDb; // non-relative
+ return 'dblockmanager:downservers:' . str_replace( ' ', '_', $lockDb );
+ }
+
+ /**
+ * Make sure remaining locks get cleared for sanity
+ */
+ function __destruct() {
+ foreach ( $this->conns as $db ) {
+ if ( $db->trxLevel() ) { // in transaction
+ try {
+ $db->rollback( __METHOD__ ); // finish transaction and kill any rows
+ } catch ( DBError $e ) {
+ // oh well
+ }
+ }
+ $db->close();
+ }
+ }
+}
+
+/**
+ * MySQL version of DBLockManager that supports shared locks.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * @ingroup LockManager
+ */
+class MySqlLockManager extends DBLockManager {
+ /** @var Array Mapping of lock types to the type actually used */
+ protected $lockTypeMap = array(
+ self::LOCK_SH => self::LOCK_SH,
+ self::LOCK_UW => self::LOCK_SH,
+ self::LOCK_EX => self::LOCK_EX
+ );
+
+ /**
+ * @param $lockDb string
+ * @param $db DatabaseBase
+ */
+ protected function initConnection( $lockDb, DatabaseBase $db ) {
+ # Let this transaction see lock rows from other transactions
+ $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
+ }
+
+ /**
+ * Get a connection to a lock DB and acquire locks on $paths.
+ * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
+ *
+ * @see DBLockManager::getLocksOnServer()
+ * @return Status
+ */
+ protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ # Build up values for INSERT clause
+ $data = array();
+ foreach ( $keys as $key ) {
+ $data[] = array( 'fls_key' => $key, 'fls_session' => $this->session );
+ }
+ # Block new writers...
+ $db->insert( 'filelocks_shared', $data, __METHOD__, array( 'IGNORE' ) );
+ # Actually do the locking queries...
+ if ( $type == self::LOCK_SH ) { // reader locks
+ # Bail if there are any existing writers...
+ $blocked = $db->selectField( 'filelocks_exclusive', '1',
+ array( 'fle_key' => $keys ),
+ __METHOD__
+ );
+ # Prospective writers that haven't yet updated filelocks_exclusive
+ # will recheck filelocks_shared after doing so and bail due to our entry.
+ } else { // writer locks
+ $encSession = $db->addQuotes( $this->session );
+ # Bail if there are any existing writers...
+ # The may detect readers, but the safe check for them is below.
+ # Note: if two writers come at the same time, both bail :)
+ $blocked = $db->selectField( 'filelocks_shared', '1',
+ array( 'fls_key' => $keys, "fls_session != $encSession" ),
+ __METHOD__
+ );
+ if ( !$blocked ) {
+ # Build up values for INSERT clause
+ $data = array();
+ foreach ( $keys as $key ) {
+ $data[] = array( 'fle_key' => $key );
+ }
+ # Block new readers/writers...
+ $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
+ # Bail if there are any existing readers...
+ $blocked = $db->selectField( 'filelocks_shared', '1',
+ array( 'fls_key' => $keys, "fls_session != $encSession" ),
+ __METHOD__
+ );
+ }
+ }
+
+ if ( $blocked ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ }
+
+ return $status;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Simple version of LockManager based on using FS lock files.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup LockManager
+ */
+
+/**
+ * Simple version of LockManager based on using FS lock files.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * This should work fine for small sites running off one server.
+ * Do not use this with 'lockDirectory' set to an NFS mount unless the
+ * NFS client is at least version 2.6.12. Otherwise, the BSD flock()
+ * locks will be ignored; see http://nfs.sourceforge.net/#section_d.
+ *
+ * @ingroup LockManager
+ * @since 1.19
+ */
+class FSLockManager extends LockManager {
+ /** @var Array Mapping of lock types to the type actually used */
+ protected $lockTypeMap = array(
+ self::LOCK_SH => self::LOCK_SH,
+ self::LOCK_UW => self::LOCK_SH,
+ self::LOCK_EX => self::LOCK_EX
+ );
+
+ protected $lockDir; // global dir for all servers
+
+ /** @var Array Map of (locked key => lock type => lock file handle) */
+ protected $handles = array();
+
+ /**
+ * Construct a new instance from configuration.
+ *
+ * $config includes:
+ * 'lockDirectory' : Directory containing the lock files
+ *
+ * @param array $config
+ */
+ function __construct( array $config ) {
+ parent::__construct( $config );
+
+ $this->lockDir = $config['lockDirectory'];
+ }
+
+ /**
+ * @see LockManager::doLock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ protected function doLock( array $paths, $type ) {
+ $status = Status::newGood();
+
+ $lockedPaths = array(); // files locked in this attempt
+ foreach ( $paths as $path ) {
+ $status->merge( $this->doSingleLock( $path, $type ) );
+ if ( $status->isOK() ) {
+ $lockedPaths[] = $path;
+ } else {
+ // Abort and unlock everything
+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
+ return $status;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see LockManager::doUnlock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ protected function doUnlock( array $paths, $type ) {
+ $status = Status::newGood();
+
+ foreach ( $paths as $path ) {
+ $status->merge( $this->doSingleUnlock( $path, $type ) );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Lock a single resource key
+ *
+ * @param $path string
+ * @param $type integer
+ * @return Status
+ */
+ protected function doSingleLock( $path, $type ) {
+ $status = Status::newGood();
+
+ if ( isset( $this->locksHeld[$path][$type] ) ) {
+ ++$this->locksHeld[$path][$type];
+ } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
+ $this->locksHeld[$path][$type] = 1;
+ } else {
+ wfSuppressWarnings();
+ $handle = fopen( $this->getLockPath( $path ), 'a+' );
+ wfRestoreWarnings();
+ if ( !$handle ) { // lock dir missing?
+ wfMkdirParents( $this->lockDir );
+ $handle = fopen( $this->getLockPath( $path ), 'a+' ); // try again
+ }
+ if ( $handle ) {
+ // Either a shared or exclusive lock
+ $lock = ( $type == self::LOCK_SH ) ? LOCK_SH : LOCK_EX;
+ if ( flock( $handle, $lock | LOCK_NB ) ) {
+ // Record this lock as active
+ $this->locksHeld[$path][$type] = 1;
+ $this->handles[$path][$type] = $handle;
+ } else {
+ fclose( $handle );
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ } else {
+ $status->fatal( 'lockmanager-fail-openlock', $path );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Unlock a single resource key
+ *
+ * @param $path string
+ * @param $type integer
+ * @return Status
+ */
+ protected function doSingleUnlock( $path, $type ) {
+ $status = Status::newGood();
+
+ if ( !isset( $this->locksHeld[$path] ) ) {
+ $status->warning( 'lockmanager-notlocked', $path );
+ } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
+ $status->warning( 'lockmanager-notlocked', $path );
+ } else {
+ $handlesToClose = array();
+ --$this->locksHeld[$path][$type];
+ if ( $this->locksHeld[$path][$type] <= 0 ) {
+ unset( $this->locksHeld[$path][$type] );
+ // If a LOCK_SH comes in while we have a LOCK_EX, we don't
+ // actually add a handler, so check for handler existence.
+ if ( isset( $this->handles[$path][$type] ) ) {
+ if ( $type === self::LOCK_EX
+ && isset( $this->locksHeld[$path][self::LOCK_SH] )
+ && !isset( $this->handles[$path][self::LOCK_SH] ) )
+ {
+ // EX lock came first: move this handle to the SH one
+ $this->handles[$path][self::LOCK_SH] = $this->handles[$path][$type];
+ } else {
+ // Mark this handle to be unlocked and closed
+ $handlesToClose[] = $this->handles[$path][$type];
+ }
+ unset( $this->handles[$path][$type] );
+ }
+ }
+ if ( !count( $this->locksHeld[$path] ) ) {
+ unset( $this->locksHeld[$path] ); // no locks on this path
+ }
+ // Unlock handles to release locks and delete
+ // any lock files that end up with no locks on them...
+ if ( wfIsWindows() ) {
+ // Windows: for any process, including this one,
+ // calling unlink() on a locked file will fail
+ $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
+ $status->merge( $this->pruneKeyLockFiles( $path ) );
+ } else {
+ // Unix: unlink() can be used on files currently open by this
+ // process and we must do so in order to avoid race conditions
+ $status->merge( $this->pruneKeyLockFiles( $path ) );
+ $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @param $path string
+ * @param $handlesToClose array
+ * @return Status
+ */
+ private function closeLockHandles( $path, array $handlesToClose ) {
+ $status = Status::newGood();
+ foreach ( $handlesToClose as $handle ) {
+ if ( !flock( $handle, LOCK_UN ) ) {
+ $status->fatal( 'lockmanager-fail-releaselock', $path );
+ }
+ if ( !fclose( $handle ) ) {
+ $status->warning( 'lockmanager-fail-closelock', $path );
+ }
+ }
+ return $status;
+ }
+
+ /**
+ * @param $path string
+ * @return Status
+ */
+ private function pruneKeyLockFiles( $path ) {
+ $status = Status::newGood();
+ if ( !isset( $this->locksHeld[$path] ) ) {
+ # No locks are held for the lock file anymore
+ if ( !unlink( $this->getLockPath( $path ) ) ) {
+ $status->warning( 'lockmanager-fail-deletelock', $path );
+ }
+ unset( $this->handles[$path] );
+ }
+ return $status;
+ }
+
+ /**
+ * Get the path to the lock file for a key
+ * @param $path string
+ * @return string
+ */
+ protected function getLockPath( $path ) {
+ $hash = self::sha1Base36( $path );
+ return "{$this->lockDir}/{$hash}.lock";
+ }
+
+ /**
+ * Make sure remaining locks get cleared for sanity
+ */
+ function __destruct() {
+ while ( count( $this->locksHeld ) ) {
+ foreach ( $this->locksHeld as $path => $locks ) {
+ $this->doSingleUnlock( $path, self::LOCK_EX );
+ $this->doSingleUnlock( $path, self::LOCK_SH );
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Version of LockManager based on using lock daemon servers.
+ *
+ * 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 LockManager
+ */
+
+/**
+ * Manage locks using a lock daemon server.
+ *
+ * Version of LockManager based on using lock daemon servers.
+ * This is meant for multi-wiki systems that may share files.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * All lock requests for a resource, identified by a hash string, will map
+ * to one bucket. Each bucket maps to one or several peer servers, each
+ * running LockServerDaemon.php, listening on a designated TCP port.
+ * A majority of peers must agree for a lock to be acquired.
+ *
+ * @ingroup LockManager
+ * @since 1.19
+ */
+class LSLockManager extends QuorumLockManager {
+ /** @var Array Mapping of lock types to the type actually used */
+ protected $lockTypeMap = array(
+ self::LOCK_SH => self::LOCK_SH,
+ self::LOCK_UW => self::LOCK_SH,
+ self::LOCK_EX => self::LOCK_EX
+ );
+
+ /** @var Array Map of server names to server config */
+ protected $lockServers; // (server name => server config array)
+
+ /** @var Array Map Server connections (server name => resource) */
+ protected $conns = array();
+
+ protected $connTimeout; // float number of seconds
+ protected $session = ''; // random SHA-1 string
+
+ /**
+ * Construct a new instance from configuration.
+ *
+ * $config paramaters include:
+ * - lockServers : Associative array of server names to configuration.
+ * Configuration is an associative array that includes:
+ * - host : IP address/hostname
+ * - port : TCP port
+ * - authKey : Secret string the lock server uses
+ * - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0,
+ * each having an odd-numbered list of server names (peers) as values.
+ * - connTimeout : Lock server connection attempt timeout. [optional]
+ *
+ * @param Array $config
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+
+ $this->lockServers = $config['lockServers'];
+ // Sanitize srvsByBucket config to prevent PHP errors
+ $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' );
+ $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
+
+ if ( isset( $config['connTimeout'] ) ) {
+ $this->connTimeout = $config['connTimeout'];
+ } else {
+ $this->connTimeout = 3; // use some sane amount
+ }
+
+ $this->session = wfRandomString( 32 ); // 128 bits
+ }
+
+ /**
+ * @see QuorumLockManager::getLocksOnServer()
+ * @return Status
+ */
+ protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ // Send out the command and get the response...
+ $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys );
+
+ if ( $response !== 'ACQUIRED' ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::freeLocksOnServer()
+ * @return Status
+ */
+ protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ // Send out the command and get the response...
+ $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $response = $this->sendCommand( $lockSrv, 'RELEASE', $type, $keys );
+
+ if ( $response !== 'RELEASED' ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-releaselock', $path );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::releaseAllLocks()
+ * @return Status
+ */
+ protected function releaseAllLocks() {
+ $status = Status::newGood();
+
+ foreach ( $this->conns as $lockSrv => $conn ) {
+ $response = $this->sendCommand( $lockSrv, 'RELEASE_ALL', '', array() );
+ if ( $response !== 'RELEASED_ALL' ) {
+ $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::isServerUp()
+ * @return bool
+ */
+ protected function isServerUp( $lockSrv ) {
+ return (bool)$this->getConnection( $lockSrv );
+ }
+
+ /**
+ * Send a command and get back the response
+ *
+ * @param $lockSrv string
+ * @param $action string
+ * @param $type string
+ * @param $values Array
+ * @return string|bool
+ */
+ protected function sendCommand( $lockSrv, $action, $type, $values ) {
+ $conn = $this->getConnection( $lockSrv );
+ if ( !$conn ) {
+ return false; // no connection
+ }
+ $authKey = $this->lockServers[$lockSrv]['authKey'];
+ // Build of the command as a flat string...
+ $values = implode( '|', $values );
+ $key = sha1( $this->session . $action . $type . $values . $authKey );
+ // Send out the command...
+ if ( fwrite( $conn, "{$this->session}:$key:$action:$type:$values\n" ) === false ) {
+ return false;
+ }
+ // Get the response...
+ $response = fgets( $conn );
+ if ( $response === false ) {
+ return false;
+ }
+ return trim( $response );
+ }
+
+ /**
+ * Get (or reuse) a connection to a lock server
+ *
+ * @param $lockSrv string
+ * @return resource
+ */
+ protected function getConnection( $lockSrv ) {
+ if ( !isset( $this->conns[$lockSrv] ) ) {
+ $cfg = $this->lockServers[$lockSrv];
+ wfSuppressWarnings();
+ $errno = $errstr = '';
+ $conn = fsockopen( $cfg['host'], $cfg['port'], $errno, $errstr, $this->connTimeout );
+ wfRestoreWarnings();
+ if ( $conn === false ) {
+ return null;
+ }
+ $sec = floor( $this->connTimeout );
+ $usec = floor( ( $this->connTimeout - floor( $this->connTimeout ) ) * 1e6 );
+ stream_set_timeout( $conn, $sec, $usec );
+ $this->conns[$lockSrv] = $conn;
+ }
+ return $this->conns[$lockSrv];
+ }
+
+ /**
+ * Make sure remaining locks get cleared for sanity
+ */
+ function __destruct() {
+ $this->releaseAllLocks();
+ foreach ( $this->conns as $conn ) {
+ fclose( $conn );
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * @defgroup LockManager Lock management
+ * @ingroup FileBackend
+ */
+
+/**
+ * Resource locking handling.
+ *
+ * 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 LockManager
+ * @author Aaron Schulz
+ */
+
+/**
+ * @brief Class for handling resource locking.
+ *
+ * Locks on resource keys can either be shared or exclusive.
+ *
+ * Implementations must keep track of what is locked by this proccess
+ * in-memory and support nested locking calls (using reference counting).
+ * At least LOCK_UW and LOCK_EX must be implemented. LOCK_SH can be a no-op.
+ * Locks should either be non-blocking or have low wait timeouts.
+ *
+ * Subclasses should avoid throwing exceptions at all costs.
+ *
+ * @ingroup LockManager
+ * @since 1.19
+ */
+abstract class LockManager {
+ /** @var Array Mapping of lock types to the type actually used */
+ protected $lockTypeMap = array(
+ self::LOCK_SH => self::LOCK_SH,
+ self::LOCK_UW => self::LOCK_EX, // subclasses may use self::LOCK_SH
+ self::LOCK_EX => self::LOCK_EX
+ );
+
+ /** @var Array Map of (resource path => lock type => count) */
+ protected $locksHeld = array();
+
+ /* Lock types; stronger locks have higher values */
+ const LOCK_SH = 1; // shared lock (for reads)
+ const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
+ const LOCK_EX = 3; // exclusive lock (for writes)
+
+ /**
+ * Construct a new instance from configuration
+ *
+ * @param $config Array
+ */
+ public function __construct( array $config ) {}
+
+ /**
+ * Lock the resources at the given abstract paths
+ *
+ * @param $paths Array List of resource names
+ * @param $type integer LockManager::LOCK_* constant
+ * @return Status
+ */
+ final public function lock( array $paths, $type = self::LOCK_EX ) {
+ wfProfileIn( __METHOD__ );
+ $status = $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * Unlock the resources at the given abstract paths
+ *
+ * @param $paths Array List of storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @return Status
+ */
+ final public function unlock( array $paths, $type = self::LOCK_EX ) {
+ wfProfileIn( __METHOD__ );
+ $status = $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * Get the base 36 SHA-1 of a string, padded to 31 digits
+ *
+ * @param $path string
+ * @return string
+ */
+ final protected static function sha1Base36( $path ) {
+ return wfBaseConvert( sha1( $path ), 16, 36, 31 );
+ }
+
+ /**
+ * Lock resources with the given keys and lock type
+ *
+ * @param $paths Array List of storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @return string
+ */
+ abstract protected function doLock( array $paths, $type );
+
+ /**
+ * Unlock resources with the given keys and lock type
+ *
+ * @param $paths Array List of storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @return string
+ */
+ abstract protected function doUnlock( array $paths, $type );
+}
+
+/**
+ * Self-releasing locks
+ *
+ * LockManager helper class to handle scoped locks, which
+ * release when an object is destroyed or goes out of scope.
+ *
+ * @ingroup LockManager
+ * @since 1.19
+ */
+class ScopedLock {
+ /** @var LockManager */
+ protected $manager;
+ /** @var Status */
+ protected $status;
+ /** @var Array List of resource paths*/
+ protected $paths;
+
+ protected $type; // integer lock type
+
+ /**
+ * @param $manager LockManager
+ * @param $paths Array List of storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @param $status Status
+ */
+ protected function __construct(
+ LockManager $manager, array $paths, $type, Status $status
+ ) {
+ $this->manager = $manager;
+ $this->paths = $paths;
+ $this->status = $status;
+ $this->type = $type;
+ }
+
+ /**
+ * Get a ScopedLock object representing a lock on resource paths.
+ * Any locks are released once this object goes out of scope.
+ * The status object is updated with any errors or warnings.
+ *
+ * @param $manager LockManager
+ * @param $paths Array List of storage paths
+ * @param $type integer LockManager::LOCK_* constant
+ * @param $status Status
+ * @return ScopedLock|null Returns null on failure
+ */
+ public static function factory(
+ LockManager $manager, array $paths, $type, Status $status
+ ) {
+ $lockStatus = $manager->lock( $paths, $type );
+ $status->merge( $lockStatus );
+ if ( $lockStatus->isOK() ) {
+ return new self( $manager, $paths, $type, $status );
+ }
+ return null;
+ }
+
+ function __destruct() {
+ $wasOk = $this->status->isOK();
+ $this->status->merge( $this->manager->unlock( $this->paths, $this->type ) );
+ if ( $wasOk ) {
+ // Make sure status is OK, despite any unlockFiles() fatals
+ $this->status->setResult( true, $this->status->value );
+ }
+ }
+}
+
+/**
+ * Version of LockManager that uses a quorum from peer servers for locks.
+ * The resource space can also be sharded into separate peer groups.
+ *
+ * @ingroup LockManager
+ * @since 1.20
+ */
+abstract class QuorumLockManager extends LockManager {
+ /** @var Array Map of bucket indexes to peer server lists */
+ protected $srvsByBucket = array(); // (bucket index => (lsrv1, lsrv2, ...))
+
+ /**
+ * @see LockManager::doLock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ final protected function doLock( array $paths, $type ) {
+ $status = Status::newGood();
+
+ $pathsToLock = array(); // (bucket => paths)
+ // Get locks that need to be acquired (buckets => locks)...
+ foreach ( $paths as $path ) {
+ if ( isset( $this->locksHeld[$path][$type] ) ) {
+ ++$this->locksHeld[$path][$type];
+ } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
+ $this->locksHeld[$path][$type] = 1;
+ } else {
+ $bucket = $this->getBucketFromKey( $path );
+ $pathsToLock[$bucket][] = $path;
+ }
+ }
+
+ $lockedPaths = array(); // files locked in this attempt
+ // Attempt to acquire these locks...
+ foreach ( $pathsToLock as $bucket => $paths ) {
+ // Try to acquire the locks for this bucket
+ $status->merge( $this->doLockingRequestBucket( $bucket, $paths, $type ) );
+ if ( !$status->isOK() ) {
+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
+ return $status;
+ }
+ // Record these locks as active
+ foreach ( $paths as $path ) {
+ $this->locksHeld[$path][$type] = 1; // locked
+ }
+ // Keep track of what locks were made in this attempt
+ $lockedPaths = array_merge( $lockedPaths, $paths );
+ }
+
+ return $status;
+ }
+
+ /**
+ * @see LockManager::doUnlock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ final protected function doUnlock( array $paths, $type ) {
+ $status = Status::newGood();
+
+ $pathsToUnlock = array();
+ foreach ( $paths as $path ) {
+ if ( !isset( $this->locksHeld[$path][$type] ) ) {
+ $status->warning( 'lockmanager-notlocked', $path );
+ } else {
+ --$this->locksHeld[$path][$type];
+ // Reference count the locks held and release locks when zero
+ if ( $this->locksHeld[$path][$type] <= 0 ) {
+ unset( $this->locksHeld[$path][$type] );
+ $bucket = $this->getBucketFromKey( $path );
+ $pathsToUnlock[$bucket][] = $path;
+ }
+ if ( !count( $this->locksHeld[$path] ) ) {
+ unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
+ }
+ }
+ }
+
+ // Remove these specific locks if possible, or at least release
+ // all locks once this process is currently not holding any locks.
+ foreach ( $pathsToUnlock as $bucket => $paths ) {
+ $status->merge( $this->doUnlockingRequestBucket( $bucket, $paths, $type ) );
+ }
+ if ( !count( $this->locksHeld ) ) {
+ $status->merge( $this->releaseAllLocks() );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Attempt to acquire locks with the peers for a bucket.
+ * This is all or nothing; if any key is locked then this totally fails.
+ *
+ * @param $bucket integer
+ * @param $paths Array List of resource keys to lock
+ * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
+ * @return Status
+ */
+ final protected function doLockingRequestBucket( $bucket, array $paths, $type ) {
+ $status = Status::newGood();
+
+ $yesVotes = 0; // locks made on trustable servers
+ $votesLeft = count( $this->srvsByBucket[$bucket] ); // remaining peers
+ $quorum = floor( $votesLeft/2 + 1 ); // simple majority
+ // Get votes for each peer, in order, until we have enough...
+ foreach ( $this->srvsByBucket[$bucket] as $lockSrv ) {
+ if ( !$this->isServerUp( $lockSrv ) ) {
+ --$votesLeft;
+ $status->warning( 'lockmanager-fail-svr-acquire', $lockSrv );
+ continue; // server down?
+ }
+ // Attempt to acquire the lock on this peer
+ $status->merge( $this->getLocksOnServer( $lockSrv, $paths, $type ) );
+ if ( !$status->isOK() ) {
+ return $status; // vetoed; resource locked
+ }
+ ++$yesVotes; // success for this peer
+ if ( $yesVotes >= $quorum ) {
+ return $status; // lock obtained
+ }
+ --$votesLeft;
+ $votesNeeded = $quorum - $yesVotes;
+ if ( $votesNeeded > $votesLeft ) {
+ break; // short-circuit
+ }
+ }
+ // At this point, we must not have met the quorum
+ $status->setResult( false );
+
+ return $status;
+ }
+
+ /**
+ * Attempt to release locks with the peers for a bucket
+ *
+ * @param $bucket integer
+ * @param $paths Array List of resource keys to lock
+ * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
+ * @return Status
+ */
+ final protected function doUnlockingRequestBucket( $bucket, array $paths, $type ) {
+ $status = Status::newGood();
+
+ foreach ( $this->srvsByBucket[$bucket] as $lockSrv ) {
+ if ( !$this->isServerUp( $lockSrv ) ) {
+ $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
+ // Attempt to release the lock on this peer
+ } else {
+ $status->merge( $this->freeLocksOnServer( $lockSrv, $paths, $type ) );
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Get the bucket for resource path.
+ * This should avoid throwing any exceptions.
+ *
+ * @param $path string
+ * @return integer
+ */
+ protected function getBucketFromKey( $path ) {
+ $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars (8 bits)
+ return (int)base_convert( $prefix, 16, 10 ) % count( $this->srvsByBucket );
+ }
+
+ /**
+ * Check if a lock server is up
+ *
+ * @param $lockSrv string
+ * @return bool
+ */
+ abstract protected function isServerUp( $lockSrv );
+
+ /**
+ * Get a connection to a lock server and acquire locks on $paths
+ *
+ * @param $lockSrv string
+ * @param $paths array
+ * @param $type integer
+ * @return Status
+ */
+ abstract protected function getLocksOnServer( $lockSrv, array $paths, $type );
+
+ /**
+ * Get a connection to a lock server and release locks on $paths.
+ *
+ * Subclasses must effectively implement this or releaseAllLocks().
+ *
+ * @param $lockSrv string
+ * @param $paths array
+ * @param $type integer
+ * @return Status
+ */
+ abstract protected function freeLocksOnServer( $lockSrv, array $paths, $type );
+
+ /**
+ * Release all locks that this session is holding.
+ *
+ * Subclasses must effectively implement this or freeLocksOnServer().
+ *
+ * @return Status
+ */
+ abstract protected function releaseAllLocks();
+}
+
+/**
+ * Simple version of LockManager that does nothing
+ * @since 1.19
+ */
+class NullLockManager extends LockManager {
+ /**
+ * @see LockManager::doLock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ protected function doLock( array $paths, $type ) {
+ return Status::newGood();
+ }
+
+ /**
+ * @see LockManager::doUnlock()
+ * @param $paths array
+ * @param $type int
+ * @return Status
+ */
+ protected function doUnlock( array $paths, $type ) {
+ return Status::newGood();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Lock manager registration handling.
+ *
+ * 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 LockManager
+ */
+
+/**
+ * Class to handle file lock manager registration
+ *
+ * @ingroup LockManager
+ * @author Aaron Schulz
+ * @since 1.19
+ */
+class LockManagerGroup {
+ /**
+ * @var LockManagerGroup
+ */
+ protected static $instance = null;
+
+ /** @var Array of (name => ('class' =>, 'config' =>, 'instance' =>)) */
+ protected $managers = array();
+
+ protected function __construct() {}
+
+ /**
+ * @return LockManagerGroup
+ */
+ public static function singleton() {
+ if ( self::$instance == null ) {
+ self::$instance = new self();
+ self::$instance->initFromGlobals();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Destroy the singleton instance, so that a new one will be created next
+ * time singleton() is called.
+ */
+ public static function destroySingleton() {
+ self::$instance = null;
+ }
+
+ /**
+ * Register lock managers from the global variables
+ *
+ * @return void
+ */
+ protected function initFromGlobals() {
+ global $wgLockManagers;
+
+ $this->register( $wgLockManagers );
+ }
+
+ /**
+ * Register an array of file lock manager configurations
+ *
+ * @param $configs Array
+ * @return void
+ * @throws MWException
+ */
+ protected function register( array $configs ) {
+ foreach ( $configs as $config ) {
+ if ( !isset( $config['name'] ) ) {
+ throw new MWException( "Cannot register a lock manager with no name." );
+ }
+ $name = $config['name'];
+ if ( !isset( $config['class'] ) ) {
+ throw new MWException( "Cannot register lock manager `{$name}` with no class." );
+ }
+ $class = $config['class'];
+ unset( $config['class'] ); // lock manager won't need this
+ $this->managers[$name] = array(
+ 'class' => $class,
+ 'config' => $config,
+ 'instance' => null
+ );
+ }
+ }
+
+ /**
+ * Get the lock manager object with a given name
+ *
+ * @param $name string
+ * @return LockManager
+ * @throws MWException
+ */
+ public function get( $name ) {
+ if ( !isset( $this->managers[$name] ) ) {
+ throw new MWException( "No lock manager defined with the name `$name`." );
+ }
+ // Lazy-load the actual lock manager instance
+ if ( !isset( $this->managers[$name]['instance'] ) ) {
+ $class = $this->managers[$name]['class'];
+ $config = $this->managers[$name]['config'];
+ $this->managers[$name]['instance'] = new $class( $config );
+ }
+ return $this->managers[$name]['instance'];
+ }
+
+ /**
+ * Get the default lock manager configured for the site.
+ * Returns NullLockManager if no lock manager could be found.
+ *
+ * @return LockManager
+ */
+ public function getDefault() {
+ return isset( $this->managers['default'] )
+ ? $this->get( 'default' )
+ : new NullLockManager( array() );
+ }
+
+ /**
+ * Get the default lock manager configured for the site
+ * or at least some other effective configured lock manager.
+ * Throws an exception if no lock manager could be found.
+ *
+ * @return LockManager
+ * @throws MWException
+ */
+ public function getAny() {
+ return isset( $this->managers['default'] )
+ ? $this->get( 'default' )
+ : $this->get( 'fsLockManager' );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Version of LockManager based on using memcached servers.
+ *
+ * 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 LockManager
+ */
+
+/**
+ * Manage locks using memcached servers.
+ *
+ * Version of LockManager based on using memcached servers.
+ * This is meant for multi-wiki systems that may share files.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * All lock requests for a resource, identified by a hash string, will map
+ * to one bucket. Each bucket maps to one or several peer servers, each running memcached.
+ * A majority of peers must agree for a lock to be acquired.
+ *
+ * @ingroup LockManager
+ * @since 1.20
+ */
+class MemcLockManager extends QuorumLockManager {
+ /** @var Array Mapping of lock types to the type actually used */
+ protected $lockTypeMap = array(
+ self::LOCK_SH => self::LOCK_SH,
+ self::LOCK_UW => self::LOCK_SH,
+ self::LOCK_EX => self::LOCK_EX
+ );
+
+ /** @var Array Map server names to MemcachedBagOStuff objects */
+ protected $bagOStuffs = array();
+ /** @var Array */
+ protected $serversUp = array(); // (server name => bool)
+
+ protected $lockExpiry; // integer; maximum time locks can be held
+ protected $session = ''; // string; random SHA-1 UUID
+ protected $wikiId = ''; // string
+
+ /**
+ * Construct a new instance from configuration.
+ *
+ * $config paramaters include:
+ * - 'lockServers' : Associative array of server names to "<IP>:<port>" strings.
+ * - 'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
+ * each having an odd-numbered list of server names (peers) as values.
+ * - 'memcConfig' : Configuration array for ObjectCache::newFromParams. [optional]
+ * If set, this must use one of the memcached classes.
+ * - 'wikiId' : Wiki ID string that all resources are relative to. [optional]
+ *
+ * @param Array $config
+ */
+ public function __construct( array $config ) {
+ parent::__construct( $config );
+
+ // Sanitize srvsByBucket config to prevent PHP errors
+ $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' );
+ $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
+
+ $memcConfig = isset( $config['memcConfig'] )
+ ? $config['memcConfig']
+ : array( 'class' => 'MemcachedPhpBagOStuff' );
+
+ foreach ( $config['lockServers'] as $name => $address ) {
+ $params = array( 'servers' => array( $address ) ) + $memcConfig;
+ $cache = ObjectCache::newFromParams( $params );
+ if ( $cache instanceof MemcachedBagOStuff ) {
+ $this->bagOStuffs[$name] = $cache;
+ } else {
+ throw new MWException(
+ 'Only MemcachedBagOStuff classes are supported by MemcLockManager.' );
+ }
+ }
+
+ $this->wikiId = isset( $config['wikiId'] ) ? $config['wikiId'] : wfWikiID();
+
+ $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
+ $this->lockExpiry = $met ? 2*(int)$met : 2*3600;
+
+ $this->session = wfRandomString( 32 );
+ }
+
+ /**
+ * @see QuorumLockManager::getLocksOnServer()
+ * @return Status
+ */
+ protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ $memc = $this->getCache( $lockSrv );
+ $keys = array_map( array( $this, 'recordKeyForPath' ), $paths ); // lock records
+
+ // Lock all of the active lock record keys...
+ if ( !$this->acquireMutexes( $memc, $keys ) ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ return;
+ }
+
+ // Fetch all the existing lock records...
+ $lockRecords = $memc->getMulti( $keys );
+
+ $now = time();
+ // Check if the requested locks conflict with existing ones...
+ foreach ( $paths as $path ) {
+ $locksKey = $this->recordKeyForPath( $path );
+ $locksHeld = isset( $lockRecords[$locksKey] )
+ ? $lockRecords[$locksKey]
+ : array( self::LOCK_SH => array(), self::LOCK_EX => array() ); // init
+ foreach ( $locksHeld[self::LOCK_EX] as $session => $expiry ) {
+ if ( $expiry < $now ) { // stale?
+ unset( $locksHeld[self::LOCK_EX][$session] );
+ } elseif ( $session !== $this->session ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ }
+ if ( $type === self::LOCK_EX ) {
+ foreach ( $locksHeld[self::LOCK_SH] as $session => $expiry ) {
+ if ( $expiry < $now ) { // stale?
+ unset( $locksHeld[self::LOCK_SH][$session] );
+ } elseif ( $session !== $this->session ) {
+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
+ }
+ }
+ }
+ if ( $status->isOK() ) {
+ // Register the session in the lock record array
+ $locksHeld[$type][$this->session] = $now + $this->lockExpiry;
+ // We will update this record if none of the other locks conflict
+ $lockRecords[$locksKey] = $locksHeld;
+ }
+ }
+
+ // If there were no lock conflicts, update all the lock records...
+ if ( $status->isOK() ) {
+ foreach ( $lockRecords as $locksKey => $locksHeld ) {
+ $memc->set( $locksKey, $locksHeld );
+ wfDebug( __METHOD__ . ": acquired lock on key $locksKey.\n" );
+ }
+ }
+
+ // Unlock all of the active lock record keys...
+ $this->releaseMutexes( $memc, $keys );
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::freeLocksOnServer()
+ * @return Status
+ */
+ protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
+ $status = Status::newGood();
+
+ $memc = $this->getCache( $lockSrv );
+ $keys = array_map( array( $this, 'recordKeyForPath' ), $paths ); // lock records
+
+ // Lock all of the active lock record keys...
+ if ( !$this->acquireMutexes( $memc, $keys ) ) {
+ foreach ( $paths as $path ) {
+ $status->fatal( 'lockmanager-fail-releaselock', $path );
+ }
+ return;
+ }
+
+ // Fetch all the existing lock records...
+ $lockRecords = $memc->getMulti( $keys );
+
+ // Remove the requested locks from all records...
+ foreach ( $paths as $path ) {
+ $locksKey = $this->recordKeyForPath( $path ); // lock record
+ if ( !isset( $lockRecords[$locksKey] ) ) {
+ continue; // nothing to do
+ }
+ $locksHeld = $lockRecords[$locksKey];
+ if ( is_array( $locksHeld ) && isset( $locksHeld[$type] ) ) {
+ unset( $locksHeld[$type][$this->session] );
+ $ok = $memc->set( $locksKey, $locksHeld );
+ } else {
+ $ok = true;
+ }
+ if ( !$ok ) {
+ $status->fatal( 'lockmanager-fail-releaselock', $path );
+ }
+ wfDebug( __METHOD__ . ": released lock on key $locksKey.\n" );
+ }
+
+ // Unlock all of the active lock record keys...
+ $this->releaseMutexes( $memc, $keys );
+
+ return $status;
+ }
+
+ /**
+ * @see QuorumLockManager::releaseAllLocks()
+ * @return Status
+ */
+ protected function releaseAllLocks() {
+ return Status::newGood(); // not supported
+ }
+
+ /**
+ * @see QuorumLockManager::isServerUp()
+ * @return bool
+ */
+ protected function isServerUp( $lockSrv ) {
+ return (bool)$this->getCache( $lockSrv );
+ }
+
+ /**
+ * Get the MemcachedBagOStuff object for a $lockSrv
+ *
+ * @param $lockSrv string Server name
+ * @return MemcachedBagOStuff|null
+ */
+ protected function getCache( $lockSrv ) {
+ $memc = null;
+ if ( isset( $this->bagOStuffs[$lockSrv] ) ) {
+ $memc = $this->bagOStuffs[$lockSrv];
+ if ( !isset( $this->serversUp[$lockSrv] ) ) {
+ $this->serversUp[$lockSrv] = $memc->set( 'MemcLockManager:ping', 1, 1 );
+ if ( !$this->serversUp[$lockSrv] ) {
+ trigger_error( __METHOD__ . ": Could not contact $lockSrv.", E_USER_WARNING );
+ }
+ }
+ if ( !$this->serversUp[$lockSrv] ) {
+ return null; // server appears to be down
+ }
+ }
+ return $memc;
+ }
+
+ /**
+ * @param $path string
+ * @return string
+ */
+ protected function recordKeyForPath( $path ) {
+ $hash = LockManager::sha1Base36( $path );
+ list( $db, $prefix ) = wfSplitWikiID( $this->wikiId );
+ return wfForeignMemcKey( $db, $prefix, __CLASS__, 'locks', $hash );
+ }
+
+ /**
+ * @param $memc MemcachedBagOStuff
+ * @param $keys Array List of keys to acquire
+ * @return bool
+ */
+ protected function acquireMutexes( MemcachedBagOStuff $memc, array $keys ) {
+ $lockedKeys = array();
+
+ // Acquire the keys in lexicographical order, to avoid deadlock problems.
+ // If P1 is waiting to acquire a key P2 has, P2 can't also be waiting for a key P1 has.
+ sort( $keys );
+
+ // Try to quickly loop to acquire the keys, but back off after a few rounds.
+ // This reduces memcached spam, especially in the rare case where a server acquires
+ // some lock keys and dies without releasing them. Lock keys expire after a few minutes.
+ $rounds = 0;
+ $start = microtime( true );
+ do {
+ if ( ( ++$rounds % 4 ) == 0 ) {
+ usleep( 1000*50 ); // 50 ms
+ }
+ foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
+ if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
+ $lockedKeys[] = $key;
+ } else {
+ continue; // acquire in order
+ }
+ }
+ } while ( count( $lockedKeys ) < count( $keys ) && ( microtime( true ) - $start ) <= 6 );
+
+ if ( count( $lockedKeys ) != count( $keys ) ) {
+ $this->releaseMutexes( $lockedKeys ); // failed; release what was locked
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $memc MemcachedBagOStuff
+ * @param $keys Array List of acquired keys
+ * @return void
+ */
+ protected function releaseMutexes( MemcachedBagOStuff $memc, array $keys ) {
+ foreach ( $keys as $key ) {
+ $memc->delete( "$key:mutex" );
+ }
+ }
+
+ /**
+ * Make sure remaining locks get cleared for sanity
+ */
+ function __destruct() {
+ while ( count( $this->locksHeld ) ) {
+ foreach ( $this->locksHeld as $path => $locks ) {
+ $this->doUnlock( array( $path ), self::LOCK_EX );
+ $this->doUnlock( array( $path ), self::LOCK_SH );
+ }
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * Non-directory file on the file system.
- *
- * 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 FileBackend
- */
-
-/**
- * Class representing a non-directory file on the file system
- *
- * @ingroup FileBackend
- */
-class FSFile {
- protected $path; // path to file
-
- /**
- * Sets up the file object
- *
- * @param $path string Path to temporary file on local disk
- * @throws MWException
- */
- public function __construct( $path ) {
- if ( FileBackend::isStoragePath( $path ) ) {
- throw new MWException( __METHOD__ . " given storage path `$path`." );
- }
- $this->path = $path;
- }
-
- /**
- * Returns the file system path
- *
- * @return String
- */
- public function getPath() {
- return $this->path;
- }
-
- /**
- * Checks if the file exists
- *
- * @return bool
- */
- public function exists() {
- return is_file( $this->path );
- }
-
- /**
- * Get the file size in bytes
- *
- * @return int|bool
- */
- public function getSize() {
- return filesize( $this->path );
- }
-
- /**
- * Get the file's last-modified timestamp
- *
- * @return string|bool TS_MW timestamp or false on failure
- */
- public function getTimestamp() {
- wfSuppressWarnings();
- $timestamp = filemtime( $this->path );
- wfRestoreWarnings();
- if ( $timestamp !== false ) {
- $timestamp = wfTimestamp( TS_MW, $timestamp );
- }
- return $timestamp;
- }
-
- /**
- * Guess the MIME type from the file contents alone
- *
- * @return string
- */
- public function getMimeType() {
- return MimeMagic::singleton()->guessMimeType( $this->path, false );
- }
-
- /**
- * Get an associative array containing information about
- * a file with the given storage path.
- *
- * @param $ext Mixed: the file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
- *
- * @return array
- */
- public function getProps( $ext = true ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__.": Getting file info for $this->path\n" );
-
- $info = self::placeholderProps();
- $info['fileExists'] = $this->exists();
-
- if ( $info['fileExists'] ) {
- $magic = MimeMagic::singleton();
-
- # get the file extension
- if ( $ext === true ) {
- $ext = self::extensionFromPath( $this->path );
- }
-
- # mime type according to file contents
- $info['file-mime'] = $this->getMimeType();
- # logical mime type
- $info['mime'] = $magic->improveTypeFromExtension( $info['file-mime'], $ext );
-
- list( $info['major_mime'], $info['minor_mime'] ) = File::splitMime( $info['mime'] );
- $info['media_type'] = $magic->getMediaType( $this->path, $info['mime'] );
-
- # Get size in bytes
- $info['size'] = $this->getSize();
-
- # Height, width and metadata
- $handler = MediaHandler::getHandler( $info['mime'] );
- if ( $handler ) {
- $tempImage = (object)array();
- $info['metadata'] = $handler->getMetadata( $tempImage, $this->path );
- $gis = $handler->getImageSize( $tempImage, $this->path, $info['metadata'] );
- if ( is_array( $gis ) ) {
- $info = $this->extractImageSizeInfo( $gis ) + $info;
- }
- }
- $info['sha1'] = $this->getSha1Base36();
-
- wfDebug(__METHOD__.": $this->path loaded, {$info['size']} bytes, {$info['mime']}.\n");
- } else {
- wfDebug(__METHOD__.": $this->path NOT FOUND!\n");
- }
-
- wfProfileOut( __METHOD__ );
- return $info;
- }
-
- /**
- * Placeholder file properties to use for files that don't exist
- *
- * @return Array
- */
- public static function placeholderProps() {
- $info = array();
- $info['fileExists'] = false;
- $info['mime'] = null;
- $info['media_type'] = MEDIATYPE_UNKNOWN;
- $info['metadata'] = '';
- $info['sha1'] = '';
- $info['width'] = 0;
- $info['height'] = 0;
- $info['bits'] = 0;
- return $info;
- }
-
- /**
- * Exract image size information
- *
- * @param $gis array
- * @return Array
- */
- protected function extractImageSizeInfo( array $gis ) {
- $info = array();
- # NOTE: $gis[2] contains a code for the image type. This is no longer used.
- $info['width'] = $gis[0];
- $info['height'] = $gis[1];
- if ( isset( $gis['bits'] ) ) {
- $info['bits'] = $gis['bits'];
- } else {
- $info['bits'] = 0;
- }
- return $info;
- }
-
- /**
- * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
- * encoding, zero padded to 31 digits.
- *
- * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
- * fairly neatly.
- *
- * @return bool|string False on failure
- */
- public function getSha1Base36() {
- wfProfileIn( __METHOD__ );
-
- wfSuppressWarnings();
- $hash = sha1_file( $this->path );
- wfRestoreWarnings();
- if ( $hash !== false ) {
- $hash = wfBaseConvert( $hash, 16, 36, 31 );
- }
-
- wfProfileOut( __METHOD__ );
- return $hash;
- }
-
- /**
- * Get the final file extension from a file system path
- *
- * @param $path string
- * @return string
- */
- public static function extensionFromPath( $path ) {
- $i = strrpos( $path, '.' );
- return strtolower( $i ? substr( $path, $i + 1 ) : '' );
- }
-
- /**
- * Get an associative array containing information about a file in the local filesystem.
- *
- * @param $path String: absolute local filesystem path
- * @param $ext Mixed: the file extension, or true to extract it from the filename.
- * Set it to false to ignore the extension.
- *
- * @return array
- */
- public static function getPropsFromPath( $path, $ext = true ) {
- $fsFile = new self( $path );
- return $fsFile->getProps( $ext );
- }
-
- /**
- * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case
- * encoding, zero padded to 31 digits.
- *
- * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36
- * fairly neatly.
- *
- * @param $path string
- *
- * @return bool|string False on failure
- */
- public static function getSha1Base36FromPath( $path ) {
- $fsFile = new self( $path );
- return $fsFile->getSha1Base36();
- }
-}
+++ /dev/null
-<?php
-/**
- * File system based backend.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * @brief Class for a file system (FS) based file backend.
- *
- * All "containers" each map to a directory under the backend's base directory.
- * For backwards-compatibility, some container paths can be set to custom paths.
- * The wiki ID will not be used in any custom paths, so this should be avoided.
- *
- * Having directories with thousands of files will diminish performance.
- * Sharding can be accomplished by using FileRepo-style hash paths.
- *
- * Status messages should avoid mentioning the internal FS paths.
- * PHP warnings are assumed to be logged rather than output.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-class FSFileBackend extends FileBackendStore {
- protected $basePath; // string; directory holding the container directories
- /** @var Array Map of container names to root paths */
- protected $containerPaths = array(); // for custom container paths
- protected $fileMode; // integer; file permission mode
-
- protected $hadWarningErrors = array();
-
- /**
- * @see FileBackendStore::__construct()
- * Additional $config params include:
- * - basePath : File system directory that holds containers.
- * - containerPaths : Map of container names to custom file system directories.
- * This should only be used for backwards-compatibility.
- * - fileMode : Octal UNIX file permissions to use on files stored.
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
-
- // Remove any possible trailing slash from directories
- if ( isset( $config['basePath'] ) ) {
- $this->basePath = rtrim( $config['basePath'], '/' ); // remove trailing slash
- } else {
- $this->basePath = null; // none; containers must have explicit paths
- }
-
- if ( isset( $config['containerPaths'] ) ) {
- $this->containerPaths = (array)$config['containerPaths'];
- foreach ( $this->containerPaths as &$path ) {
- $path = rtrim( $path, '/' ); // remove trailing slash
- }
- }
-
- $this->fileMode = isset( $config['fileMode'] )
- ? $config['fileMode']
- : 0644;
- }
-
- /**
- * @see FileBackendStore::resolveContainerPath()
- * @param $container string
- * @param $relStoragePath string
- * @return null|string
- */
- protected function resolveContainerPath( $container, $relStoragePath ) {
- // Check that container has a root directory
- if ( isset( $this->containerPaths[$container] ) || isset( $this->basePath ) ) {
- // Check for sane relative paths (assume the base paths are OK)
- if ( $this->isLegalRelPath( $relStoragePath ) ) {
- return $relStoragePath;
- }
- }
- return null;
- }
-
- /**
- * Sanity check a relative file system path for validity
- *
- * @param $path string Normalized relative path
- * @return bool
- */
- protected function isLegalRelPath( $path ) {
- // Check for file names longer than 255 chars
- if ( preg_match( '![^/]{256}!', $path ) ) { // ext3/NTFS
- return false;
- }
- if ( wfIsWindows() ) { // NTFS
- return !preg_match( '![:*?"<>|]!', $path );
- } else {
- return true;
- }
- }
-
- /**
- * Given the short (unresolved) and full (resolved) name of
- * a container, return the file system path of the container.
- *
- * @param $shortCont string
- * @param $fullCont string
- * @return string|null
- */
- protected function containerFSRoot( $shortCont, $fullCont ) {
- if ( isset( $this->containerPaths[$shortCont] ) ) {
- return $this->containerPaths[$shortCont];
- } elseif ( isset( $this->basePath ) ) {
- return "{$this->basePath}/{$fullCont}";
- }
- return null; // no container base path defined
- }
-
- /**
- * Get the absolute file system path for a storage path
- *
- * @param $storagePath string Storage path
- * @return string|null
- */
- protected function resolveToFSPath( $storagePath ) {
- list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
- if ( $relPath === null ) {
- return null; // invalid
- }
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $storagePath );
- $fsPath = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- if ( $relPath != '' ) {
- $fsPath .= "/{$relPath}";
- }
- return $fsPath;
- }
-
- /**
- * @see FileBackendStore::isPathUsableInternal()
- * @return bool
- */
- public function isPathUsableInternal( $storagePath ) {
- $fsPath = $this->resolveToFSPath( $storagePath );
- if ( $fsPath === null ) {
- return false; // invalid
- }
- $parentDir = dirname( $fsPath );
-
- if ( file_exists( $fsPath ) ) {
- $ok = is_file( $fsPath ) && is_writable( $fsPath );
- } else {
- $ok = is_dir( $parentDir ) && is_writable( $parentDir );
- }
-
- return $ok;
- }
-
- /**
- * @see FileBackendStore::doStoreInternal()
- * @return Status
- */
- protected function doStoreInternal( array $params ) {
- $status = Status::newGood();
-
- $dest = $this->resolveToFSPath( $params['dst'] );
- if ( $dest === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- if ( file_exists( $dest ) ) {
- if ( !empty( $params['overwrite'] ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- } else {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- }
-
- if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
- wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
- wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
- ) );
- $status->value = new FSFileOpHandle( $this, $params, 'Store', $cmd, $dest );
- } else { // immediate write
- $ok = copy( $params['src'], $dest );
- // In some cases (at least over NFS), copy() returns true when it fails
- if ( !$ok || ( filesize( $params['src'] ) !== filesize( $dest ) ) ) {
- if ( $ok ) { // PHP bug
- unlink( $dest ); // remove broken file
- trigger_error( __METHOD__ . ": copy() failed but returned true." );
- }
- $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
- return $status;
- }
- $this->chmod( $dest );
- }
-
- return $status;
- }
-
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseStore( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
- /**
- * @see FileBackendStore::doCopyInternal()
- * @return Status
- */
- protected function doCopyInternal( array $params ) {
- $status = Status::newGood();
-
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- $dest = $this->resolveToFSPath( $params['dst'] );
- if ( $dest === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- if ( file_exists( $dest ) ) {
- if ( !empty( $params['overwrite'] ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- } else {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- }
-
- if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
- wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
- wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
- ) );
- $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd, $dest );
- } else { // immediate write
- $ok = copy( $source, $dest );
- // In some cases (at least over NFS), copy() returns true when it fails
- if ( !$ok || ( filesize( $source ) !== filesize( $dest ) ) ) {
- if ( $ok ) { // PHP bug
- unlink( $dest ); // remove broken file
- trigger_error( __METHOD__ . ": copy() failed but returned true." );
- }
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- return $status;
- }
- $this->chmod( $dest );
- }
-
- return $status;
- }
-
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCopy( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
- /**
- * @see FileBackendStore::doMoveInternal()
- * @return Status
- */
- protected function doMoveInternal( array $params ) {
- $status = Status::newGood();
-
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- $dest = $this->resolveToFSPath( $params['dst'] );
- if ( $dest === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- if ( file_exists( $dest ) ) {
- if ( !empty( $params['overwrite'] ) ) {
- // Windows does not support moving over existing files
- if ( wfIsWindows() ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- }
- } else {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- }
-
- if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'MOVE' : 'mv',
- wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
- wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
- ) );
- $status->value = new FSFileOpHandle( $this, $params, 'Move', $cmd );
- } else { // immediate write
- $ok = rename( $source, $dest );
- clearstatcache(); // file no longer at source
- if ( !$ok ) {
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- return $status;
- }
- }
-
- return $status;
- }
-
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseMove( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
- /**
- * @see FileBackendStore::doDeleteInternal()
- * @return Status
- */
- protected function doDeleteInternal( array $params ) {
- $status = Status::newGood();
-
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- if ( !is_file( $source ) ) {
- if ( empty( $params['ignoreMissingSource'] ) ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- }
- return $status; // do nothing; either OK or bad status
- }
-
- if ( !empty( $params['async'] ) ) { // deferred
- $cmd = implode( ' ', array( wfIsWindows() ? 'DEL' : 'unlink',
- wfEscapeShellArg( $this->cleanPathSlashes( $source ) )
- ) );
- $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
- } else { // immediate write
- $ok = unlink( $source );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- return $status;
- }
- }
-
- return $status;
- }
-
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseDelete( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
- /**
- * @see FileBackendStore::doCreateInternal()
- * @return Status
- */
- protected function doCreateInternal( array $params ) {
- $status = Status::newGood();
-
- $dest = $this->resolveToFSPath( $params['dst'] );
- if ( $dest === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- if ( file_exists( $dest ) ) {
- if ( !empty( $params['overwrite'] ) ) {
- $ok = unlink( $dest );
- if ( !$ok ) {
- $status->fatal( 'backend-fail-delete', $params['dst'] );
- return $status;
- }
- } else {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- }
-
- if ( !empty( $params['async'] ) ) { // deferred
- $tempFile = TempFSFile::factory( 'create_', 'tmp' );
- if ( !$tempFile ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- return $status;
- }
- $bytes = file_put_contents( $tempFile->getPath(), $params['content'] );
- if ( $bytes === false ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- return $status;
- }
- $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
- wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
- wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
- ) );
- $status->value = new FSFileOpHandle( $this, $params, 'Create', $cmd, $dest );
- $tempFile->bind( $status->value );
- } else { // immediate write
- $bytes = file_put_contents( $dest, $params['content'] );
- if ( $bytes === false ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- return $status;
- }
- $this->chmod( $dest );
- }
-
- return $status;
- }
-
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCreate( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
- /**
- * @see FileBackendStore::doPrepareInternal()
- * @return Status
- */
- protected function doPrepareInternal( $fullCont, $dirRel, array $params ) {
- $status = Status::newGood();
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- $existed = is_dir( $dir ); // already there?
- if ( !wfMkdirParents( $dir ) ) { // make directory and its parents
- $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
- } elseif ( !is_writable( $dir ) ) {
- $status->fatal( 'directoryreadonlyerror', $params['dir'] );
- } elseif ( !is_readable( $dir ) ) {
- $status->fatal( 'directorynotreadableerror', $params['dir'] );
- }
- if ( is_dir( $dir ) && !$existed ) {
- // Respect any 'noAccess' or 'noListing' flags...
- $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
- }
- return $status;
- }
-
- /**
- * @see FileBackendStore::doSecureInternal()
- * @return Status
- */
- protected function doSecureInternal( $fullCont, $dirRel, array $params ) {
- $status = Status::newGood();
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- // Seed new directories with a blank index.html, to prevent crawling...
- if ( !empty( $params['noListing'] ) && !file_exists( "{$dir}/index.html" ) ) {
- $bytes = file_put_contents( "{$dir}/index.html", $this->indexHtmlPrivate() );
- if ( $bytes === false ) {
- $status->fatal( 'backend-fail-create', $params['dir'] . '/index.html' );
- return $status;
- }
- }
- // Add a .htaccess file to the root of the container...
- if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
- $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
- if ( $bytes === false ) {
- $storeDir = "mwstore://{$this->name}/{$shortCont}";
- $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
- return $status;
- }
- }
- return $status;
- }
-
- /**
- * @see FileBackendStore::doPublishInternal()
- * @return Status
- */
- protected function doPublishInternal( $fullCont, $dirRel, array $params ) {
- $status = Status::newGood();
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- // Unseed new directories with a blank index.html, to allow crawling...
- if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
- $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
- if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
- $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
- return $status;
- }
- }
- // Remove the .htaccess file from the root of the container...
- if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
- $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
- if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
- $storeDir = "mwstore://{$this->name}/{$shortCont}";
- $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
- return $status;
- }
- }
- return $status;
- }
-
- /**
- * @see FileBackendStore::doCleanInternal()
- * @return Status
- */
- protected function doCleanInternal( $fullCont, $dirRel, array $params ) {
- $status = Status::newGood();
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- wfSuppressWarnings();
- if ( is_dir( $dir ) ) {
- rmdir( $dir ); // remove directory if empty
- }
- wfRestoreWarnings();
- return $status;
- }
-
- /**
- * @see FileBackendStore::doFileExists()
- * @return array|bool|null
- */
- protected function doGetFileStat( array $params ) {
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- return false; // invalid storage path
- }
-
- $this->trapWarnings(); // don't trust 'false' if there were errors
- $stat = is_file( $source ) ? stat( $source ) : false; // regular files only
- $hadError = $this->untrapWarnings();
-
- if ( $stat ) {
- return array(
- 'mtime' => wfTimestamp( TS_MW, $stat['mtime'] ),
- 'size' => $stat['size']
- );
- } elseif ( !$hadError ) {
- return false; // file does not exist
- } else {
- return null; // failure
- }
- }
-
- /**
- * @see FileBackendStore::doClearCache()
- */
- protected function doClearCache( array $paths = null ) {
- clearstatcache(); // clear the PHP file stat cache
- }
-
- /**
- * @see FileBackendStore::doDirectoryExists()
- * @return bool|null
- */
- protected function doDirectoryExists( $fullCont, $dirRel, array $params ) {
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
-
- $this->trapWarnings(); // don't trust 'false' if there were errors
- $exists = is_dir( $dir );
- $hadError = $this->untrapWarnings();
-
- return $hadError ? null : $exists;
- }
-
- /**
- * @see FileBackendStore::getDirectoryListInternal()
- * @return Array|null
- */
- public function getDirectoryListInternal( $fullCont, $dirRel, array $params ) {
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- $exists = is_dir( $dir );
- if ( !$exists ) {
- wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
- return array(); // nothing under this dir
- } elseif ( !is_readable( $dir ) ) {
- wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
- }
- return new FSFileBackendDirList( $dir, $params );
- }
-
- /**
- * @see FileBackendStore::getFileListInternal()
- * @return array|FSFileBackendFileList|null
- */
- public function getFileListInternal( $fullCont, $dirRel, array $params ) {
- list( $b, $shortCont, $r ) = FileBackend::splitStoragePath( $params['dir'] );
- $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
- $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
- $exists = is_dir( $dir );
- if ( !$exists ) {
- wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
- return array(); // nothing under this dir
- } elseif ( !is_readable( $dir ) ) {
- wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
- }
- return new FSFileBackendFileList( $dir, $params );
- }
-
- /**
- * @see FileBackendStore::getLocalReference()
- * @return FSFile|null
- */
- public function getLocalReference( array $params ) {
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- return null;
- }
- return new FSFile( $source );
- }
-
- /**
- * @see FileBackendStore::getLocalCopy()
- * @return null|TempFSFile
- */
- public function getLocalCopy( array $params ) {
- $source = $this->resolveToFSPath( $params['src'] );
- if ( $source === null ) {
- return null;
- }
-
- // Create a new temporary file with the same extension...
- $ext = FileBackend::extensionFromPath( $params['src'] );
- $tmpFile = TempFSFile::factory( wfBaseName( $source ) . '_', $ext );
- if ( !$tmpFile ) {
- return null;
- }
- $tmpPath = $tmpFile->getPath();
-
- // Copy the source file over the temp file
- $ok = copy( $source, $tmpPath );
- if ( !$ok ) {
- return null;
- }
-
- $this->chmod( $tmpPath );
-
- return $tmpFile;
- }
-
- /**
- * @see FileBackendStore::directoriesAreVirtual()
- * @return bool
- */
- protected function directoriesAreVirtual() {
- return false;
- }
-
- /**
- * @see FileBackendStore::doExecuteOpHandlesInternal()
- * @return Array List of corresponding Status objects
- */
- protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
- $statuses = array();
-
- $pipes = array();
- foreach ( $fileOpHandles as $index => $fileOpHandle ) {
- $pipes[$index] = popen( "{$fileOpHandle->cmd} 2>&1", 'r' );
- }
-
- $errs = array();
- foreach ( $pipes as $index => $pipe ) {
- // Result will be empty on success in *NIX. On Windows,
- // it may be something like " 1 file(s) [copied|moved].".
- $errs[$index] = stream_get_contents( $pipe );
- fclose( $pipe );
- }
-
- foreach ( $fileOpHandles as $index => $fileOpHandle ) {
- $status = Status::newGood();
- $function = '_getResponse' . $fileOpHandle->call;
- $this->$function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
- $statuses[$index] = $status;
- if ( $status->isOK() && $fileOpHandle->chmodPath ) {
- $this->chmod( $fileOpHandle->chmodPath );
- }
- }
-
- clearstatcache(); // files changed
- return $statuses;
- }
-
- /**
- * Chmod a file, suppressing the warnings
- *
- * @param $path string Absolute file system path
- * @return bool Success
- */
- protected function chmod( $path ) {
- wfSuppressWarnings();
- $ok = chmod( $path, $this->fileMode );
- wfRestoreWarnings();
-
- return $ok;
- }
-
- /**
- * Return the text of an index.html file to hide directory listings
- *
- * @return string
- */
- protected function indexHtmlPrivate() {
- return '';
- }
-
- /**
- * Return the text of a .htaccess file to make a directory private
- *
- * @return string
- */
- protected function htaccessPrivate() {
- return "Deny from all\n";
- }
-
- /**
- * Clean up directory separators for the given OS
- *
- * @param $path string FS path
- * @return string
- */
- protected function cleanPathSlashes( $path ) {
- return wfIsWindows() ? strtr( $path, '/', '\\' ) : $path;
- }
-
- /**
- * Listen for E_WARNING errors and track whether any happen
- *
- * @return bool
- */
- protected function trapWarnings() {
- $this->hadWarningErrors[] = false; // push to stack
- set_error_handler( array( $this, 'handleWarning' ), E_WARNING );
- return false; // invoke normal PHP error handler
- }
-
- /**
- * Stop listening for E_WARNING errors and return true if any happened
- *
- * @return bool
- */
- protected function untrapWarnings() {
- restore_error_handler(); // restore previous handler
- return array_pop( $this->hadWarningErrors ); // pop from stack
- }
-
- /**
- * @return bool
- */
- private function handleWarning() {
- $this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
- return true; // suppress from PHP handler
- }
-}
-
-/**
- * @see FileBackendStoreOpHandle
- */
-class FSFileOpHandle extends FileBackendStoreOpHandle {
- public $cmd; // string; shell command
- public $chmodPath; // string; file to chmod
-
- /**
- * @param $backend
- * @param $params array
- * @param $call
- * @param $cmd
- * @param $chmodPath null
- */
- public function __construct( $backend, array $params, $call, $cmd, $chmodPath = null ) {
- $this->backend = $backend;
- $this->params = $params;
- $this->call = $call;
- $this->cmd = $cmd;
- $this->chmodPath = $chmodPath;
- }
-}
-
-/**
- * Wrapper around RecursiveDirectoryIterator/DirectoryIterator that
- * catches exception or does any custom behavoir that we may want.
- * Do not use this class from places outside FSFileBackend.
- *
- * @ingroup FileBackend
- */
-abstract class FSFileBackendList implements Iterator {
- /** @var Iterator */
- protected $iter;
- protected $suffixStart; // integer
- protected $pos = 0; // integer
- /** @var Array */
- protected $params = array();
-
- /**
- * @param $dir string file system directory
- * @param $params array
- */
- public function __construct( $dir, array $params ) {
- $dir = realpath( $dir ); // normalize
- $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/"
- $this->params = $params;
-
- try {
- $this->iter = $this->initIterator( $dir );
- } catch ( UnexpectedValueException $e ) {
- $this->iter = null; // bad permissions? deleted?
- }
- }
-
- /**
- * Return an appropriate iterator object to wrap
- *
- * @param $dir string file system directory
- * @return Iterator
- */
- protected function initIterator( $dir ) {
- if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
- # Get an iterator that will get direct sub-nodes
- return new DirectoryIterator( $dir );
- } else { // recursive
- # Get an iterator that will return leaf nodes (non-directories)
- # RecursiveDirectoryIterator extends FilesystemIterator.
- # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
- $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
- return new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator( $dir, $flags ),
- RecursiveIteratorIterator::CHILD_FIRST // include dirs
- );
- }
- }
-
- /**
- * @see Iterator::key()
- * @return integer
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::current()
- * @return string|bool String or false
- */
- public function current() {
- return $this->getRelPath( $this->iter->current()->getPathname() );
- }
-
- /**
- * @see Iterator::next()
- * @return void
- */
- public function next() {
- try {
- $this->iter->next();
- $this->filterViaNext();
- } catch ( UnexpectedValueException $e ) {
- $this->iter = null;
- }
- ++$this->pos;
- }
-
- /**
- * @see Iterator::rewind()
- * @return void
- */
- public function rewind() {
- $this->pos = 0;
- try {
- $this->iter->rewind();
- $this->filterViaNext();
- } catch ( UnexpectedValueException $e ) {
- $this->iter = null;
- }
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- return $this->iter && $this->iter->valid();
- }
-
- /**
- * Filter out items by advancing to the next ones
- */
- protected function filterViaNext() {}
-
- /**
- * Return only the relative path and normalize slashes to FileBackend-style.
- * Uses the "real path" since the suffix is based upon that.
- *
- * @param $path string
- * @return string
- */
- protected function getRelPath( $path ) {
- return strtr( substr( realpath( $path ), $this->suffixStart ), '\\', '/' );
- }
-}
-
-class FSFileBackendDirList extends FSFileBackendList {
- protected function filterViaNext() {
- while ( $this->iter->valid() ) {
- if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
- $this->iter->next(); // skip non-directories and dot files
- } else {
- break;
- }
- }
- }
-}
-
-class FSFileBackendFileList extends FSFileBackendList {
- protected function filterViaNext() {
- while ( $this->iter->valid() ) {
- if ( !$this->iter->current()->isFile() ) {
- $this->iter->next(); // skip non-files and dot files
- } else {
- break;
- }
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * @defgroup FileBackend File backend
- * @ingroup FileRepo
- *
- * File backend is used to interact with file storage systems,
- * such as the local file system, NFS, or cloud storage systems.
- */
-
-/**
- * Base class for all file backends.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * @brief Base class for all file backend classes (including multi-write backends).
- *
- * This class defines the methods as abstract that subclasses must implement.
- * Outside callers can assume that all backends will have these functions.
- *
- * All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
- * The "<path>" portion is a relative path that uses UNIX file system (FS)
- * notation, though any particular backend may not actually be using a local
- * filesystem.
- * Therefore, the relative paths are only virtual.
- *
- * Backend contents are stored under wiki-specific container names by default.
- * For legacy reasons, this has no effect for the FS backend class, and per-wiki
- * segregation must be done by setting the container paths appropriately.
- *
- * FS-based backends are somewhat more restrictive due to the existence of real
- * directory files; a regular file cannot have the same name as a directory. Other
- * backends with virtual directories may not have this limitation. Callers should
- * store files in such a way that no files and directories are under the same path.
- *
- * Methods should avoid throwing exceptions at all costs.
- * As a corollary, external dependencies should be kept to a minimum.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-abstract class FileBackend {
- protected $name; // string; unique backend name
- protected $wikiId; // string; unique wiki name
- protected $readOnly; // string; read-only explanation message
- protected $parallelize; // string; when to do operations in parallel
- protected $concurrency; // integer; how many operations can be done in parallel
-
- /** @var LockManager */
- protected $lockManager;
- /** @var FileJournal */
- protected $fileJournal;
-
- /**
- * Create a new backend instance from configuration.
- * This should only be called from within FileBackendGroup.
- *
- * $config includes:
- * - name : The unique name of this backend.
- * This should consist of alphanumberic, '-', and '_' characters.
- * This name should not be changed after use.
- * - wikiId : Prefix to container names that is unique to this wiki.
- * It should only consist of alphanumberic, '-', and '_' characters.
- * - lockManager : Registered name of a file lock manager to use.
- * - fileJournal : File journal configuration; see FileJournal::factory().
- * Journals simply log changes to files stored in the backend.
- * - readOnly : Write operations are disallowed if this is a non-empty string.
- * It should be an explanation for the backend being read-only.
- * - parallelize : When to do file operations in parallel (when possible).
- * Allowed values are "implicit", "explicit" and "off".
- * - concurrency : How many file operations can be done in parallel.
- *
- * @param $config Array
- * @throws MWException
- */
- public function __construct( array $config ) {
- $this->name = $config['name'];
- if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
- throw new MWException( "Backend name `{$this->name}` is invalid." );
- }
- $this->wikiId = isset( $config['wikiId'] )
- ? $config['wikiId']
- : wfWikiID(); // e.g. "my_wiki-en_"
- $this->lockManager = ( $config['lockManager'] instanceof LockManager )
- ? $config['lockManager']
- : LockManagerGroup::singleton()->get( $config['lockManager'] );
- $this->fileJournal = isset( $config['fileJournal'] )
- ? ( ( $config['fileJournal'] instanceof FileJournal )
- ? $config['fileJournal']
- : FileJournal::factory( $config['fileJournal'], $this->name ) )
- : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
- $this->readOnly = isset( $config['readOnly'] )
- ? (string)$config['readOnly']
- : '';
- $this->parallelize = isset( $config['parallelize'] )
- ? (string)$config['parallelize']
- : 'off';
- $this->concurrency = isset( $config['concurrency'] )
- ? (int)$config['concurrency']
- : 50;
- }
-
- /**
- * Get the unique backend name.
- * We may have multiple different backends of the same type.
- * For example, we can have two Swift backends using different proxies.
- *
- * @return string
- */
- final public function getName() {
- return $this->name;
- }
-
- /**
- * Check if this backend is read-only
- *
- * @return bool
- */
- final public function isReadOnly() {
- return ( $this->readOnly != '' );
- }
-
- /**
- * Get an explanatory message if this backend is read-only
- *
- * @return string|bool Returns false if the backend is not read-only
- */
- final public function getReadOnlyReason() {
- return ( $this->readOnly != '' ) ? $this->readOnly : false;
- }
-
- /**
- * This is the main entry point into the backend for write operations.
- * Callers supply an ordered list of operations to perform as a transaction.
- * Files will be locked, the stat cache cleared, and then the operations attempted.
- * If any serious errors occur, all attempted operations will be rolled back.
- *
- * $ops is an array of arrays. The outer array holds a list of operations.
- * Each inner array is a set of key value pairs that specify an operation.
- *
- * Supported operations and their parameters. The supported actions are:
- * - create
- * - store
- * - copy
- * - move
- * - delete
- * - null
- *
- * a) Create a new file in storage with the contents of a string
- * @code
- * array(
- * 'op' => 'create',
- * 'dst' => <storage path>,
- * 'content' => <string of new file contents>,
- * 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
- * );
- * @endcode
- *
- * b) Copy a file system file into storage
- * @code
- * array(
- * 'op' => 'store',
- * 'src' => <file system path>,
- * 'dst' => <storage path>,
- * 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
- * )
- * @endcode
- *
- * c) Copy a file within storage
- * @code
- * array(
- * 'op' => 'copy',
- * 'src' => <storage path>,
- * 'dst' => <storage path>,
- * 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
- * )
- * @endcode
- *
- * d) Move a file within storage
- * @code
- * array(
- * 'op' => 'move',
- * 'src' => <storage path>,
- * 'dst' => <storage path>,
- * 'overwrite' => <boolean>,
- * 'overwriteSame' => <boolean>
- * )
- * @endcode
- *
- * e) Delete a file within storage
- * @code
- * array(
- * 'op' => 'delete',
- * 'src' => <storage path>,
- * 'ignoreMissingSource' => <boolean>
- * )
- * @endcode
- *
- * f) Do nothing (no-op)
- * @code
- * array(
- * 'op' => 'null',
- * )
- * @endcode
- *
- * Boolean flags for operations (operation-specific):
- * - ignoreMissingSource : The operation will simply succeed and do
- * nothing if the source file does not exist.
- * - overwrite : Any destination file will be overwritten.
- * - overwriteSame : An error will not be given if a file already
- * exists at the destination that has the same
- * contents as the new contents to be written there.
- *
- * $opts is an associative of boolean flags, including:
- * - force : Operation precondition errors no longer trigger an abort.
- * Any remaining operations are still attempted. Unexpected
- * failures may still cause remaning operations to be aborted.
- * - nonLocking : No locks are acquired for the operations.
- * This can increase performance for non-critical writes.
- * This has no effect unless the 'force' flag is set.
- * - allowStale : Don't require the latest available data.
- * This can increase performance for non-critical writes.
- * This has no effect unless the 'force' flag is set.
- * - nonJournaled : Don't log this operation batch in the file journal.
- * This limits the ability of recovery scripts.
- * - parallelize : Try to do operations in parallel when possible.
- * - bypassReadOnly : Allow writes in read-only mode (since 1.20).
- *
- * @remarks Remarks on locking:
- * File system paths given to operations should refer to files that are
- * already locked or otherwise safe from modification from other processes.
- * Normally these files will be new temp files, which should be adequate.
- *
- * @par Return value:
- *
- * This returns a Status, which contains all warnings and fatals that occured
- * during the operation. The 'failCount', 'successCount', and 'success' members
- * will reflect each operation attempted.
- *
- * The status will be "OK" unless:
- * - a) unexpected operation errors occurred (network partitions, disk full...)
- * - b) significant operation errors occured and 'force' was not set
- *
- * @param $ops Array List of operations to execute in order
- * @param $opts Array Batch operation options
- * @return Status
- */
- final public function doOperations( array $ops, array $opts = array() ) {
- if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- if ( empty( $opts['force'] ) ) { // sanity
- unset( $opts['nonLocking'] );
- unset( $opts['allowStale'] );
- }
- $opts['concurrency'] = 1; // off
- if ( $this->parallelize === 'implicit' ) {
- if ( !isset( $opts['parallelize'] ) || $opts['parallelize'] ) {
- $opts['concurrency'] = $this->concurrency;
- }
- } elseif ( $this->parallelize === 'explicit' ) {
- if ( !empty( $opts['parallelize'] ) ) {
- $opts['concurrency'] = $this->concurrency;
- }
- }
- return $this->doOperationsInternal( $ops, $opts );
- }
-
- /**
- * @see FileBackend::doOperations()
- */
- abstract protected function doOperationsInternal( array $ops, array $opts );
-
- /**
- * Same as doOperations() except it takes a single operation.
- * If you are doing a batch of operations that should either
- * all succeed or all fail, then use that function instead.
- *
- * @see FileBackend::doOperations()
- *
- * @param $op Array Operation
- * @param $opts Array Operation options
- * @return Status
- */
- final public function doOperation( array $op, array $opts = array() ) {
- return $this->doOperations( array( $op ), $opts );
- }
-
- /**
- * Performs a single create operation.
- * This sets $params['op'] to 'create' and passes it to doOperation().
- *
- * @see FileBackend::doOperation()
- *
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
- * @return Status
- */
- final public function create( array $params, array $opts = array() ) {
- return $this->doOperation( array( 'op' => 'create' ) + $params, $opts );
- }
-
- /**
- * Performs a single store operation.
- * This sets $params['op'] to 'store' and passes it to doOperation().
- *
- * @see FileBackend::doOperation()
- *
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
- * @return Status
- */
- final public function store( array $params, array $opts = array() ) {
- return $this->doOperation( array( 'op' => 'store' ) + $params, $opts );
- }
-
- /**
- * Performs a single copy operation.
- * This sets $params['op'] to 'copy' and passes it to doOperation().
- *
- * @see FileBackend::doOperation()
- *
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
- * @return Status
- */
- final public function copy( array $params, array $opts = array() ) {
- return $this->doOperation( array( 'op' => 'copy' ) + $params, $opts );
- }
-
- /**
- * Performs a single move operation.
- * This sets $params['op'] to 'move' and passes it to doOperation().
- *
- * @see FileBackend::doOperation()
- *
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
- * @return Status
- */
- final public function move( array $params, array $opts = array() ) {
- return $this->doOperation( array( 'op' => 'move' ) + $params, $opts );
- }
-
- /**
- * Performs a single delete operation.
- * This sets $params['op'] to 'delete' and passes it to doOperation().
- *
- * @see FileBackend::doOperation()
- *
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
- * @return Status
- */
- final public function delete( array $params, array $opts = array() ) {
- return $this->doOperation( array( 'op' => 'delete' ) + $params, $opts );
- }
-
- /**
- * Perform a set of independent file operations on some files.
- *
- * This does no locking, nor journaling, and possibly no stat calls.
- * Any destination files that already exist will be overwritten.
- * This should *only* be used on non-original files, like cache files.
- *
- * Supported operations and their parameters:
- * - create
- * - store
- * - copy
- * - move
- * - delete
- * - null
- *
- * a) Create a new file in storage with the contents of a string
- * @code
- * array(
- * 'op' => 'create',
- * 'dst' => <storage path>,
- * 'content' => <string of new file contents>
- * )
- * @endcode
- * b) Copy a file system file into storage
- * @code
- * array(
- * 'op' => 'store',
- * 'src' => <file system path>,
- * 'dst' => <storage path>
- * )
- * @endcode
- * c) Copy a file within storage
- * @code
- * array(
- * 'op' => 'copy',
- * 'src' => <storage path>,
- * 'dst' => <storage path>
- * )
- * @endcode
- * d) Move a file within storage
- * @code
- * array(
- * 'op' => 'move',
- * 'src' => <storage path>,
- * 'dst' => <storage path>
- * )
- * @endcode
- * e) Delete a file within storage
- * @code
- * array(
- * 'op' => 'delete',
- * 'src' => <storage path>,
- * 'ignoreMissingSource' => <boolean>
- * )
- * @endcode
- * f) Do nothing (no-op)
- * @code
- * array(
- * 'op' => 'null',
- * )
- * @endcode
- *
- * @par Boolean flags for operations (operation-specific):
- * - ignoreMissingSource : The operation will simply succeed and do
- * nothing if the source file does not exist.
- *
- * $opts is an associative of boolean flags, including:
- * - bypassReadOnly : Allow writes in read-only mode (since 1.20)
- *
- * @par Return value:
- * This returns a Status, which contains all warnings and fatals that occured
- * during the operation. The 'failCount', 'successCount', and 'success' members
- * will reflect each operation attempted for the given files. The status will be
- * considered "OK" as long as no fatal errors occured.
- *
- * @param $ops Array Set of operations to execute
- * @param $opts Array Batch operation options
- * @return Status
- * @since 1.20
- */
- final public function doQuickOperations( array $ops, array $opts = array() ) {
- if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- foreach ( $ops as &$op ) {
- $op['overwrite'] = true; // avoids RTTs in key/value stores
- }
- return $this->doQuickOperationsInternal( $ops );
- }
-
- /**
- * @see FileBackend::doQuickOperations()
- * @since 1.20
- */
- abstract protected function doQuickOperationsInternal( array $ops );
-
- /**
- * Same as doQuickOperations() except it takes a single operation.
- * If you are doing a batch of operations, then use that function instead.
- *
- * @see FileBackend::doQuickOperations()
- *
- * @param $op Array Operation
- * @return Status
- * @since 1.20
- */
- final public function doQuickOperation( array $op ) {
- return $this->doQuickOperations( array( $op ) );
- }
-
- /**
- * Performs a single quick create operation.
- * This sets $params['op'] to 'create' and passes it to doQuickOperation().
- *
- * @see FileBackend::doQuickOperation()
- *
- * @param $params Array Operation parameters
- * @return Status
- * @since 1.20
- */
- final public function quickCreate( array $params ) {
- return $this->doQuickOperation( array( 'op' => 'create' ) + $params );
- }
-
- /**
- * Performs a single quick store operation.
- * This sets $params['op'] to 'store' and passes it to doQuickOperation().
- *
- * @see FileBackend::doQuickOperation()
- *
- * @param $params Array Operation parameters
- * @return Status
- * @since 1.20
- */
- final public function quickStore( array $params ) {
- return $this->doQuickOperation( array( 'op' => 'store' ) + $params );
- }
-
- /**
- * Performs a single quick copy operation.
- * This sets $params['op'] to 'copy' and passes it to doQuickOperation().
- *
- * @see FileBackend::doQuickOperation()
- *
- * @param $params Array Operation parameters
- * @return Status
- * @since 1.20
- */
- final public function quickCopy( array $params ) {
- return $this->doQuickOperation( array( 'op' => 'copy' ) + $params );
- }
-
- /**
- * Performs a single quick move operation.
- * This sets $params['op'] to 'move' and passes it to doQuickOperation().
- *
- * @see FileBackend::doQuickOperation()
- *
- * @param $params Array Operation parameters
- * @return Status
- * @since 1.20
- */
- final public function quickMove( array $params ) {
- return $this->doQuickOperation( array( 'op' => 'move' ) + $params );
- }
-
- /**
- * Performs a single quick delete operation.
- * This sets $params['op'] to 'delete' and passes it to doQuickOperation().
- *
- * @see FileBackend::doQuickOperation()
- *
- * @param $params Array Operation parameters
- * @return Status
- * @since 1.20
- */
- final public function quickDelete( array $params ) {
- return $this->doQuickOperation( array( 'op' => 'delete' ) + $params );
- }
-
- /**
- * Concatenate a list of storage files into a single file system file.
- * The target path should refer to a file that is already locked or
- * otherwise safe from modification from other processes. Normally,
- * the file will be a new temp file, which should be adequate.
- *
- * @param $params Array Operation parameters
- * $params include:
- * - srcs : ordered source storage paths (e.g. chunk1, chunk2, ...)
- * - dst : file system path to 0-byte temp file
- * @return Status
- */
- abstract public function concatenate( array $params );
-
- /**
- * Prepare a storage directory for usage.
- * This will create any required containers and parent directories.
- * Backends using key/value stores only need to create the container.
- *
- * The 'noAccess' and 'noListing' parameters works the same as in secure(),
- * except they are only applied *if* the directory/container had to be created.
- * These flags should always be set for directories that have private files.
- *
- * @param $params Array
- * $params include:
- * - dir : storage directory
- * - noAccess : try to deny file access (since 1.20)
- * - noListing : try to deny file listing (since 1.20)
- * - bypassReadOnly : allow writes in read-only mode (since 1.20)
- * @return Status
- */
- final public function prepare( array $params ) {
- if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- return $this->doPrepare( $params );
- }
-
- /**
- * @see FileBackend::prepare()
- */
- abstract protected function doPrepare( array $params );
-
- /**
- * Take measures to block web access to a storage directory and
- * the container it belongs to. FS backends might add .htaccess
- * files whereas key/value store backends might revoke container
- * access to the storage user representing end-users in web requests.
- * This is not guaranteed to actually do anything.
- *
- * @param $params Array
- * $params include:
- * - dir : storage directory
- * - noAccess : try to deny file access
- * - noListing : try to deny file listing
- * - bypassReadOnly : allow writes in read-only mode (since 1.20)
- * @return Status
- */
- final public function secure( array $params ) {
- if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- return $this->doSecure( $params );
- }
-
- /**
- * @see FileBackend::secure()
- */
- abstract protected function doSecure( array $params );
-
- /**
- * Remove measures to block web access to a storage directory and
- * the container it belongs to. FS backends might remove .htaccess
- * files whereas key/value store backends might grant container
- * access to the storage user representing end-users in web requests.
- * This essentially can undo the result of secure() calls.
- *
- * @param $params Array
- * $params include:
- * - dir : storage directory
- * - access : try to allow file access
- * - listing : try to allow file listing
- * - bypassReadOnly : allow writes in read-only mode (since 1.20)
- * @return Status
- * @since 1.20
- */
- final public function publish( array $params ) {
- if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- return $this->doPublish( $params );
- }
-
- /**
- * @see FileBackend::publish()
- */
- abstract protected function doPublish( array $params );
-
- /**
- * Delete a storage directory if it is empty.
- * Backends using key/value stores may do nothing unless the directory
- * is that of an empty container, in which case it should be deleted.
- *
- * @param $params Array
- * $params include:
- * - dir : storage directory
- * - recursive : recursively delete empty subdirectories first (since 1.20)
- * - bypassReadOnly : allow writes in read-only mode (since 1.20)
- * @return Status
- */
- final public function clean( array $params ) {
- if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
- return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
- }
- return $this->doClean( $params );
- }
-
- /**
- * @see FileBackend::clean()
- */
- abstract protected function doClean( array $params );
-
- /**
- * Check if a file exists at a storage path in the backend.
- * This returns false if only a directory exists at the path.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return bool|null Returns null on failure
- */
- abstract public function fileExists( array $params );
-
- /**
- * Get the last-modified timestamp of the file at a storage path.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return string|bool TS_MW timestamp or false on failure
- */
- abstract public function getFileTimestamp( array $params );
-
- /**
- * Get the contents of a file at a storage path in the backend.
- * This should be avoided for potentially large files.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return string|bool Returns false on failure
- */
- abstract public function getFileContents( array $params );
-
- /**
- * Get the size (bytes) of a file at a storage path in the backend.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return integer|bool Returns false on failure
- */
- abstract public function getFileSize( array $params );
-
- /**
- * Get quick information about a file at a storage path in the backend.
- * If the file does not exist, then this returns false.
- * Otherwise, the result is an associative array that includes:
- * - mtime : the last-modified timestamp (TS_MW)
- * - size : the file size (bytes)
- * Additional values may be included for internal use only.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return Array|bool|null Returns null on failure
- */
- abstract public function getFileStat( array $params );
-
- /**
- * Get a SHA-1 hash of the file at a storage path in the backend.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return string|bool Hash string or false on failure
- */
- abstract public function getFileSha1Base36( array $params );
-
- /**
- * Get the properties of the file at a storage path in the backend.
- * Returns FSFile::placeholderProps() on failure.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return Array
- */
- abstract public function getFileProps( array $params );
-
- /**
- * Stream the file at a storage path in the backend.
- * If the file does not exists, a 404 error will be given.
- * Appropriate HTTP headers (Status, Content-Type, Content-Length)
- * must be sent if streaming began, while none should be sent otherwise.
- * Implementations should flush the output buffer before sending data.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - headers : additional HTTP headers to send on success
- * - latest : use the latest available data
- * @return Status
- */
- abstract public function streamFile( array $params );
-
- /**
- * Returns a file system file, identical to the file at a storage path.
- * The file returned is either:
- * - a) A local copy of the file at a storage path in the backend.
- * The temporary copy will have the same extension as the source.
- * - b) An original of the file at a storage path in the backend.
- * Temporary files may be purged when the file object falls out of scope.
- *
- * Write operations should *never* be done on this file as some backends
- * may do internal tracking or may be instances of FileBackendMultiWrite.
- * In that later case, there are copies of the file that must stay in sync.
- * Additionally, further calls to this function may return the same file.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return FSFile|null Returns null on failure
- */
- abstract public function getLocalReference( array $params );
-
- /**
- * Get a local copy on disk of the file at a storage path in the backend.
- * The temporary copy will have the same file extension as the source.
- * Temporary files may be purged when the file object falls out of scope.
- *
- * @param $params Array
- * $params include:
- * - src : source storage path
- * - latest : use the latest available data
- * @return TempFSFile|null Returns null on failure
- */
- abstract public function getLocalCopy( array $params );
-
- /**
- * Check if a directory exists at a given storage path.
- * Backends using key/value stores will check if the path is a
- * virtual directory, meaning there are files under the given directory.
- *
- * Storage backends with eventual consistency might return stale data.
- *
- * @param $params array
- * $params include:
- * - dir : storage directory
- * @return bool|null Returns null on failure
- * @since 1.20
- */
- abstract public function directoryExists( array $params );
-
- /**
- * Get an iterator to list *all* directories under a storage directory.
- * If the directory is of the form "mwstore://backend/container",
- * then all directories in the container should be listed.
- * If the directory is of form "mwstore://backend/container/dir",
- * then all directories directly under that directory should be listed.
- * Results should be storage directories relative to the given directory.
- *
- * Storage backends with eventual consistency might return stale data.
- *
- * @param $params array
- * $params include:
- * - dir : storage directory
- * - topOnly : only return direct child dirs of the directory
- * @return Traversable|Array|null Returns null on failure
- * @since 1.20
- */
- abstract public function getDirectoryList( array $params );
-
- /**
- * Same as FileBackend::getDirectoryList() except only lists
- * directories that are immediately under the given directory.
- *
- * Storage backends with eventual consistency might return stale data.
- *
- * @param $params array
- * $params include:
- * - dir : storage directory
- * @return Traversable|Array|null Returns null on failure
- * @since 1.20
- */
- final public function getTopDirectoryList( array $params ) {
- return $this->getDirectoryList( array( 'topOnly' => true ) + $params );
- }
-
- /**
- * Get an iterator to list *all* stored files under a storage directory.
- * If the directory is of the form "mwstore://backend/container",
- * then all files in the container should be listed.
- * If the directory is of form "mwstore://backend/container/dir",
- * then all files under that directory should be listed.
- * Results should be storage paths relative to the given directory.
- *
- * Storage backends with eventual consistency might return stale data.
- *
- * @param $params array
- * $params include:
- * - dir : storage directory
- * - topOnly : only return direct child files of the directory (since 1.20)
- * @return Traversable|Array|null Returns null on failure
- */
- abstract public function getFileList( array $params );
-
- /**
- * Same as FileBackend::getFileList() except only lists
- * files that are immediately under the given directory.
- *
- * Storage backends with eventual consistency might return stale data.
- *
- * @param $params array
- * $params include:
- * - dir : storage directory
- * @return Traversable|Array|null Returns null on failure
- * @since 1.20
- */
- final public function getTopFileList( array $params ) {
- return $this->getFileList( array( 'topOnly' => true ) + $params );
- }
-
- /**
- * Invalidate any in-process file existence and property cache.
- * If $paths is given, then only the cache for those files will be cleared.
- *
- * @param $paths Array Storage paths (optional)
- * @return void
- */
- public function clearCache( array $paths = null ) {}
-
- /**
- * Lock the files at the given storage paths in the backend.
- * This will either lock all the files or none (on failure).
- *
- * Callers should consider using getScopedFileLocks() instead.
- *
- * @param $paths Array Storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @return Status
- */
- final public function lockFiles( array $paths, $type ) {
- return $this->lockManager->lock( $paths, $type );
- }
-
- /**
- * Unlock the files at the given storage paths in the backend.
- *
- * @param $paths Array Storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @return Status
- */
- final public function unlockFiles( array $paths, $type ) {
- return $this->lockManager->unlock( $paths, $type );
- }
-
- /**
- * Lock the files at the given storage paths in the backend.
- * This will either lock all the files or none (on failure).
- * On failure, the status object will be updated with errors.
- *
- * Once the return value goes out scope, the locks will be released and
- * the status updated. Unlock fatals will not change the status "OK" value.
- *
- * @param $paths Array Storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status Status to update on lock/unlock
- * @return ScopedLock|null Returns null on failure
- */
- final public function getScopedFileLocks( array $paths, $type, Status $status ) {
- return ScopedLock::factory( $this->lockManager, $paths, $type, $status );
- }
-
- /**
- * Get an array of scoped locks needed for a batch of file operations.
- *
- * Normally, FileBackend::doOperations() handles locking, unless
- * the 'nonLocking' param is passed in. This function is useful if you
- * want the files to be locked for a broader scope than just when the
- * files are changing. For example, if you need to update DB metadata,
- * you may want to keep the files locked until finished.
- *
- * @see FileBackend::doOperations()
- *
- * @param $ops Array List of file operations to FileBackend::doOperations()
- * @param $status Status Status to update on lock/unlock
- * @return Array List of ScopedFileLocks or null values
- * @since 1.20
- */
- abstract public function getScopedLocksForOps( array $ops, Status $status );
-
- /**
- * Get the root storage path of this backend.
- * All container paths are "subdirectories" of this path.
- *
- * @return string Storage path
- * @since 1.20
- */
- final public function getRootStoragePath() {
- return "mwstore://{$this->name}";
- }
-
- /**
- * Get the file journal object for this backend
- *
- * @return FileJournal
- */
- final public function getJournal() {
- return $this->fileJournal;
- }
-
- /**
- * Check if a given path is a "mwstore://" path.
- * This does not do any further validation or any existence checks.
- *
- * @param $path string
- * @return bool
- */
- final public static function isStoragePath( $path ) {
- return ( strpos( $path, 'mwstore://' ) === 0 );
- }
-
- /**
- * Split a storage path into a backend name, a container name,
- * and a relative file path. The relative path may be the empty string.
- * This does not do any path normalization or traversal checks.
- *
- * @param $storagePath string
- * @return Array (backend, container, rel object) or (null, null, null)
- */
- final public static function splitStoragePath( $storagePath ) {
- if ( self::isStoragePath( $storagePath ) ) {
- // Remove the "mwstore://" prefix and split the path
- $parts = explode( '/', substr( $storagePath, 10 ), 3 );
- if ( count( $parts ) >= 2 && $parts[0] != '' && $parts[1] != '' ) {
- if ( count( $parts ) == 3 ) {
- return $parts; // e.g. "backend/container/path"
- } else {
- return array( $parts[0], $parts[1], '' ); // e.g. "backend/container"
- }
- }
- }
- return array( null, null, null );
- }
-
- /**
- * Normalize a storage path by cleaning up directory separators.
- * Returns null if the path is not of the format of a valid storage path.
- *
- * @param $storagePath string
- * @return string|null
- */
- final public static function normalizeStoragePath( $storagePath ) {
- list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
- if ( $relPath !== null ) { // must be for this backend
- $relPath = self::normalizeContainerPath( $relPath );
- if ( $relPath !== null ) {
- return ( $relPath != '' )
- ? "mwstore://{$backend}/{$container}/{$relPath}"
- : "mwstore://{$backend}/{$container}";
- }
- }
- return null;
- }
-
- /**
- * Get the parent storage directory of a storage path.
- * This returns a path like "mwstore://backend/container",
- * "mwstore://backend/container/...", or null if there is no parent.
- *
- * @param $storagePath string
- * @return string|null
- */
- final public static function parentStoragePath( $storagePath ) {
- $storagePath = dirname( $storagePath );
- list( $b, $cont, $rel ) = self::splitStoragePath( $storagePath );
- return ( $rel === null ) ? null : $storagePath;
- }
-
- /**
- * Get the final extension from a storage or FS path
- *
- * @param $path string
- * @return string
- */
- final public static function extensionFromPath( $path ) {
- $i = strrpos( $path, '.' );
- return strtolower( $i ? substr( $path, $i + 1 ) : '' );
- }
-
- /**
- * Check if a relative path has no directory traversals
- *
- * @param $path string
- * @return bool
- * @since 1.20
- */
- final public static function isPathTraversalFree( $path ) {
- return ( self::normalizeContainerPath( $path ) !== null );
- }
-
- /**
- * Validate and normalize a relative storage path.
- * Null is returned if the path involves directory traversal.
- * Traversal is insecure for FS backends and broken for others.
- *
- * This uses the same traversal protection as Title::secureAndSplit().
- *
- * @param $path string Storage path relative to a container
- * @return string|null
- */
- final protected static function normalizeContainerPath( $path ) {
- // Normalize directory separators
- $path = strtr( $path, '\\', '/' );
- // Collapse any consecutive directory separators
- $path = preg_replace( '![/]{2,}!', '/', $path );
- // Remove any leading directory separator
- $path = ltrim( $path, '/' );
- // Use the same traversal protection as Title::secureAndSplit()
- if ( strpos( $path, '.' ) !== false ) {
- if (
- $path === '.' ||
- $path === '..' ||
- strpos( $path, './' ) === 0 ||
- strpos( $path, '../' ) === 0 ||
- strpos( $path, '/./' ) !== false ||
- strpos( $path, '/../' ) !== false
- ) {
- return null;
- }
- }
- return $path;
- }
-}
+++ /dev/null
-<?php
-/**
- * File backend registration handling.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * Class to handle file backend registration
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-class FileBackendGroup {
- /**
- * @var FileBackendGroup
- */
- protected static $instance = null;
-
- /** @var Array (name => ('class' => string, 'config' => array, 'instance' => object)) */
- protected $backends = array();
-
- protected function __construct() {}
-
- /**
- * @return FileBackendGroup
- */
- public static function singleton() {
- if ( self::$instance == null ) {
- self::$instance = new self();
- self::$instance->initFromGlobals();
- }
- return self::$instance;
- }
-
- /**
- * Destroy the singleton instance
- *
- * @return void
- */
- public static function destroySingleton() {
- self::$instance = null;
- }
-
- /**
- * Register file backends from the global variables
- *
- * @return void
- */
- protected function initFromGlobals() {
- global $wgLocalFileRepo, $wgForeignFileRepos, $wgFileBackends;
-
- // Register explicitly defined backends
- $this->register( $wgFileBackends );
-
- $autoBackends = array();
- // Automatically create b/c backends for file repos...
- $repos = array_merge( $wgForeignFileRepos, array( $wgLocalFileRepo ) );
- foreach ( $repos as $info ) {
- $backendName = $info['backend'];
- if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
- continue; // already defined (or set to the object for some reason)
- }
- $repoName = $info['name'];
- // Local vars that used to be FSRepo members...
- $directory = $info['directory'];
- $deletedDir = isset( $info['deletedDir'] )
- ? $info['deletedDir']
- : false; // deletion disabled
- $thumbDir = isset( $info['thumbDir'] )
- ? $info['thumbDir']
- : "{$directory}/thumb";
- $fileMode = isset( $info['fileMode'] )
- ? $info['fileMode']
- : 0644;
- // Get the FS backend configuration
- $autoBackends[] = array(
- 'name' => $backendName,
- 'class' => 'FSFileBackend',
- 'lockManager' => 'fsLockManager',
- 'containerPaths' => array(
- "{$repoName}-public" => "{$directory}",
- "{$repoName}-thumb" => $thumbDir,
- "{$repoName}-deleted" => $deletedDir,
- "{$repoName}-temp" => "{$directory}/temp"
- ),
- 'fileMode' => $fileMode,
- );
- }
-
- // Register implicitly defined backends
- $this->register( $autoBackends );
- }
-
- /**
- * Register an array of file backend configurations
- *
- * @param $configs Array
- * @return void
- * @throws MWException
- */
- protected function register( array $configs ) {
- foreach ( $configs as $config ) {
- if ( !isset( $config['name'] ) ) {
- throw new MWException( "Cannot register a backend with no name." );
- }
- $name = $config['name'];
- if ( !isset( $config['class'] ) ) {
- throw new MWException( "Cannot register backend `{$name}` with no class." );
- }
- $class = $config['class'];
-
- unset( $config['class'] ); // backend won't need this
- $this->backends[$name] = array(
- 'class' => $class,
- 'config' => $config,
- 'instance' => null
- );
- }
- }
-
- /**
- * Get the backend object with a given name
- *
- * @param $name string
- * @return FileBackend
- * @throws MWException
- */
- public function get( $name ) {
- if ( !isset( $this->backends[$name] ) ) {
- throw new MWException( "No backend defined with the name `$name`." );
- }
- // Lazy-load the actual backend instance
- if ( !isset( $this->backends[$name]['instance'] ) ) {
- $class = $this->backends[$name]['class'];
- $config = $this->backends[$name]['config'];
- $this->backends[$name]['instance'] = new $class( $config );
- }
- return $this->backends[$name]['instance'];
- }
-
- /**
- * Get the config array for a backend object with a given name
- *
- * @param $name string
- * @return Array
- * @throws MWException
- */
- public function config( $name ) {
- if ( !isset( $this->backends[$name] ) ) {
- throw new MWException( "No backend defined with the name `$name`." );
- }
- $class = $this->backends[$name]['class'];
- return array( 'class' => $class ) + $this->backends[$name]['config'];
- }
-
- /**
- * Get an appropriate backend object from a storage path
- *
- * @param $storagePath string
- * @return FileBackend|null Backend or null on failure
- */
- public function backendFromPath( $storagePath ) {
- list( $backend, $c, $p ) = FileBackend::splitStoragePath( $storagePath );
- if ( $backend !== null && isset( $this->backends[$backend] ) ) {
- return $this->get( $backend );
- }
- return null;
- }
-}
+++ /dev/null
-<?php
-/**
- * Proxy backend that mirrors writes to several internal backends.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * @brief Proxy backend that mirrors writes to several internal backends.
- *
- * This class defines a multi-write backend. Multiple backends can be
- * registered to this proxy backend and it will act as a single backend.
- * Use this when all access to those backends is through this proxy backend.
- * At least one of the backends must be declared the "master" backend.
- *
- * Only use this class when transitioning from one storage system to another.
- *
- * Read operations are only done on the 'master' backend for consistency.
- * Write operations are performed on all backends, in the order defined.
- * If an operation fails on one backend it will be rolled back from the others.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-class FileBackendMultiWrite extends FileBackend {
- /** @var Array Prioritized list of FileBackendStore objects */
- protected $backends = array(); // array of (backend index => backends)
- protected $masterIndex = -1; // integer; index of master backend
- protected $syncChecks = 0; // integer bitfield
-
- /* Possible internal backend consistency checks */
- const CHECK_SIZE = 1;
- const CHECK_TIME = 2;
- const CHECK_SHA1 = 4;
-
- /**
- * Construct a proxy backend that consists of several internal backends.
- * Locking, journaling, and read-only checks are handled by the proxy backend.
- *
- * Additional $config params include:
- * - backends : Array of backend config and multi-backend settings.
- * Each value is the config used in the constructor of a
- * FileBackendStore class, but with these additional settings:
- * - class : The name of the backend class
- * - isMultiMaster : This must be set for one backend.
- * - template: : If given a backend name, this will use
- * the config of that backend as a template.
- * Values specified here take precedence.
- * - syncChecks : Integer bitfield of internal backend sync checks to perform.
- * Possible bits include the FileBackendMultiWrite::CHECK_* constants.
- * There are constants for SIZE, TIME, and SHA1.
- * The checks are done before allowing any file operations.
- * @param $config Array
- * @throws MWException
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
- $namesUsed = array();
- // Construct backends here rather than via registration
- // to keep these backends hidden from outside the proxy.
- foreach ( $config['backends'] as $index => $config ) {
- if ( isset( $config['template'] ) ) {
- // Config is just a modified version of a registered backend's.
- // This should only be used when that config is used only by this backend.
- $config = $config + FileBackendGroup::singleton()->config( $config['template'] );
- }
- $name = $config['name'];
- if ( isset( $namesUsed[$name] ) ) { // don't break FileOp predicates
- throw new MWException( "Two or more backends defined with the name $name." );
- }
- $namesUsed[$name] = 1;
- // Alter certain sub-backend settings for sanity
- unset( $config['readOnly'] ); // use proxy backend setting
- unset( $config['fileJournal'] ); // use proxy backend journal
- $config['wikiId'] = $this->wikiId; // use the proxy backend wiki ID
- $config['lockManager'] = 'nullLockManager'; // lock under proxy backend
- if ( !empty( $config['isMultiMaster'] ) ) {
- if ( $this->masterIndex >= 0 ) {
- throw new MWException( 'More than one master backend defined.' );
- }
- $this->masterIndex = $index; // this is the "master"
- $config['fileJournal'] = $this->fileJournal; // log under proxy backend
- }
- // Create sub-backend object
- if ( !isset( $config['class'] ) ) {
- throw new MWException( 'No class given for a backend config.' );
- }
- $class = $config['class'];
- $this->backends[$index] = new $class( $config );
- }
- if ( $this->masterIndex < 0 ) { // need backends and must have a master
- throw new MWException( 'No master backend defined.' );
- }
- $this->syncChecks = isset( $config['syncChecks'] )
- ? $config['syncChecks']
- : self::CHECK_SIZE;
- }
-
- /**
- * @see FileBackend::doOperationsInternal()
- * @return Status
- */
- final protected function doOperationsInternal( array $ops, array $opts ) {
- $status = Status::newGood();
-
- $mbe = $this->backends[$this->masterIndex]; // convenience
-
- // Get the paths to lock from the master backend
- $realOps = $this->substOpBatchPaths( $ops, $mbe );
- $paths = $mbe->getPathsToLockForOpsInternal( $mbe->getOperationsInternal( $realOps ) );
- // Get the paths under the proxy backend's name
- $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
- $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
- // Try to lock those files for the scope of this function...
- if ( empty( $opts['nonLocking'] ) ) {
- // Try to lock those files for the scope of this function...
- $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
- $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
- if ( !$status->isOK() ) {
- return $status; // abort
- }
- }
- // Clear any cache entries (after locks acquired)
- $this->clearCache();
- // Do a consistency check to see if the backends agree
- $status->merge( $this->consistencyCheck( array_merge( $paths['sh'], $paths['ex'] ) ) );
- if ( !$status->isOK() ) {
- return $status; // abort
- }
- // Actually attempt the operation batch on the master backend...
- $masterStatus = $mbe->doOperations( $realOps, $opts );
- $status->merge( $masterStatus );
- // Propagate the operations to the clone backends...
- foreach ( $this->backends as $index => $backend ) {
- if ( $index !== $this->masterIndex ) { // not done already
- $realOps = $this->substOpBatchPaths( $ops, $backend );
- $status->merge( $backend->doOperations( $realOps, $opts ) );
- }
- }
- // Make 'success', 'successCount', and 'failCount' fields reflect
- // the overall operation, rather than all the batches for each backend.
- // Do this by only using success values from the master backend's batch.
- $status->success = $masterStatus->success;
- $status->successCount = $masterStatus->successCount;
- $status->failCount = $masterStatus->failCount;
-
- return $status;
- }
-
- /**
- * Check that a set of files are consistent across all internal backends
- *
- * @param $paths Array
- * @return Status
- */
- public function consistencyCheck( array $paths ) {
- $status = Status::newGood();
- if ( $this->syncChecks == 0 || count( $this->backends ) <= 1 ) {
- return $status; // skip checks
- }
-
- $mBackend = $this->backends[$this->masterIndex];
- foreach ( array_unique( $paths ) as $path ) {
- $params = array( 'src' => $path, 'latest' => true );
- $mParams = $this->substOpPaths( $params, $mBackend );
- // Stat the file on the 'master' backend
- $mStat = $mBackend->getFileStat( $mParams );
- if ( $this->syncChecks & self::CHECK_SHA1 ) {
- $mSha1 = $mBackend->getFileSha1( $mParams );
- } else {
- $mSha1 = false;
- }
- $mUsable = $mBackend->isPathUsableInternal( $mParams['src'] );
- // Check of all clone backends agree with the master...
- foreach ( $this->backends as $index => $cBackend ) {
- if ( $index === $this->masterIndex ) {
- continue; // master
- }
- $cParams = $this->substOpPaths( $params, $cBackend );
- $cStat = $cBackend->getFileStat( $cParams );
- if ( $mStat ) { // file is in master
- if ( !$cStat ) { // file should exist
- $status->fatal( 'backend-fail-synced', $path );
- continue;
- }
- if ( $this->syncChecks & self::CHECK_SIZE ) {
- if ( $cStat['size'] != $mStat['size'] ) { // wrong size
- $status->fatal( 'backend-fail-synced', $path );
- continue;
- }
- }
- if ( $this->syncChecks & self::CHECK_TIME ) {
- $mTs = wfTimestamp( TS_UNIX, $mStat['mtime'] );
- $cTs = wfTimestamp( TS_UNIX, $cStat['mtime'] );
- if ( abs( $mTs - $cTs ) > 30 ) { // outdated file somewhere
- $status->fatal( 'backend-fail-synced', $path );
- continue;
- }
- }
- if ( $this->syncChecks & self::CHECK_SHA1 ) {
- if ( $cBackend->getFileSha1( $cParams ) !== $mSha1 ) { // wrong SHA1
- $status->fatal( 'backend-fail-synced', $path );
- continue;
- }
- }
- } else { // file is not in master
- if ( $cStat ) { // file should not exist
- $status->fatal( 'backend-fail-synced', $path );
- }
- }
- if ( $mUsable !== $cBackend->isPathUsableInternal( $cParams['src'] ) ) {
- $status->fatal( 'backend-fail-synced', $path );
- }
- }
- }
-
- return $status;
- }
-
- /**
- * Substitute the backend name in storage path parameters
- * for a set of operations with that of a given internal backend.
- *
- * @param $ops Array List of file operation arrays
- * @param $backend FileBackendStore
- * @return Array
- */
- protected function substOpBatchPaths( array $ops, FileBackendStore $backend ) {
- $newOps = array(); // operations
- foreach ( $ops as $op ) {
- $newOp = $op; // operation
- foreach ( array( 'src', 'srcs', 'dst', 'dir' ) as $par ) {
- if ( isset( $newOp[$par] ) ) { // string or array
- $newOp[$par] = $this->substPaths( $newOp[$par], $backend );
- }
- }
- $newOps[] = $newOp;
- }
- return $newOps;
- }
-
- /**
- * Same as substOpBatchPaths() but for a single operation
- *
- * @param $ops array File operation array
- * @param $backend FileBackendStore
- * @return Array
- */
- protected function substOpPaths( array $ops, FileBackendStore $backend ) {
- $newOps = $this->substOpBatchPaths( array( $ops ), $backend );
- return $newOps[0];
- }
-
- /**
- * Substitute the backend of storage paths with an internal backend's name
- *
- * @param $paths Array|string List of paths or single string path
- * @param $backend FileBackendStore
- * @return Array|string
- */
- protected function substPaths( $paths, FileBackendStore $backend ) {
- return preg_replace(
- '!^mwstore://' . preg_quote( $this->name ) . '/!',
- StringUtils::escapeRegexReplacement( "mwstore://{$backend->getName()}/" ),
- $paths // string or array
- );
- }
-
- /**
- * Substitute the backend of internal storage paths with the proxy backend's name
- *
- * @param $paths Array|string List of paths or single string path
- * @return Array|string
- */
- protected function unsubstPaths( $paths ) {
- return preg_replace(
- '!^mwstore://([^/]+)!',
- StringUtils::escapeRegexReplacement( "mwstore://{$this->name}" ),
- $paths // string or array
- );
- }
-
- /**
- * @see FileBackend::doQuickOperationsInternal()
- * @return Status
- */
- protected function doQuickOperationsInternal( array $ops ) {
- $status = Status::newGood();
- // Do the operations on the master backend; setting Status fields...
- $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
- $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
- $status->merge( $masterStatus );
- // Propagate the operations to the clone backends...
- foreach ( $this->backends as $index => $backend ) {
- if ( $index !== $this->masterIndex ) { // not done already
- $realOps = $this->substOpBatchPaths( $ops, $backend );
- $status->merge( $backend->doQuickOperations( $realOps ) );
- }
- }
- // Make 'success', 'successCount', and 'failCount' fields reflect
- // the overall operation, rather than all the batches for each backend.
- // Do this by only using success values from the master backend's batch.
- $status->success = $masterStatus->success;
- $status->successCount = $masterStatus->successCount;
- $status->failCount = $masterStatus->failCount;
- return $status;
- }
-
- /**
- * @see FileBackend::doPrepare()
- * @return Status
- */
- protected function doPrepare( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doPrepare( $realParams ) );
- }
- return $status;
- }
-
- /**
- * @see FileBackend::doSecure()
- * @param $params array
- * @return Status
- */
- protected function doSecure( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doSecure( $realParams ) );
- }
- return $status;
- }
-
- /**
- * @see FileBackend::doPublish()
- * @param $params array
- * @return Status
- */
- protected function doPublish( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doPublish( $realParams ) );
- }
- return $status;
- }
-
- /**
- * @see FileBackend::doClean()
- * @param $params array
- * @return Status
- */
- protected function doClean( array $params ) {
- $status = Status::newGood();
- foreach ( $this->backends as $backend ) {
- $realParams = $this->substOpPaths( $params, $backend );
- $status->merge( $backend->doClean( $realParams ) );
- }
- return $status;
- }
-
- /**
- * @see FileBackend::concatenate()
- * @param $params array
- * @return Status
- */
- public function concatenate( array $params ) {
- // We are writing to an FS file, so we don't need to do this per-backend
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->concatenate( $realParams );
- }
-
- /**
- * @see FileBackend::fileExists()
- * @param $params array
- */
- public function fileExists( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->fileExists( $realParams );
- }
-
- /**
- * @see FileBackend::getFileTimestamp()
- * @param $params array
- * @return bool|string
- */
- public function getFileTimestamp( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileTimestamp( $realParams );
- }
-
- /**
- * @see FileBackend::getFileSize()
- * @param $params array
- * @return bool|int
- */
- public function getFileSize( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileSize( $realParams );
- }
-
- /**
- * @see FileBackend::getFileStat()
- * @param $params array
- * @return Array|bool|null
- */
- public function getFileStat( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileStat( $realParams );
- }
-
- /**
- * @see FileBackend::getFileContents()
- * @param $params array
- * @return bool|string
- */
- public function getFileContents( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileContents( $realParams );
- }
-
- /**
- * @see FileBackend::getFileSha1Base36()
- * @param $params array
- * @return bool|string
- */
- public function getFileSha1Base36( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileSha1Base36( $realParams );
- }
-
- /**
- * @see FileBackend::getFileProps()
- * @param $params array
- * @return Array
- */
- public function getFileProps( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileProps( $realParams );
- }
-
- /**
- * @see FileBackend::streamFile()
- * @param $params array
- * @return \Status
- */
- public function streamFile( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->streamFile( $realParams );
- }
-
- /**
- * @see FileBackend::getLocalReference()
- * @param $params array
- * @return FSFile|null
- */
- public function getLocalReference( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getLocalReference( $realParams );
- }
-
- /**
- * @see FileBackend::getLocalCopy()
- * @param $params array
- * @return null|TempFSFile
- */
- public function getLocalCopy( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getLocalCopy( $realParams );
- }
-
- /**
- * @see FileBackend::directoryExists()
- * @param $params array
- * @return bool|null
- */
- public function directoryExists( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->directoryExists( $realParams );
- }
-
- /**
- * @see FileBackend::getSubdirectoryList()
- * @param $params array
- * @return Array|null|Traversable
- */
- public function getDirectoryList( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getDirectoryList( $realParams );
- }
-
- /**
- * @see FileBackend::getFileList()
- * @param $params array
- * @return Array|null|\Traversable
- */
- public function getFileList( array $params ) {
- $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
- return $this->backends[$this->masterIndex]->getFileList( $realParams );
- }
-
- /**
- * @see FileBackend::clearCache()
- */
- public function clearCache( array $paths = null ) {
- foreach ( $this->backends as $backend ) {
- $realPaths = is_array( $paths ) ? $this->substPaths( $paths, $backend ) : null;
- $backend->clearCache( $realPaths );
- }
- }
-
- /**
- * @see FileBackend::getScopedLocksForOps()
- */
- public function getScopedLocksForOps( array $ops, Status $status ) {
- $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $ops );
- // Get the paths to lock from the master backend
- $paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
- // Get the paths under the proxy backend's name
- $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
- $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
- return array(
- $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
- $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
- );
- }
-}
+++ /dev/null
-<?php
-/**
- * Base class for all backends using particular storage medium.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * @brief Base class for all backends using particular storage medium.
- *
- * This class defines the methods as abstract that subclasses must implement.
- * Outside callers should *not* use functions with "Internal" in the name.
- *
- * The FileBackend operations are implemented using basic functions
- * such as storeInternal(), copyInternal(), deleteInternal() and the like.
- * This class is also responsible for path resolution and sanitization.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-abstract class FileBackendStore extends FileBackend {
- /** @var BagOStuff */
- protected $memCache;
- /** @var ProcessCacheLRU */
- protected $cheapCache; // Map of paths to small (RAM/disk) cache items
- /** @var ProcessCacheLRU */
- protected $expensiveCache; // Map of paths to large (RAM/disk) cache items
-
- /** @var Array Map of container names to sharding settings */
- protected $shardViaHashLevels = array(); // (container name => config array)
-
- protected $maxFileSize = 4294967296; // integer bytes (4GiB)
-
- /**
- * @see FileBackend::__construct()
- *
- * @param $config Array
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
- $this->memCache = new EmptyBagOStuff(); // disabled by default
- $this->cheapCache = new ProcessCacheLRU( 300 );
- $this->expensiveCache = new ProcessCacheLRU( 5 );
- }
-
- /**
- * Get the maximum allowable file size given backend
- * medium restrictions and basic performance constraints.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * @return integer Bytes
- */
- final public function maxFileSizeInternal() {
- return $this->maxFileSize;
- }
-
- /**
- * Check if a file can be created at a given storage path.
- * FS backends should check if the parent directory exists and the file is writable.
- * Backends using key/value stores should check if the container exists.
- *
- * @param $storagePath string
- * @return bool
- */
- abstract public function isPathUsableInternal( $storagePath );
-
- /**
- * Create a file in the backend with the given contents.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * $params include:
- * - content : the raw file contents
- * - dst : destination storage path
- * - overwrite : overwrite any file that exists at the destination
- * - async : Status will be returned immediately if supported.
- * If the status is OK, then its value field will be
- * set to a FileBackendStoreOpHandle object.
- *
- * @param $params Array
- * @return Status
- */
- final public function createInternal( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
- $status = Status::newFatal( 'backend-fail-maxsize',
- $params['dst'], $this->maxFileSizeInternal() );
- } else {
- $status = $this->doCreateInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::createInternal()
- */
- abstract protected function doCreateInternal( array $params );
-
- /**
- * Store a file into the backend from a file on disk.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * $params include:
- * - src : source path on disk
- * - dst : destination storage path
- * - overwrite : overwrite any file that exists at the destination
- * - async : Status will be returned immediately if supported.
- * If the status is OK, then its value field will be
- * set to a FileBackendStoreOpHandle object.
- *
- * @param $params Array
- * @return Status
- */
- final public function storeInternal( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
- $status = Status::newFatal( 'backend-fail-maxsize',
- $params['dst'], $this->maxFileSizeInternal() );
- } else {
- $status = $this->doStoreInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::storeInternal()
- */
- abstract protected function doStoreInternal( array $params );
-
- /**
- * Copy a file from one storage path to another in the backend.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * $params include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : overwrite any file that exists at the destination
- * - async : Status will be returned immediately if supported.
- * If the status is OK, then its value field will be
- * set to a FileBackendStoreOpHandle object.
- *
- * @param $params Array
- * @return Status
- */
- final public function copyInternal( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = $this->doCopyInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::copyInternal()
- */
- abstract protected function doCopyInternal( array $params );
-
- /**
- * Delete a file at the storage path.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * $params include:
- * - src : source storage path
- * - ignoreMissingSource : do nothing if the source file does not exist
- * - async : Status will be returned immediately if supported.
- * If the status is OK, then its value field will be
- * set to a FileBackendStoreOpHandle object.
- *
- * @param $params Array
- * @return Status
- */
- final public function deleteInternal( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = $this->doDeleteInternal( $params );
- $this->clearCache( array( $params['src'] ) );
- $this->deleteFileCache( $params['src'] ); // persistent cache
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::deleteInternal()
- */
- abstract protected function doDeleteInternal( array $params );
-
- /**
- * Move a file from one storage path to another in the backend.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * $params include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : overwrite any file that exists at the destination
- * - async : Status will be returned immediately if supported.
- * If the status is OK, then its value field will be
- * set to a FileBackendStoreOpHandle object.
- *
- * @param $params Array
- * @return Status
- */
- final public function moveInternal( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = $this->doMoveInternal( $params );
- $this->clearCache( array( $params['src'], $params['dst'] ) );
- $this->deleteFileCache( $params['src'] ); // persistent cache
- $this->deleteFileCache( $params['dst'] ); // persistent cache
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::moveInternal()
- * @return Status
- */
- protected function doMoveInternal( array $params ) {
- unset( $params['async'] ); // two steps, won't work here :)
- // Copy source to dest
- $status = $this->copyInternal( $params );
- if ( $status->isOK() ) {
- // Delete source (only fails due to races or medium going down)
- $status->merge( $this->deleteInternal( array( 'src' => $params['src'] ) ) );
- $status->setResult( true, $status->value ); // ignore delete() errors
- }
- return $status;
- }
-
- /**
- * No-op file operation that does nothing.
- * Do not call this function from places outside FileBackend and FileOp.
- *
- * @param $params Array
- * @return Status
- */
- final public function nullInternal( array $params ) {
- return Status::newGood();
- }
-
- /**
- * @see FileBackend::concatenate()
- * @return Status
- */
- final public function concatenate( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- // Try to lock the source files for the scope of this function
- $scopeLockS = $this->getScopedFileLocks( $params['srcs'], LockManager::LOCK_UW, $status );
- if ( $status->isOK() ) {
- // Actually do the concatenation
- $status->merge( $this->doConcatenate( $params ) );
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::concatenate()
- * @return Status
- */
- protected function doConcatenate( array $params ) {
- $status = Status::newGood();
- $tmpPath = $params['dst']; // convenience
-
- // Check that the specified temp file is valid...
- wfSuppressWarnings();
- $ok = ( is_file( $tmpPath ) && !filesize( $tmpPath ) );
- wfRestoreWarnings();
- if ( !$ok ) { // not present or not empty
- $status->fatal( 'backend-fail-opentemp', $tmpPath );
- return $status;
- }
-
- // Build up the temp file using the source chunks (in order)...
- $tmpHandle = fopen( $tmpPath, 'ab' );
- if ( $tmpHandle === false ) {
- $status->fatal( 'backend-fail-opentemp', $tmpPath );
- return $status;
- }
- foreach ( $params['srcs'] as $virtualSource ) {
- // Get a local FS version of the chunk
- $tmpFile = $this->getLocalReference( array( 'src' => $virtualSource ) );
- if ( !$tmpFile ) {
- $status->fatal( 'backend-fail-read', $virtualSource );
- return $status;
- }
- // Get a handle to the local FS version
- $sourceHandle = fopen( $tmpFile->getPath(), 'r' );
- if ( $sourceHandle === false ) {
- fclose( $tmpHandle );
- $status->fatal( 'backend-fail-read', $virtualSource );
- return $status;
- }
- // Append chunk to file (pass chunk size to avoid magic quotes)
- if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
- fclose( $sourceHandle );
- fclose( $tmpHandle );
- $status->fatal( 'backend-fail-writetemp', $tmpPath );
- return $status;
- }
- fclose( $sourceHandle );
- }
- if ( !fclose( $tmpHandle ) ) {
- $status->fatal( 'backend-fail-closetemp', $tmpPath );
- return $status;
- }
-
- clearstatcache(); // temp file changed
-
- return $status;
- }
-
- /**
- * @see FileBackend::doPrepare()
- * @return Status
- */
- final protected function doPrepare( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
-
- $status = Status::newGood();
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // invalid storage path
- }
-
- if ( $shard !== null ) { // confined to a single container/shard
- $status->merge( $this->doPrepareInternal( $fullCont, $dir, $params ) );
- } else { // directory is on several shards
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
- $status->merge( $this->doPrepareInternal( "{$fullCont}{$suffix}", $dir, $params ) );
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::doPrepare()
- * @return Status
- */
- protected function doPrepareInternal( $container, $dir, array $params ) {
- return Status::newGood();
- }
-
- /**
- * @see FileBackend::doSecure()
- * @return Status
- */
- final protected function doSecure( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // invalid storage path
- }
-
- if ( $shard !== null ) { // confined to a single container/shard
- $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
- } else { // directory is on several shards
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
- $status->merge( $this->doSecureInternal( "{$fullCont}{$suffix}", $dir, $params ) );
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::doSecure()
- * @return Status
- */
- protected function doSecureInternal( $container, $dir, array $params ) {
- return Status::newGood();
- }
-
- /**
- * @see FileBackend::doPublish()
- * @return Status
- */
- final protected function doPublish( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // invalid storage path
- }
-
- if ( $shard !== null ) { // confined to a single container/shard
- $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
- } else { // directory is on several shards
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
- $status->merge( $this->doPublishInternal( "{$fullCont}{$suffix}", $dir, $params ) );
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::doPublish()
- * @return Status
- */
- protected function doPublishInternal( $container, $dir, array $params ) {
- return Status::newGood();
- }
-
- /**
- * @see FileBackend::doClean()
- * @return Status
- */
- final protected function doClean( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- // Recursive: first delete all empty subdirs recursively
- if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
- $subDirsRel = $this->getTopDirectoryList( array( 'dir' => $params['dir'] ) );
- if ( $subDirsRel !== null ) { // no errors
- foreach ( $subDirsRel as $subDirRel ) {
- $subDir = $params['dir'] . "/{$subDirRel}"; // full path
- $status->merge( $this->doClean( array( 'dir' => $subDir ) + $params ) );
- }
- }
- }
-
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // invalid storage path
- }
-
- // Attempt to lock this directory...
- $filesLockEx = array( $params['dir'] );
- $scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
- if ( !$status->isOK() ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // abort
- }
-
- if ( $shard !== null ) { // confined to a single container/shard
- $status->merge( $this->doCleanInternal( $fullCont, $dir, $params ) );
- $this->deleteContainerCache( $fullCont ); // purge cache
- } else { // directory is on several shards
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
- $status->merge( $this->doCleanInternal( "{$fullCont}{$suffix}", $dir, $params ) );
- $this->deleteContainerCache( "{$fullCont}{$suffix}" ); // purge cache
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::doClean()
- * @return Status
- */
- protected function doCleanInternal( $container, $dir, array $params ) {
- return Status::newGood();
- }
-
- /**
- * @see FileBackend::fileExists()
- * @return bool|null
- */
- final public function fileExists( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $stat = $this->getFileStat( $params );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return ( $stat === null ) ? null : (bool)$stat; // null => failure
- }
-
- /**
- * @see FileBackend::getFileTimestamp()
- * @return bool
- */
- final public function getFileTimestamp( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $stat = $this->getFileStat( $params );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat ? $stat['mtime'] : false;
- }
-
- /**
- * @see FileBackend::getFileSize()
- * @return bool
- */
- final public function getFileSize( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $stat = $this->getFileStat( $params );
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat ? $stat['size'] : false;
- }
-
- /**
- * @see FileBackend::getFileStat()
- * @return bool
- */
- final public function getFileStat( array $params ) {
- $path = self::normalizeStoragePath( $params['src'] );
- if ( $path === null ) {
- return false; // invalid storage path
- }
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $latest = !empty( $params['latest'] ); // use latest data?
- if ( !$this->cheapCache->has( $path, 'stat' ) ) {
- $this->primeFileCache( array( $path ) ); // check persistent cache
- }
- if ( $this->cheapCache->has( $path, 'stat' ) ) {
- $stat = $this->cheapCache->get( $path, 'stat' );
- // If we want the latest data, check that this cached
- // value was in fact fetched with the latest available data.
- if ( !$latest || $stat['latest'] ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat;
- }
- }
- wfProfileIn( __METHOD__ . '-miss' );
- wfProfileIn( __METHOD__ . '-miss-' . $this->name );
- $stat = $this->doGetFileStat( $params );
- wfProfileOut( __METHOD__ . '-miss-' . $this->name );
- wfProfileOut( __METHOD__ . '-miss' );
- if ( is_array( $stat ) ) { // don't cache negatives
- $stat['latest'] = $latest;
- $this->cheapCache->set( $path, 'stat', $stat );
- $this->setFileCache( $path, $stat ); // update persistent cache
- if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
- $this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
- }
- } else {
- wfDebug( __METHOD__ . ": File $path does not exist.\n" );
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat;
- }
-
- /**
- * @see FileBackendStore::getFileStat()
- */
- abstract protected function doGetFileStat( array $params );
-
- /**
- * @see FileBackend::getFileContents()
- * @return bool|string
- */
- public function getFileContents( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $tmpFile = $this->getLocalReference( $params );
- if ( !$tmpFile ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return false;
- }
- wfSuppressWarnings();
- $data = file_get_contents( $tmpFile->getPath() );
- wfRestoreWarnings();
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $data;
- }
-
- /**
- * @see FileBackend::getFileSha1Base36()
- * @return bool|string
- */
- final public function getFileSha1Base36( array $params ) {
- $path = self::normalizeStoragePath( $params['src'] );
- if ( $path === null ) {
- return false; // invalid storage path
- }
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $latest = !empty( $params['latest'] ); // use latest data?
- if ( $this->cheapCache->has( $path, 'sha1' ) ) {
- $stat = $this->cheapCache->get( $path, 'sha1' );
- // If we want the latest data, check that this cached
- // value was in fact fetched with the latest available data.
- if ( !$latest || $stat['latest'] ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat['hash'];
- }
- }
- wfProfileIn( __METHOD__ . '-miss' );
- wfProfileIn( __METHOD__ . '-miss-' . $this->name );
- $hash = $this->doGetFileSha1Base36( $params );
- wfProfileOut( __METHOD__ . '-miss-' . $this->name );
- wfProfileOut( __METHOD__ . '-miss' );
- if ( $hash ) { // don't cache negatives
- $this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $hash, 'latest' => $latest ) );
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $hash;
- }
-
- /**
- * @see FileBackendStore::getFileSha1Base36()
- * @return bool|string
- */
- protected function doGetFileSha1Base36( array $params ) {
- $fsFile = $this->getLocalReference( $params );
- if ( !$fsFile ) {
- return false;
- } else {
- return $fsFile->getSha1Base36();
- }
- }
-
- /**
- * @see FileBackend::getFileProps()
- * @return Array
- */
- final public function getFileProps( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $fsFile = $this->getLocalReference( $params );
- $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $props;
- }
-
- /**
- * @see FileBackend::getLocalReference()
- * @return TempFSFile|null
- */
- public function getLocalReference( array $params ) {
- $path = self::normalizeStoragePath( $params['src'] );
- if ( $path === null ) {
- return null; // invalid storage path
- }
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $latest = !empty( $params['latest'] ); // use latest data?
- if ( $this->expensiveCache->has( $path, 'localRef' ) ) {
- $val = $this->expensiveCache->get( $path, 'localRef' );
- // If we want the latest data, check that this cached
- // value was in fact fetched with the latest available data.
- if ( !$latest || $val['latest'] ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $val['object'];
- }
- }
- $tmpFile = $this->getLocalCopy( $params );
- if ( $tmpFile ) { // don't cache negatives
- $this->expensiveCache->set( $path, 'localRef',
- array( 'object' => $tmpFile, 'latest' => $latest ) );
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $tmpFile;
- }
-
- /**
- * @see FileBackend::streamFile()
- * @return Status
- */
- final public function streamFile( array $params ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- $info = $this->getFileStat( $params );
- if ( !$info ) { // let StreamFile handle the 404
- $status->fatal( 'backend-fail-notexists', $params['src'] );
- }
-
- // Set output buffer and HTTP headers for stream
- $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : array();
- $res = StreamFile::prepareForStream( $params['src'], $info, $extraHeaders );
- if ( $res == StreamFile::NOT_MODIFIED ) {
- // do nothing; client cache is up to date
- } elseif ( $res == StreamFile::READY_STREAM ) {
- wfProfileIn( __METHOD__ . '-send' );
- wfProfileIn( __METHOD__ . '-send-' . $this->name );
- $status = $this->doStreamFile( $params );
- wfProfileOut( __METHOD__ . '-send-' . $this->name );
- wfProfileOut( __METHOD__ . '-send' );
- } else {
- $status->fatal( 'backend-fail-stream', $params['src'] );
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackendStore::streamFile()
- * @return Status
- */
- protected function doStreamFile( array $params ) {
- $status = Status::newGood();
-
- $fsFile = $this->getLocalReference( $params );
- if ( !$fsFile ) {
- $status->fatal( 'backend-fail-stream', $params['src'] );
- } elseif ( !readfile( $fsFile->getPath() ) ) {
- $status->fatal( 'backend-fail-stream', $params['src'] );
- }
-
- return $status;
- }
-
- /**
- * @see FileBackend::directoryExists()
- * @return bool|null
- */
- final public function directoryExists( array $params ) {
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) {
- return false; // invalid storage path
- }
- if ( $shard !== null ) { // confined to a single container/shard
- return $this->doDirectoryExists( $fullCont, $dir, $params );
- } else { // directory is on several shards
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- $res = false; // response
- foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
- $exists = $this->doDirectoryExists( "{$fullCont}{$suffix}", $dir, $params );
- if ( $exists ) {
- $res = true;
- break; // found one!
- } elseif ( $exists === null ) { // error?
- $res = null; // if we don't find anything, it is indeterminate
- }
- }
- return $res;
- }
- }
-
- /**
- * @see FileBackendStore::directoryExists()
- *
- * @param $container string Resolved container name
- * @param $dir string Resolved path relative to container
- * @param $params Array
- * @return bool|null
- */
- abstract protected function doDirectoryExists( $container, $dir, array $params );
-
- /**
- * @see FileBackend::getDirectoryList()
- * @return Traversable|Array|null Returns null on failure
- */
- final public function getDirectoryList( array $params ) {
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) { // invalid storage path
- return null;
- }
- if ( $shard !== null ) {
- // File listing is confined to a single container/shard
- return $this->getDirectoryListInternal( $fullCont, $dir, $params );
- } else {
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- // File listing spans multiple containers/shards
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- return new FileBackendStoreShardDirIterator( $this,
- $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
- }
- }
-
- /**
- * Do not call this function from places outside FileBackend
- *
- * @see FileBackendStore::getDirectoryList()
- *
- * @param $container string Resolved container name
- * @param $dir string Resolved path relative to container
- * @param $params Array
- * @return Traversable|Array|null Returns null on failure
- */
- abstract public function getDirectoryListInternal( $container, $dir, array $params );
-
- /**
- * @see FileBackend::getFileList()
- * @return Traversable|Array|null Returns null on failure
- */
- final public function getFileList( array $params ) {
- list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
- if ( $dir === null ) { // invalid storage path
- return null;
- }
- if ( $shard !== null ) {
- // File listing is confined to a single container/shard
- return $this->getFileListInternal( $fullCont, $dir, $params );
- } else {
- wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
- // File listing spans multiple containers/shards
- list( $b, $shortCont, $r ) = self::splitStoragePath( $params['dir'] );
- return new FileBackendStoreShardFileIterator( $this,
- $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
- }
- }
-
- /**
- * Do not call this function from places outside FileBackend
- *
- * @see FileBackendStore::getFileList()
- *
- * @param $container string Resolved container name
- * @param $dir string Resolved path relative to container
- * @param $params Array
- * @return Traversable|Array|null Returns null on failure
- */
- abstract public function getFileListInternal( $container, $dir, array $params );
-
- /**
- * Return a list of FileOp objects from a list of operations.
- * Do not call this function from places outside FileBackend.
- *
- * The result must have the same number of items as the input.
- * An exception is thrown if an unsupported operation is requested.
- *
- * @param $ops Array Same format as doOperations()
- * @return Array List of FileOp objects
- * @throws MWException
- */
- final public function getOperationsInternal( array $ops ) {
- $supportedOps = array(
- 'store' => 'StoreFileOp',
- 'copy' => 'CopyFileOp',
- 'move' => 'MoveFileOp',
- 'delete' => 'DeleteFileOp',
- 'create' => 'CreateFileOp',
- 'null' => 'NullFileOp'
- );
-
- $performOps = array(); // array of FileOp objects
- // Build up ordered array of FileOps...
- foreach ( $ops as $operation ) {
- $opName = $operation['op'];
- if ( isset( $supportedOps[$opName] ) ) {
- $class = $supportedOps[$opName];
- // Get params for this operation
- $params = $operation;
- // Append the FileOp class
- $performOps[] = new $class( $this, $params );
- } else {
- throw new MWException( "Operation '$opName' is not supported." );
- }
- }
-
- return $performOps;
- }
-
- /**
- * Get a list of storage paths to lock for a list of operations
- * Returns an array with 'sh' (shared) and 'ex' (exclusive) keys,
- * each corresponding to a list of storage paths to be locked.
- *
- * @param $performOps Array List of FileOp objects
- * @return Array ('sh' => list of paths, 'ex' => list of paths)
- */
- final public function getPathsToLockForOpsInternal( array $performOps ) {
- // Build up a list of files to lock...
- $paths = array( 'sh' => array(), 'ex' => array() );
- foreach ( $performOps as $fileOp ) {
- $paths['sh'] = array_merge( $paths['sh'], $fileOp->storagePathsRead() );
- $paths['ex'] = array_merge( $paths['ex'], $fileOp->storagePathsChanged() );
- }
- // Optimization: if doing an EX lock anyway, don't also set an SH one
- $paths['sh'] = array_diff( $paths['sh'], $paths['ex'] );
- // Get a shared lock on the parent directory of each path changed
- $paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
-
- return $paths;
- }
-
- /**
- * @see FileBackend::getScopedLocksForOps()
- * @return Array
- */
- public function getScopedLocksForOps( array $ops, Status $status ) {
- $paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
- return array(
- $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
- $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
- );
- }
-
- /**
- * @see FileBackend::doOperationsInternal()
- * @return Status
- */
- final protected function doOperationsInternal( array $ops, array $opts ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- // Build up a list of FileOps...
- $performOps = $this->getOperationsInternal( $ops );
-
- // Acquire any locks as needed...
- if ( empty( $opts['nonLocking'] ) ) {
- // Build up a list of files to lock...
- $paths = $this->getPathsToLockForOpsInternal( $performOps );
- // Try to lock those files for the scope of this function...
- $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
- $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
- if ( !$status->isOK() ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status; // abort
- }
- }
-
- // Clear any file cache entries (after locks acquired)
- $this->clearCache();
-
- // Load from the persistent file and container caches
- $this->primeFileCache( $performOps );
- $this->primeContainerCache( $performOps );
-
- // Actually attempt the operation batch...
- $subStatus = FileOpBatch::attempt( $performOps, $opts, $this->fileJournal );
-
- // Merge errors into status fields
- $status->merge( $subStatus );
- $status->success = $subStatus->success; // not done in merge()
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @see FileBackend::doQuickOperationsInternal()
- * @return Status
- * @throws MWException
- */
- final protected function doQuickOperationsInternal( array $ops ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- $status = Status::newGood();
-
- $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'null' );
- $async = ( $this->parallelize === 'implicit' );
- $maxConcurrency = $this->concurrency; // throttle
-
- $statuses = array(); // array of (index => Status)
- $fileOpHandles = array(); // list of (index => handle) arrays
- $curFileOpHandles = array(); // current handle batch
- // Perform the sync-only ops and build up op handles for the async ops...
- foreach ( $ops as $index => $params ) {
- if ( !in_array( $params['op'], $supportedOps ) ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- throw new MWException( "Operation '{$params['op']}' is not supported." );
- }
- $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
- $subStatus = $this->$method( array( 'async' => $async ) + $params );
- if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
- if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
- $fileOpHandles[] = $curFileOpHandles; // push this batch
- $curFileOpHandles = array();
- }
- $curFileOpHandles[$index] = $subStatus->value; // keep index
- } else { // error or completed
- $statuses[$index] = $subStatus; // keep index
- }
- }
- if ( count( $curFileOpHandles ) ) {
- $fileOpHandles[] = $curFileOpHandles; // last batch
- }
- // Do all the async ops that can be done concurrently...
- foreach ( $fileOpHandles as $fileHandleBatch ) {
- $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
- }
- // Marshall and merge all the responses...
- foreach ( $statuses as $index => $subStatus ) {
- $status->merge( $subStatus );
- if ( $subStatus->isOK() ) {
- $status->success[$index] = true;
- ++$status->successCount;
- } else {
- $status->success[$index] = false;
- ++$status->failCount;
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * Execute a list of FileBackendStoreOpHandle handles in parallel.
- * The resulting Status object fields will correspond
- * to the order in which the handles where given.
- *
- * @param $handles Array List of FileBackendStoreOpHandle objects
- * @return Array Map of Status objects
- * @throws MWException
- */
- final public function executeOpHandlesInternal( array $fileOpHandles ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
- foreach ( $fileOpHandles as $fileOpHandle ) {
- if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
- throw new MWException( "Given a non-FileBackendStoreOpHandle object." );
- } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
- throw new MWException( "Given a FileBackendStoreOpHandle for the wrong backend." );
- }
- }
- $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
- foreach ( $fileOpHandles as $fileOpHandle ) {
- $fileOpHandle->closeResources();
- }
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $res;
- }
-
- /**
- * @see FileBackendStore::executeOpHandlesInternal()
- * @return Array List of corresponding Status objects
- */
- protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
- foreach ( $fileOpHandles as $fileOpHandle ) { // OK if empty
- throw new MWException( "This backend supports no asynchronous operations." );
- }
- return array();
- }
-
- /**
- * @see FileBackend::clearCache()
- */
- final public function clearCache( array $paths = null ) {
- if ( is_array( $paths ) ) {
- $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
- $paths = array_filter( $paths, 'strlen' ); // remove nulls
- }
- if ( $paths === null ) {
- $this->cheapCache->clear();
- $this->expensiveCache->clear();
- } else {
- foreach ( $paths as $path ) {
- $this->cheapCache->clear( $path );
- $this->expensiveCache->clear( $path );
- }
- }
- $this->doClearCache( $paths );
- }
-
- /**
- * Clears any additional stat caches for storage paths
- *
- * @see FileBackend::clearCache()
- *
- * @param $paths Array Storage paths (optional)
- * @return void
- */
- protected function doClearCache( array $paths = null ) {}
-
- /**
- * Is this a key/value store where directories are just virtual?
- * Virtual directories exists in so much as files exists that are
- * prefixed with the directory path followed by a forward slash.
- *
- * @return bool
- */
- abstract protected function directoriesAreVirtual();
-
- /**
- * Check if a container name is valid.
- * This checks for for length and illegal characters.
- *
- * @param $container string
- * @return bool
- */
- final protected static function isValidContainerName( $container ) {
- // This accounts for Swift and S3 restrictions while leaving room
- // for things like '.xxx' (hex shard chars) or '.seg' (segments).
- // This disallows directory separators or traversal characters.
- // Note that matching strings URL encode to the same string;
- // in Swift, the length restriction is *after* URL encoding.
- return preg_match( '/^[a-z0-9][a-z0-9-_]{0,199}$/i', $container );
- }
-
- /**
- * Splits a storage path into an internal container name,
- * an internal relative file name, and a container shard suffix.
- * Any shard suffix is already appended to the internal container name.
- * This also checks that the storage path is valid and within this backend.
- *
- * If the container is sharded but a suffix could not be determined,
- * this means that the path can only refer to a directory and can only
- * be scanned by looking in all the container shards.
- *
- * @param $storagePath string
- * @return Array (container, path, container suffix) or (null, null, null) if invalid
- */
- final protected function resolveStoragePath( $storagePath ) {
- list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
- if ( $backend === $this->name ) { // must be for this backend
- $relPath = self::normalizeContainerPath( $relPath );
- if ( $relPath !== null ) {
- // Get shard for the normalized path if this container is sharded
- $cShard = $this->getContainerShard( $container, $relPath );
- // Validate and sanitize the relative path (backend-specific)
- $relPath = $this->resolveContainerPath( $container, $relPath );
- if ( $relPath !== null ) {
- // Prepend any wiki ID prefix to the container name
- $container = $this->fullContainerName( $container );
- if ( self::isValidContainerName( $container ) ) {
- // Validate and sanitize the container name (backend-specific)
- $container = $this->resolveContainerName( "{$container}{$cShard}" );
- if ( $container !== null ) {
- return array( $container, $relPath, $cShard );
- }
- }
- }
- }
- }
- return array( null, null, null );
- }
-
- /**
- * Like resolveStoragePath() except null values are returned if
- * the container is sharded and the shard could not be determined.
- *
- * @see FileBackendStore::resolveStoragePath()
- *
- * @param $storagePath string
- * @return Array (container, path) or (null, null) if invalid
- */
- final protected function resolveStoragePathReal( $storagePath ) {
- list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
- if ( $cShard !== null ) {
- return array( $container, $relPath );
- }
- return array( null, null );
- }
-
- /**
- * Get the container name shard suffix for a given path.
- * Any empty suffix means the container is not sharded.
- *
- * @param $container string Container name
- * @param $relPath string Storage path relative to the container
- * @return string|null Returns null if shard could not be determined
- */
- final protected function getContainerShard( $container, $relPath ) {
- list( $levels, $base, $repeat ) = $this->getContainerHashLevels( $container );
- if ( $levels == 1 || $levels == 2 ) {
- // Hash characters are either base 16 or 36
- $char = ( $base == 36 ) ? '[0-9a-z]' : '[0-9a-f]';
- // Get a regex that represents the shard portion of paths.
- // The concatenation of the captures gives us the shard.
- if ( $levels === 1 ) { // 16 or 36 shards per container
- $hashDirRegex = '(' . $char . ')';
- } else { // 256 or 1296 shards per container
- if ( $repeat ) { // verbose hash dir format (e.g. "a/ab/abc")
- $hashDirRegex = $char . '/(' . $char . '{2})';
- } else { // short hash dir format (e.g. "a/b/c")
- $hashDirRegex = '(' . $char . ')/(' . $char . ')';
- }
- }
- // Allow certain directories to be above the hash dirs so as
- // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
- // They must be 2+ chars to avoid any hash directory ambiguity.
- $m = array();
- if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
- return '.' . implode( '', array_slice( $m, 1 ) );
- }
- return null; // failed to match
- }
- return ''; // no sharding
- }
-
- /**
- * Check if a storage path maps to a single shard.
- * Container dirs like "a", where the container shards on "x/xy",
- * can reside on several shards. Such paths are tricky to handle.
- *
- * @param $storagePath string Storage path
- * @return bool
- */
- final public function isSingleShardPathInternal( $storagePath ) {
- list( $c, $r, $shard ) = $this->resolveStoragePath( $storagePath );
- return ( $shard !== null );
- }
-
- /**
- * Get the sharding config for a container.
- * If greater than 0, then all file storage paths within
- * the container are required to be hashed accordingly.
- *
- * @param $container string
- * @return Array (integer levels, integer base, repeat flag) or (0, 0, false)
- */
- final protected function getContainerHashLevels( $container ) {
- if ( isset( $this->shardViaHashLevels[$container] ) ) {
- $config = $this->shardViaHashLevels[$container];
- $hashLevels = (int)$config['levels'];
- if ( $hashLevels == 1 || $hashLevels == 2 ) {
- $hashBase = (int)$config['base'];
- if ( $hashBase == 16 || $hashBase == 36 ) {
- return array( $hashLevels, $hashBase, $config['repeat'] );
- }
- }
- }
- return array( 0, 0, false ); // no sharding
- }
-
- /**
- * Get a list of full container shard suffixes for a container
- *
- * @param $container string
- * @return Array
- */
- final protected function getContainerSuffixes( $container ) {
- $shards = array();
- list( $digits, $base ) = $this->getContainerHashLevels( $container );
- if ( $digits > 0 ) {
- $numShards = pow( $base, $digits );
- for ( $index = 0; $index < $numShards; $index++ ) {
- $shards[] = '.' . wfBaseConvert( $index, 10, $base, $digits );
- }
- }
- return $shards;
- }
-
- /**
- * Get the full container name, including the wiki ID prefix
- *
- * @param $container string
- * @return string
- */
- final protected function fullContainerName( $container ) {
- if ( $this->wikiId != '' ) {
- return "{$this->wikiId}-$container";
- } else {
- return $container;
- }
- }
-
- /**
- * Resolve a container name, checking if it's allowed by the backend.
- * This is intended for internal use, such as encoding illegal chars.
- * Subclasses can override this to be more restrictive.
- *
- * @param $container string
- * @return string|null
- */
- protected function resolveContainerName( $container ) {
- return $container;
- }
-
- /**
- * Resolve a relative storage path, checking if it's allowed by the backend.
- * This is intended for internal use, such as encoding illegal chars or perhaps
- * getting absolute paths (e.g. FS based backends). Note that the relative path
- * may be the empty string (e.g. the path is simply to the container).
- *
- * @param $container string Container name
- * @param $relStoragePath string Storage path relative to the container
- * @return string|null Path or null if not valid
- */
- protected function resolveContainerPath( $container, $relStoragePath ) {
- return $relStoragePath;
- }
-
- /**
- * Get the cache key for a container
- *
- * @param $container string Resolved container name
- * @return string
- */
- private function containerCacheKey( $container ) {
- return wfMemcKey( 'backend', $this->getName(), 'container', $container );
- }
-
- /**
- * Set the cached info for a container
- *
- * @param $container string Resolved container name
- * @param $val mixed Information to cache
- */
- final protected function setContainerCache( $container, $val ) {
- $this->memCache->add( $this->containerCacheKey( $container ), $val, 14*86400 );
- }
-
- /**
- * Delete the cached info for a container.
- * The cache key is salted for a while to prevent race conditions.
- *
- * @param $container string Resolved container name
- */
- final protected function deleteContainerCache( $container ) {
- if ( !$this->memCache->set( $this->containerCacheKey( $container ), 'PURGED', 300 ) ) {
- trigger_error( "Unable to delete stat cache for container $container." );
- }
- }
-
- /**
- * Do a batch lookup from cache for container stats for all containers
- * used in a list of container names, storage paths, or FileOp objects.
- *
- * @param $items Array
- * @return void
- */
- final protected function primeContainerCache( array $items ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
-
- $paths = array(); // list of storage paths
- $contNames = array(); // (cache key => resolved container name)
- // Get all the paths/containers from the items...
- foreach ( $items as $item ) {
- if ( $item instanceof FileOp ) {
- $paths = array_merge( $paths, $item->storagePathsRead() );
- $paths = array_merge( $paths, $item->storagePathsChanged() );
- } elseif ( self::isStoragePath( $item ) ) {
- $paths[] = $item;
- } elseif ( is_string( $item ) ) { // full container name
- $contNames[$this->containerCacheKey( $item )] = $item;
- }
- }
- // Get all the corresponding cache keys for paths...
- foreach ( $paths as $path ) {
- list( $fullCont, $r, $s ) = $this->resolveStoragePath( $path );
- if ( $fullCont !== null ) { // valid path for this backend
- $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
- }
- }
-
- $contInfo = array(); // (resolved container name => cache value)
- // Get all cache entries for these container cache keys...
- $values = $this->memCache->getMulti( array_keys( $contNames ) );
- foreach ( $values as $cacheKey => $val ) {
- $contInfo[$contNames[$cacheKey]] = $val;
- }
-
- // Populate the container process cache for the backend...
- $this->doPrimeContainerCache( array_filter( $contInfo, 'is_array' ) );
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- }
-
- /**
- * Fill the backend-specific process cache given an array of
- * resolved container names and their corresponding cached info.
- * Only containers that actually exist should appear in the map.
- *
- * @param $containerInfo Array Map of resolved container names to cached info
- * @return void
- */
- protected function doPrimeContainerCache( array $containerInfo ) {}
-
- /**
- * Get the cache key for a file path
- *
- * @param $path string Storage path
- * @return string
- */
- private function fileCacheKey( $path ) {
- return wfMemcKey( 'backend', $this->getName(), 'file', sha1( $path ) );
- }
-
- /**
- * Set the cached stat info for a file path
- *
- * @param $path string Storage path
- * @param $val mixed Information to cache
- */
- final protected function setFileCache( $path, $val ) {
- $this->memCache->add( $this->fileCacheKey( $path ), $val, 7*86400 );
- }
-
- /**
- * Delete the cached stat info for a file path.
- * The cache key is salted for a while to prevent race conditions.
- *
- * @param $path string Storage path
- */
- final protected function deleteFileCache( $path ) {
- if ( !$this->memCache->set( $this->fileCacheKey( $path ), 'PURGED', 300 ) ) {
- trigger_error( "Unable to delete stat cache for file $path." );
- }
- }
-
- /**
- * Do a batch lookup from cache for file stats for all paths
- * used in a list of storage paths or FileOp objects.
- *
- * @param $items Array List of storage paths or FileOps
- * @return void
- */
- final protected function primeFileCache( array $items ) {
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-' . $this->name );
-
- $paths = array(); // list of storage paths
- $pathNames = array(); // (cache key => storage path)
- // Get all the paths/containers from the items...
- foreach ( $items as $item ) {
- if ( $item instanceof FileOp ) {
- $paths = array_merge( $paths, $item->storagePathsRead() );
- $paths = array_merge( $paths, $item->storagePathsChanged() );
- } elseif ( self::isStoragePath( $item ) ) {
- $paths[] = $item;
- }
- }
- // Get all the corresponding cache keys for paths...
- foreach ( $paths as $path ) {
- list( $cont, $rel, $s ) = $this->resolveStoragePath( $path );
- if ( $rel !== null ) { // valid path for this backend
- $pathNames[$this->fileCacheKey( $path )] = $path;
- }
- }
- // Get all cache entries for these container cache keys...
- $values = $this->memCache->getMulti( array_keys( $pathNames ) );
- foreach ( $values as $cacheKey => $val ) {
- if ( is_array( $val ) ) {
- $path = $pathNames[$cacheKey];
- $this->cheapCache->set( $path, 'stat', $val );
- if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
- $this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
- }
- }
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- }
-}
-
-/**
- * FileBackendStore helper class for performing asynchronous file operations.
- *
- * For example, calling FileBackendStore::createInternal() with the "async"
- * param flag may result in a Status that contains this object as a value.
- * This class is largely backend-specific and is mostly just "magic" to be
- * passed to FileBackendStore::executeOpHandlesInternal().
- */
-abstract class FileBackendStoreOpHandle {
- /** @var Array */
- public $params = array(); // params to caller functions
- /** @var FileBackendStore */
- public $backend;
- /** @var Array */
- public $resourcesToClose = array();
-
- public $call; // string; name that identifies the function called
-
- /**
- * Close all open file handles
- *
- * @return void
- */
- public function closeResources() {
- array_map( 'fclose', $this->resourcesToClose );
- }
-}
-
-/**
- * FileBackendStore helper function to handle listings that span container shards.
- * Do not use this class from places outside of FileBackendStore.
- *
- * @ingroup FileBackend
- */
-abstract class FileBackendStoreShardListIterator implements Iterator {
- /** @var FileBackendStore */
- protected $backend;
- /** @var Array */
- protected $params;
- /** @var Array */
- protected $shardSuffixes;
- protected $container; // string; full container name
- protected $directory; // string; resolved relative path
-
- /** @var Traversable */
- protected $iter;
- protected $curShard = 0; // integer
- protected $pos = 0; // integer
-
- /** @var Array */
- protected $multiShardPaths = array(); // (rel path => 1)
-
- /**
- * @param $backend FileBackendStore
- * @param $container string Full storage container name
- * @param $dir string Storage directory relative to container
- * @param $suffixes Array List of container shard suffixes
- * @param $params Array
- */
- public function __construct(
- FileBackendStore $backend, $container, $dir, array $suffixes, array $params
- ) {
- $this->backend = $backend;
- $this->container = $container;
- $this->directory = $dir;
- $this->shardSuffixes = $suffixes;
- $this->params = $params;
- }
-
- /**
- * @see Iterator::key()
- * @return integer
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- if ( $this->iter instanceof Iterator ) {
- return $this->iter->valid();
- } elseif ( is_array( $this->iter ) ) {
- return ( current( $this->iter ) !== false ); // no paths can have this value
- }
- return false; // some failure?
- }
-
- /**
- * @see Iterator::current()
- * @return string|bool String or false
- */
- public function current() {
- return ( $this->iter instanceof Iterator )
- ? $this->iter->current()
- : current( $this->iter );
- }
-
- /**
- * @see Iterator::next()
- * @return void
- */
- public function next() {
- ++$this->pos;
- ( $this->iter instanceof Iterator ) ? $this->iter->next() : next( $this->iter );
- do {
- $continue = false; // keep scanning shards?
- $this->filterViaNext(); // filter out duplicates
- // Find the next non-empty shard if no elements are left
- if ( !$this->valid() ) {
- $this->nextShardIteratorIfNotValid();
- $continue = $this->valid(); // re-filter unless we ran out of shards
- }
- } while ( $continue );
- }
-
- /**
- * @see Iterator::rewind()
- * @return void
- */
- public function rewind() {
- $this->pos = 0;
- $this->curShard = 0;
- $this->setIteratorFromCurrentShard();
- do {
- $continue = false; // keep scanning shards?
- $this->filterViaNext(); // filter out duplicates
- // Find the next non-empty shard if no elements are left
- if ( !$this->valid() ) {
- $this->nextShardIteratorIfNotValid();
- $continue = $this->valid(); // re-filter unless we ran out of shards
- }
- } while ( $continue );
- }
-
- /**
- * Filter out duplicate items by advancing to the next ones
- */
- protected function filterViaNext() {
- while ( $this->valid() ) {
- $rel = $this->iter->current(); // path relative to given directory
- $path = $this->params['dir'] . "/{$rel}"; // full storage path
- if ( $this->backend->isSingleShardPathInternal( $path ) ) {
- break; // path is only on one shard; no issue with duplicates
- } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
- // Don't keep listing paths that are on multiple shards
- ( $this->iter instanceof Iterator ) ? $this->iter->next() : next( $this->iter );
- } else {
- $this->multiShardPaths[$rel] = 1;
- break;
- }
- }
- }
-
- /**
- * If the list iterator for this container shard is out of items,
- * then move on to the next container that has items.
- * If there are none, then it advances to the last container.
- */
- protected function nextShardIteratorIfNotValid() {
- while ( !$this->valid() && ++$this->curShard < count( $this->shardSuffixes ) ) {
- $this->setIteratorFromCurrentShard();
- }
- }
-
- /**
- * Set the list iterator to that of the current container shard
- */
- protected function setIteratorFromCurrentShard() {
- $this->iter = $this->listFromShard(
- $this->container . $this->shardSuffixes[$this->curShard],
- $this->directory, $this->params );
- // Start loading results so that current() works
- if ( $this->iter ) {
- ( $this->iter instanceof Iterator ) ? $this->iter->rewind() : reset( $this->iter );
- }
- }
-
- /**
- * Get the list for a given container shard
- *
- * @param $container string Resolved container name
- * @param $dir string Resolved path relative to container
- * @param $params Array
- * @return Traversable|Array|null
- */
- abstract protected function listFromShard( $container, $dir, array $params );
-}
-
-/**
- * Iterator for listing directories
- */
-class FileBackendStoreShardDirIterator extends FileBackendStoreShardListIterator {
- /**
- * @see FileBackendStoreShardListIterator::listFromShard()
- * @return Array|null|Traversable
- */
- protected function listFromShard( $container, $dir, array $params ) {
- return $this->backend->getDirectoryListInternal( $container, $dir, $params );
- }
-}
-
-/**
- * Iterator for listing regular files
- */
-class FileBackendStoreShardFileIterator extends FileBackendStoreShardListIterator {
- /**
- * @see FileBackendStoreShardListIterator::listFromShard()
- * @return Array|null|Traversable
- */
- protected function listFromShard( $container, $dir, array $params ) {
- return $this->backend->getFileListInternal( $container, $dir, $params );
- }
-}
+++ /dev/null
-<?php
-/**
- * Helper class for representing operations with transaction support.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * FileBackend helper class for representing operations.
- * Do not use this class from places outside FileBackend.
- *
- * Methods called from FileOpBatch::attempt() should avoid throwing
- * exceptions at all costs. FileOp objects should be lightweight in order
- * to support large arrays in memory and serialization.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-abstract class FileOp {
- /** @var Array */
- protected $params = array();
- /** @var FileBackendStore */
- protected $backend;
-
- protected $state = self::STATE_NEW; // integer
- protected $failed = false; // boolean
- protected $async = false; // boolean
- protected $useLatest = true; // boolean
- protected $batchId; // string
-
- protected $sourceSha1; // string
- protected $destSameAsSource; // boolean
-
- /* Object life-cycle */
- const STATE_NEW = 1;
- const STATE_CHECKED = 2;
- const STATE_ATTEMPTED = 3;
-
- /**
- * Build a new file operation transaction
- *
- * @param $backend FileBackendStore
- * @param $params Array
- * @throws MWException
- */
- final public function __construct( FileBackendStore $backend, array $params ) {
- $this->backend = $backend;
- list( $required, $optional ) = $this->allowedParams();
- foreach ( $required as $name ) {
- if ( isset( $params[$name] ) ) {
- $this->params[$name] = $params[$name];
- } else {
- throw new MWException( "File operation missing parameter '$name'." );
- }
- }
- foreach ( $optional as $name ) {
- if ( isset( $params[$name] ) ) {
- $this->params[$name] = $params[$name];
- }
- }
- $this->params = $params;
- }
-
- /**
- * Set the batch UUID this operation belongs to
- *
- * @param $batchId string
- * @return void
- */
- final public function setBatchId( $batchId ) {
- $this->batchId = $batchId;
- }
-
- /**
- * Whether to allow stale data for file reads and stat checks
- *
- * @param $allowStale bool
- * @return void
- */
- final public function allowStaleReads( $allowStale ) {
- $this->useLatest = !$allowStale;
- }
-
- /**
- * Get the value of the parameter with the given name
- *
- * @param $name string
- * @return mixed Returns null if the parameter is not set
- */
- final public function getParam( $name ) {
- return isset( $this->params[$name] ) ? $this->params[$name] : null;
- }
-
- /**
- * Check if this operation failed precheck() or attempt()
- *
- * @return bool
- */
- final public function failed() {
- return $this->failed;
- }
-
- /**
- * Get a new empty predicates array for precheck()
- *
- * @return Array
- */
- final public static function newPredicates() {
- return array( 'exists' => array(), 'sha1' => array() );
- }
-
- /**
- * Get a new empty dependency tracking array for paths read/written to
- *
- * @return Array
- */
- final public static function newDependencies() {
- return array( 'read' => array(), 'write' => array() );
- }
-
- /**
- * Update a dependency tracking array to account for this operation
- *
- * @param $deps Array Prior path reads/writes; format of FileOp::newPredicates()
- * @return Array
- */
- final public function applyDependencies( array $deps ) {
- $deps['read'] += array_fill_keys( $this->storagePathsRead(), 1 );
- $deps['write'] += array_fill_keys( $this->storagePathsChanged(), 1 );
- return $deps;
- }
-
- /**
- * Check if this operation changes files listed in $paths
- *
- * @param $paths Array Prior path reads/writes; format of FileOp::newPredicates()
- * @return boolean
- */
- final public function dependsOn( array $deps ) {
- foreach ( $this->storagePathsChanged() as $path ) {
- if ( isset( $deps['read'][$path] ) || isset( $deps['write'][$path] ) ) {
- return true; // "output" or "anti" dependency
- }
- }
- foreach ( $this->storagePathsRead() as $path ) {
- if ( isset( $deps['write'][$path] ) ) {
- return true; // "flow" dependency
- }
- }
- return false;
- }
-
- /**
- * Get the file journal entries for this file operation
- *
- * @param $oPredicates Array Pre-op info about files (format of FileOp::newPredicates)
- * @param $nPredicates Array Post-op info about files (format of FileOp::newPredicates)
- * @return Array
- */
- final public function getJournalEntries( array $oPredicates, array $nPredicates ) {
- $nullEntries = array();
- $updateEntries = array();
- $deleteEntries = array();
- $pathsUsed = array_merge( $this->storagePathsRead(), $this->storagePathsChanged() );
- foreach ( $pathsUsed as $path ) {
- $nullEntries[] = array( // assertion for recovery
- 'op' => 'null',
- 'path' => $path,
- 'newSha1' => $this->fileSha1( $path, $oPredicates )
- );
- }
- foreach ( $this->storagePathsChanged() as $path ) {
- if ( $nPredicates['sha1'][$path] === false ) { // deleted
- $deleteEntries[] = array(
- 'op' => 'delete',
- 'path' => $path,
- 'newSha1' => ''
- );
- } else { // created/updated
- $updateEntries[] = array(
- 'op' => $this->fileExists( $path, $oPredicates ) ? 'update' : 'create',
- 'path' => $path,
- 'newSha1' => $nPredicates['sha1'][$path]
- );
- }
- }
- return array_merge( $nullEntries, $updateEntries, $deleteEntries );
- }
-
- /**
- * Check preconditions of the operation without writing anything
- *
- * @param $predicates Array
- * @return Status
- */
- final public function precheck( array &$predicates ) {
- if ( $this->state !== self::STATE_NEW ) {
- return Status::newFatal( 'fileop-fail-state', self::STATE_NEW, $this->state );
- }
- $this->state = self::STATE_CHECKED;
- $status = $this->doPrecheck( $predicates );
- if ( !$status->isOK() ) {
- $this->failed = true;
- }
- return $status;
- }
-
- /**
- * @return Status
- */
- protected function doPrecheck( array &$predicates ) {
- return Status::newGood();
- }
-
- /**
- * Attempt the operation
- *
- * @return Status
- */
- final public function attempt() {
- if ( $this->state !== self::STATE_CHECKED ) {
- return Status::newFatal( 'fileop-fail-state', self::STATE_CHECKED, $this->state );
- } elseif ( $this->failed ) { // failed precheck
- return Status::newFatal( 'fileop-fail-attempt-precheck' );
- }
- $this->state = self::STATE_ATTEMPTED;
- $status = $this->doAttempt();
- if ( !$status->isOK() ) {
- $this->failed = true;
- $this->logFailure( 'attempt' );
- }
- return $status;
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- return Status::newGood();
- }
-
- /**
- * Attempt the operation in the background
- *
- * @return Status
- */
- final public function attemptAsync() {
- $this->async = true;
- $result = $this->attempt();
- $this->async = false;
- return $result;
- }
-
- /**
- * Get the file operation parameters
- *
- * @return Array (required params list, optional params list)
- */
- protected function allowedParams() {
- return array( array(), array() );
- }
-
- /**
- * Adjust params to FileBackendStore internal file calls
- *
- * @param $params Array
- * @return Array (required params list, optional params list)
- */
- protected function setFlags( array $params ) {
- return array( 'async' => $this->async ) + $params;
- }
-
- /**
- * Get a list of storage paths read from for this operation
- *
- * @return Array
- */
- final public function storagePathsRead() {
- return array_map( 'FileBackend::normalizeStoragePath', $this->doStoragePathsRead() );
- }
-
- /**
- * @see FileOp::storagePathsRead()
- * @return Array
- */
- protected function doStoragePathsRead() {
- return array();
- }
-
- /**
- * Get a list of storage paths written to for this operation
- *
- * @return Array
- */
- final public function storagePathsChanged() {
- return array_map( 'FileBackend::normalizeStoragePath', $this->doStoragePathsChanged() );
- }
-
- /**
- * @see FileOp::storagePathsChanged()
- * @return Array
- */
- protected function doStoragePathsChanged() {
- return array();
- }
-
- /**
- * Check for errors with regards to the destination file already existing.
- * This also updates the destSameAsSource and sourceSha1 member variables.
- * A bad status will be returned if there is no chance it can be overwritten.
- *
- * @param $predicates Array
- * @return Status
- */
- protected function precheckDestExistence( array $predicates ) {
- $status = Status::newGood();
- // Get hash of source file/string and the destination file
- $this->sourceSha1 = $this->getSourceSha1Base36(); // FS file or data string
- if ( $this->sourceSha1 === null ) { // file in storage?
- $this->sourceSha1 = $this->fileSha1( $this->params['src'], $predicates );
- }
- $this->destSameAsSource = false;
- if ( $this->fileExists( $this->params['dst'], $predicates ) ) {
- if ( $this->getParam( 'overwrite' ) ) {
- return $status; // OK
- } elseif ( $this->getParam( 'overwriteSame' ) ) {
- $dhash = $this->fileSha1( $this->params['dst'], $predicates );
- // Check if hashes are valid and match each other...
- if ( !strlen( $this->sourceSha1 ) || !strlen( $dhash ) ) {
- $status->fatal( 'backend-fail-hashes' );
- } elseif ( $this->sourceSha1 !== $dhash ) {
- // Give an error if the files are not identical
- $status->fatal( 'backend-fail-notsame', $this->params['dst'] );
- } else {
- $this->destSameAsSource = true; // OK
- }
- return $status; // do nothing; either OK or bad status
- } else {
- $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] );
- return $status;
- }
- }
- return $status;
- }
-
- /**
- * precheckDestExistence() helper function to get the source file SHA-1.
- * Subclasses should overwride this iff the source is not in storage.
- *
- * @return string|bool Returns false on failure
- */
- protected function getSourceSha1Base36() {
- return null; // N/A
- }
-
- /**
- * Check if a file will exist in storage when this operation is attempted
- *
- * @param $source string Storage path
- * @param $predicates Array
- * @return bool
- */
- final protected function fileExists( $source, array $predicates ) {
- if ( isset( $predicates['exists'][$source] ) ) {
- return $predicates['exists'][$source]; // previous op assures this
- } else {
- $params = array( 'src' => $source, 'latest' => $this->useLatest );
- return $this->backend->fileExists( $params );
- }
- }
-
- /**
- * Get the SHA-1 of a file in storage when this operation is attempted
- *
- * @param $source string Storage path
- * @param $predicates Array
- * @return string|bool False on failure
- */
- final protected function fileSha1( $source, array $predicates ) {
- if ( isset( $predicates['sha1'][$source] ) ) {
- return $predicates['sha1'][$source]; // previous op assures this
- } else {
- $params = array( 'src' => $source, 'latest' => $this->useLatest );
- return $this->backend->getFileSha1Base36( $params );
- }
- }
-
- /**
- * Get the backend this operation is for
- *
- * @return FileBackendStore
- */
- public function getBackend() {
- return $this->backend;
- }
-
- /**
- * Log a file operation failure and preserve any temp files
- *
- * @param $action string
- * @return void
- */
- final public function logFailure( $action ) {
- $params = $this->params;
- $params['failedAction'] = $action;
- try {
- wfDebugLog( 'FileOperation', get_class( $this ) .
- " failed (batch #{$this->batchId}): " . FormatJson::encode( $params ) );
- } catch ( Exception $e ) {
- // bad config? debug log error?
- }
- }
-}
-
-/**
- * Store a file into the backend from a file on the file system.
- * Parameters similar to FileBackendStore::storeInternal(), which include:
- * - src : source path on file system
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
- */
-class StoreFileOp extends FileOp {
- /**
- * @return array
- */
- protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
- }
-
- /**
- * @param $predicates array
- * @return Status
- */
- protected function doPrecheck( array &$predicates ) {
- $status = Status::newGood();
- // Check if the source file exists on the file system
- if ( !is_file( $this->params['src'] ) ) {
- $status->fatal( 'backend-fail-notexists', $this->params['src'] );
- return $status;
- // Check if the source file is too big
- } elseif ( filesize( $this->params['src'] ) > $this->backend->maxFileSizeInternal() ) {
- $status->fatal( 'backend-fail-maxsize',
- $this->params['dst'], $this->backend->maxFileSizeInternal() );
- $status->fatal( 'backend-fail-store', $this->params['src'], $this->params['dst'] );
- return $status;
- // Check if a file can be placed at the destination
- } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
- $status->fatal( 'backend-fail-usable', $this->params['dst'] );
- $status->fatal( 'backend-fail-store', $this->params['src'], $this->params['dst'] );
- return $status;
- }
- // Check if destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
- if ( $status->isOK() ) {
- // Update file existence predicates
- $predicates['exists'][$this->params['dst']] = true;
- $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
- }
- return $status; // safe to call attempt()
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- // Store the file at the destination
- if ( !$this->destSameAsSource ) {
- return $this->backend->storeInternal( $this->setFlags( $this->params ) );
- }
- return Status::newGood();
- }
-
- /**
- * @return bool|string
- */
- protected function getSourceSha1Base36() {
- wfSuppressWarnings();
- $hash = sha1_file( $this->params['src'] );
- wfRestoreWarnings();
- if ( $hash !== false ) {
- $hash = wfBaseConvert( $hash, 16, 36, 31 );
- }
- return $hash;
- }
-
- protected function doStoragePathsChanged() {
- return array( $this->params['dst'] );
- }
-}
-
-/**
- * Create a file in the backend with the given content.
- * Parameters similar to FileBackendStore::createInternal(), which include:
- * - content : the raw file contents
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
- */
-class CreateFileOp extends FileOp {
- protected function allowedParams() {
- return array( array( 'content', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
- }
-
- protected function doPrecheck( array &$predicates ) {
- $status = Status::newGood();
- // Check if the source data is too big
- if ( strlen( $this->getParam( 'content' ) ) > $this->backend->maxFileSizeInternal() ) {
- $status->fatal( 'backend-fail-maxsize',
- $this->params['dst'], $this->backend->maxFileSizeInternal() );
- $status->fatal( 'backend-fail-create', $this->params['dst'] );
- return $status;
- // Check if a file can be placed at the destination
- } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
- $status->fatal( 'backend-fail-usable', $this->params['dst'] );
- $status->fatal( 'backend-fail-create', $this->params['dst'] );
- return $status;
- }
- // Check if destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
- if ( $status->isOK() ) {
- // Update file existence predicates
- $predicates['exists'][$this->params['dst']] = true;
- $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
- }
- return $status; // safe to call attempt()
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- if ( !$this->destSameAsSource ) {
- // Create the file at the destination
- return $this->backend->createInternal( $this->setFlags( $this->params ) );
- }
- return Status::newGood();
- }
-
- /**
- * @return bool|String
- */
- protected function getSourceSha1Base36() {
- return wfBaseConvert( sha1( $this->params['content'] ), 16, 36, 31 );
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsChanged() {
- return array( $this->params['dst'] );
- }
-}
-
-/**
- * Copy a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::copyInternal(), which include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
- */
-class CopyFileOp extends FileOp {
- /**
- * @return array
- */
- protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
- }
-
- /**
- * @param $predicates array
- * @return Status
- */
- protected function doPrecheck( array &$predicates ) {
- $status = Status::newGood();
- // Check if the source file exists
- if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
- $status->fatal( 'backend-fail-notexists', $this->params['src'] );
- return $status;
- // Check if a file can be placed at the destination
- } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
- $status->fatal( 'backend-fail-usable', $this->params['dst'] );
- $status->fatal( 'backend-fail-copy', $this->params['src'], $this->params['dst'] );
- return $status;
- }
- // Check if destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
- if ( $status->isOK() ) {
- // Update file existence predicates
- $predicates['exists'][$this->params['dst']] = true;
- $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
- }
- return $status; // safe to call attempt()
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- // Do nothing if the src/dst paths are the same
- if ( $this->params['src'] !== $this->params['dst'] ) {
- // Copy the file into the destination
- if ( !$this->destSameAsSource ) {
- return $this->backend->copyInternal( $this->setFlags( $this->params ) );
- }
- }
- return Status::newGood();
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsRead() {
- return array( $this->params['src'] );
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsChanged() {
- return array( $this->params['dst'] );
- }
-}
-
-/**
- * Move a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::moveInternal(), which include:
- * - src : source storage path
- * - dst : destination storage path
- * - overwrite : do nothing and pass if an identical file exists at destination
- * - overwriteSame : override any existing file at destination
- */
-class MoveFileOp extends FileOp {
- /**
- * @return array
- */
- protected function allowedParams() {
- return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
- }
-
- /**
- * @param $predicates array
- * @return Status
- */
- protected function doPrecheck( array &$predicates ) {
- $status = Status::newGood();
- // Check if the source file exists
- if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
- $status->fatal( 'backend-fail-notexists', $this->params['src'] );
- return $status;
- // Check if a file can be placed at the destination
- } elseif ( !$this->backend->isPathUsableInternal( $this->params['dst'] ) ) {
- $status->fatal( 'backend-fail-usable', $this->params['dst'] );
- $status->fatal( 'backend-fail-move', $this->params['src'], $this->params['dst'] );
- return $status;
- }
- // Check if destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
- if ( $status->isOK() ) {
- // Update file existence predicates
- $predicates['exists'][$this->params['src']] = false;
- $predicates['sha1'][$this->params['src']] = false;
- $predicates['exists'][$this->params['dst']] = true;
- $predicates['sha1'][$this->params['dst']] = $this->sourceSha1;
- }
- return $status; // safe to call attempt()
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- // Do nothing if the src/dst paths are the same
- if ( $this->params['src'] !== $this->params['dst'] ) {
- if ( !$this->destSameAsSource ) {
- // Move the file into the destination
- return $this->backend->moveInternal( $this->setFlags( $this->params ) );
- } else {
- // Just delete source as the destination needs no changes
- $params = array( 'src' => $this->params['src'] );
- return $this->backend->deleteInternal( $this->setFlags( $params ) );
- }
- }
- return Status::newGood();
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsRead() {
- return array( $this->params['src'] );
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsChanged() {
- return array( $this->params['src'], $this->params['dst'] );
- }
-}
-
-/**
- * Delete a file at the given storage path from the backend.
- * Parameters similar to FileBackendStore::deleteInternal(), which include:
- * - src : source storage path
- * - ignoreMissingSource : don't return an error if the file does not exist
- */
-class DeleteFileOp extends FileOp {
- /**
- * @return array
- */
- protected function allowedParams() {
- return array( array( 'src' ), array( 'ignoreMissingSource' ) );
- }
-
- protected $needsDelete = true;
-
- /**
- * @param array $predicates
- * @return Status
- */
- protected function doPrecheck( array &$predicates ) {
- $status = Status::newGood();
- // Check if the source file exists
- if ( !$this->fileExists( $this->params['src'], $predicates ) ) {
- if ( !$this->getParam( 'ignoreMissingSource' ) ) {
- $status->fatal( 'backend-fail-notexists', $this->params['src'] );
- return $status;
- }
- $this->needsDelete = false;
- }
- // Update file existence predicates
- $predicates['exists'][$this->params['src']] = false;
- $predicates['sha1'][$this->params['src']] = false;
- return $status; // safe to call attempt()
- }
-
- /**
- * @return Status
- */
- protected function doAttempt() {
- if ( $this->needsDelete ) {
- // Delete the source file
- return $this->backend->deleteInternal( $this->setFlags( $this->params ) );
- }
- return Status::newGood();
- }
-
- /**
- * @return array
- */
- protected function doStoragePathsChanged() {
- return array( $this->params['src'] );
- }
-}
-
-/**
- * Placeholder operation that has no params and does nothing
- */
-class NullFileOp extends FileOp {}
+++ /dev/null
-<?php
-/**
- * Helper class for representing batch file operations.
- *
- * 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 FileBackend
- * @author Aaron Schulz
- */
-
-/**
- * Helper class for representing batch file operations.
- * Do not use this class from places outside FileBackend.
- *
- * Methods should avoid throwing exceptions at all costs.
- *
- * @ingroup FileBackend
- * @since 1.20
- */
-class FileOpBatch {
- /* Timeout related parameters */
- const MAX_BATCH_SIZE = 1000; // integer
-
- /**
- * Attempt to perform a series of file operations.
- * Callers are responsible for handling file locking.
- *
- * $opts is an array of options, including:
- * - force : Errors that would normally cause a rollback do not.
- * The remaining operations are still attempted if any fail.
- * - allowStale : Don't require the latest available data.
- * This can increase performance for non-critical writes.
- * This has no effect unless the 'force' flag is set.
- * - nonJournaled : Don't log this operation batch in the file journal.
- * - concurrency : Try to do this many operations in parallel when possible.
- *
- * The resulting Status will be "OK" unless:
- * - a) unexpected operation errors occurred (network partitions, disk full...)
- * - b) significant operation errors occured and 'force' was not set
- *
- * @param $performOps Array List of FileOp operations
- * @param $opts Array Batch operation options
- * @param $journal FileJournal Journal to log operations to
- * @return Status
- */
- public static function attempt( array $performOps, array $opts, FileJournal $journal ) {
- wfProfileIn( __METHOD__ );
- $status = Status::newGood();
-
- $n = count( $performOps );
- if ( $n > self::MAX_BATCH_SIZE ) {
- $status->fatal( 'backend-fail-batchsize', $n, self::MAX_BATCH_SIZE );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- $batchId = $journal->getTimestampedUUID();
- $allowStale = !empty( $opts['allowStale'] );
- $ignoreErrors = !empty( $opts['force'] );
- $journaled = empty( $opts['nonJournaled'] );
- $maxConcurrency = isset( $opts['concurrency'] ) ? $opts['concurrency'] : 1;
-
- $entries = array(); // file journal entry list
- $predicates = FileOp::newPredicates(); // account for previous ops in prechecks
- $curBatch = array(); // concurrent FileOp sub-batch accumulation
- $curBatchDeps = FileOp::newDependencies(); // paths used in FileOp sub-batch
- $pPerformOps = array(); // ordered list of concurrent FileOp sub-batches
- $lastBackend = null; // last op backend name
- // Do pre-checks for each operation; abort on failure...
- foreach ( $performOps as $index => $fileOp ) {
- $backendName = $fileOp->getBackend()->getName();
- $fileOp->setBatchId( $batchId ); // transaction ID
- $fileOp->allowStaleReads( $allowStale ); // consistency level
- // Decide if this op can be done concurrently within this sub-batch
- // or if a new concurrent sub-batch must be started after this one...
- if ( $fileOp->dependsOn( $curBatchDeps )
- || count( $curBatch ) >= $maxConcurrency
- || ( $backendName !== $lastBackend && count( $curBatch ) )
- ) {
- $pPerformOps[] = $curBatch; // push this batch
- $curBatch = array(); // start a new sub-batch
- $curBatchDeps = FileOp::newDependencies();
- }
- $lastBackend = $backendName;
- $curBatch[$index] = $fileOp; // keep index
- // Update list of affected paths in this batch
- $curBatchDeps = $fileOp->applyDependencies( $curBatchDeps );
- // Simulate performing the operation...
- $oldPredicates = $predicates;
- $subStatus = $fileOp->precheck( $predicates ); // updates $predicates
- $status->merge( $subStatus );
- if ( $subStatus->isOK() ) {
- if ( $journaled ) { // journal log entries
- $entries = array_merge( $entries,
- $fileOp->getJournalEntries( $oldPredicates, $predicates ) );
- }
- } else { // operation failed?
- $status->success[$index] = false;
- ++$status->failCount;
- if ( !$ignoreErrors ) {
- wfProfileOut( __METHOD__ );
- return $status; // abort
- }
- }
- }
- // Push the last sub-batch
- if ( count( $curBatch ) ) {
- $pPerformOps[] = $curBatch;
- }
-
- // Log the operations in the file journal...
- if ( count( $entries ) ) {
- $subStatus = $journal->logChangeBatch( $entries, $batchId );
- if ( !$subStatus->isOK() ) {
- wfProfileOut( __METHOD__ );
- return $subStatus; // abort
- }
- }
-
- if ( $ignoreErrors ) { // treat precheck() fatals as mere warnings
- $status->setResult( true, $status->value );
- }
-
- // Attempt each operation (in parallel if allowed and possible)...
- if ( count( $pPerformOps ) < count( $performOps ) ) {
- self::runBatchParallel( $pPerformOps, $status );
- } else {
- self::runBatchSeries( $performOps, $status );
- }
-
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * Attempt a list of file operations in series.
- * This will abort remaining ops on failure.
- *
- * @param $performOps Array
- * @param $status Status
- * @return bool Success
- */
- protected static function runBatchSeries( array $performOps, Status $status ) {
- foreach ( $performOps as $index => $fileOp ) {
- if ( $fileOp->failed() ) {
- continue; // nothing to do
- }
- $subStatus = $fileOp->attempt();
- $status->merge( $subStatus );
- if ( $subStatus->isOK() ) {
- $status->success[$index] = true;
- ++$status->successCount;
- } else {
- $status->success[$index] = false;
- ++$status->failCount;
- // We can't continue (even with $ignoreErrors) as $predicates is wrong.
- // Log the remaining ops as failed for recovery...
- for ( $i = ($index + 1); $i < count( $performOps ); $i++ ) {
- $performOps[$i]->logFailure( 'attempt_aborted' );
- }
- return false; // bail out
- }
- }
- return true;
- }
-
- /**
- * Attempt a list of file operations sub-batches in series.
- *
- * The operations *in* each sub-batch will be done in parallel.
- * The caller is responsible for making sure the operations
- * within any given sub-batch do not depend on each other.
- * This will abort remaining ops on failure.
- *
- * @param $pPerformOps Array
- * @param $status Status
- * @return bool Success
- */
- protected static function runBatchParallel( array $pPerformOps, Status $status ) {
- $aborted = false;
- foreach ( $pPerformOps as $performOpsBatch ) {
- if ( $aborted ) { // check batch op abort flag...
- // We can't continue (even with $ignoreErrors) as $predicates is wrong.
- // Log the remaining ops as failed for recovery...
- foreach ( $performOpsBatch as $i => $fileOp ) {
- $performOpsBatch[$i]->logFailure( 'attempt_aborted' );
- }
- continue;
- }
- $statuses = array();
- $opHandles = array();
- // Get the backend; all sub-batch ops belong to a single backend
- $backend = reset( $performOpsBatch )->getBackend();
- // If attemptAsync() returns synchronously, it was either an
- // error Status or the backend just doesn't support async ops.
- foreach ( $performOpsBatch as $i => $fileOp ) {
- if ( !$fileOp->failed() ) { // failed => already has Status
- $subStatus = $fileOp->attemptAsync();
- if ( $subStatus->value instanceof FileBackendStoreOpHandle ) {
- $opHandles[$i] = $subStatus->value; // deferred
- } else {
- $statuses[$i] = $subStatus; // done already
- }
- }
- }
- // Try to do all the operations concurrently...
- $statuses = $statuses + $backend->executeOpHandlesInternal( $opHandles );
- // Marshall and merge all the responses (blocking)...
- foreach ( $performOpsBatch as $i => $fileOp ) {
- if ( !$fileOp->failed() ) { // failed => already has Status
- $subStatus = $statuses[$i];
- $status->merge( $subStatus );
- if ( $subStatus->isOK() ) {
- $status->success[$i] = true;
- ++$status->successCount;
- } else {
- $status->success[$i] = false;
- ++$status->failCount;
- $aborted = true; // set abort flag; we can't continue
- }
- }
- }
- }
- return $status;
- }
-}
+++ /dev/null
-<?php
-/**
- * OpenStack Swift based file backend.
- *
- * 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 FileBackend
- * @author Russ Nelson
- * @author Aaron Schulz
- */
-
-/**
- * @brief Class for an OpenStack Swift based file backend.
- *
- * This requires the SwiftCloudFiles MediaWiki extension, which includes
- * the php-cloudfiles library (https://github.com/rackspace/php-cloudfiles).
- * php-cloudfiles requires the curl, fileinfo, and mb_string PHP extensions.
- *
- * Status messages should avoid mentioning the Swift account name.
- * Likewise, error suppression should be used to avoid path disclosure.
- *
- * @ingroup FileBackend
- * @since 1.19
- */
-class SwiftFileBackend extends FileBackendStore {
- /** @var CF_Authentication */
- protected $auth; // Swift authentication handler
- protected $authTTL; // integer seconds
- protected $swiftAnonUser; // string; username to handle unauthenticated requests
- protected $swiftUseCDN; // boolean; whether CloudFiles CDN is enabled
- protected $swiftCDNExpiry; // integer; how long to cache things in the CDN
- protected $swiftCDNPurgable; // boolean; whether object CDN purging is enabled
-
- protected $maxContCacheSize = 300; // integer; max containers with entries
-
- /** @var CF_Connection */
- protected $conn; // Swift connection handle
- protected $connStarted = 0; // integer UNIX timestamp
- protected $connContainers = array(); // container object cache
- protected $connException; // CloudFiles exception
-
- /**
- * @see FileBackendStore::__construct()
- * Additional $config params include:
- * - swiftAuthUrl : Swift authentication server URL
- * - swiftUser : Swift user used by MediaWiki (account:username)
- * - swiftKey : Swift authentication key for the above user
- * - swiftAuthTTL : Swift authentication TTL (seconds)
- * - swiftAnonUser : Swift user used for end-user requests (account:username).
- * If set, then views of public containers are assumed to go
- * through this user. If not set, then public containers are
- * accessible to unauthenticated requests via ".r:*" in the ACL.
- * - swiftUseCDN : Whether a Cloud Files Content Delivery Network is set up
- * - swiftCDNExpiry : How long (in seconds) to store content in the CDN.
- * If files may likely change, this should probably not exceed
- * a few days. For example, deletions may take this long to apply.
- * If object purging is enabled, however, this is not an issue.
- * - swiftCDNPurgable : Whether object purge requests are allowed by the CDN.
- * - shardViaHashLevels : Map of container names to sharding config with:
- * - base : base of hash characters, 16 or 36
- * - levels : the number of hash levels (and digits)
- * - repeat : hash subdirectories are prefixed with all the
- * parent hash directory names (e.g. "a/ab/abc")
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
- if ( !MWInit::classExists( 'CF_Constants' ) ) {
- throw new MWException( 'SwiftCloudFiles extension not installed.' );
- }
- // Required settings
- $this->auth = new CF_Authentication(
- $config['swiftUser'],
- $config['swiftKey'],
- null, // account; unused
- $config['swiftAuthUrl']
- );
- // Optional settings
- $this->authTTL = isset( $config['swiftAuthTTL'] )
- ? $config['swiftAuthTTL']
- : 5 * 60; // some sane number
- $this->swiftAnonUser = isset( $config['swiftAnonUser'] )
- ? $config['swiftAnonUser']
- : '';
- $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
- ? $config['shardViaHashLevels']
- : '';
- $this->swiftUseCDN = isset( $config['swiftUseCDN'] )
- ? $config['swiftUseCDN']
- : false;
- $this->swiftCDNExpiry = isset( $config['swiftCDNExpiry'] )
- ? $config['swiftCDNExpiry']
- : 3600; // hour
- $this->swiftCDNPurgable = isset( $config['swiftCDNPurgable'] )
- ? $config['swiftCDNPurgable']
- : true;
- // Cache container info to mask latency
- $this->memCache = wfGetMainCache();
- }
-
- /**
- * @see FileBackendStore::resolveContainerPath()
- * @return null
- */
- protected function resolveContainerPath( $container, $relStoragePath ) {
- if ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
- return null; // too long for Swift
- }
- return $relStoragePath;
- }
-
- /**
- * @see FileBackendStore::isPathUsableInternal()
- * @return bool
- */
- public function isPathUsableInternal( $storagePath ) {
- list( $container, $rel ) = $this->resolveStoragePathReal( $storagePath );
- if ( $rel === null ) {
- return false; // invalid
- }
-
- try {
- $this->getContainer( $container );
- return true; // container exists
- } catch ( NoSuchContainerException $e ) {
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__, array( 'path' => $storagePath ) );
- }
-
- return false;
- }
-
- /**
- * @see FileBackendStore::doCreateInternal()
- * @return Status
- */
- protected function doCreateInternal( array $params ) {
- $status = Status::newGood();
-
- list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
- if ( $dstRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- // (a) Check the destination container and object
- try {
- $dContObj = $this->getContainer( $dstCont );
- if ( empty( $params['overwrite'] ) &&
- $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
- {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- return $status;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Get a SHA-1 hash of the object
- $sha1Hash = wfBaseConvert( sha1( $params['content'] ), 16, 36, 31 );
-
- // (c) Actually create the object
- try {
- // Create a fresh CF_Object with no fields preloaded.
- // We don't want to preserve headers, metadata, and such.
- $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
- // Note: metadata keys stored as [Upper case char][[Lower case char]...]
- $obj->metadata = array( 'Sha1base36' => $sha1Hash );
- // Manually set the ETag (https://github.com/rackspace/php-cloudfiles/issues/59).
- // The MD5 here will be checked within Swift against its own MD5.
- $obj->set_etag( md5( $params['content'] ) );
- // Use the same content type as StreamFile for security
- $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] );
- if ( !empty( $params['async'] ) ) { // deferred
- $handle = $obj->write_async( $params['content'] );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $handle );
- $status->value->affectedObjects[] = $obj;
- } else { // actually write the object in Swift
- $obj->write( $params['content'] );
- $this->purgeCDNCache( array( $obj ) );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( BadContentTypeException $e ) {
- $status->fatal( 'backend-fail-contenttype', $params['dst'] );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see SwiftFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCreate( CF_Async_Op $cfOp, Status $status, array $params ) {
- try {
- $cfOp->getLastResponse();
- } catch ( BadContentTypeException $e ) {
- $status->fatal( 'backend-fail-contenttype', $params['dst'] );
- }
- }
-
- /**
- * @see FileBackendStore::doStoreInternal()
- * @return Status
- */
- protected function doStoreInternal( array $params ) {
- $status = Status::newGood();
-
- list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
- if ( $dstRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- // (a) Check the destination container and object
- try {
- $dContObj = $this->getContainer( $dstCont );
- if ( empty( $params['overwrite'] ) &&
- $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
- {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- return $status;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Get a SHA-1 hash of the object
- $sha1Hash = sha1_file( $params['src'] );
- if ( $sha1Hash === false ) { // source doesn't exist?
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- return $status;
- }
- $sha1Hash = wfBaseConvert( $sha1Hash, 16, 36, 31 );
-
- // (c) Actually store the object
- try {
- // Create a fresh CF_Object with no fields preloaded.
- // We don't want to preserve headers, metadata, and such.
- $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
- // Note: metadata keys stored as [Upper case char][[Lower case char]...]
- $obj->metadata = array( 'Sha1base36' => $sha1Hash );
- // The MD5 here will be checked within Swift against its own MD5.
- $obj->set_etag( md5_file( $params['src'] ) );
- // Use the same content type as StreamFile for security
- $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] );
- if ( !empty( $params['async'] ) ) { // deferred
- wfSuppressWarnings();
- $fp = fopen( $params['src'], 'rb' );
- wfRestoreWarnings();
- if ( !$fp ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- } else {
- $handle = $obj->write_async( $fp, filesize( $params['src'] ), true );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $handle );
- $status->value->resourcesToClose[] = $fp;
- $status->value->affectedObjects[] = $obj;
- }
- } else { // actually write the object in Swift
- $obj->load_from_filename( $params['src'], true ); // calls $obj->write()
- $this->purgeCDNCache( array( $obj ) );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( BadContentTypeException $e ) {
- $status->fatal( 'backend-fail-contenttype', $params['dst'] );
- } catch ( IOException $e ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see SwiftFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseStore( CF_Async_Op $cfOp, Status $status, array $params ) {
- try {
- $cfOp->getLastResponse();
- } catch ( BadContentTypeException $e ) {
- $status->fatal( 'backend-fail-contenttype', $params['dst'] );
- } catch ( IOException $e ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- }
- }
-
- /**
- * @see FileBackendStore::doCopyInternal()
- * @return Status
- */
- protected function doCopyInternal( array $params ) {
- $status = Status::newGood();
-
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
- if ( $dstRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- // (a) Check the source/destination containers and destination object
- try {
- $sContObj = $this->getContainer( $srcCont );
- $dContObj = $this->getContainer( $dstCont );
- if ( empty( $params['overwrite'] ) &&
- $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
- {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- return $status;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Actually copy the file to the destination
- try {
- $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
- if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $handle );
- $status->value->affectedObjects[] = $dstObj;
- } else { // actually write the object in Swift
- $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel );
- $this->purgeCDNCache( array( $dstObj ) );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( NoSuchObjectException $e ) { // source object does not exist
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see SwiftFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCopy( CF_Async_Op $cfOp, Status $status, array $params ) {
- try {
- $cfOp->getLastResponse();
- } catch ( NoSuchObjectException $e ) { // source object does not exist
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- }
- }
-
- /**
- * @see FileBackendStore::doMoveInternal()
- * @return Status
- */
- protected function doMoveInternal( array $params ) {
- $status = Status::newGood();
-
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] );
- if ( $dstRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
- return $status;
- }
-
- // (a) Check the source/destination containers and destination object
- try {
- $sContObj = $this->getContainer( $srcCont );
- $dContObj = $this->getContainer( $dstCont );
- if ( empty( $params['overwrite'] ) &&
- $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) )
- {
- $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
- return $status;
- }
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- return $status;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Actually move the file to the destination
- try {
- $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
- $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
- if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $handle );
- $status->value->affectedObjects[] = $srcObj;
- $status->value->affectedObjects[] = $dstObj;
- } else { // actually write the object in Swift
- $sContObj->move_object_to( $srcRel, $dContObj, $dstRel );
- $this->purgeCDNCache( array( $srcObj, $dstObj ) );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( NoSuchObjectException $e ) { // source object does not exist
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see SwiftFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseMove( CF_Async_Op $cfOp, Status $status, array $params ) {
- try {
- $cfOp->getLastResponse();
- } catch ( NoSuchObjectException $e ) { // source object does not exist
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- }
- }
-
- /**
- * @see FileBackendStore::doDeleteInternal()
- * @return Status
- */
- protected function doDeleteInternal( array $params ) {
- $status = Status::newGood();
-
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- return $status;
- }
-
- try {
- $sContObj = $this->getContainer( $srcCont );
- $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
- if ( !empty( $params['async'] ) ) { // deferred
- $handle = $sContObj->delete_object_async( $srcRel );
- $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $handle );
- $status->value->affectedObjects[] = $srcObj;
- } else { // actually write the object in Swift
- $sContObj->delete_object( $srcRel );
- $this->purgeCDNCache( array( $srcObj ) );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- } catch ( NoSuchObjectException $e ) {
- if ( empty( $params['ignoreMissingSource'] ) ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- }
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see SwiftFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseDelete( CF_Async_Op $cfOp, Status $status, array $params ) {
- try {
- $cfOp->getLastResponse();
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- } catch ( NoSuchObjectException $e ) {
- if ( empty( $params['ignoreMissingSource'] ) ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- }
- }
- }
-
- /**
- * @see FileBackendStore::doPrepareInternal()
- * @return Status
- */
- protected function doPrepareInternal( $fullCont, $dir, array $params ) {
- $status = Status::newGood();
-
- // (a) Check if container already exists
- try {
- $contObj = $this->getContainer( $fullCont );
- // NoSuchContainerException not thrown: container must exist
- return $status; // already exists
- } catch ( NoSuchContainerException $e ) {
- // NoSuchContainerException thrown: container does not exist
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Create container as needed
- try {
- $contObj = $this->createContainer( $fullCont );
- if ( !empty( $params['noAccess'] ) ) {
- // Make container private to end-users...
- $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
- } else {
- // Make container public to end-users...
- $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
- }
- if ( $this->swiftUseCDN ) { // Rackspace style CDN
- $contObj->make_public( $this->swiftCDNExpiry );
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- return $status;
- }
-
- /**
- * @see FileBackendStore::doSecureInternal()
- * @return Status
- */
- protected function doSecureInternal( $fullCont, $dir, array $params ) {
- $status = Status::newGood();
- if ( empty( $params['noAccess'] ) ) {
- return $status; // nothing to do
- }
-
- // Restrict container from end-users...
- try {
- // doPrepareInternal() should have been called,
- // so the Swift container should already exist...
- $contObj = $this->getContainer( $fullCont ); // normally a cache hit
- // NoSuchContainerException not thrown: container must exist
-
- // Make container private to end-users...
- $status->merge( $this->setContainerAccess(
- $contObj,
- array( $this->auth->username ), // read
- array( $this->auth->username ) // write
- ) );
- if ( $this->swiftUseCDN && $contObj->is_public() ) { // Rackspace style CDN
- $contObj->make_private();
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see FileBackendStore::doPublishInternal()
- * @return Status
- */
- protected function doPublishInternal( $fullCont, $dir, array $params ) {
- $status = Status::newGood();
-
- // Unrestrict container from end-users...
- try {
- // doPrepareInternal() should have been called,
- // so the Swift container should already exist...
- $contObj = $this->getContainer( $fullCont ); // normally a cache hit
- // NoSuchContainerException not thrown: container must exist
-
- // Make container public to end-users...
- if ( $this->swiftAnonUser != '' ) {
- $status->merge( $this->setContainerAccess(
- $contObj,
- array( $this->auth->username, $this->swiftAnonUser ), // read
- array( $this->auth->username, $this->swiftAnonUser ) // write
- ) );
- } else {
- $status->merge( $this->setContainerAccess(
- $contObj,
- array( $this->auth->username, '.r:*' ), // read
- array( $this->auth->username ) // write
- ) );
- }
- if ( $this->swiftUseCDN && !$contObj->is_public() ) { // Rackspace style CDN
- $contObj->make_public();
- }
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see FileBackendStore::doCleanInternal()
- * @return Status
- */
- protected function doCleanInternal( $fullCont, $dir, array $params ) {
- $status = Status::newGood();
-
- // Only containers themselves can be removed, all else is virtual
- if ( $dir != '' ) {
- return $status; // nothing to do
- }
-
- // (a) Check the container
- try {
- $contObj = $this->getContainer( $fullCont, true );
- } catch ( NoSuchContainerException $e ) {
- return $status; // ok, nothing to do
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- // (b) Delete the container if empty
- if ( $contObj->object_count == 0 ) {
- try {
- $this->deleteContainer( $fullCont );
- } catch ( NoSuchContainerException $e ) {
- return $status; // race?
- } catch ( NonEmptyContainerException $e ) {
- return $status; // race? consistency delay?
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
- }
-
- return $status;
- }
-
- /**
- * @see FileBackendStore::doFileExists()
- * @return array|bool|null
- */
- protected function doGetFileStat( array $params ) {
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- return false; // invalid storage path
- }
-
- $stat = false;
- try {
- $contObj = $this->getContainer( $srcCont );
- $srcObj = $contObj->get_object( $srcRel, $this->headersFromParams( $params ) );
- $this->addMissingMetadata( $srcObj, $params['src'] );
- $stat = array(
- // Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
- 'mtime' => wfTimestamp( TS_MW, $srcObj->last_modified ),
- 'size' => (int)$srcObj->content_length,
- 'sha1' => $srcObj->metadata['Sha1base36']
- );
- } catch ( NoSuchContainerException $e ) {
- } catch ( NoSuchObjectException $e ) {
- } catch ( CloudFilesException $e ) { // some other exception?
- $stat = null;
- $this->handleException( $e, null, __METHOD__, $params );
- }
-
- return $stat;
- }
-
- /**
- * Fill in any missing object metadata and save it to Swift
- *
- * @param $obj CF_Object
- * @param $path string Storage path to object
- * @return bool Success
- * @throws Exception cloudfiles exceptions
- */
- protected function addMissingMetadata( CF_Object $obj, $path ) {
- if ( isset( $obj->metadata['Sha1base36'] ) ) {
- return true; // nothing to do
- }
- $status = Status::newGood();
- $scopeLockS = $this->getScopedFileLocks( array( $path ), LockManager::LOCK_UW, $status );
- if ( $status->isOK() ) {
- # Do not stat the file in getLocalCopy() to avoid infinite loops
- $tmpFile = $this->getLocalCopy( array( 'src' => $path, 'latest' => 1, 'nostat' => 1 ) );
- if ( $tmpFile ) {
- $hash = $tmpFile->getSha1Base36();
- if ( $hash !== false ) {
- $obj->metadata['Sha1base36'] = $hash;
- $obj->sync_metadata(); // save to Swift
- return true; // success
- }
- }
- }
- $obj->metadata['Sha1base36'] = false;
- return false; // failed
- }
-
- /**
- * @see FileBackend::getFileContents()
- * @return bool|null|string
- */
- public function getFileContents( array $params ) {
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- return false; // invalid storage path
- }
-
- if ( !$this->fileExists( $params ) ) {
- return null;
- }
-
- $data = false;
- try {
- $sContObj = $this->getContainer( $srcCont );
- $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
- $data = $obj->read( $this->headersFromParams( $params ) );
- } catch ( NoSuchContainerException $e ) {
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__, $params );
- }
-
- return $data;
- }
-
- /**
- * @see FileBackendStore::doDirectoryExists()
- * @return bool|null
- */
- protected function doDirectoryExists( $fullCont, $dir, array $params ) {
- try {
- $container = $this->getContainer( $fullCont );
- $prefix = ( $dir == '' ) ? null : "{$dir}/";
- return ( count( $container->list_objects( 1, null, $prefix ) ) > 0 );
- } catch ( NoSuchContainerException $e ) {
- return false;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__,
- array( 'cont' => $fullCont, 'dir' => $dir ) );
- }
-
- return null; // error
- }
-
- /**
- * @see FileBackendStore::getDirectoryListInternal()
- * @return SwiftFileBackendDirList
- */
- public function getDirectoryListInternal( $fullCont, $dir, array $params ) {
- return new SwiftFileBackendDirList( $this, $fullCont, $dir, $params );
- }
-
- /**
- * @see FileBackendStore::getFileListInternal()
- * @return SwiftFileBackendFileList
- */
- public function getFileListInternal( $fullCont, $dir, array $params ) {
- return new SwiftFileBackendFileList( $this, $fullCont, $dir, $params );
- }
-
- /**
- * Do not call this function outside of SwiftFileBackendFileList
- *
- * @param $fullCont string Resolved container name
- * @param $dir string Resolved storage directory with no trailing slash
- * @param $after string|null Storage path of file to list items after
- * @param $limit integer Max number of items to list
- * @param $params Array Includes flag for 'topOnly'
- * @return Array List of relative paths of dirs directly under $dir
- */
- public function getDirListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
- $dirs = array();
- if ( $after === INF ) {
- return $dirs; // nothing more
- }
- wfProfileIn( __METHOD__ . '-' . $this->name );
-
- try {
- $container = $this->getContainer( $fullCont );
- $prefix = ( $dir == '' ) ? null : "{$dir}/";
- // Non-recursive: only list dirs right under $dir
- if ( !empty( $params['topOnly'] ) ) {
- $objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
- foreach ( $objects as $object ) { // files and dirs
- if ( substr( $object, -1 ) === '/' ) {
- $dirs[] = $object; // directories end in '/'
- }
- }
- // Recursive: list all dirs under $dir and its subdirs
- } else {
- // Get directory from last item of prior page
- $lastDir = $this->getParentDir( $after ); // must be first page
- $objects = $container->list_objects( $limit, $after, $prefix );
- foreach ( $objects as $object ) { // files
- $objectDir = $this->getParentDir( $object ); // directory of object
- if ( $objectDir !== false ) { // file has a parent dir
- // Swift stores paths in UTF-8, using binary sorting.
- // See function "create_container_table" in common/db.py.
- // If a directory is not "greater" than the last one,
- // then it was already listed by the calling iterator.
- if ( $objectDir > $lastDir ) {
- $pDir = $objectDir;
- do { // add dir and all its parent dirs
- $dirs[] = "{$pDir}/";
- $pDir = $this->getParentDir( $pDir );
- } while ( $pDir !== false // sanity
- && $pDir > $lastDir // not done already
- && strlen( $pDir ) > strlen( $dir ) // within $dir
- );
- }
- $lastDir = $objectDir;
- }
- }
- }
- if ( count( $objects ) < $limit ) {
- $after = INF; // avoid a second RTT
- } else {
- $after = end( $objects ); // update last item
- }
- } catch ( NoSuchContainerException $e ) {
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__,
- array( 'cont' => $fullCont, 'dir' => $dir ) );
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- return $dirs;
- }
-
- protected function getParentDir( $path ) {
- return ( strpos( $path, '/' ) !== false ) ? dirname( $path ) : false;
- }
-
- /**
- * Do not call this function outside of SwiftFileBackendFileList
- *
- * @param $fullCont string Resolved container name
- * @param $dir string Resolved storage directory with no trailing slash
- * @param $after string|null Storage path of file to list items after
- * @param $limit integer Max number of items to list
- * @param $params Array Includes flag for 'topOnly'
- * @return Array List of relative paths of files under $dir
- */
- public function getFileListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
- $files = array();
- if ( $after === INF ) {
- return $files; // nothing more
- }
- wfProfileIn( __METHOD__ . '-' . $this->name );
-
- try {
- $container = $this->getContainer( $fullCont );
- $prefix = ( $dir == '' ) ? null : "{$dir}/";
- // Non-recursive: only list files right under $dir
- if ( !empty( $params['topOnly'] ) ) { // files and dirs
- $objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
- foreach ( $objects as $object ) {
- if ( substr( $object, -1 ) !== '/' ) {
- $files[] = $object; // directories end in '/'
- }
- }
- // Recursive: list all files under $dir and its subdirs
- } else { // files
- $objects = $container->list_objects( $limit, $after, $prefix );
- $files = $objects;
- }
- if ( count( $objects ) < $limit ) {
- $after = INF; // avoid a second RTT
- } else {
- $after = end( $objects ); // update last item
- }
- } catch ( NoSuchContainerException $e ) {
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__,
- array( 'cont' => $fullCont, 'dir' => $dir ) );
- }
-
- wfProfileOut( __METHOD__ . '-' . $this->name );
- return $files;
- }
-
- /**
- * @see FileBackendStore::doGetFileSha1base36()
- * @return bool
- */
- protected function doGetFileSha1base36( array $params ) {
- $stat = $this->getFileStat( $params );
- if ( $stat ) {
- return $stat['sha1'];
- } else {
- return false;
- }
- }
-
- /**
- * @see FileBackendStore::doStreamFile()
- * @return Status
- */
- protected function doStreamFile( array $params ) {
- $status = Status::newGood();
-
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- $status->fatal( 'backend-fail-invalidpath', $params['src'] );
- }
-
- try {
- $cont = $this->getContainer( $srcCont );
- } catch ( NoSuchContainerException $e ) {
- $status->fatal( 'backend-fail-stream', $params['src'] );
- return $status;
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- return $status;
- }
-
- try {
- $output = fopen( 'php://output', 'wb' );
- $obj = new CF_Object( $cont, $srcRel, false, false ); // skip HEAD
- $obj->stream( $output, $this->headersFromParams( $params ) );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status, __METHOD__, $params );
- }
-
- return $status;
- }
-
- /**
- * @see FileBackendStore::getLocalCopy()
- * @return null|TempFSFile
- */
- public function getLocalCopy( array $params ) {
- list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
- if ( $srcRel === null ) {
- return null;
- }
-
- # Check the recursion guard to avoid loops when filling metadata
- if ( empty( $params['nostat'] ) && !$this->fileExists( $params ) ) {
- return null;
- }
-
- $tmpFile = null;
- try {
- $sContObj = $this->getContainer( $srcCont );
- $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
- // Get source file extension
- $ext = FileBackend::extensionFromPath( $srcRel );
- // Create a new temporary file...
- $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext );
- if ( $tmpFile ) {
- $handle = fopen( $tmpFile->getPath(), 'wb' );
- if ( $handle ) {
- $obj->stream( $handle, $this->headersFromParams( $params ) );
- fclose( $handle );
- } else {
- $tmpFile = null; // couldn't open temp file
- }
- }
- } catch ( NoSuchContainerException $e ) {
- $tmpFile = null;
- } catch ( CloudFilesException $e ) { // some other exception?
- $tmpFile = null;
- $this->handleException( $e, null, __METHOD__, $params );
- }
-
- return $tmpFile;
- }
-
- /**
- * @see FileBackendStore::directoriesAreVirtual()
- * @return bool
- */
- protected function directoriesAreVirtual() {
- return true;
- }
-
- /**
- * Get headers to send to Swift when reading a file based
- * on a FileBackend params array, e.g. that of getLocalCopy().
- * $params is currently only checked for a 'latest' flag.
- *
- * @param $params Array
- * @return Array
- */
- protected function headersFromParams( array $params ) {
- $hdrs = array();
- if ( !empty( $params['latest'] ) ) {
- $hdrs[] = 'X-Newest: true';
- }
- return $hdrs;
- }
-
- /**
- * @see FileBackendStore::doExecuteOpHandlesInternal()
- * @return Array List of corresponding Status objects
- */
- protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
- $statuses = array();
-
- $cfOps = array(); // list of CF_Async_Op objects
- foreach ( $fileOpHandles as $index => $fileOpHandle ) {
- $cfOps[$index] = $fileOpHandle->cfOp;
- }
- $batch = new CF_Async_Op_Batch( $cfOps );
-
- $cfOps = $batch->execute();
- foreach ( $cfOps as $index => $cfOp ) {
- $status = Status::newGood();
- try { // catch exceptions; update status
- $function = '_getResponse' . $fileOpHandles[$index]->call;
- $this->$function( $cfOp, $status, $fileOpHandles[$index]->params );
- $this->purgeCDNCache( $fileOpHandles[$index]->affectedObjects );
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, $status,
- __CLASS__ . ":$function", $fileOpHandles[$index]->params );
- }
- $statuses[$index] = $status;
- }
-
- return $statuses;
- }
-
- /**
- * Set read/write permissions for a Swift container.
- *
- * $readGrps is a list of the possible criteria for a request to have
- * access to read a container. Each item is one of the following formats:
- * - account:user : Grants access if the request is by the given user
- * - .r:<regex> : Grants access if the request is from a referrer host that
- * matches the expression and the request is not for a listing.
- * Setting this to '*' effectively makes a container public.
- * - .rlistings:<regex> : Grants access if the request is from a referrer host that
- * matches the expression and the request for a listing.
- *
- * $writeGrps is a list of the possible criteria for a request to have
- * access to write to a container. Each item is of the following format:
- * - account:user : Grants access if the request is by the given user
- *
- * @see http://swift.openstack.org/misc.html#acls
- *
- * In general, we don't allow listings to end-users. It's not useful, isn't well-defined
- * (lists are truncated to 10000 item with no way to page), and is just a performance risk.
- *
- * @param $contObj CF_Container Swift container
- * @param $readGrps Array List of read access routes
- * @param $writeGrps Array List of write access routes
- * @return Status
- */
- protected function setContainerAccess(
- CF_Container $contObj, array $readGrps, array $writeGrps
- ) {
- $creds = $contObj->cfs_auth->export_credentials();
-
- $url = $creds['storage_url'] . '/' . rawurlencode( $contObj->name );
-
- // Note: 10 second timeout consistent with php-cloudfiles
- $req = new CurlHttpRequest( $url, array( 'method' => 'POST', 'timeout' => 10 ) );
- $req->setHeader( 'X-Auth-Token', $creds['auth_token'] );
- $req->setHeader( 'X-Container-Read', implode( ',', $readGrps ) );
- $req->setHeader( 'X-Container-Write', implode( ',', $writeGrps ) );
-
- return $req->execute(); // should return 204
- }
-
- /**
- * Purge the CDN cache of affected objects if CDN caching is enabled.
- * This is for Rackspace/Akamai CDNs.
- *
- * @param $objects Array List of CF_Object items
- * @return void
- */
- public function purgeCDNCache( array $objects ) {
- if ( $this->swiftUseCDN && $this->swiftCDNPurgable ) {
- foreach ( $objects as $object ) {
- try {
- $object->purge_from_cdn();
- } catch ( CDNNotEnabledException $e ) {
- // CDN not enabled; nothing to see here
- } catch ( CloudFilesException $e ) {
- $this->handleException( $e, null, __METHOD__,
- array( 'cont' => $object->container->name, 'obj' => $object->name ) );
- }
- }
- }
- }
-
- /**
- * Get a connection to the Swift proxy
- *
- * @return CF_Connection|bool False on failure
- * @throws CloudFilesException
- */
- protected function getConnection() {
- if ( $this->connException instanceof Exception ) {
- throw $this->connException; // failed last attempt
- }
- // Session keys expire after a while, so we renew them periodically
- if ( $this->conn && ( time() - $this->connStarted ) > $this->authTTL ) {
- $this->conn->close(); // close active cURL connections
- $this->conn = null;
- }
- // Authenticate with proxy and get a session key...
- if ( !$this->conn ) {
- $this->connStarted = 0;
- $this->connContainers = array();
- try {
- $this->auth->authenticate();
- $this->conn = new CF_Connection( $this->auth );
- $this->connStarted = time();
- } catch ( CloudFilesException $e ) {
- $this->connException = $e; // don't keep re-trying
- throw $e; // throw it back
- }
- }
- return $this->conn;
- }
-
- /**
- * @see FileBackendStore::doClearCache()
- */
- protected function doClearCache( array $paths = null ) {
- $this->connContainers = array(); // clear container object cache
- }
-
- /**
- * Get a Swift container object, possibly from process cache.
- * Use $reCache if the file count or byte count is needed.
- *
- * @param $container string Container name
- * @param $bypassCache bool Bypass all caches and load from Swift
- * @return CF_Container
- * @throws CloudFilesException
- */
- protected function getContainer( $container, $bypassCache = false ) {
- $conn = $this->getConnection(); // Swift proxy connection
- if ( $bypassCache ) { // purge cache
- unset( $this->connContainers[$container] );
- } elseif ( !isset( $this->connContainers[$container] ) ) {
- $this->primeContainerCache( array( $container ) ); // check persistent cache
- }
- if ( !isset( $this->connContainers[$container] ) ) {
- $contObj = $conn->get_container( $container );
- // NoSuchContainerException not thrown: container must exist
- if ( count( $this->connContainers ) >= $this->maxContCacheSize ) { // trim cache?
- reset( $this->connContainers );
- unset( $this->connContainers[key( $this->connContainers )] );
- }
- $this->connContainers[$container] = $contObj; // cache it
- if ( !$bypassCache ) {
- $this->setContainerCache( $container, // update persistent cache
- array( 'bytes' => $contObj->bytes_used, 'count' => $contObj->object_count )
- );
- }
- }
- return $this->connContainers[$container];
- }
-
- /**
- * Create a Swift container
- *
- * @param $container string Container name
- * @return CF_Container
- * @throws CloudFilesException
- */
- protected function createContainer( $container ) {
- $conn = $this->getConnection(); // Swift proxy connection
- $contObj = $conn->create_container( $container );
- $this->connContainers[$container] = $contObj; // cache it
- return $contObj;
- }
-
- /**
- * Delete a Swift container
- *
- * @param $container string Container name
- * @return void
- * @throws CloudFilesException
- */
- protected function deleteContainer( $container ) {
- $conn = $this->getConnection(); // Swift proxy connection
- unset( $this->connContainers[$container] ); // purge cache
- $conn->delete_container( $container );
- }
-
- /**
- * @see FileBackendStore::doPrimeContainerCache()
- * @return void
- */
- protected function doPrimeContainerCache( array $containerInfo ) {
- try {
- $conn = $this->getConnection(); // Swift proxy connection
- foreach ( $containerInfo as $container => $info ) {
- $this->connContainers[$container] = new CF_Container(
- $conn->cfs_auth,
- $conn->cfs_http,
- $container,
- $info['count'],
- $info['bytes']
- );
- }
- } catch ( CloudFilesException $e ) { // some other exception?
- $this->handleException( $e, null, __METHOD__, array() );
- }
- }
-
- /**
- * Log an unexpected exception for this backend.
- * This also sets the Status object to have a fatal error.
- *
- * @param $e Exception
- * @param $status Status|null
- * @param $func string
- * @param $params Array
- * @return void
- */
- protected function handleException( Exception $e, $status, $func, array $params ) {
- if ( $status instanceof Status ) {
- if ( $e instanceof AuthenticationException ) {
- $status->fatal( 'backend-fail-connect', $this->name );
- } else {
- $status->fatal( 'backend-fail-internal', $this->name );
- }
- }
- if ( $e->getMessage() ) {
- trigger_error( "$func: " . $e->getMessage(), E_USER_WARNING );
- }
- wfDebugLog( 'SwiftBackend',
- get_class( $e ) . " in '{$func}' (given '" . FormatJson::encode( $params ) . "')" .
- ( $e->getMessage() ? ": {$e->getMessage()}" : "" )
- );
- }
-}
-
-/**
- * @see FileBackendStoreOpHandle
- */
-class SwiftFileOpHandle extends FileBackendStoreOpHandle {
- /** @var CF_Async_Op */
- public $cfOp;
- /** @var Array */
- public $affectedObjects = array();
-
- public function __construct( $backend, array $params, $call, CF_Async_Op $cfOp ) {
- $this->backend = $backend;
- $this->params = $params;
- $this->call = $call;
- $this->cfOp = $cfOp;
- }
-}
-
-/**
- * SwiftFileBackend helper class to page through listings.
- * Swift also has a listing limit of 10,000 objects for sanity.
- * Do not use this class from places outside SwiftFileBackend.
- *
- * @ingroup FileBackend
- */
-abstract class SwiftFileBackendList implements Iterator {
- /** @var Array */
- protected $bufferIter = array();
- protected $bufferAfter = null; // string; list items *after* this path
- protected $pos = 0; // integer
- /** @var Array */
- protected $params = array();
-
- /** @var SwiftFileBackend */
- protected $backend;
- protected $container; // string; container name
- protected $dir; // string; storage directory
- protected $suffixStart; // integer
-
- const PAGE_SIZE = 9000; // file listing buffer size
-
- /**
- * @param $backend SwiftFileBackend
- * @param $fullCont string Resolved container name
- * @param $dir string Resolved directory relative to container
- * @param $params Array
- */
- public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) {
- $this->backend = $backend;
- $this->container = $fullCont;
- $this->dir = $dir;
- if ( substr( $this->dir, -1 ) === '/' ) {
- $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
- }
- if ( $this->dir == '' ) { // whole container
- $this->suffixStart = 0;
- } else { // dir within container
- $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/"
- }
- $this->params = $params;
- }
-
- /**
- * @see Iterator::key()
- * @return integer
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::next()
- * @return void
- */
- public function next() {
- // Advance to the next file in the page
- next( $this->bufferIter );
- ++$this->pos;
- // Check if there are no files left in this page and
- // advance to the next page if this page was not empty.
- if ( !$this->valid() && count( $this->bufferIter ) ) {
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
- }
-
- /**
- * @see Iterator::rewind()
- * @return void
- */
- public function rewind() {
- $this->pos = 0;
- $this->bufferAfter = null;
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- if ( $this->bufferIter === null ) {
- return false; // some failure?
- } else {
- return ( current( $this->bufferIter ) !== false ); // no paths can have this value
- }
- }
-
- /**
- * Get the given list portion (page)
- *
- * @param $container string Resolved container name
- * @param $dir string Resolved path relative to container
- * @param $after string|null
- * @param $limit integer
- * @param $params Array
- * @return Traversable|Array|null Returns null on failure
- */
- abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params );
-}
-
-/**
- * Iterator for listing directories
- */
-class SwiftFileBackendDirList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- return substr( current( $this->bufferIter ), $this->suffixStart, -1 );
- }
-
- /**
- * @see SwiftFileBackendList::pageFromList()
- * @return Array|null
- */
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}
-
-/**
- * Iterator for listing regular files
- */
-class SwiftFileBackendFileList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- return substr( current( $this->bufferIter ), $this->suffixStart );
- }
-
- /**
- * @see SwiftFileBackendList::pageFromList()
- * @return Array|null
- */
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}
+++ /dev/null
-<?php
-/**
- * Location holder of files stored temporarily
- *
- * 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 FileBackend
- */
-
-/**
- * This class is used to hold the location and do limited manipulation
- * of files stored temporarily (this will be whatever wfTempDir() returns)
- *
- * @ingroup FileBackend
- */
-class TempFSFile extends FSFile {
- protected $canDelete = false; // bool; garbage collect the temp file
-
- /** @var Array of active temp files to purge on shutdown */
- protected static $instances = array();
-
- /**
- * Make a new temporary file on the file system.
- * Temporary files may be purged when the file object falls out of scope.
- *
- * @param $prefix string
- * @param $extension string
- * @return TempFSFile|null
- */
- public static function factory( $prefix, $extension = '' ) {
- $base = wfTempDir() . '/' . $prefix . dechex( mt_rand( 0, 99999999 ) );
- $ext = ( $extension != '' ) ? ".{$extension}" : "";
- for ( $attempt = 1; true; $attempt++ ) {
- $path = "{$base}-{$attempt}{$ext}";
- wfSuppressWarnings();
- $newFileHandle = fopen( $path, 'x' );
- wfRestoreWarnings();
- if ( $newFileHandle ) {
- fclose( $newFileHandle );
- break; // got it
- }
- if ( $attempt >= 15 ) {
- return null; // give up
- }
- }
- $tmpFile = new self( $path );
- $tmpFile->canDelete = true; // safely instantiated
- return $tmpFile;
- }
-
- /**
- * Purge this file off the file system
- *
- * @return bool Success
- */
- public function purge() {
- $this->canDelete = false; // done
- wfSuppressWarnings();
- $ok = unlink( $this->path );
- wfRestoreWarnings();
- return $ok;
- }
-
- /**
- * Clean up the temporary file only after an object goes out of scope
- *
- * @param $object Object
- * @return void
- */
- public function bind( $object ) {
- if ( is_object( $object ) ) {
- $object->tempFSFileReferences[] = $this;
- }
- }
-
- /**
- * Set flag to not clean up after the temporary file
- *
- * @return void
- */
- public function preserve() {
- $this->canDelete = false;
- }
-
- /**
- * Cleans up after the temporary file by deleting it
- */
- function __destruct() {
- if ( $this->canDelete ) {
- wfSuppressWarnings();
- unlink( $this->path );
- wfRestoreWarnings();
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * Version of FileJournal that logs to a DB table.
- *
- * 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 FileJournal
- * @author Aaron Schulz
- */
-
-/**
- * Version of FileJournal that logs to a DB table
- * @since 1.20
- */
-class DBFileJournal extends FileJournal {
- protected $wiki = false; // string; wiki DB name
-
- /**
- * Construct a new instance from configuration.
- * $config includes:
- * 'wiki' : wiki name to use for LoadBalancer
- *
- * @param $config Array
- */
- protected function __construct( array $config ) {
- parent::__construct( $config );
-
- $this->wiki = $config['wiki'];
- }
-
- /**
- * @see FileJournal::logChangeBatch()
- * @return Status
- */
- protected function doLogChangeBatch( array $entries, $batchId ) {
- $status = Status::newGood();
-
- try {
- $dbw = $this->getMasterDB();
- } catch ( DBError $e ) {
- $status->fatal( 'filejournal-fail-dbconnect', $this->backend );
- return $status;
- }
-
- $now = wfTimestamp( TS_UNIX );
-
- $data = array();
- foreach ( $entries as $entry ) {
- $data[] = array(
- 'fj_batch_uuid' => $batchId,
- 'fj_backend' => $this->backend,
- 'fj_op' => $entry['op'],
- 'fj_path' => $entry['path'],
- 'fj_path_sha1' => wfBaseConvert( sha1( $entry['path'] ), 16, 36, 31 ),
- 'fj_new_sha1' => $entry['newSha1'],
- 'fj_timestamp' => $dbw->timestamp( $now )
- );
- }
-
- try {
- $dbw->begin();
- $dbw->insert( 'filejournal', $data, __METHOD__ );
- $dbw->commit();
- } catch ( DBError $e ) {
- $status->fatal( 'filejournal-fail-dbquery', $this->backend );
- return $status;
- }
-
- return $status;
- }
-
- /**
- * @see FileJournal::doGetChangeEntries()
- * @return Array
- * @throws DBError
- */
- protected function doGetChangeEntries( $start, $limit ) {
- $dbw = $this->getMasterDB();
-
- $res = $dbw->select( 'filejournal', '*',
- array(
- 'fj_backend' => $this->backend,
- 'fj_id >= ' . $dbw->addQuotes( (int)$start ) ), // $start may be 0
- __METHOD__,
- array_merge( array( 'ORDER BY' => 'fj_id ASC' ),
- $limit ? array( 'LIMIT' => $limit ) : array() )
- );
-
- $entries = array();
- foreach ( $res as $row ) {
- $item = array();
- foreach ( (array)$row as $key => $value ) {
- $item[substr( $key, 3 )] = $value; // "fj_op" => "op"
- }
- $entries[] = $item;
- }
-
- return $entries;
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- * @throws DBError
- */
- protected function doPurgeOldLogs() {
- $status = Status::newGood();
- if ( $this->ttlDays <= 0 ) {
- return $status; // nothing to do
- }
-
- $dbw = $this->getMasterDB();
- $dbCutoff = $dbw->timestamp( time() - 86400 * $this->ttlDays );
-
- $dbw->begin();
- $dbw->delete( 'filejournal',
- array( 'fj_timestamp < ' . $dbw->addQuotes( $dbCutoff ) ),
- __METHOD__
- );
- $dbw->commit();
-
- return $status;
- }
-
- /**
- * Get a master connection to the logging DB
- *
- * @return DatabaseBase
- * @throws DBError
- */
- protected function getMasterDB() {
- $lb = wfGetLBFactory()->newMainLB();
- return $lb->getConnection( DB_MASTER, array(), $this->wiki );
- }
-}
+++ /dev/null
-<?php
-/**
- * @defgroup FileJournal File journal
- * @ingroup FileBackend
- */
-
-/**
- * File operation journaling.
- *
- * 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 FileJournal
- * @author Aaron Schulz
- */
-
-/**
- * @brief Class for handling file operation journaling.
- *
- * Subclasses should avoid throwing exceptions at all costs.
- *
- * @ingroup FileJournal
- * @since 1.20
- */
-abstract class FileJournal {
- protected $backend; // string
- protected $ttlDays; // integer
-
- /**
- * Construct a new instance from configuration.
- * $config includes:
- * 'ttlDays' : days to keep log entries around (false means "forever")
- *
- * @param $config Array
- */
- protected function __construct( array $config ) {
- $this->ttlDays = isset( $config['ttlDays'] ) ? $config['ttlDays'] : false;
- }
-
- /**
- * Create an appropriate FileJournal object from config
- *
- * @param $config Array
- * @param $backend string A registered file backend name
- * @throws MWException
- * @return FileJournal
- */
- final public static function factory( array $config, $backend ) {
- $class = $config['class'];
- $jrn = new $class( $config );
- if ( !$jrn instanceof self ) {
- throw new MWException( "Class given is not an instance of FileJournal." );
- }
- $jrn->backend = $backend;
- return $jrn;
- }
-
- /**
- * Get a statistically unique ID string
- *
- * @return string <9 char TS_MW timestamp in base 36><22 random base 36 chars>
- */
- final public function getTimestampedUUID() {
- $s = '';
- for ( $i = 0; $i < 5; $i++ ) {
- $s .= mt_rand( 0, 2147483647 );
- }
- $s = wfBaseConvert( sha1( $s ), 16, 36, 31 );
- return substr( wfBaseConvert( wfTimestamp( TS_MW ), 10, 36, 9 ) . $s, 0, 31 );
- }
-
- /**
- * Log changes made by a batch file operation.
- * $entries is an array of log entries, each of which contains:
- * op : Basic operation name (create, store, copy, delete)
- * path : The storage path of the file
- * newSha1 : The final base 36 SHA-1 of the file
- * Note that 'false' should be used as the SHA-1 for non-existing files.
- *
- * @param $entries Array List of file operations (each an array of parameters)
- * @param $batchId string UUID string that identifies the operation batch
- * @return Status
- */
- final public function logChangeBatch( array $entries, $batchId ) {
- if ( !count( $entries ) ) {
- return Status::newGood();
- }
- return $this->doLogChangeBatch( $entries, $batchId );
- }
-
- /**
- * @see FileJournal::logChangeBatch()
- *
- * @param $entries Array List of file operations (each an array of parameters)
- * @param $batchId string UUID string that identifies the operation batch
- * @return Status
- */
- abstract protected function doLogChangeBatch( array $entries, $batchId );
-
- /**
- * Get an array of file change log entries.
- * A starting change ID and/or limit can be specified.
- *
- * The result as a list of associative arrays, each having:
- * id : unique, monotonic, ID for this change
- * batch_uuid : UUID for an operation batch
- * backend : the backend name
- * op : primitive operation (create,update,delete)
- * path : affected storage path
- * path_sha1 : base 36 sha1 of the affected storage path
- * timestamp : TS_MW timestamp of the batch change
-
- * Also, $next is updated to the ID of the next entry.
- *
- * @param $start integer Starting change ID or null
- * @param $limit integer Maximum number of items to return
- * @param &$next string
- * @return Array
- */
- final public function getChangeEntries( $start = null, $limit = 0, &$next = null ) {
- $entries = $this->doGetChangeEntries( $start, $limit ? $limit + 1 : 0 );
- if ( $limit && count( $entries ) > $limit ) {
- $last = array_pop( $entries ); // remove the extra entry
- $next = $last['id']; // update for next call
- } else {
- $next = null; // end of list
- }
- return $entries;
- }
-
- /**
- * @see FileJournal::getChangeEntries()
- * @return Array
- */
- abstract protected function doGetChangeEntries( $start, $limit );
-
- /**
- * Purge any old log entries
- *
- * @return Status
- */
- final public function purgeOldLogs() {
- return $this->doPurgeOldLogs();
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- */
- abstract protected function doPurgeOldLogs();
-}
-
-/**
- * Simple version of FileJournal that does nothing
- * @since 1.20
- */
-class NullFileJournal extends FileJournal {
- /**
- * @see FileJournal::logChangeBatch()
- * @param $entries array
- * @param $batchId string
- * @return Status
- */
- protected function doLogChangeBatch( array $entries, $batchId ) {
- return Status::newGood();
- }
-
- /**
- * @see FileJournal::doGetChangeEntries()
- * @return Array
- */
- protected function doGetChangeEntries( $start, $limit ) {
- return array();
- }
-
- /**
- * @see FileJournal::purgeOldLogs()
- * @return Status
- */
- protected function doPurgeOldLogs() {
- return Status::newGood();
- }
-}
+++ /dev/null
-<?php
-/**
- * Version of LockManager based on using DB table locks.
- *
- * 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 LockManager
- */
-
-/**
- * Version of LockManager based on using DB table locks.
- * This is meant for multi-wiki systems that may share files.
- * All locks are blocking, so it might be useful to set a small
- * lock-wait timeout via server config to curtail deadlocks.
- *
- * All lock requests for a resource, identified by a hash string, will map
- * to one bucket. Each bucket maps to one or several peer DBs, each on their
- * own server, all having the filelocks.sql tables (with row-level locking).
- * A majority of peer DBs must agree for a lock to be acquired.
- *
- * Caching is used to avoid hitting servers that are down.
- *
- * @ingroup LockManager
- * @since 1.19
- */
-class DBLockManager extends LockManager {
- /** @var Array Map of DB names to server config */
- protected $dbServers; // (DB name => server config array)
- /** @var Array Map of bucket indexes to peer DB lists */
- protected $dbsByBucket; // (bucket index => (ldb1, ldb2, ...))
- /** @var BagOStuff */
- protected $statusCache;
-
- protected $lockExpiry; // integer number of seconds
- protected $safeDelay; // integer number of seconds
-
- protected $session = 0; // random integer
- /** @var Array Map Database connections (DB name => Database) */
- protected $conns = array();
-
- /**
- * Construct a new instance from configuration.
- *
- * $config paramaters include:
- * 'dbServers' : Associative array of DB names to server configuration.
- * Configuration is an associative array that includes:
- * 'host' - DB server name
- * 'dbname' - DB name
- * 'type' - DB type (mysql,postgres,...)
- * 'user' - DB user
- * 'password' - DB user password
- * 'tablePrefix' - DB table prefix
- * 'flags' - DB flags (see DatabaseBase)
- * 'dbsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
- * each having an odd-numbered list of DB names (peers) as values.
- * Any DB named 'localDBMaster' will automatically use the DB master
- * settings for this wiki (without the need for a dbServers entry).
- * 'lockExpiry' : Lock timeout (seconds) for dropped connections. [optional]
- * This tells the DB server how long to wait before assuming
- * connection failure and releasing all the locks for a session.
- *
- * @param Array $config
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
-
- $this->dbServers = isset( $config['dbServers'] )
- ? $config['dbServers']
- : array(); // likely just using 'localDBMaster'
- // Sanitize dbsByBucket config to prevent PHP errors
- $this->dbsByBucket = array_filter( $config['dbsByBucket'], 'is_array' );
- $this->dbsByBucket = array_values( $this->dbsByBucket ); // consecutive
-
- if ( isset( $config['lockExpiry'] ) ) {
- $this->lockExpiry = $config['lockExpiry'];
- } else {
- $met = ini_get( 'max_execution_time' );
- $this->lockExpiry = $met ? $met : 60; // use some sane amount if 0
- }
- $this->safeDelay = ( $this->lockExpiry <= 0 )
- ? 60 // pick a safe-ish number to match DB timeout default
- : $this->lockExpiry; // cover worst case
-
- foreach ( $this->dbsByBucket as $bucket ) {
- if ( count( $bucket ) > 1 ) {
- // Tracks peers that couldn't be queried recently to avoid lengthy
- // connection timeouts. This is useless if each bucket has one peer.
- $this->statusCache = wfGetMainCache();
- break;
- }
- }
-
- $this->session = '';
- for ( $i = 0; $i < 5; $i++ ) {
- $this->session .= mt_rand( 0, 2147483647 );
- }
- $this->session = wfBaseConvert( sha1( $this->session ), 16, 36, 31 );
- }
-
- /**
- * @see LockManager::doLock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doLock( array $paths, $type ) {
- $status = Status::newGood();
-
- $pathsToLock = array();
- // Get locks that need to be acquired (buckets => locks)...
- foreach ( $paths as $path ) {
- if ( isset( $this->locksHeld[$path][$type] ) ) {
- ++$this->locksHeld[$path][$type];
- } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
- $this->locksHeld[$path][$type] = 1;
- } else {
- $bucket = $this->getBucketFromKey( $path );
- $pathsToLock[$bucket][] = $path;
- }
- }
-
- $lockedPaths = array(); // files locked in this attempt
- // Attempt to acquire these locks...
- foreach ( $pathsToLock as $bucket => $paths ) {
- // Try to acquire the locks for this bucket
- $res = $this->doLockingQueryAll( $bucket, $paths, $type );
- if ( $res === 'cantacquire' ) {
- // Resources already locked by another process.
- // Abort and unlock everything we just locked.
- foreach ( $paths as $path ) {
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- $status->merge( $this->doUnlock( $lockedPaths, $type ) );
- return $status;
- } elseif ( $res !== true ) {
- // Couldn't contact any DBs for this bucket.
- // Abort and unlock everything we just locked.
- $status->fatal( 'lockmanager-fail-db-bucket', $bucket );
- $status->merge( $this->doUnlock( $lockedPaths, $type ) );
- return $status;
- }
- // Record these locks as active
- foreach ( $paths as $path ) {
- $this->locksHeld[$path][$type] = 1; // locked
- }
- // Keep track of what locks were made in this attempt
- $lockedPaths = array_merge( $lockedPaths, $paths );
- }
-
- return $status;
- }
-
- /**
- * @see LockManager::doUnlock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doUnlock( array $paths, $type ) {
- $status = Status::newGood();
-
- foreach ( $paths as $path ) {
- if ( !isset( $this->locksHeld[$path] ) ) {
- $status->warning( 'lockmanager-notlocked', $path );
- } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
- $status->warning( 'lockmanager-notlocked', $path );
- } else {
- --$this->locksHeld[$path][$type];
- if ( $this->locksHeld[$path][$type] <= 0 ) {
- unset( $this->locksHeld[$path][$type] );
- }
- if ( !count( $this->locksHeld[$path] ) ) {
- unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
- }
- }
- }
-
- // Reference count the locks held and COMMIT when zero
- if ( !count( $this->locksHeld ) ) {
- $status->merge( $this->finishLockTransactions() );
- }
-
- return $status;
- }
-
- /**
- * Get a connection to a lock DB and acquire locks on $paths.
- * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
- *
- * @param $lockDb string
- * @param $paths Array
- * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
- * @return bool Resources able to be locked
- * @throws DBError
- */
- protected function doLockingQuery( $lockDb, array $paths, $type ) {
- if ( $type == self::LOCK_EX ) { // writer locks
- $db = $this->getConnection( $lockDb );
- if ( !$db ) {
- return false; // bad config
- }
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
- # Build up values for INSERT clause
- $data = array();
- foreach ( $keys as $key ) {
- $data[] = array( 'fle_key' => $key );
- }
- # Wait on any existing writers and block new ones if we get in
- $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
- }
- return true;
- }
-
- /**
- * Attempt to acquire locks with the peers for a bucket.
- * This should avoid throwing any exceptions.
- *
- * @param $bucket integer
- * @param $paths Array List of resource keys to lock
- * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
- * @return bool|string One of (true, 'cantacquire', 'dberrors')
- */
- protected function doLockingQueryAll( $bucket, array $paths, $type ) {
- $yesVotes = 0; // locks made on trustable DBs
- $votesLeft = count( $this->dbsByBucket[$bucket] ); // remaining DBs
- $quorum = floor( $votesLeft/2 + 1 ); // simple majority
- // Get votes for each DB, in order, until we have enough...
- foreach ( $this->dbsByBucket[$bucket] as $lockDb ) {
- // Check that DB is not *known* to be down
- if ( $this->cacheCheckFailures( $lockDb ) ) {
- try {
- // Attempt to acquire the lock on this DB
- if ( !$this->doLockingQuery( $lockDb, $paths, $type ) ) {
- return 'cantacquire'; // vetoed; resource locked
- }
- ++$yesVotes; // success for this peer
- if ( $yesVotes >= $quorum ) {
- return true; // lock obtained
- }
- } catch ( DBConnectionError $e ) {
- $this->cacheRecordFailure( $lockDb );
- } catch ( DBError $e ) {
- if ( $this->lastErrorIndicatesLocked( $lockDb ) ) {
- return 'cantacquire'; // vetoed; resource locked
- }
- }
- }
- --$votesLeft;
- $votesNeeded = $quorum - $yesVotes;
- if ( $votesNeeded > $votesLeft ) {
- // In "trust cache" mode we don't have to meet the quorum
- break; // short-circuit
- }
- }
- // At this point, we must not have meet the quorum
- return 'dberrors'; // not enough votes to ensure correctness
- }
-
- /**
- * Get (or reuse) a connection to a lock DB
- *
- * @param $lockDb string
- * @return DatabaseBase
- * @throws DBError
- */
- protected function getConnection( $lockDb ) {
- if ( !isset( $this->conns[$lockDb] ) ) {
- $db = null;
- if ( $lockDb === 'localDBMaster' ) {
- $lb = wfGetLBFactory()->newMainLB();
- $db = $lb->getConnection( DB_MASTER );
- } elseif ( isset( $this->dbServers[$lockDb] ) ) {
- $config = $this->dbServers[$lockDb];
- $db = DatabaseBase::factory( $config['type'], $config );
- }
- if ( !$db ) {
- return null; // config error?
- }
- $this->conns[$lockDb] = $db;
- $this->conns[$lockDb]->clearFlag( DBO_TRX );
- # If the connection drops, try to avoid letting the DB rollback
- # and release the locks before the file operations are finished.
- # This won't handle the case of DB server restarts however.
- $options = array();
- if ( $this->lockExpiry > 0 ) {
- $options['connTimeout'] = $this->lockExpiry;
- }
- $this->conns[$lockDb]->setSessionOptions( $options );
- $this->initConnection( $lockDb, $this->conns[$lockDb] );
- }
- if ( !$this->conns[$lockDb]->trxLevel() ) {
- $this->conns[$lockDb]->begin( __METHOD__ ); // start transaction
- }
- return $this->conns[$lockDb];
- }
-
- /**
- * Do additional initialization for new lock DB connection
- *
- * @param $lockDb string
- * @param $db DatabaseBase
- * @return void
- * @throws DBError
- */
- protected function initConnection( $lockDb, DatabaseBase $db ) {}
-
- /**
- * Commit all changes to lock-active databases.
- * This should avoid throwing any exceptions.
- *
- * @return Status
- */
- protected function finishLockTransactions() {
- $status = Status::newGood();
- foreach ( $this->conns as $lockDb => $db ) {
- if ( $db->trxLevel() ) { // in transaction
- try {
- $db->rollback( __METHOD__ ); // finish transaction and kill any rows
- } catch ( DBError $e ) {
- $status->fatal( 'lockmanager-fail-db-release', $lockDb );
- }
- }
- }
- return $status;
- }
-
- /**
- * Check if the last DB error for $lockDb indicates
- * that a requested resource was locked by another process.
- * This should avoid throwing any exceptions.
- *
- * @param $lockDb string
- * @return bool
- */
- protected function lastErrorIndicatesLocked( $lockDb ) {
- if ( isset( $this->conns[$lockDb] ) ) { // sanity
- $db = $this->conns[$lockDb];
- return ( $db->wasDeadlock() || $db->wasLockTimeout() );
- }
- return false;
- }
-
- /**
- * Checks if the DB has not recently had connection/query errors.
- * This just avoids wasting time on doomed connection attempts.
- *
- * @param $lockDb string
- * @return bool
- */
- protected function cacheCheckFailures( $lockDb ) {
- if ( $this->statusCache && $this->safeDelay > 0 ) {
- $path = $this->getMissKey( $lockDb );
- $misses = $this->statusCache->get( $path );
- return !$misses;
- }
- return true;
- }
-
- /**
- * Log a lock request failure to the cache
- *
- * @param $lockDb string
- * @return bool Success
- */
- protected function cacheRecordFailure( $lockDb ) {
- if ( $this->statusCache && $this->safeDelay > 0 ) {
- $path = $this->getMissKey( $lockDb );
- $misses = $this->statusCache->get( $path );
- if ( $misses ) {
- return $this->statusCache->incr( $path );
- } else {
- return $this->statusCache->add( $path, 1, $this->safeDelay );
- }
- }
- return true;
- }
-
- /**
- * Get a cache key for recent query misses for a DB
- *
- * @param $lockDb string
- * @return string
- */
- protected function getMissKey( $lockDb ) {
- return 'lockmanager:querymisses:' . str_replace( ' ', '_', $lockDb );
- }
-
- /**
- * Get the bucket for resource path.
- * This should avoid throwing any exceptions.
- *
- * @param $path string
- * @return integer
- */
- protected function getBucketFromKey( $path ) {
- $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars (8 bits)
- return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->dbsByBucket );
- }
-
- /**
- * Make sure remaining locks get cleared for sanity
- */
- function __destruct() {
- foreach ( $this->conns as $lockDb => $db ) {
- if ( $db->trxLevel() ) { // in transaction
- try {
- $db->rollback( __METHOD__ ); // finish transaction and kill any rows
- } catch ( DBError $e ) {
- // oh well
- }
- }
- $db->close();
- }
- }
-}
-
-/**
- * MySQL version of DBLockManager that supports shared locks.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * @ingroup LockManager
- */
-class MySqlLockManager extends DBLockManager {
- /** @var Array Mapping of lock types to the type actually used */
- protected $lockTypeMap = array(
- self::LOCK_SH => self::LOCK_SH,
- self::LOCK_UW => self::LOCK_SH,
- self::LOCK_EX => self::LOCK_EX
- );
-
- /**
- * @param $lockDb string
- * @param $db DatabaseBase
- */
- protected function initConnection( $lockDb, DatabaseBase $db ) {
- # Let this transaction see lock rows from other transactions
- $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
- }
-
- /**
- * @param $lockDb string
- * @param $paths array
- * @param $type int
- * @return bool
- */
- protected function doLockingQuery( $lockDb, array $paths, $type ) {
- $db = $this->getConnection( $lockDb );
- if ( !$db ) {
- return false;
- }
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
- # Build up values for INSERT clause
- $data = array();
- foreach ( $keys as $key ) {
- $data[] = array( 'fls_key' => $key, 'fls_session' => $this->session );
- }
- # Block new writers...
- $db->insert( 'filelocks_shared', $data, __METHOD__, array( 'IGNORE' ) );
- # Actually do the locking queries...
- if ( $type == self::LOCK_SH ) { // reader locks
- # Bail if there are any existing writers...
- $blocked = $db->selectField( 'filelocks_exclusive', '1',
- array( 'fle_key' => $keys ),
- __METHOD__
- );
- # Prospective writers that haven't yet updated filelocks_exclusive
- # will recheck filelocks_shared after doing so and bail due to our entry.
- } else { // writer locks
- $encSession = $db->addQuotes( $this->session );
- # Bail if there are any existing writers...
- # The may detect readers, but the safe check for them is below.
- # Note: if two writers come at the same time, both bail :)
- $blocked = $db->selectField( 'filelocks_shared', '1',
- array( 'fls_key' => $keys, "fls_session != $encSession" ),
- __METHOD__
- );
- if ( !$blocked ) {
- # Build up values for INSERT clause
- $data = array();
- foreach ( $keys as $key ) {
- $data[] = array( 'fle_key' => $key );
- }
- # Block new readers/writers...
- $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
- # Bail if there are any existing readers...
- $blocked = $db->selectField( 'filelocks_shared', '1',
- array( 'fls_key' => $keys, "fls_session != $encSession" ),
- __METHOD__
- );
- }
- }
- return !$blocked;
- }
-}
+++ /dev/null
-<?php
-/**
- * Simple version of LockManager based on using FS lock files.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup LockManager
- */
-
-/**
- * Simple version of LockManager based on using FS lock files.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * This should work fine for small sites running off one server.
- * Do not use this with 'lockDirectory' set to an NFS mount unless the
- * NFS client is at least version 2.6.12. Otherwise, the BSD flock()
- * locks will be ignored; see http://nfs.sourceforge.net/#section_d.
- *
- * @ingroup LockManager
- * @since 1.19
- */
-class FSLockManager extends LockManager {
- /** @var Array Mapping of lock types to the type actually used */
- protected $lockTypeMap = array(
- self::LOCK_SH => self::LOCK_SH,
- self::LOCK_UW => self::LOCK_SH,
- self::LOCK_EX => self::LOCK_EX
- );
-
- protected $lockDir; // global dir for all servers
-
- /** @var Array Map of (locked key => lock type => lock file handle) */
- protected $handles = array();
-
- /**
- * Construct a new instance from configuration.
- *
- * $config includes:
- * 'lockDirectory' : Directory containing the lock files
- *
- * @param array $config
- */
- function __construct( array $config ) {
- parent::__construct( $config );
-
- $this->lockDir = $config['lockDirectory'];
- }
-
- /**
- * @see LockManager::doLock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doLock( array $paths, $type ) {
- $status = Status::newGood();
-
- $lockedPaths = array(); // files locked in this attempt
- foreach ( $paths as $path ) {
- $status->merge( $this->doSingleLock( $path, $type ) );
- if ( $status->isOK() ) {
- $lockedPaths[] = $path;
- } else {
- // Abort and unlock everything
- $status->merge( $this->doUnlock( $lockedPaths, $type ) );
- return $status;
- }
- }
-
- return $status;
- }
-
- /**
- * @see LockManager::doUnlock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doUnlock( array $paths, $type ) {
- $status = Status::newGood();
-
- foreach ( $paths as $path ) {
- $status->merge( $this->doSingleUnlock( $path, $type ) );
- }
-
- return $status;
- }
-
- /**
- * Lock a single resource key
- *
- * @param $path string
- * @param $type integer
- * @return Status
- */
- protected function doSingleLock( $path, $type ) {
- $status = Status::newGood();
-
- if ( isset( $this->locksHeld[$path][$type] ) ) {
- ++$this->locksHeld[$path][$type];
- } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
- $this->locksHeld[$path][$type] = 1;
- } else {
- wfSuppressWarnings();
- $handle = fopen( $this->getLockPath( $path ), 'a+' );
- wfRestoreWarnings();
- if ( !$handle ) { // lock dir missing?
- wfMkdirParents( $this->lockDir );
- $handle = fopen( $this->getLockPath( $path ), 'a+' ); // try again
- }
- if ( $handle ) {
- // Either a shared or exclusive lock
- $lock = ( $type == self::LOCK_SH ) ? LOCK_SH : LOCK_EX;
- if ( flock( $handle, $lock | LOCK_NB ) ) {
- // Record this lock as active
- $this->locksHeld[$path][$type] = 1;
- $this->handles[$path][$type] = $handle;
- } else {
- fclose( $handle );
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- } else {
- $status->fatal( 'lockmanager-fail-openlock', $path );
- }
- }
-
- return $status;
- }
-
- /**
- * Unlock a single resource key
- *
- * @param $path string
- * @param $type integer
- * @return Status
- */
- protected function doSingleUnlock( $path, $type ) {
- $status = Status::newGood();
-
- if ( !isset( $this->locksHeld[$path] ) ) {
- $status->warning( 'lockmanager-notlocked', $path );
- } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
- $status->warning( 'lockmanager-notlocked', $path );
- } else {
- $handlesToClose = array();
- --$this->locksHeld[$path][$type];
- if ( $this->locksHeld[$path][$type] <= 0 ) {
- unset( $this->locksHeld[$path][$type] );
- // If a LOCK_SH comes in while we have a LOCK_EX, we don't
- // actually add a handler, so check for handler existence.
- if ( isset( $this->handles[$path][$type] ) ) {
- if ( $type === self::LOCK_EX
- && isset( $this->locksHeld[$path][self::LOCK_SH] )
- && !isset( $this->handles[$path][self::LOCK_SH] ) )
- {
- // EX lock came first: move this handle to the SH one
- $this->handles[$path][self::LOCK_SH] = $this->handles[$path][$type];
- } else {
- // Mark this handle to be unlocked and closed
- $handlesToClose[] = $this->handles[$path][$type];
- }
- unset( $this->handles[$path][$type] );
- }
- }
- if ( !count( $this->locksHeld[$path] ) ) {
- unset( $this->locksHeld[$path] ); // no locks on this path
- }
- // Unlock handles to release locks and delete
- // any lock files that end up with no locks on them...
- if ( wfIsWindows() ) {
- // Windows: for any process, including this one,
- // calling unlink() on a locked file will fail
- $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
- $status->merge( $this->pruneKeyLockFiles( $path ) );
- } else {
- // Unix: unlink() can be used on files currently open by this
- // process and we must do so in order to avoid race conditions
- $status->merge( $this->pruneKeyLockFiles( $path ) );
- $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
- }
- }
-
- return $status;
- }
-
- /**
- * @param $path string
- * @param $handlesToClose array
- * @return Status
- */
- private function closeLockHandles( $path, array $handlesToClose ) {
- $status = Status::newGood();
- foreach ( $handlesToClose as $handle ) {
- if ( !flock( $handle, LOCK_UN ) ) {
- $status->fatal( 'lockmanager-fail-releaselock', $path );
- }
- if ( !fclose( $handle ) ) {
- $status->warning( 'lockmanager-fail-closelock', $path );
- }
- }
- return $status;
- }
-
- /**
- * @param $path string
- * @return Status
- */
- private function pruneKeyLockFiles( $path ) {
- $status = Status::newGood();
- if ( !isset( $this->locksHeld[$path] ) ) {
- # No locks are held for the lock file anymore
- if ( !unlink( $this->getLockPath( $path ) ) ) {
- $status->warning( 'lockmanager-fail-deletelock', $path );
- }
- unset( $this->handles[$path] );
- }
- return $status;
- }
-
- /**
- * Get the path to the lock file for a key
- * @param $path string
- * @return string
- */
- protected function getLockPath( $path ) {
- $hash = self::sha1Base36( $path );
- return "{$this->lockDir}/{$hash}.lock";
- }
-
- /**
- * Make sure remaining locks get cleared for sanity
- */
- function __destruct() {
- while ( count( $this->locksHeld ) ) {
- foreach ( $this->locksHeld as $path => $locks ) {
- $this->doSingleUnlock( $path, self::LOCK_EX );
- $this->doSingleUnlock( $path, self::LOCK_SH );
- }
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * Version of LockManager based on using lock daemon servers.
- *
- * 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 LockManager
- */
-
-/**
- * Manage locks using a lock daemon server.
- *
- * Version of LockManager based on using lock daemon servers.
- * This is meant for multi-wiki systems that may share files.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * All lock requests for a resource, identified by a hash string, will map
- * to one bucket. Each bucket maps to one or several peer servers, each
- * running LockServerDaemon.php, listening on a designated TCP port.
- * A majority of peers must agree for a lock to be acquired.
- *
- * @ingroup LockManager
- * @since 1.19
- */
-class LSLockManager extends QuorumLockManager {
- /** @var Array Mapping of lock types to the type actually used */
- protected $lockTypeMap = array(
- self::LOCK_SH => self::LOCK_SH,
- self::LOCK_UW => self::LOCK_SH,
- self::LOCK_EX => self::LOCK_EX
- );
-
- /** @var Array Map of server names to server config */
- protected $lockServers; // (server name => server config array)
-
- /** @var Array Map Server connections (server name => resource) */
- protected $conns = array();
-
- protected $connTimeout; // float number of seconds
- protected $session = ''; // random SHA-1 string
-
- /**
- * Construct a new instance from configuration.
- *
- * $config paramaters include:
- * - lockServers : Associative array of server names to configuration.
- * Configuration is an associative array that includes:
- * - host : IP address/hostname
- * - port : TCP port
- * - authKey : Secret string the lock server uses
- * - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0,
- * each having an odd-numbered list of server names (peers) as values.
- * - connTimeout : Lock server connection attempt timeout. [optional]
- *
- * @param Array $config
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
-
- $this->lockServers = $config['lockServers'];
- // Sanitize srvsByBucket config to prevent PHP errors
- $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' );
- $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
-
- if ( isset( $config['connTimeout'] ) ) {
- $this->connTimeout = $config['connTimeout'];
- } else {
- $this->connTimeout = 3; // use some sane amount
- }
-
- $this->session = wfRandomString( 32 ); // 128 bits
- }
-
- /**
- * @see QuorumLockManager::getLocksOnServer()
- * @return Status
- */
- protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
- $status = Status::newGood();
-
- // Send out the command and get the response...
- $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
- $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys );
-
- if ( $response !== 'ACQUIRED' ) {
- foreach ( $paths as $path ) {
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- }
-
- return $status;
- }
-
- /**
- * @see QuorumLockManager::freeLocksOnServer()
- * @return Status
- */
- protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
- $status = Status::newGood();
-
- // Send out the command and get the response...
- $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
- $response = $this->sendCommand( $lockSrv, 'RELEASE', $type, $keys );
-
- if ( $response !== 'RELEASED' ) {
- foreach ( $paths as $path ) {
- $status->fatal( 'lockmanager-fail-releaselock', $path );
- }
- }
-
- return $status;
- }
-
- /**
- * @see QuorumLockManager::releaseAllLocks()
- * @return Status
- */
- protected function releaseAllLocks() {
- $status = Status::newGood();
-
- foreach ( $this->conns as $lockSrv => $conn ) {
- $response = $this->sendCommand( $lockSrv, 'RELEASE_ALL', '', array() );
- if ( $response !== 'RELEASED_ALL' ) {
- $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
- }
- }
-
- return $status;
- }
-
- /**
- * @see QuorumLockManager::isServerUp()
- * @return bool
- */
- protected function isServerUp( $lockSrv ) {
- return (bool)$this->getConnection( $lockSrv );
- }
-
- /**
- * Send a command and get back the response
- *
- * @param $lockSrv string
- * @param $action string
- * @param $type string
- * @param $values Array
- * @return string|bool
- */
- protected function sendCommand( $lockSrv, $action, $type, $values ) {
- $conn = $this->getConnection( $lockSrv );
- if ( !$conn ) {
- return false; // no connection
- }
- $authKey = $this->lockServers[$lockSrv]['authKey'];
- // Build of the command as a flat string...
- $values = implode( '|', $values );
- $key = sha1( $this->session . $action . $type . $values . $authKey );
- // Send out the command...
- if ( fwrite( $conn, "{$this->session}:$key:$action:$type:$values\n" ) === false ) {
- return false;
- }
- // Get the response...
- $response = fgets( $conn );
- if ( $response === false ) {
- return false;
- }
- return trim( $response );
- }
-
- /**
- * Get (or reuse) a connection to a lock server
- *
- * @param $lockSrv string
- * @return resource
- */
- protected function getConnection( $lockSrv ) {
- if ( !isset( $this->conns[$lockSrv] ) ) {
- $cfg = $this->lockServers[$lockSrv];
- wfSuppressWarnings();
- $errno = $errstr = '';
- $conn = fsockopen( $cfg['host'], $cfg['port'], $errno, $errstr, $this->connTimeout );
- wfRestoreWarnings();
- if ( $conn === false ) {
- return null;
- }
- $sec = floor( $this->connTimeout );
- $usec = floor( ( $this->connTimeout - floor( $this->connTimeout ) ) * 1e6 );
- stream_set_timeout( $conn, $sec, $usec );
- $this->conns[$lockSrv] = $conn;
- }
- return $this->conns[$lockSrv];
- }
-
- /**
- * Make sure remaining locks get cleared for sanity
- */
- function __destruct() {
- $this->releaseAllLocks();
- foreach ( $this->conns as $conn ) {
- fclose( $conn );
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * @defgroup LockManager Lock management
- * @ingroup FileBackend
- */
-
-/**
- * Resource locking handling.
- *
- * 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 LockManager
- * @author Aaron Schulz
- */
-
-/**
- * @brief Class for handling resource locking.
- *
- * Locks on resource keys can either be shared or exclusive.
- *
- * Implementations must keep track of what is locked by this proccess
- * in-memory and support nested locking calls (using reference counting).
- * At least LOCK_UW and LOCK_EX must be implemented. LOCK_SH can be a no-op.
- * Locks should either be non-blocking or have low wait timeouts.
- *
- * Subclasses should avoid throwing exceptions at all costs.
- *
- * @ingroup LockManager
- * @since 1.19
- */
-abstract class LockManager {
- /** @var Array Mapping of lock types to the type actually used */
- protected $lockTypeMap = array(
- self::LOCK_SH => self::LOCK_SH,
- self::LOCK_UW => self::LOCK_EX, // subclasses may use self::LOCK_SH
- self::LOCK_EX => self::LOCK_EX
- );
-
- /** @var Array Map of (resource path => lock type => count) */
- protected $locksHeld = array();
-
- /* Lock types; stronger locks have higher values */
- const LOCK_SH = 1; // shared lock (for reads)
- const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
- const LOCK_EX = 3; // exclusive lock (for writes)
-
- /**
- * Construct a new instance from configuration
- *
- * @param $config Array
- */
- public function __construct( array $config ) {}
-
- /**
- * Lock the resources at the given abstract paths
- *
- * @param $paths Array List of resource names
- * @param $type integer LockManager::LOCK_* constant
- * @return Status
- */
- final public function lock( array $paths, $type = self::LOCK_EX ) {
- wfProfileIn( __METHOD__ );
- $status = $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * Unlock the resources at the given abstract paths
- *
- * @param $paths Array List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @return Status
- */
- final public function unlock( array $paths, $type = self::LOCK_EX ) {
- wfProfileIn( __METHOD__ );
- $status = $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * Get the base 36 SHA-1 of a string, padded to 31 digits
- *
- * @param $path string
- * @return string
- */
- final protected static function sha1Base36( $path ) {
- return wfBaseConvert( sha1( $path ), 16, 36, 31 );
- }
-
- /**
- * Lock resources with the given keys and lock type
- *
- * @param $paths Array List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @return string
- */
- abstract protected function doLock( array $paths, $type );
-
- /**
- * Unlock resources with the given keys and lock type
- *
- * @param $paths Array List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @return string
- */
- abstract protected function doUnlock( array $paths, $type );
-}
-
-/**
- * Self-releasing locks
- *
- * LockManager helper class to handle scoped locks, which
- * release when an object is destroyed or goes out of scope.
- *
- * @ingroup LockManager
- * @since 1.19
- */
-class ScopedLock {
- /** @var LockManager */
- protected $manager;
- /** @var Status */
- protected $status;
- /** @var Array List of resource paths*/
- protected $paths;
-
- protected $type; // integer lock type
-
- /**
- * @param $manager LockManager
- * @param $paths Array List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
- */
- protected function __construct(
- LockManager $manager, array $paths, $type, Status $status
- ) {
- $this->manager = $manager;
- $this->paths = $paths;
- $this->status = $status;
- $this->type = $type;
- }
-
- /**
- * Get a ScopedLock object representing a lock on resource paths.
- * Any locks are released once this object goes out of scope.
- * The status object is updated with any errors or warnings.
- *
- * @param $manager LockManager
- * @param $paths Array List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
- * @return ScopedLock|null Returns null on failure
- */
- public static function factory(
- LockManager $manager, array $paths, $type, Status $status
- ) {
- $lockStatus = $manager->lock( $paths, $type );
- $status->merge( $lockStatus );
- if ( $lockStatus->isOK() ) {
- return new self( $manager, $paths, $type, $status );
- }
- return null;
- }
-
- function __destruct() {
- $wasOk = $this->status->isOK();
- $this->status->merge( $this->manager->unlock( $this->paths, $this->type ) );
- if ( $wasOk ) {
- // Make sure status is OK, despite any unlockFiles() fatals
- $this->status->setResult( true, $this->status->value );
- }
- }
-}
-
-/**
- * Version of LockManager that uses a quorum from peer servers for locks.
- * The resource space can also be sharded into separate peer groups.
- *
- * @ingroup LockManager
- * @since 1.20
- */
-abstract class QuorumLockManager extends LockManager {
- /** @var Array Map of bucket indexes to peer server lists */
- protected $srvsByBucket = array(); // (bucket index => (lsrv1, lsrv2, ...))
-
- /**
- * @see LockManager::doLock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- final protected function doLock( array $paths, $type ) {
- $status = Status::newGood();
-
- $pathsToLock = array(); // (bucket => paths)
- // Get locks that need to be acquired (buckets => locks)...
- foreach ( $paths as $path ) {
- if ( isset( $this->locksHeld[$path][$type] ) ) {
- ++$this->locksHeld[$path][$type];
- } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
- $this->locksHeld[$path][$type] = 1;
- } else {
- $bucket = $this->getBucketFromKey( $path );
- $pathsToLock[$bucket][] = $path;
- }
- }
-
- $lockedPaths = array(); // files locked in this attempt
- // Attempt to acquire these locks...
- foreach ( $pathsToLock as $bucket => $paths ) {
- // Try to acquire the locks for this bucket
- $status->merge( $this->doLockingRequestBucket( $bucket, $paths, $type ) );
- if ( !$status->isOK() ) {
- $status->merge( $this->doUnlock( $lockedPaths, $type ) );
- return $status;
- }
- // Record these locks as active
- foreach ( $paths as $path ) {
- $this->locksHeld[$path][$type] = 1; // locked
- }
- // Keep track of what locks were made in this attempt
- $lockedPaths = array_merge( $lockedPaths, $paths );
- }
-
- return $status;
- }
-
- /**
- * @see LockManager::doUnlock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- final protected function doUnlock( array $paths, $type ) {
- $status = Status::newGood();
-
- $pathsToUnlock = array();
- foreach ( $paths as $path ) {
- if ( !isset( $this->locksHeld[$path][$type] ) ) {
- $status->warning( 'lockmanager-notlocked', $path );
- } else {
- --$this->locksHeld[$path][$type];
- // Reference count the locks held and release locks when zero
- if ( $this->locksHeld[$path][$type] <= 0 ) {
- unset( $this->locksHeld[$path][$type] );
- $bucket = $this->getBucketFromKey( $path );
- $pathsToUnlock[$bucket][] = $path;
- }
- if ( !count( $this->locksHeld[$path] ) ) {
- unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
- }
- }
- }
-
- // Remove these specific locks if possible, or at least release
- // all locks once this process is currently not holding any locks.
- foreach ( $pathsToUnlock as $bucket => $paths ) {
- $status->merge( $this->doUnlockingRequestBucket( $bucket, $paths, $type ) );
- }
- if ( !count( $this->locksHeld ) ) {
- $status->merge( $this->releaseAllLocks() );
- }
-
- return $status;
- }
-
- /**
- * Attempt to acquire locks with the peers for a bucket.
- * This is all or nothing; if any key is locked then this totally fails.
- *
- * @param $bucket integer
- * @param $paths Array List of resource keys to lock
- * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
- * @return Status
- */
- final protected function doLockingRequestBucket( $bucket, array $paths, $type ) {
- $status = Status::newGood();
-
- $yesVotes = 0; // locks made on trustable servers
- $votesLeft = count( $this->srvsByBucket[$bucket] ); // remaining peers
- $quorum = floor( $votesLeft/2 + 1 ); // simple majority
- // Get votes for each peer, in order, until we have enough...
- foreach ( $this->srvsByBucket[$bucket] as $lockSrv ) {
- if ( !$this->isServerUp( $lockSrv ) ) {
- --$votesLeft;
- $status->warning( 'lockmanager-fail-svr-acquire', $lockSrv );
- continue; // server down?
- }
- // Attempt to acquire the lock on this peer
- $status->merge( $this->getLocksOnServer( $lockSrv, $paths, $type ) );
- if ( !$status->isOK() ) {
- return $status; // vetoed; resource locked
- }
- ++$yesVotes; // success for this peer
- if ( $yesVotes >= $quorum ) {
- return $status; // lock obtained
- }
- --$votesLeft;
- $votesNeeded = $quorum - $yesVotes;
- if ( $votesNeeded > $votesLeft ) {
- break; // short-circuit
- }
- }
- // At this point, we must not have met the quorum
- $status->setResult( false );
-
- return $status;
- }
-
- /**
- * Attempt to release locks with the peers for a bucket
- *
- * @param $bucket integer
- * @param $paths Array List of resource keys to lock
- * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
- * @return Status
- */
- final protected function doUnlockingRequestBucket( $bucket, array $paths, $type ) {
- $status = Status::newGood();
-
- foreach ( $this->srvsByBucket[$bucket] as $lockSrv ) {
- if ( !$this->isServerUp( $lockSrv ) ) {
- $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
- // Attempt to release the lock on this peer
- } else {
- $status->merge( $this->freeLocksOnServer( $lockSrv, $paths, $type ) );
- }
- }
-
- return $status;
- }
-
- /**
- * Get the bucket for resource path.
- * This should avoid throwing any exceptions.
- *
- * @param $path string
- * @return integer
- */
- protected function getBucketFromKey( $path ) {
- $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars (8 bits)
- return (int)base_convert( $prefix, 16, 10 ) % count( $this->srvsByBucket );
- }
-
- /**
- * Check if a lock server is up
- *
- * @param $lockSrv string
- * @return bool
- */
- abstract protected function isServerUp( $lockSrv );
-
- /**
- * Get a connection to a lock server and acquire locks on $paths
- *
- * @param $lockSrv string
- * @param $paths array
- * @param $type integer
- * @return Status
- */
- abstract protected function getLocksOnServer( $lockSrv, array $paths, $type );
-
- /**
- * Get a connection to a lock server and release locks on $paths.
- *
- * Subclasses must effectively implement this or releaseAllLocks().
- *
- * @param $lockSrv string
- * @param $paths array
- * @param $type integer
- * @return Status
- */
- abstract protected function freeLocksOnServer( $lockSrv, array $paths, $type );
-
- /**
- * Release all locks that this session is holding.
- *
- * Subclasses must effectively implement this or freeLocksOnServer().
- *
- * @return Status
- */
- abstract protected function releaseAllLocks();
-}
-
-/**
- * Simple version of LockManager that does nothing
- * @since 1.19
- */
-class NullLockManager extends LockManager {
- /**
- * @see LockManager::doLock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doLock( array $paths, $type ) {
- return Status::newGood();
- }
-
- /**
- * @see LockManager::doUnlock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
- protected function doUnlock( array $paths, $type ) {
- return Status::newGood();
- }
-}
+++ /dev/null
-<?php
-/**
- * Lock manager registration handling.
- *
- * 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 LockManager
- */
-
-/**
- * Class to handle file lock manager registration
- *
- * @ingroup LockManager
- * @author Aaron Schulz
- * @since 1.19
- */
-class LockManagerGroup {
- /**
- * @var LockManagerGroup
- */
- protected static $instance = null;
-
- /** @var Array of (name => ('class' =>, 'config' =>, 'instance' =>)) */
- protected $managers = array();
-
- protected function __construct() {}
-
- /**
- * @return LockManagerGroup
- */
- public static function singleton() {
- if ( self::$instance == null ) {
- self::$instance = new self();
- self::$instance->initFromGlobals();
- }
- return self::$instance;
- }
-
- /**
- * Destroy the singleton instance, so that a new one will be created next
- * time singleton() is called.
- */
- public static function destroySingleton() {
- self::$instance = null;
- }
-
- /**
- * Register lock managers from the global variables
- *
- * @return void
- */
- protected function initFromGlobals() {
- global $wgLockManagers;
-
- $this->register( $wgLockManagers );
- }
-
- /**
- * Register an array of file lock manager configurations
- *
- * @param $configs Array
- * @return void
- * @throws MWException
- */
- protected function register( array $configs ) {
- foreach ( $configs as $config ) {
- if ( !isset( $config['name'] ) ) {
- throw new MWException( "Cannot register a lock manager with no name." );
- }
- $name = $config['name'];
- if ( !isset( $config['class'] ) ) {
- throw new MWException( "Cannot register lock manager `{$name}` with no class." );
- }
- $class = $config['class'];
- unset( $config['class'] ); // lock manager won't need this
- $this->managers[$name] = array(
- 'class' => $class,
- 'config' => $config,
- 'instance' => null
- );
- }
- }
-
- /**
- * Get the lock manager object with a given name
- *
- * @param $name string
- * @return LockManager
- * @throws MWException
- */
- public function get( $name ) {
- if ( !isset( $this->managers[$name] ) ) {
- throw new MWException( "No lock manager defined with the name `$name`." );
- }
- // Lazy-load the actual lock manager instance
- if ( !isset( $this->managers[$name]['instance'] ) ) {
- $class = $this->managers[$name]['class'];
- $config = $this->managers[$name]['config'];
- $this->managers[$name]['instance'] = new $class( $config );
- }
- return $this->managers[$name]['instance'];
- }
-
- /**
- * Get the default lock manager configured for the site.
- * Returns NullLockManager if no lock manager could be found.
- *
- * @return LockManager
- */
- public function getDefault() {
- return isset( $this->managers['default'] )
- ? $this->get( 'default' )
- : new NullLockManager( array() );
- }
-
- /**
- * Get the default lock manager configured for the site
- * or at least some other effective configured lock manager.
- * Throws an exception if no lock manager could be found.
- *
- * @return LockManager
- * @throws MWException
- */
- public function getAny() {
- return isset( $this->managers['default'] )
- ? $this->get( 'default' )
- : $this->get( 'fsLockManager' );
- }
-}
+++ /dev/null
-<?php
-/**
- * Version of LockManager based on using memcached servers.
- *
- * 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 LockManager
- */
-
-/**
- * Manage locks using memcached servers.
- *
- * Version of LockManager based on using memcached servers.
- * This is meant for multi-wiki systems that may share files.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * All lock requests for a resource, identified by a hash string, will map
- * to one bucket. Each bucket maps to one or several peer servers, each running memcached.
- * A majority of peers must agree for a lock to be acquired.
- *
- * @ingroup LockManager
- * @since 1.20
- */
-class MemcLockManager extends QuorumLockManager {
- /** @var Array Mapping of lock types to the type actually used */
- protected $lockTypeMap = array(
- self::LOCK_SH => self::LOCK_SH,
- self::LOCK_UW => self::LOCK_SH,
- self::LOCK_EX => self::LOCK_EX
- );
-
- /** @var Array Map server names to MemcachedBagOStuff objects */
- protected $bagOStuffs = array();
- /** @var Array */
- protected $serversUp = array(); // (server name => bool)
-
- protected $lockExpiry; // integer; maximum time locks can be held
- protected $session = ''; // string; random SHA-1 UUID
- protected $wikiId = ''; // string
-
- /**
- * Construct a new instance from configuration.
- *
- * $config paramaters include:
- * - 'lockServers' : Associative array of server names to "<IP>:<port>" strings.
- * - 'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0,
- * each having an odd-numbered list of server names (peers) as values.
- * - 'memcConfig' : Configuration array for ObjectCache::newFromParams. [optional]
- * If set, this must use one of the memcached classes.
- * - 'wikiId' : Wiki ID string that all resources are relative to. [optional]
- *
- * @param Array $config
- */
- public function __construct( array $config ) {
- parent::__construct( $config );
-
- // Sanitize srvsByBucket config to prevent PHP errors
- $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' );
- $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
-
- $memcConfig = isset( $config['memcConfig'] )
- ? $config['memcConfig']
- : array( 'class' => 'MemcachedPhpBagOStuff' );
-
- foreach ( $config['lockServers'] as $name => $address ) {
- $params = array( 'servers' => array( $address ) ) + $memcConfig;
- $cache = ObjectCache::newFromParams( $params );
- if ( $cache instanceof MemcachedBagOStuff ) {
- $this->bagOStuffs[$name] = $cache;
- } else {
- throw new MWException(
- 'Only MemcachedBagOStuff classes are supported by MemcLockManager.' );
- }
- }
-
- $this->wikiId = isset( $config['wikiId'] ) ? $config['wikiId'] : wfWikiID();
-
- $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
- $this->lockExpiry = $met ? 2*(int)$met : 2*3600;
-
- $this->session = wfRandomString( 32 );
- }
-
- /**
- * @see QuorumLockManager::getLocksOnServer()
- * @return Status
- */
- protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
- $status = Status::newGood();
-
- $memc = $this->getCache( $lockSrv );
- $keys = array_map( array( $this, 'recordKeyForPath' ), $paths ); // lock records
-
- // Lock all of the active lock record keys...
- if ( !$this->acquireMutexes( $memc, $keys ) ) {
- foreach ( $paths as $path ) {
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- return;
- }
-
- // Fetch all the existing lock records...
- $lockRecords = $memc->getMulti( $keys );
-
- $now = time();
- // Check if the requested locks conflict with existing ones...
- foreach ( $paths as $path ) {
- $locksKey = $this->recordKeyForPath( $path );
- $locksHeld = isset( $lockRecords[$locksKey] )
- ? $lockRecords[$locksKey]
- : array( self::LOCK_SH => array(), self::LOCK_EX => array() ); // init
- foreach ( $locksHeld[self::LOCK_EX] as $session => $expiry ) {
- if ( $expiry < $now ) { // stale?
- unset( $locksHeld[self::LOCK_EX][$session] );
- } elseif ( $session !== $this->session ) {
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- }
- if ( $type === self::LOCK_EX ) {
- foreach ( $locksHeld[self::LOCK_SH] as $session => $expiry ) {
- if ( $expiry < $now ) { // stale?
- unset( $locksHeld[self::LOCK_SH][$session] );
- } elseif ( $session !== $this->session ) {
- $status->fatal( 'lockmanager-fail-acquirelock', $path );
- }
- }
- }
- if ( $status->isOK() ) {
- // Register the session in the lock record array
- $locksHeld[$type][$this->session] = $now + $this->lockExpiry;
- // We will update this record if none of the other locks conflict
- $lockRecords[$locksKey] = $locksHeld;
- }
- }
-
- // If there were no lock conflicts, update all the lock records...
- if ( $status->isOK() ) {
- foreach ( $lockRecords as $locksKey => $locksHeld ) {
- $memc->set( $locksKey, $locksHeld );
- wfDebug( __METHOD__ . ": acquired lock on key $locksKey.\n" );
- }
- }
-
- // Unlock all of the active lock record keys...
- $this->releaseMutexes( $memc, $keys );
-
- return $status;
- }
-
- /**
- * @see QuorumLockManager::freeLocksOnServer()
- * @return Status
- */
- protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
- $status = Status::newGood();
-
- $memc = $this->getCache( $lockSrv );
- $keys = array_map( array( $this, 'recordKeyForPath' ), $paths ); // lock records
-
- // Lock all of the active lock record keys...
- if ( !$this->acquireMutexes( $memc, $keys ) ) {
- foreach ( $paths as $path ) {
- $status->fatal( 'lockmanager-fail-releaselock', $path );
- }
- return;
- }
-
- // Fetch all the existing lock records...
- $lockRecords = $memc->getMulti( $keys );
-
- // Remove the requested locks from all records...
- foreach ( $paths as $path ) {
- $locksKey = $this->recordKeyForPath( $path ); // lock record
- if ( !isset( $lockRecords[$locksKey] ) ) {
- continue; // nothing to do
- }
- $locksHeld = $lockRecords[$locksKey];
- if ( is_array( $locksHeld ) && isset( $locksHeld[$type] ) ) {
- unset( $locksHeld[$type][$this->session] );
- $ok = $memc->set( $locksKey, $locksHeld );
- } else {
- $ok = true;
- }
- if ( !$ok ) {
- $status->fatal( 'lockmanager-fail-releaselock', $path );
- }
- wfDebug( __METHOD__ . ": released lock on key $locksKey.\n" );
- }
-
- // Unlock all of the active lock record keys...
- $this->releaseMutexes( $memc, $keys );
-
- return $status;
- }
-
- /**
- * @see QuorumLockManager::releaseAllLocks()
- * @return Status
- */
- protected function releaseAllLocks() {
- return Status::newGood(); // not supported
- }
-
- /**
- * @see QuorumLockManager::isServerUp()
- * @return bool
- */
- protected function isServerUp( $lockSrv ) {
- return (bool)$this->getCache( $lockSrv );
- }
-
- /**
- * Get the MemcachedBagOStuff object for a $lockSrv
- *
- * @param $lockSrv string Server name
- * @return MemcachedBagOStuff|null
- */
- protected function getCache( $lockSrv ) {
- $memc = null;
- if ( isset( $this->bagOStuffs[$lockSrv] ) ) {
- $memc = $this->bagOStuffs[$lockSrv];
- if ( !isset( $this->serversUp[$lockSrv] ) ) {
- $this->serversUp[$lockSrv] = $memc->set( 'MemcLockManager:ping', 1, 1 );
- if ( !$this->serversUp[$lockSrv] ) {
- trigger_error( __METHOD__ . ": Could not contact $lockSrv.", E_USER_WARNING );
- }
- }
- if ( !$this->serversUp[$lockSrv] ) {
- return null; // server appears to be down
- }
- }
- return $memc;
- }
-
- /**
- * @param $path string
- * @return string
- */
- protected function recordKeyForPath( $path ) {
- $hash = LockManager::sha1Base36( $path );
- list( $db, $prefix ) = wfSplitWikiID( $this->wikiId );
- return wfForeignMemcKey( $db, $prefix, __CLASS__, 'locks', $hash );
- }
-
- /**
- * @param $memc MemcachedBagOStuff
- * @param $keys Array List of keys to acquire
- * @return bool
- */
- protected function acquireMutexes( MemcachedBagOStuff $memc, array $keys ) {
- $lockedKeys = array();
-
- // Acquire the keys in lexicographical order, to avoid deadlock problems.
- // If P1 is waiting to acquire a key P2 has, P2 can't also be waiting for a key P1 has.
- sort( $keys );
-
- // Try to quickly loop to acquire the keys, but back off after a few rounds.
- // This reduces memcached spam, especially in the rare case where a server acquires
- // some lock keys and dies without releasing them. Lock keys expire after a few minutes.
- $rounds = 0;
- $start = microtime( true );
- do {
- if ( ( ++$rounds % 4 ) == 0 ) {
- usleep( 1000*50 ); // 50 ms
- }
- foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
- if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
- $lockedKeys[] = $key;
- } else {
- continue; // acquire in order
- }
- }
- } while ( count( $lockedKeys ) < count( $keys ) && ( microtime( true ) - $start ) <= 6 );
-
- if ( count( $lockedKeys ) != count( $keys ) ) {
- $this->releaseMutexes( $lockedKeys ); // failed; release what was locked
- return false;
- }
-
- return true;
- }
-
- /**
- * @param $memc MemcachedBagOStuff
- * @param $keys Array List of acquired keys
- * @return void
- */
- protected function releaseMutexes( MemcachedBagOStuff $memc, array $keys ) {
- foreach ( $keys as $key ) {
- $memc->delete( "$key:mutex" );
- }
- }
-
- /**
- * Make sure remaining locks get cleared for sanity
- */
- function __destruct() {
- while ( count( $this->locksHeld ) ) {
- foreach ( $this->locksHeld as $path => $locks ) {
- $this->doUnlock( array( $path ), self::LOCK_EX );
- $this->doUnlock( array( $path ), self::LOCK_SH );
- }
- }
- }
-}
}
/**
- * Callback for usort() to do file sorts by title
+ * Callback for usort() to do file sorts by name
*
* @param $a File
* @param $b File
*
- * @return Integer: result of title comparison
+ * @return Integer: result of name comparison
*/
public static function compare( File $a, File $b ) {
- return Title::compare( $a->getTitle(), $b->getTitle() );
+ return strcmp( $a->getName(), $b->getName() );
}
/**
*/
function getDescriptionText() {
global $wgParser;
- $revision = Revision::newFromTitle( $this->title, false, Revision::AVOID_MASTER );
+ $revision = Revision::newFromTitle( $this->title, false, Revision::READ_NORMAL );
if ( !$revision ) return false;
$text = $revision->getText();
if ( !$text ) return false;
$up = DatabaseUpdater::newForDB( $this->db );
$up->doUpdates();
} catch ( MWException $e ) {
- echo "\nAn error occured:\n";
+ echo "\nAn error occurred:\n";
echo $e->getText();
$ret = false;
}
'config-type-postgres' => '{{optional}}',
'config-type-sqlite' => '{{optional}}',
'config-type-oracle' => '{{optional}}',
+ 'config-support-info' => 'Parameters:
+* $1 - a list of DBMSs that MediaWiki supports, composed with other config-type-* and config-support-* messages.',
'config-support-mysql' => 'Parameters:
* $1 - a link to the MySQL home page having the anchor text "MySQL".',
'config-support-postgres' => 'Parameters:
//1.19
array( 'addIndex', 'logging', 'i05', 'patch-logging_type_action_index.sql'),
- array( 'addTable', 'globaltemplatelinks', 'patch-globaltemplatelinks.sql' ),
- array( 'addTable', 'globalnamespaces', 'patch-globalnamespaces.sql' ),
- array( 'addTable', 'globalinterwiki', 'patch-globalinterwiki.sql' ),
array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1_field.sql' ),
array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1_field.sql' ),
array( 'doRemoveNotNullEmptyDefaults2' ),
array( 'addIndex', 'page', 'i03', 'patch-page_redirect_namespace_len.sql' ),
- array( 'modifyField', 'user', 'ug_group', 'patch-ug_group-length-increase.sql' ),
+ array( 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase.sql' ),
array( 'addField', 'uploadstash', 'us_chunk_inx', 'patch-us_chunk_inx_field.sql' ),
array( 'addField', 'job', 'job_timestamp', 'patch-job_timestamp_field.sql' ),
array( 'addIndex', 'job', 'i02', 'patch-job_timestamp_index.sql' ),
+ array( 'doPageRestrictionsPKUKFix' ),
+ array( 'modifyField', 'user_former_groups', 'ufg_group', 'patch-ufg_group-length-increase.sql' ),
//1.20
array( 'addTable', 'config', 'patch-config.sql' ),
+ array( 'addIndex', 'ipblocks', 'i05', 'patch-ipblocks_i05_index.sql' ),
+ array( 'addIndex', 'revision', 'i05', 'patch-revision_i05_index.sql' ),
// KEEP THIS AT THE BOTTOM!!
array( 'doRebuildDuplicateFunction' ),
$this->output( "ok\n" );
}
+ /**
+ * Fixed wrong PK, UK definition
+ */
+ protected function doPageRestrictionsPKUKFix() {
+ $this->output( "Altering PAGE_RESTRICTIONS keys ... " );
+
+ $meta = $this->db->query( 'SELECT column_name FROM all_cons_columns WHERE owner = \''.strtoupper($this->db->getDBname()).'\' AND constraint_name = \'MW_PAGE_RESTRICTIONS_PK\' AND rownum = 1' );
+ $row = $meta->fetchRow();
+ if ( $row['column_name'] == 'PR_ID' ) {
+ $this->output( "seems to be up to date.\n" );
+ return;
+ }
+
+ $this->applyPatch( 'patch-page_restrictions_pkuk_fix.sql', false );
+ $this->output( "ok\n" );
+ }
+
/**
* rebuilding of the function that duplicates tables for tests
*/
$dbSupport .= wfMsgNoTrans( "config-support-$type", $link ) . "\n";
}
$this->addHTML( $this->parent->getInfoBox(
- wfMsg( 'config-support-info', $dbSupport ) ) );
+ wfMsg( 'config-support-info', trim( $dbSupport ) ) ) );
foreach ( $this->parent->getVar( '_CompiledDBs' ) as $type ) {
$installer = $this->parent->getDBInstaller( $type );
*
* @param $command String: Job command
* @param $title Title: Associated title
- * @param $params Array: Job parameters
+ * @param $params Array|bool: Job parameters
* @param $id Int: Job identifier
+ * @throws MWException
* @return Job
*/
static function factory( $command, Title $title, $params = false, $id = 0 ) {
/**
* @param $command
* @param $title
- * @param $params array
- * @param int $id
+ * @param $params array|bool
+ * @param $id int
*/
function __construct( $command, $title, $params = false, $id = 0 ) {
$this->command = $command;
* @return boolean success
*/
function run() {
- global $wgParser, $wgContLang;
wfProfileIn( __METHOD__ );
$linkCache = LinkCache::singleton();
return false;
}
- $revision = Revision::newFromTitle( $this->title );
+ # Wait for the DB of the current/next slave DB handle to catch up to the master.
+ # This way, we get the correct page_latest for templates or files that just changed
+ # milliseconds ago, having triggered this job to begin with.
+ if ( isset( $this->params['masterPos'] ) ) {
+ wfGetLB()->waitFor( $this->params['masterPos'] );
+ }
+
+ $revision = Revision::newFromTitle( $this->title, 0, Revision::READ_NORMAL );
if ( !$revision ) {
- $this->error = 'refreshLinks: Article not found "' . $this->title->getPrefixedDBkey() . '"';
+ $this->error = 'refreshLinks: Article not found "' .
+ $this->title->getPrefixedDBkey() . '"';
wfProfileOut( __METHOD__ );
- return false;
+ return false; // XXX: what if it was just deleted?
}
- wfProfileIn( __METHOD__.'-parse' );
- $options = ParserOptions::newFromUserAndLang( new User, $wgContLang );
- $parserOutput = $wgParser->parse( $revision->getText(), $this->title, $options, true, true, $revision->getId() );
- wfProfileOut( __METHOD__.'-parse' );
- wfProfileIn( __METHOD__.'-update' );
+ self::runForTitleInternal( $this->title, $revision, __METHOD__ );
- $updates = $parserOutput->getSecondaryDataUpdates( $this->title, false );
- DataUpdate::runUpdates( $updates );
-
- wfProfileOut( __METHOD__.'-update' );
wfProfileOut( __METHOD__ );
return true;
}
+
+ public static function runForTitleInternal( Title $title, Revision $revision, $fname ) {
+ global $wgParser, $wgContLang;
+
+ wfProfileIn( $fname . '-parse' );
+ $options = ParserOptions::newFromUserAndLang( new User, $wgContLang );
+ $parserOutput = $wgParser->parse(
+ $revision->getText(), $title, $options, true, true, $revision->getId() );
+ wfProfileOut( $fname . '-parse' );
+
+ wfProfileIn( $fname . '-update' );
+ $updates = $parserOutput->getSecondaryDataUpdates( $title, false );
+ DataUpdate::runUpdates( $updates );
+ wfProfileOut( $fname . '-update' );
+ }
}
/**
* @ingroup JobQueue
*/
class RefreshLinksJob2 extends Job {
+ const MAX_TITLES_RUN = 10;
function __construct( $title, $params, $id = 0 ) {
parent::__construct( 'refreshLinks2', $title, $params, $id );
* @return boolean success
*/
function run() {
- global $wgParser, $wgContLang;
-
wfProfileIn( __METHOD__ );
$linkCache = LinkCache::singleton();
$linkCache->clear();
- if( is_null( $this->title ) ) {
+ if ( is_null( $this->title ) ) {
$this->error = "refreshLinks2: Invalid title";
wfProfileOut( __METHOD__ );
return false;
- }
- if( !isset($this->params['start']) || !isset($this->params['end']) ) {
+ } elseif ( !isset( $this->params['start'] ) || !isset( $this->params['end'] ) ) {
$this->error = "refreshLinks2: Invalid params";
wfProfileOut( __METHOD__ );
return false;
}
+
// Back compat for pre-r94435 jobs
$table = isset( $this->params['table'] ) ? $this->params['table'] : 'templatelinks';
- $titles = $this->title->getBacklinkCache()->getLinks(
- $table, $this->params['start'], $this->params['end']);
-
- # Not suitable for page load triggered job running!
- # Gracefully switch to refreshLinks jobs if this happens.
- if( php_sapi_name() != 'cli' ) {
+
+ // Avoid slave lag when fetching templates
+ if ( isset( $this->params['masterPos'] ) ) {
+ $masterPos = $this->params['masterPos'];
+ } elseif ( wfGetLB()->getServerCount() > 1 ) {
+ $masterPos = wfGetLB()->getMasterPos();
+ } else {
+ $masterPos = false;
+ }
+
+ $titles = $this->title->getBacklinkCache()->getLinks(
+ $table, $this->params['start'], $this->params['end'] );
+
+ if ( $titles->count() > self::MAX_TITLES_RUN ) {
+ # We don't want to parse too many pages per job as it can starve other jobs.
+ # If there are too many pages to parse, break this up into smaller jobs. By passing
+ # in the master position here we can cut down on the time spent waiting for slaves to
+ # catch up by the runners handling these jobs since time will have passed between now
+ # and when they pop these jobs off the queue.
+ $start = 0; // batch start
+ $end = 0; // batch end
+ $bsize = 0; // batch size
+ $first = true; // first of batch
+ $jobs = array();
+ foreach ( $titles as $title ) {
+ $start = $first ? $title->getArticleId() : $start;
+ $end = $title->getArticleId();
+ $first = false;
+ if ( ++$bsize >= self::MAX_TITLES_RUN ) {
+ $jobs[] = new RefreshLinksJob2( $this->title, array(
+ 'table' => $table,
+ 'start' => $start,
+ 'end' => $end,
+ 'masterPos' => $masterPos
+ ) );
+ $first = true;
+ $start = $end = $bsize = 0;
+ }
+ }
+ if ( $bsize > 0 ) { // group remaining pages into a job
+ $jobs[] = new RefreshLinksJob2( $this->title, array(
+ 'table' => $table,
+ 'start' => $start,
+ 'end' => $end,
+ 'masterPos' => $masterPos
+ ) );
+ }
+ Job::batchInsert( $jobs );
+ } elseif ( php_sapi_name() != 'cli' ) {
+ # Not suitable for page load triggered job running!
+ # Gracefully switch to refreshLinks jobs if this happens.
$jobs = array();
foreach ( $titles as $title ) {
- $jobs[] = new RefreshLinksJob( $title, '' );
+ $jobs[] = new RefreshLinksJob( $title, array( 'masterPos' => $masterPos ) );
}
Job::batchInsert( $jobs );
-
- wfProfileOut( __METHOD__ );
- return true;
- }
- $options = ParserOptions::newFromUserAndLang( new User, $wgContLang );
- # Re-parse each page that transcludes this page and update their tracking links...
- foreach ( $titles as $title ) {
- $revision = Revision::newFromTitle( $title );
- if ( !$revision ) {
- $this->error = 'refreshLinks: Article not found "' . $title->getPrefixedDBkey() . '"';
- wfProfileOut( __METHOD__ );
- return false;
+ } else {
+ # Wait for the DB of the current/next slave DB handle to catch up to the master.
+ # This way, we get the correct page_latest for templates or files that just changed
+ # milliseconds ago, having triggered this job to begin with.
+ if ( $masterPos ) {
+ wfGetLB()->waitFor( $masterPos );
+ }
+ # Re-parse each page that transcludes this page and update their tracking links...
+ foreach ( $titles as $title ) {
+ $revision = Revision::newFromTitle( $title, 0, Revision::READ_NORMAL );
+ if ( !$revision ) {
+ $this->error = 'refreshLinks: Article not found "' .
+ $title->getPrefixedDBkey() . '"';
+ continue; // skip this page
+ }
+ RefreshLinksJob::runForTitleInternal( $title, $revision, __METHOD__ );
+ wfWaitForSlaves();
}
- wfProfileIn( __METHOD__.'-parse' );
- $parserOutput = $wgParser->parse( $revision->getText(), $title, $options, true, true, $revision->getId() );
- wfProfileOut( __METHOD__.'-parse' );
- wfProfileIn( __METHOD__.'-update' );
-
- $updates = $parserOutput->getSecondaryDataUpdates( $title, false );
- DataUpdate::runUpdates( $updates );
-
- wfProfileOut( __METHOD__.'-update' );
- wfWaitForSlaves();
}
- wfProfileOut( __METHOD__ );
+ wfProfileOut( __METHOD__ );
return true;
}
}
* @return string
*/
public static function encode( $value, $isHtml = false ) {
- // Some versions of PHP have a broken json_encode, see PHP bug
- // 46944. Test encoding an affected character (U+20000) to
- // avoid this.
- if ( !function_exists( 'json_encode' ) || $isHtml || strtolower( json_encode( "\xf0\xa0\x80\x80" ) ) != '"\ud840\udc00"' ) {
+ if ( !function_exists( 'json_encode' ) || ( $isHtml && version_compare( PHP_VERSION, '5.4.0', '<' ) ) ) {
$json = new Services_JSON();
return $json->encode( $value, $isHtml );
} else {
- return json_encode( $value );
+ return json_encode( $value, $isHtml ? JSON_PRETTY_PRINT : 0 );
}
}
--- /dev/null
+<?php
+
+/**
+ * Extends ArrayObject and does two things:
+ *
+ * Allows for deriving classes to easily intercept additions
+ * and deletions for purposes such as additional indexing.
+ *
+ * Enforces the objects to be of a certain type, so this
+ * can be replied upon, much like if this had true support
+ * for generics, which sadly enough is not possible in PHP.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.20
+ *
+ * @file
+ * @ingroup Diff
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class GenericArrayObject extends ArrayObject {
+
+ /**
+ * Returns the name of an interface/class that the element should implement/extend.
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public abstract function getObjectType();
+
+ /**
+ * @see SiteList::getNewOffset()
+ * @since 1.20
+ * @var integer
+ */
+ protected $indexOffset = 0;
+
+ /**
+ * Finds a new offset for when appending an element.
+ * The base class does this, so it would be better to integrate,
+ * but there does not appear to be any way to do this...
+ *
+ * @since 1.20
+ *
+ * @return integer
+ */
+ protected function getNewOffset() {
+ while ( true ) {
+ if ( !$this->offsetExists( $this->indexOffset ) ) {
+ return $this->indexOffset;
+ }
+
+ $this->indexOffset++;
+ }
+ }
+
+ /**
+ * Constructor.
+ * @see ArrayObject::__construct
+ *
+ * @since 1.20
+ *
+ * @param null|array $input
+ * @param int $flags
+ * @param string $iterator_class
+ */
+ public function __construct( $input = null, $flags = 0, $iterator_class = 'ArrayIterator' ) {
+ parent::__construct( array(), $flags, $iterator_class );
+
+ if ( !is_null( $input ) ) {
+ foreach ( $input as $offset => $value ) {
+ $this->offsetSet( $offset, $value );
+ }
+ }
+ }
+
+ /**
+ * @see ArrayObject::append
+ *
+ * @since 1.20
+ *
+ * @param mixed $value
+ */
+ public function append( $value ) {
+ $this->setElement( null, $value );
+ }
+
+ /**
+ * @see ArrayObject::offsetSet()
+ *
+ * @since 1.20
+ *
+ * @param mixed $index
+ * @param mixed $value
+ */
+ public function offsetSet( $index, $value ) {
+ $this->setElement( $index, $value );
+ }
+
+ /**
+ * Returns if the provided value has the same type as the elements
+ * that can be added to this ArrayObject.
+ *
+ * @since 1.20
+ *
+ * @param mixed $value
+ *
+ * @return boolean
+ */
+ protected function hasValidType( $value ) {
+ $class = $this->getObjectType();
+ return $value instanceof $class;
+ }
+
+ /**
+ * Method that actually sets the element and holds
+ * all common code needed for set operations, including
+ * type checking and offset resolving.
+ *
+ * If you want to do additional indexing or have code that
+ * otherwise needs to be executed whenever an element is added,
+ * you can overload @see preSetElement.
+ *
+ * @since 1.20
+ *
+ * @param mixed $index
+ * @param mixed $value
+ *
+ * @throws Exception
+ */
+ protected function setElement( $index, $value ) {
+ if ( !$this->hasValidType( $value ) ) {
+ throw new Exception(
+ 'Can only add ' . $this->getObjectType() . ' implementing objects to ' . get_called_class() . '.'
+ );
+ }
+
+ if ( is_null( $index ) ) {
+ $index = $this->getNewOffset();
+ }
+
+ if ( $this->preSetElement( $index, $value ) ) {
+ parent::offsetSet( $index, $value );
+ }
+ }
+
+ /**
+ * Gets called before a new element is added to the ArrayObject.
+ *
+ * At this point the index is always set (ie not null) and the
+ * value is always of the type returned by @see getObjectType.
+ *
+ * Should return a boolean. When false is returned the element
+ * does not get added to the ArrayObject.
+ *
+ * @since 1.20
+ *
+ * @param integer|string $index
+ * @param mixed $value
+ *
+ * @return boolean
+ */
+ protected function preSetElement( $index, $value ) {
+ return true;
+ }
+
+ /**
+ * @see Serializable::serialize
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public function serialize() {
+ return serialize( $this->getSerializationData() );
+ }
+
+ /**
+ * Returns an array holding all the data that should go into serialization calls.
+ * This is intended to allow overloading without having to reimplement the
+ * behaviour of this base class.
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ protected function getSerializationData() {
+ return array(
+ 'data' => $this->getArrayCopy(),
+ 'index' => $this->indexOffset,
+ );
+ }
+
+ /**
+ * @see Serializable::unserialize
+ *
+ * @since 1.20
+ *
+ * @param string $serialization
+ *
+ * @return array
+ */
+ public function unserialize( $serialization ) {
+ $serializationData = unserialize( $serialization );
+
+ foreach ( $serializationData['data'] as $offset => $value ) {
+ // Just set the element, bypassing checks and offset resolving,
+ // as these elements have already gone through this.
+ parent::offsetSet( $offset, $value );
+ }
+
+ $this->indexOffset = $serializationData['index'];
+
+ return $serializationData;
+ }
+
+ /**
+ * Returns if the ArrayObject has no elements.
+ *
+ * @since 1.20
+ *
+ * @return boolean
+ */
+ public function isEmpty() {
+ return $this->count() === 0;
+ }
+
+}
public function getIRCActionText() {
$this->plaintext = true;
$this->irctext = true;
- $text = $this->getActionText();
$entry = $this->entry;
$parameters = $entry->getParameters();
* @param $image File File associated with this thumbnail
* @param $params array Array with scaler params
*
- * @return MediaTransformError Error object if error occured, false (=no error) otherwise
+ * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
*/
protected function transformImageMagick( $image, $params ) {
# use ImageMagick
* @param $image File File associated with this thumbnail
* @param $params array Array with scaler params
*
- * @return MediaTransformError Error object if error occured, false (=no error) otherwise
+ * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
*/
protected function transformImageMagickExt( $image, $params ) {
global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea;
* @param $image File File associated with this thumbnail
* @param $params array Array with scaler params
*
- * @return MediaTransformError Error object if error occured, false (=no error) otherwise
+ * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
*/
protected function transformCustom( $image, $params ) {
# Use a custom convert command
}
/**
- * Log an error that occured in an external process
+ * Log an error that occurred in an external process
*
* @param $retval int
* @param $err int
* @param $image File File associated with this thumbnail
* @param $params array Array with scaler params
*
- * @return MediaTransformError Error object if error occured, false (=no error) otherwise
+ * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
*/
protected function transformGd( $image, $params ) {
# Use PHP's builtin GD library functions.
return $exptime;
}
}
+
+ /**
+ * Convert an optionally absolute expiry time to a relative time. If an
+ * absolute time is specified which is in the past, use a short expiry time.
+ *
+ * @param $exptime integer
+ * @return integer
+ */
+ protected function convertToRelative( $exptime ) {
+ if ( $exptime >= 86400 * 3650 /* 10 years */ ) {
+ $exptime -= time();
+ if ( $exptime <= 0 ) {
+ $exptime = 1;
+ }
+ return $exptime;
+ } else {
+ return $exptime;
+ }
+ }
}
$params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
}
if ( !isset( $params['connect_timeout'] ) ) {
- $params['connect_timeout'] = 0.1;
+ $params['connect_timeout'] = 0.5;
}
return $params;
}
$this->_host_dead = array();
$this->_timeout_seconds = 0;
- $this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 100000;
+ $this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 500000;
$this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1;
$this->_connect_attempts = 2;
$this->stats['delete'] = 1;
}
$cmd = "delete $key $time\r\n";
- if( !$this->_safe_fwrite( $sock, $cmd, strlen( $cmd ) ) ) {
- $this->_dead_sock( $sock );
+ if( !$this->_fwrite( $sock, $cmd ) ) {
return false;
}
- $res = trim( fgets( $sock ) );
+ $res = $this->_fgets( $sock );
if ( $this->_debug ) {
$this->_debugprint( sprintf( "MemCache: delete %s (%s)\n", $key, $res ) );
}
$cmd = "get $key\r\n";
- if ( !$this->_safe_fwrite( $sock, $cmd, strlen( $cmd ) ) ) {
- $this->_dead_sock( $sock );
+ if ( !$this->_fwrite( $sock, $cmd ) ) {
wfProfileOut( __METHOD__ );
return false;
}
}
$key = is_array( $key ) ? $key[1] : $key;
if ( !isset( $sock_keys[$sock] ) ) {
- $sock_keys[$sock] = array();
+ $sock_keys[ intval( $sock ) ] = array();
$socks[] = $sock;
}
- $sock_keys[$sock][] = $key;
+ $sock_keys[ intval( $sock ) ][] = $key;
}
$gather = array();
// Send out the requests
foreach ( $socks as $sock ) {
$cmd = 'get';
- foreach ( $sock_keys[$sock] as $key ) {
+ foreach ( $sock_keys[ intval( $sock ) ] as $key ) {
$cmd .= ' ' . $key;
}
$cmd .= "\r\n";
- if ( $this->_safe_fwrite( $sock, $cmd, strlen( $cmd ) ) ) {
+ if ( $this->_fwrite( $sock, $cmd ) ) {
$gather[] = $sock;
- } else {
- $this->_dead_sock( $sock );
}
}
* Passes through $cmd to the memcache server connected by $sock; returns
* output as an array (null array if no output)
*
- * NOTE: due to a possible bug in how PHP reads while using fgets(), each
- * line may not be terminated by a "\r\n". More specifically, my testing
- * has shown that, on FreeBSD at least, each line is terminated only
- * with a "\n". This is with the PHP flag auto_detect_line_endings set
- * to false (the default).
- *
* @param $sock Resource: socket to send command on
* @param $cmd String: command to run
*
return array();
}
- if ( !$this->_safe_fwrite( $sock, $cmd, strlen( $cmd ) ) ) {
+ if ( !$this->_fwrite( $sock, $cmd ) ) {
return array();
}
$ret = array();
while ( true ) {
- $res = fgets( $sock );
+ $res = $this->_fgets( $sock );
$ret[] = $res;
if ( preg_match( '/^END/', $res ) ) {
break;
wfRestoreWarnings();
}
if ( !$sock ) {
- if ( $this->_debug ) {
- $this->_debugprint( "Error connecting to $host: $errstr\n" );
- }
+ $this->_error_log( "Error connecting to $host: $errstr\n" );
+ $this->_dead_host( $host );
return false;
}
// Initialise timeout
stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
+ // If the connection was persistent, flush the read buffer in case there
+ // was a previous incomplete request on this connection
+ if ( $this->_persistent ) {
+ $this->_flush_read_buffer( $sock );
+ }
return true;
}
}
if ( $this->_single_sock !== null ) {
- $this->_flush_read_buffer( $this->_single_sock );
return $this->sock_to_host( $this->_single_sock );
}
$host = $this->_buckets[$hv % $this->_bucketcount];
$sock = $this->sock_to_host( $host );
if ( is_resource( $sock ) ) {
- $this->_flush_read_buffer( $sock );
return $sock;
}
$hv = $this->_hashfunc( $hv . $realkey );
} else {
$this->stats[$cmd] = 1;
}
- if ( !$this->_safe_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
- $this->_dead_sock( $sock );
+ if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
return null;
}
- $line = fgets( $sock );
+ $line = $this->_fgets( $sock );
$match = array();
if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
return null;
*
* @param $sock Resource: socket to read from
* @param $ret Array: returned values
+ * @return boolean True for success, false for failure
*
- * @return bool|int
* @access private
*/
function _load_items( $sock, &$ret ) {
while ( 1 ) {
- $decl = fgets( $sock );
- if ( $decl == "END\r\n" ) {
+ $decl = $this->_fgets( $sock );
+ if( $decl === false ) {
+ return false;
+ } elseif ( $decl == "END" ) {
return true;
- } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match ) ) {
+ } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)$/', $decl, $match ) ) {
list( $rkey, $flags, $len ) = array( $match[1], $match[2], $match[3] );
- $bneed = $len + 2;
- $offset = 0;
-
- while ( $bneed > 0 ) {
- $data = fread( $sock, $bneed );
- $n = strlen( $data );
- if ( $n == 0 ) {
- break;
- }
- $offset += $n;
- $bneed -= $n;
- if ( isset( $ret[$rkey] ) ) {
- $ret[$rkey] .= $data;
- } else {
- $ret[$rkey] = $data;
- }
+ $data = $this->_fread( $sock, $len + 2 );
+ if ( $data === false ) {
+ return false;
}
-
- if ( $offset != $len + 2 ) {
- // Something is borked!
- if ( $this->_debug ) {
- $this->_debugprint( sprintf( "Something is borked! key %s expecting %d got %d length\n", $rkey, $len + 2, $offset ) );
- }
-
- unset( $ret[$rkey] );
- $this->_close_sock( $sock );
+ if ( substr( $data, -2 ) !== "\r\n" ) {
+ $this->_handle_error( $sock,
+ 'line ending missing from data block from $1' );
return false;
}
+ $data = substr( $data, 0, -2 );
+ $ret[$rkey] = $data;
if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
$ret[$rkey] = gzuncompress( $ret[$rkey] );
}
- $ret[$rkey] = rtrim( $ret[$rkey] );
-
if ( $flags & self::SERIALIZED ) {
$ret[$rkey] = unserialize( $ret[$rkey] );
}
} else {
- $this->_debugprint( "Error parsing memcached response\n" );
- return 0;
+ $this->_handle_error( $sock, 'Error parsing response from $1' );
+ return false;
}
}
}
$flags |= self::COMPRESSED;
}
}
- if ( !$this->_safe_fwrite( $sock, "$cmd $key $flags $exp $len\r\n$val\r\n" ) ) {
- $this->_dead_sock( $sock );
+ if ( !$this->_fwrite( $sock, "$cmd $key $flags $exp $len\r\n$val\r\n" ) ) {
return false;
}
- $line = trim( fgets( $sock ) );
+ $line = $this->_fgets( $sock );
if ( $this->_debug ) {
$this->_debugprint( sprintf( "%s %s (%s)\n", $cmd, $key, $line ) );
}
if ( !$this->_connect_sock( $sock, $host ) ) {
- $this->_dead_host( $host );
return null;
}
}
/**
- * @param $str string
+ * @param $text string
+ */
+ function _debugprint( $text ) {
+ global $wgDebugLogGroups;
+ if( !isset( $wgDebugLogGroups['memcached'] ) ) {
+ # Prefix message since it will end up in main debug log file
+ $text = "memcached: $text";
+ }
+ wfDebugLog( 'memcached', $text );
+ }
+
+ /**
+ * @param $text string
*/
- function _debugprint( $str ) {
- print( $str );
+ function _error_log( $text ) {
+ wfDebugLog( 'memcached-serious', "Memcached error: $text" );
}
/**
- * Write to a stream, timing out after the correct amount of time
+ * Write to a stream. If there is an error, mark the socket dead.
*
- * @return Boolean: false on failure, true on success
+ * @param $sock The socket
+ * @param $buf The string to write
+ * @return bool True on success, false on failure
*/
- /*
- function _safe_fwrite( $f, $buf, $len = false ) {
- stream_set_blocking( $f, 0 );
+ function _fwrite( $sock, $buf ) {
+ $bytesWritten = 0;
+ $bufSize = strlen( $buf );
+ while ( $bytesWritten < $bufSize ) {
+ $result = fwrite( $sock, $buf );
+ $data = stream_get_meta_data( $sock );
+ if ( $data['timed_out'] ) {
+ $this->_handle_error( $sock, 'timeout writing to $1' );
+ return false;
+ }
+ // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
+ if ( $result === false || $result === 0 ) {
+ $this->_handle_error( $sock, 'error writing to $1' );
+ return false;
+ }
+ $bytesWritten += $result;
+ }
- if ( $len === false ) {
- wfDebug( "Writing " . strlen( $buf ) . " bytes\n" );
- $bytesWritten = fwrite( $f, $buf );
- } else {
- wfDebug( "Writing $len bytes\n" );
- $bytesWritten = fwrite( $f, $buf, $len );
+ return true;
+ }
+
+ /**
+ * Handle an I/O error. Mark the socket dead and log an error.
+ */
+ function _handle_error( $sock, $msg ) {
+ $peer = stream_socket_get_name( $sock, true /** remote **/ );
+ if ( strval( $peer ) === '' ) {
+ $peer = array_search( $sock, $this->_cache_sock );
+ if ( $peer === false ) {
+ $peer = '[unknown host]';
+ }
}
- $n = stream_select( $r = null, $w = array( $f ), $e = null, 10, 0 );
- # $this->_timeout_seconds, $this->_timeout_microseconds );
+ $msg = str_replace( '$1', $peer, $msg );
+ $this->_error_log( "$msg\n" );
+ $this->_dead_sock( $sock );
+ }
- wfDebug( "stream_select returned $n\n" );
- stream_set_blocking( $f, 1 );
- return $n == 1;
- return $bytesWritten;
- }*/
+ /**
+ * Read the specified number of bytes from a stream. If there is an error,
+ * mark the socket dead.
+ *
+ * @param $sock The socket
+ * @param $len The number of bytes to read
+ * @return The string on success, false on failure.
+ */
+ function _fread( $sock, $len ) {
+ $buf = '';
+ while ( $len > 0 ) {
+ $result = fread( $sock, $len );
+ $data = stream_get_meta_data( $sock );
+ if ( $data['timed_out'] ) {
+ $this->_handle_error( $sock, 'timeout reading from $1' );
+ return false;
+ }
+ if ( $result === false ) {
+ $this->_handle_error( $sock, 'error reading buffer from $1' );
+ return false;
+ }
+ if ( $result === '' ) {
+ // This will happen if the remote end of the socket is shut down
+ $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
+ return false;
+ }
+ $len -= strlen( $result );
+ $buf .= $result;
+ }
+ return $buf;
+ }
/**
- * Original behaviour
- * @param $f
- * @param $buf
- * @param $len bool
- * @return int
+ * Read a line from a stream. If there is an error, mark the socket dead.
+ * The \r\n line ending is stripped from the response.
+ *
+ * @param $sock The socket
+ * @return The string on success, false on failure
*/
- function _safe_fwrite( $f, $buf, $len = false ) {
- if ( $len === false ) {
- $bytesWritten = fwrite( $f, $buf );
+ function _fgets( $sock ) {
+ $result = fgets( $sock );
+ // fgets() may return a partial line if there is a select timeout after
+ // a successful recv(), so we have to check for a timeout even if we
+ // got a string response.
+ $data = stream_get_meta_data( $sock );
+ if ( $data['timed_out'] ) {
+ $this->_handle_error( $sock, 'timeout reading line from $1' );
+ return false;
+ }
+ if ( $result === false ) {
+ $this->_handle_error( $sock, 'error reading line from $1' );
+ return false;
+ }
+ if ( substr( $result, -2 ) === "\r\n" ) {
+ $result = substr( $result, 0, -2 );
+ } elseif ( substr( $result, -1 ) === "\n" ) {
+ $result = substr( $result, 0, -1 );
} else {
- $bytesWritten = fwrite( $f, $buf, $len );
+ $this->_handle_error( $sock, 'line ending missing in response from $1' );
+ return false;
}
- return $bytesWritten;
+ return $result;
}
/**
// }}}
}
-// vim: sts=3 sw=3 et
// }}}
class MemCachedClientforWiki extends MWMemcached {
-
- function _debugprint( $text ) {
- global $wgDebugLogGroups;
- if( !isset( $wgDebugLogGroups['memcached'] ) ) {
- # Prefix message since it will end up in main debug log file
- $text = "memcached: $text";
- }
- wfDebugLog( 'memcached', $text );
- }
}
--- /dev/null
+<?php
+/*
+ * Session storage in object cache.
+ *
+ * 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 Cache
+ */
+class ObjectCacheSessionHandler {
+ /**
+ * Install a session handler for the current web request
+ */
+ static function install() {
+ session_set_save_handler(
+ array( __CLASS__, 'open' ),
+ array( __CLASS__, 'close' ),
+ array( __CLASS__, 'read' ),
+ array( __CLASS__, 'write' ),
+ array( __CLASS__, 'destroy' ),
+ array( __CLASS__, 'gc' ) );
+
+ // It's necessary to register a shutdown function to call session_write_close(),
+ // because by the time the request shutdown function for the session module is
+ // called, $wgMemc has already been destroyed. Shutdown functions registered
+ // this way are called before object destruction.
+ register_shutdown_function( array( __CLASS__, 'handleShutdown' ) );
+ }
+
+ /**
+ * Get the cache storage object to use for session storage
+ */
+ static function getCache() {
+ global $wgSessionCacheType;
+ return ObjectCache::getInstance( $wgSessionCacheType );
+ }
+
+ /**
+ * Get a cache key for the given session id.
+ *
+ * @param $id String: session id
+ * @return String: cache key
+ */
+ static function getKey( $id ) {
+ return wfMemcKey( 'session', $id );
+ }
+
+ /**
+ * Callback when opening a session.
+ *
+ * @param $save_path String: path used to store session files, unused
+ * @param $session_name String: session name
+ * @return Boolean: success
+ */
+ static function open( $save_path, $session_name ) {
+ return true;
+ }
+
+ /**
+ * Callback when closing a session.
+ * NOP.
+ *
+ * @return Boolean: success
+ */
+ static function close() {
+ return true;
+ }
+
+ /**
+ * Callback when reading session data.
+ *
+ * @param $id String: session id
+ * @return Mixed: session data
+ */
+ static function read( $id ) {
+ $data = self::getCache()->get( self::getKey( $id ) );
+ if( $data === false ) {
+ return '';
+ }
+ return $data;
+ }
+
+ /**
+ * Callback when writing session data.
+ *
+ * @param $id String: session id
+ * @param $data Mixed: session data
+ * @return Boolean: success
+ */
+ static function write( $id, $data ) {
+ global $wgObjectCacheSessionExpiry;
+ self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry );
+ return true;
+ }
+
+ /**
+ * Callback to destroy a session when calling session_destroy().
+ *
+ * @param $id String: session id
+ * @return Boolean: success
+ */
+ static function destroy( $id ) {
+ self::getCache()->delete( self::getKey( $id ) );
+ return true;
+ }
+
+ /**
+ * Callback to execute garbage collection.
+ * NOP: Object caches perform garbage collection implicitly
+ *
+ * @param $maxlifetime Integer: maximum session life time
+ * @return Boolean: success
+ */
+ static function gc( $maxlifetime ) {
+ return true;
+ }
+
+ /**
+ * Shutdown function. See the comment inside ObjectCacheSessionHandler::install
+ * for rationale.
+ */
+ static function handleShutdown() {
+ session_write_close();
+ }
+}
--- /dev/null
+<?php
+
+class RedisBagOStuff extends BagOStuff {
+ protected $connectTimeout, $persistent, $password, $automaticFailover;
+
+ /**
+ * A list of server names, from $params['servers']
+ */
+ protected $servers;
+
+ /**
+ * A cache of Redis objects, representing connections to Redis servers.
+ * The key is the server name.
+ */
+ protected $conns = array();
+
+ /**
+ * An array listing "dead" servers which have had a connection error in
+ * the past. Servers are marked dead for a limited period of time, to
+ * avoid excessive overhead from repeated connection timeouts. The key in
+ * the array is the server name, the value is the UNIX timestamp at which
+ * the server is resurrected.
+ */
+ protected $deadServers = array();
+
+ /**
+ * Construct a RedisBagOStuff object. Parameters are:
+ *
+ * - servers: An array of server names. A server name may be a hostname,
+ * a hostname/port combination or the absolute path of a UNIX socket.
+ * If a hostname is specified but no port, the standard port number
+ * 6379 will be used. Required.
+ *
+ * - connectTimeout: The timeout for new connections, in seconds. Optional,
+ * default is 1 second.
+ *
+ * - persistent: Set this to true to allow connections to persist across
+ * multiple web requests. False by default.
+ *
+ * - password: The authentication password, will be sent to Redis in
+ * clear text. Optional, if it is unspecified, no AUTH command will be
+ * sent.
+ *
+ * - automaticFailover: If this is false, then each key will be mapped to
+ * a single server, and if that server is down, any requests for that key
+ * will fail. If this is true, a connection failure will cause the client
+ * to immediately try the next server in the list (as determined by a
+ * consistent hashing algorithm). True by default. This has the
+ * potential to create consistency issues if a server is slow enough to
+ * flap, for example if it is in swap death.
+ */
+ function __construct( $params ) {
+ if ( !extension_loaded( 'redis' ) ) {
+ throw new MWException( __CLASS__. ' requires the phpredis extension: ' .
+ 'https://github.com/nicolasff/phpredis' );
+ }
+
+ $this->servers = $params['servers'];
+ $this->connectTimeout = isset( $params['connectTimeout'] )
+ ? $params['connectTimeout'] : 1;
+ $this->persistent = !empty( $params['persistent'] );
+ if ( isset( $params['password'] ) ) {
+ $this->password = $params['password'];
+ }
+ if ( isset( $params['automaticFailover'] ) ) {
+ $this->automaticFailover = $params['automaticFailover'];
+ } else {
+ $this->automaticFailover = true;
+ }
+ }
+
+ public function get( $key ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ try {
+ $result = $conn->get( $key );
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+ $this->logRequest( 'get', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ public function set( $key, $value, $expiry = 0 ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ $expiry = $this->convertToRelative( $expiry );
+ try {
+ if ( !$expiry ) {
+ // No expiry, that is very different from zero expiry in Redis
+ $result = $conn->set( $key, $value );
+ } else {
+ $result = $conn->setex( $key, $expiry, $value );
+ }
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+
+ $this->logRequest( 'set', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ public function delete( $key, $time = 0 ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ try {
+ $conn->delete( $key );
+ // Return true even if the key didn't exist
+ $result = true;
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+ $this->logRequest( 'delete', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ public function getMulti( array $keys ) {
+ wfProfileIn( __METHOD__ );
+ $batches = array();
+ $conns = array();
+ foreach ( $keys as $key ) {
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ continue;
+ }
+ $conns[$server] = $conn;
+ $batches[$server][] = $key;
+ }
+ $result = array();
+ foreach ( $batches as $server => $batchKeys ) {
+ $conn = $conns[$server];
+ try {
+ $conn->multi( Redis::PIPELINE );
+ foreach ( $batchKeys as $key ) {
+ $conn->get( $key );
+ }
+ $batchResult = $conn->exec();
+ if ( $batchResult === false ) {
+ $this->debug( "multi request to $server failed" );
+ continue;
+ }
+ foreach ( $batchResult as $i => $value ) {
+ if ( $value !== false ) {
+ $result[$batchKeys[$i]] = $value;
+ }
+ }
+ } catch ( RedisException $e ) {
+ $this->handleException( $server, $e );
+ }
+ }
+
+ $this->debug( "getMulti for " . count( $keys ) . " keys " .
+ "returned " . count( $result ) . " results" );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ public function add( $key, $value, $expiry = 0 ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ $expiry = $this->convertToRelative( $expiry );
+ try {
+ $result = $conn->setnx( $key, $value );
+ if ( $result && $expiry ) {
+ $conn->expire( $key, $expiry );
+ }
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+ $this->logRequest( 'add', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ /**
+ * Non-atomic implementation of replace(). Could perhaps be done atomically
+ * with WATCH or scripting, but this function is rarely used.
+ */
+ public function replace( $key, $value, $expiry = 0 ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ if ( !$conn->exists( $key ) ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $expiry = $this->convertToRelative( $expiry );
+ try {
+ if ( !$expiry ) {
+ $result = $conn->set( $key, $value );
+ } else {
+ $result = $conn->setex( $key, $expiry, $value );
+ }
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+
+ $this->logRequest( 'replace', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ /**
+ * Non-atomic implementation of incr().
+ *
+ * Probably all callers actually want incr() to atomically initialise
+ * values to zero if they don't exist, as provided by the Redis INCR
+ * command. But we are constrained by the memcached-like interface to
+ * return null in that case. Once the key exists, further increments are
+ * atomic.
+ */
+ public function incr( $key, $value = 1 ) {
+ wfProfileIn( __METHOD__ );
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ if ( !$conn->exists( $key ) ) {
+ wfProfileOut( __METHOD__ );
+ return null;
+ }
+ try {
+ $result = $conn->incrBy( $key, $value );
+ } catch ( RedisException $e ) {
+ $result = false;
+ $this->handleException( $server, $e );
+ }
+
+ $this->logRequest( 'incr', $key, $server, $result );
+ wfProfileOut( __METHOD__ );
+ return $result;
+ }
+
+ /**
+ * Get a Redis object with a connection suitable for fetching the specified key
+ */
+ protected function getConnection( $key ) {
+ if ( count( $this->servers ) === 1 ) {
+ $candidates = $this->servers;
+ } else {
+ // Use consistent hashing
+ $hashes = array();
+ foreach ( $this->servers as $server ) {
+ $hashes[$server] = md5( $server . '/' . $key );
+ }
+ asort( $hashes );
+ if ( !$this->automaticFailover ) {
+ reset( $hashes );
+ $candidates = array( key( $hashes ) );
+ } else {
+ $candidates = array_keys( $hashes );
+ }
+ }
+
+ foreach ( $candidates as $server ) {
+ $conn = $this->getConnectionToServer( $server );
+ if ( $conn ) {
+ return array( $server, $conn );
+ }
+ }
+ return array( false, false );
+ }
+
+ /**
+ * Get a connection to the server with the specified name. Connections
+ * are cached, and failures are persistent to avoid multiple timeouts.
+ *
+ * @return Redis object, or false on failure
+ */
+ protected function getConnectionToServer( $server ) {
+ if ( isset( $this->deadServers[$server] ) ) {
+ $now = time();
+ if ( $now > $this->deadServers[$server] ) {
+ // Dead time expired
+ unset( $this->deadServers[$server] );
+ } else {
+ // Server is dead
+ $this->debug( "server $server is marked down for another " .
+ ($this->deadServers[$server] - $now ) .
+ " seconds, can't get connection" );
+ return false;
+ }
+ }
+
+ if ( isset( $this->conns[$server] ) ) {
+ return $this->conns[$server];
+ }
+
+ if ( substr( $server, 0, 1 ) === '/' ) {
+ // UNIX domain socket
+ // These are required by the redis extension to start with a slash, but
+ // we still need to set the port to a special value to make it work.
+ $host = $server;
+ $port = 0;
+ } else {
+ // TCP connection
+ $hostPort = IP::splitHostAndPort( $server );
+ if ( !$hostPort ) {
+ throw new MWException( __CLASS__.": invalid configured server \"$server\"" );
+ }
+ list( $host, $port ) = $hostPort;
+ if ( $port === false ) {
+ $port = 6379;
+ }
+ }
+ $conn = new Redis;
+ try {
+ if ( $this->persistent ) {
+ $this->debug( "opening persistent connection to $host:$port" );
+ $result = $conn->pconnect( $host, $port, $this->connectTimeout );
+ } else {
+ $this->debug( "opening non-persistent connection to $host:$port" );
+ $result = $conn->connect( $host, $port, $this->connectTimeout );
+ }
+ if ( !$result ) {
+ $this->logError( "could not connect to server $server" );
+ // Mark server down for 30s to avoid further timeouts
+ $this->deadServers[$server] = time() + 30;
+ return false;
+ }
+ if ( $this->password !== null ) {
+ if ( !$conn->auth( $this->password ) ) {
+ $this->logError( "authentication error connecting to $server" );
+ }
+ }
+ } catch ( RedisException $e ) {
+ $this->deadServers[$server] = time() + 30;
+ wfDebugLog( 'redis', "Redis exception: " . $e->getMessage() . "\n" );
+ return false;
+ }
+
+ $conn->setOption( Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP );
+ $this->conns[$server] = $conn;
+ return $conn;
+ }
+
+ /**
+ * Log a fatal error
+ */
+ protected function logError( $msg ) {
+ wfDebugLog( 'redis', "Redis error: $msg\n" );
+ }
+
+ /**
+ * The redis extension throws an exception in response to various read, write
+ * and protocol errors. Sometimes it also closes the connection, sometimes
+ * not. The safest response for us is to explicitly destroy the connection
+ * object and let it be reopened during the next request.
+ */
+ protected function handleException( $server, $e ) {
+ wfDebugLog( 'redis', "Redis exception on server $server: " . $e->getMessage() . "\n" );
+ unset( $this->conns[$server] );
+ }
+
+ /**
+ * Send information about a single request to the debug log
+ */
+ public function logRequest( $method, $key, $server, $result ) {
+ $this->debug( "$method $key on $server: " .
+ ( $result === false ? "failure" : "success" ) );
+ }
+}
+
--- /dev/null
+<?php
+
+/**
+ * @todo document
+ *
+ * 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 Parser
+ */
+class CacheTime {
+
+ var $mVersion = Parser::VERSION, # Compatibility check
+ $mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
+ $mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncachable. Used in ParserCache.
+ $mContainsOldMagic; # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
+
+ function getCacheTime() { return $this->mCacheTime; }
+
+ function containsOldMagic() { return $this->mContainsOldMagic; }
+ function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
+
+ /**
+ * setCacheTime() sets the timestamp expressing when the page has been rendered.
+ * This doesn not control expiry, see updateCacheExpiry() for that!
+ * @param $t string
+ * @return string
+ */
+ function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
+
+ /**
+ * Sets the number of seconds after which this object should expire.
+ * This value is used with the ParserCache.
+ * If called with a value greater than the value provided at any previous call,
+ * the new call has no effect. The value returned by getCacheExpiry is smaller
+ * or equal to the smallest number that was provided as an argument to
+ * updateCacheExpiry().
+ *
+ * @param $seconds number
+ */
+ function updateCacheExpiry( $seconds ) {
+ $seconds = (int)$seconds;
+
+ if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
+ $this->mCacheExpiry = $seconds;
+ }
+
+ // hack: set old-style marker for uncacheable entries.
+ if ( $this->mCacheExpiry !== null && $this->mCacheExpiry <= 0 ) {
+ $this->mCacheTime = -1;
+ }
+ }
+
+ /**
+ * Returns the number of seconds after which this object should expire.
+ * This method is used by ParserCache to determine how long the ParserOutput can be cached.
+ * The timestamp of expiry can be calculated by adding getCacheExpiry() to getCacheTime().
+ * The value returned by getCacheExpiry is smaller or equal to the smallest number
+ * that was provided to a call of updateCacheExpiry(), and smaller or equal to the
+ * value of $wgParserCacheExpireTime.
+ * @return int|mixed|null
+ */
+ function getCacheExpiry() {
+ global $wgParserCacheExpireTime;
+
+ if ( $this->mCacheTime < 0 ) {
+ return 0;
+ } // old-style marker for "not cachable"
+
+ $expire = $this->mCacheExpiry;
+
+ if ( $expire === null ) {
+ $expire = $wgParserCacheExpireTime;
+ } else {
+ $expire = min( $expire, $wgParserCacheExpireTime );
+ }
+
+ if( $this->containsOldMagic() ) { //compatibility hack
+ $expire = min( $expire, 3600 ); # 1 hour
+ }
+
+ if ( $expire <= 0 ) {
+ return 0; // not cachable
+ } else {
+ return $expire;
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ function isCacheable() {
+ return $this->getCacheExpiry() > 0;
+ }
+
+ /**
+ * Return true if this cached output object predates the global or
+ * per-article cache invalidation timestamps, or if it comes from
+ * an incompatible older version.
+ *
+ * @param $touched String: the affected article's last touched timestamp
+ * @return Boolean
+ */
+ public function expired( $touched ) {
+ global $wgCacheEpoch;
+ return !$this->isCacheable() || // parser says it's uncacheable
+ $this->getCacheTime() < $touched ||
+ $this->getCacheTime() <= $wgCacheEpoch ||
+ $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
+ !isset( $this->mVersion ) ||
+ version_compare( $this->mVersion, Parser::VERSION, "lt" );
+ }
+
+}
\ No newline at end of file
// or {{filepath|300px}}, {{filepath|200x300px}}, {{filepath|nowiki|200x300px}}, {{filepath|200x300px|nowiki}}
public static function filepath( $parser, $name='', $argA='', $argB='' ) {
$file = wfFindFile( $name );
- $isNowiki = false;
if( $argA == 'nowiki' ) {
// {{filepath: | option [| size] }}
/**
* Get the target language for the content being parsed. This is usually the
* language that the content is in.
+ *
+ * @since 1.19
+ *
+ * @return Language|null
*/
- function getTargetLanguage() {
+ public function getTargetLanguage() {
$target = $this->mOptions->getTargetLanguage();
+
if ( $target !== null ) {
return $target;
} elseif( $this->mOptions->getInterfaceMessage() ) {
return $this->mOptions->getUserLangObj();
} elseif( is_null( $this->mTitle ) ) {
- throw new MWException( __METHOD__.': $this->mTitle is null' );
+ throw new MWException( __METHOD__ . ': $this->mTitle is null' );
}
+
return $this->mTitle->getPageLanguage();
}
# Get the revision
$rev = $id
? Revision::newFromId( $id )
- : Revision::newFromTitle( $title );
+ : Revision::newFromTitle( $title, 0, Revision::READ_NORMAL );
$rev_id = $rev ? $rev->getId() : 0;
# If there is no current revision, there is no page
if ( $id === false && !$rev ) {
# Linker does the rest
$time = isset( $options['time'] ) ? $options['time'] : false;
- $ret = Linker::makeImageLink2( $title, $file, $params['frame'], $params['handler'],
+ $ret = Linker::makeImageLink( $this, $title, $file, $params['frame'], $params['handler'],
$time, $descQuery, $this->mOptions->getThumbSize() );
# Give the handler a chance to modify the parser object
<?php
+
/**
- * Output of the PHP parser
+ * Output of the PHP parser.
*
* 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
* @file
* @ingroup Parser
*/
-
-/**
- * @todo document
- * @ingroup Parser
- */
-class CacheTime {
- var $mVersion = Parser::VERSION, # Compatibility check
- $mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
- $mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncachable. Used in ParserCache.
- $mContainsOldMagic; # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
-
- function getCacheTime() { return $this->mCacheTime; }
-
- function containsOldMagic() { return $this->mContainsOldMagic; }
- function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
-
- /**
- * setCacheTime() sets the timestamp expressing when the page has been rendered.
- * This doesn not control expiry, see updateCacheExpiry() for that!
- * @param $t string
- * @return string
- */
- function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
-
- /**
- * Sets the number of seconds after which this object should expire.
- * This value is used with the ParserCache.
- * If called with a value greater than the value provided at any previous call,
- * the new call has no effect. The value returned by getCacheExpiry is smaller
- * or equal to the smallest number that was provided as an argument to
- * updateCacheExpiry().
- *
- * @param $seconds number
- */
- function updateCacheExpiry( $seconds ) {
- $seconds = (int)$seconds;
-
- if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
- $this->mCacheExpiry = $seconds;
- }
-
- // hack: set old-style marker for uncacheable entries.
- if ( $this->mCacheExpiry !== null && $this->mCacheExpiry <= 0 ) {
- $this->mCacheTime = -1;
- }
- }
-
- /**
- * Returns the number of seconds after which this object should expire.
- * This method is used by ParserCache to determine how long the ParserOutput can be cached.
- * The timestamp of expiry can be calculated by adding getCacheExpiry() to getCacheTime().
- * The value returned by getCacheExpiry is smaller or equal to the smallest number
- * that was provided to a call of updateCacheExpiry(), and smaller or equal to the
- * value of $wgParserCacheExpireTime.
- * @return int|mixed|null
- */
- function getCacheExpiry() {
- global $wgParserCacheExpireTime;
-
- if ( $this->mCacheTime < 0 ) {
- return 0;
- } // old-style marker for "not cachable"
-
- $expire = $this->mCacheExpiry;
-
- if ( $expire === null ) {
- $expire = $wgParserCacheExpireTime;
- } else {
- $expire = min( $expire, $wgParserCacheExpireTime );
- }
-
- if( $this->containsOldMagic() ) { //compatibility hack
- $expire = min( $expire, 3600 ); # 1 hour
- }
-
- if ( $expire <= 0 ) {
- return 0; // not cachable
- } else {
- return $expire;
- }
- }
-
- /**
- * @return bool
- */
- function isCacheable() {
- return $this->getCacheExpiry() > 0;
- }
-
- /**
- * Return true if this cached output object predates the global or
- * per-article cache invalidation timestamps, or if it comes from
- * an incompatible older version.
- *
- * @param $touched String: the affected article's last touched timestamp
- * @return Boolean
- */
- public function expired( $touched ) {
- global $wgCacheEpoch;
- return !$this->isCacheable() || // parser says it's uncacheable
- $this->getCacheTime() < $touched ||
- $this->getCacheTime() <= $wgCacheEpoch ||
- $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
- !isset( $this->mVersion ) ||
- version_compare( $this->mVersion, Parser::VERSION, "lt" );
- }
-}
-
class ParserOutput extends CacheTime {
var $mText, # The output text
$mLanguageLinks, # List of the full text of language links, in the order they appear
* Adds an update job to the output. Any update jobs added to the output will eventually bexecuted in order to
* store any secondary information extracted from the page's content.
*
- * @param StorageUpdate $update
+ * @since 1.20
+ *
+ * @param DataUpdate $update
*/
public function addSecondaryDataUpdate( DataUpdate $update ) {
$this->mSecondaryDataUpdates[] = $update;
* extracted from the page's content, including a LinksUpdate object for all links stored in
* this ParserOutput object.
*
+ * @since 1.20
+ *
* @param $title Title of the page we're updating. If not given, a title object will be created based on $this->getTitleText()
* @param $recursive Boolean: queue jobs for recursive updates?
*
* @return Array. An array of instances of DataUpdate
*/
public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
- if ( !$title ) {
+ if ( is_null( $title ) ) {
$title = Title::newFromText( $this->getTitleText() );
}
$linksUpdate = new LinksUpdate( $title, $this, $recursive );
- if ( !$this->mSecondaryDataUpdates ) {
+ if ( $this->mSecondaryDataUpdates === array() ) {
return array( $linksUpdate );
} else {
$updates = array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
return $updates;
}
+
}
$children = array();
for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
if ( isset( $child->name ) && $child->name === $name ) {
- $children[] = $name;
+ $children[] = $child;
}
}
return $children;
$children = array();
for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
if ( isset( $child->name ) && $child->name === $name ) {
- $children[] = $name;
+ $children[] = $child;
}
}
return $children;
$this->mTitle = $title;
if ( !is_null( $this->mTitle ) ) {
$this->mRevision = Revision::newFromTitle(
- $this->mTitle, false, Revision::AVOID_MASTER );
+ $this->mTitle, false, Revision::READ_NORMAL );
if ( $this->mTitle->getNamespace() === NS_FILE )
$this->mImage = wfFindFile( $this->mTitle );
}
'tabindex' => '2',
'options' => self::getSuggestedDurations(),
'other' => $this->msg( 'ipbother' )->text(),
+ 'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
),
'Reason' => array(
'type' => 'selectandother',
}
if( $block->mExpiry == 'infinity' ) {
- $fields['Expiry']['default'] = 'indefinite';
+ $fields['Expiry']['default'] = 'infinite';
} else {
$fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->mExpiry );
}
$page = $this->msg( 'booksources' )->inContentLanguage()->text();
$title = Title::makeTitleSafe( NS_PROJECT, $page ); # Show list in content language
if( is_object( $title ) && $title->exists() ) {
- $rev = Revision::newFromTitle( $title, false, Revision::AVOID_MASTER );
+ $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
$this->getOutput()->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $rev->getText() ) );
return true;
}
*/
protected $cacheEnabled = true;
+ /**
+ * Gets called after @see SpecialPage::execute.
+ *
+ * @since 1.20
+ *
+ * @param $subPage string|null
+ */
+ protected function afterExecute( $subPage ) {
+ $this->saveCache();
+
+ parent::afterExecute( $subPage );
+ }
+
/**
* Sets if the cache should be enabled or not.
*
* @param boolean|null $cacheEnabled Sets if the cache should be enabled or not.
*/
public function startCache( $cacheExpiry = null, $cacheEnabled = null ) {
- $this->cacheHelper = new CacheHelper();
+ if ( !isset( $this->cacheHelper ) ) {
+ $this->cacheHelper = new CacheHelper();
- $this->cacheHelper->setCacheEnabled( $this->cacheEnabled );
- $this->cacheHelper->setOnInitializedHandler( array( $this, 'onCacheInitialized' ) );
+ $this->cacheHelper->setCacheEnabled( $this->cacheEnabled );
+ $this->cacheHelper->setOnInitializedHandler( array( $this, 'onCacheInitialized' ) );
- $keyArgs = $this->getCacheKey();
+ $keyArgs = $this->getCacheKey();
- if ( array_key_exists( 'action', $keyArgs ) && $keyArgs['action'] === 'purge' ) {
- unset( $keyArgs['action'] );
- }
+ if ( array_key_exists( 'action', $keyArgs ) && $keyArgs['action'] === 'purge' ) {
+ unset( $keyArgs['action'] );
+ }
- $this->cacheHelper->setCacheKey( $keyArgs );
+ $this->cacheHelper->setCacheKey( $keyArgs );
- if ( $this->getRequest()->getText( 'action' ) === 'purge' ) {
- $this->cacheHelper->rebuildOnDemand();
+ if ( $this->getRequest()->getText( 'action' ) === 'purge' ) {
+ $this->cacheHelper->rebuildOnDemand();
+ }
}
$this->cacheHelper->startCache( $cacheExpiry, $cacheEnabled );
* @since 1.20
*/
public function saveCache() {
- $this->cacheHelper->saveCache();
+ if ( isset( $this->cacheHelper ) ) {
+ $this->cacheHelper->saveCache();
+ }
}
/**
$from = str_replace( ' ', '_', $from );
if( $from !== '' ) {
$from = Title::capitalize( $from, NS_CATEGORY );
- $this->mOffset = $from;
+ $this->setOffset( $from );
+ $this->setIncludeOffset( true );
}
}
* @ingroup SpecialPage
*/
class SpecialChangeEmail extends UnlistedSpecialPage {
+
+ /**
+ * Users password
+ * @var string
+ */
+ protected $mPassword;
+
+ /**
+ * Users new email address
+ * @var string
+ */
+ protected $mNewEmail;
+
public function __construct() {
parent::__construct( 'ChangeEmail' );
}
+ /**
+ * @return Bool
+ */
function isListed() {
global $wgAuth;
return $wgAuth->allowPropChange( 'emailaddress' );
function execute( $par ) {
global $wgAuth;
- $this->checkReadOnly();
-
$this->setHeaders();
$this->outputHeader();
+ $out = $this->getOutput();
+ $out->disallowUserJs();
+ $out->addModules( 'mediawiki.special.changeemail' );
+
if ( !$wgAuth->allowPropChange( 'emailaddress' ) ) {
$this->error( 'cannotchangeemail' );
return;
return;
}
- $out = $this->getOutput();
- $out->disallowUserJs();
- $out->addModules( 'mediawiki.special.changeemail' );
+ $this->checkReadOnly();
$this->mPassword = $request->getVal( 'wpPassword' );
$this->mNewEmail = $request->getVal( 'wpNewEmail' );
$this->showForm();
}
+ /**
+ * @param $type string
+ */
protected function doReturnTo( $type = 'hard' ) {
$titleObj = Title::newFromText( $this->getRequest()->getVal( 'returnto' ) );
if ( !$titleObj instanceof Title ) {
}
}
+ /**
+ * @param $msg string
+ */
protected function error( $msg ) {
$this->getOutput()->wrapWikiMsg( "<p class='error'>\n$1\n</p>", $msg );
}
protected function showForm() {
+ global $wgRequirePasswordforEmailChange;
$user = $this->getUser();
$oldEmailText = $user->getEmail()
Html::hidden( 'token', $user->getEditToken() ) . "\n" .
Html::hidden( 'returnto', $this->getRequest()->getVal( 'returnto' ) ) . "\n" .
$this->msg( 'changeemail-text' )->parseAsBlock() . "\n" .
- Xml::openElement( 'table', array( 'id' => 'mw-changeemail-table' ) ) . "\n" .
- $this->pretty( array(
- array( 'wpName', 'username', 'text', $user->getName() ),
- array( 'wpOldEmail', 'changeemail-oldemail', 'text', $oldEmailText ),
- array( 'wpNewEmail', 'changeemail-newemail', 'input', $this->mNewEmail ),
- array( 'wpPassword', 'yourpassword', 'password', $this->mPassword ),
- ) ) . "\n" .
+ Xml::openElement( 'table', array( 'id' => 'mw-changeemail-table' ) ) . "\n"
+ );
+ $items = array(
+ array( 'wpName', 'username', 'text', $user->getName() ),
+ array( 'wpOldEmail', 'changeemail-oldemail', 'text', $oldEmailText ),
+ array( 'wpNewEmail', 'changeemail-newemail', 'input', $this->mNewEmail ),
+ );
+ if ( $wgRequirePasswordforEmailChange ) {
+ $items[] = array( 'wpPassword', 'yourpassword', 'password', $this->mPassword );
+ }
+
+ $this->getOutput()->addHTML(
+ $this->pretty( $items ) .
+ "\n" .
"<tr>\n" .
"<td></td>\n" .
'<td class="mw-input">' .
);
}
+ /**
+ * @param $fields array
+ * @return string
+ */
protected function pretty( $fields ) {
$out = '';
foreach ( $fields as $list ) {
}
/**
+ * @param $user User
+ * @param $pass string
+ * @param $newaddr string
* @return bool|string true or string on success, false on failure
*/
protected function attemptChange( User $user, $pass, $newaddr ) {
return false;
}
- if ( !$user->checkTemporaryPassword( $pass ) && !$user->checkPassword( $pass ) ) {
+ global $wgRequirePasswordforEmailChange;
+ if ( $wgRequirePasswordforEmailChange && !$user->checkTemporaryPassword( $pass ) && !$user->checkPassword( $pass ) ) {
$this->error( 'wrongpassword' );
return false;
}
function execute( $par ) {
global $wgAuth;
- $this->checkReadOnly();
+ $this->setHeaders();
+ $this->outputHeader();
+ $this->getOutput()->disallowUserJs();
$request = $this->getRequest();
$this->mUserName = trim( $request->getVal( 'wpName' ) );
$this->mRetype = $request->getVal( 'wpRetype' );
$this->mDomain = $request->getVal( 'wpDomain' );
- $this->setHeaders();
- $this->outputHeader();
- $this->getOutput()->disallowUserJs();
-
$user = $this->getUser();
if( !$request->wasPosted() && !$user->isLoggedIn() ) {
$this->error( $this->msg( 'resetpass-no-info' )->text() );
return;
}
+ $this->checkReadOnly();
+
if( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'token' ) ) ) {
try {
$this->mDomain = $wgAuth->getDomain();
parent::__construct( $name );
}
- function isExpensive() { return true; }
- function isSyndicated() { return false; }
+ function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
function getPageHeader() {
return $this->msg( 'disambiguations-text' )->parseAsBlock();
}
- function getQueryInfo() {
+ /**
+ * @return string|bool False on failure
+ */
+ function getQueryFromLinkBatch() {
$dbr = wfGetDB( DB_SLAVE );
$dMsgText = $this->msg( 'disambiguationspage' )->inContentLanguage()->text();
$linkBatch = new LinkBatch;
# If the text can be treated as a title, use it verbatim.
# Otherwise, pull the titles from the links table
- $dp = Title::newFromText($dMsgText);
+ $dp = Title::newFromText( $dMsgText );
if( $dp ) {
if( $dp->getNamespace() != NS_TEMPLATE ) {
# @todo FIXME: We assume the disambiguation message is a template but
}
}
$set = $linkBatch->constructSet( 'tl', $dbr );
+
if( $set === false ) {
# We must always return a valid SQL query, but this way
# the DB will always quickly return an empty result
$set = 'FALSE';
- wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
+ wfDebug( "Mediawiki:disambiguationspage message does not link to any templates!\n" );
}
+ return $set;
+ }
+ function getQueryInfo() {
// @todo FIXME: What are pagelinks and p2 doing here?
return array (
- 'tables' => array( 'templatelinks', 'p1' => 'page', 'pagelinks', 'p2' => 'page' ),
- 'fields' => array( 'p1.page_namespace AS namespace',
- 'p1.page_title AS title',
- 'pl_from AS value' ),
- 'conds' => array( $set,
- 'p1.page_id = tl_from',
- 'pl_namespace = p1.page_namespace',
- 'pl_title = p1.page_title',
- 'p2.page_id = pl_from',
- 'p2.page_namespace' => MWNamespace::getContentNamespaces() )
+ 'tables' => array(
+ 'templatelinks',
+ 'p1' => 'page',
+ 'pagelinks',
+ 'p2' => 'page'
+ ),
+ 'fields' => array(
+ 'p1.page_namespace AS namespace',
+ 'p1.page_title AS title',
+ 'pl_from AS value'
+ ),
+ 'conds' => array(
+ $this->getQueryFromLinkBatch(),
+ 'p1.page_id = tl_from',
+ 'pl_namespace = p1.page_namespace',
+ 'pl_title = p1.page_title',
+ 'p2.page_id = pl_from',
+ 'p2.page_namespace' => MWNamespace::getContentNamespaces()
+ )
);
}
$dp = Title::makeTitle( $result->namespace, $result->title );
$from = Linker::link( $title );
- $edit = Linker::link( $title, $this->msg( 'parentheses', $this->msg( 'editlink' )->text() )->escaped(),
- array(), array( 'redirect' => 'no', 'action' => 'edit' ) );
- $arr = $this->getLanguage()->getArrow();
- $to = Linker::link( $dp );
+ $edit = Linker::link(
+ $title,
+ $this->msg( 'parentheses', $this->msg( 'editlink' )->text() )->escaped(),
+ array(),
+ array( 'redirect' => 'no', 'action' => 'edit' )
+ );
+ $arr = $this->getLanguage()->getArrow();
+ $to = Linker::link( $dp );
return "$from $edit $arr $to";
}
if ( count( $fields ) > 1 && $count > 30 ) {
$this->toc = Linker::tocIndent();
$tocLength = 0;
- foreach( $fields as $key => $data ) {
+ foreach( $fields as $data ) {
# strip out the 'ns' prefix from the section name:
$ns = substr( $data['section'], 2 );
'label-message' => 'emailsubject',
'maxlength' => 200,
'size' => 60,
- 'required' => 1,
+ 'required' => true,
),
'Text' => array(
'type' => 'textarea',
'rows' => 20,
'cols' => 80,
'label-message' => 'emailmessage',
- 'required' => 1,
+ 'required' => true,
),
'CCMe' => array(
'type' => 'check',
$out = $this->getOutput();
if( $user->isAllowed( 'importupload' ) ) {
- $out->addWikiMsg( "importtext" );
$out->addHTML(
Xml::fieldset( $this->msg( 'import-upload' )->text() ).
Xml::openElement( 'form', array( 'enctype' => 'multipart/form-data', 'method' => 'post',
'action' => $action, 'id' => 'mw-import-upload-form' ) ) .
+ $this->msg( 'importtext' )->parseAsBlock() .
Html::hidden( 'action', 'submit' ) .
Html::hidden( 'source', 'upload' ) .
Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
return 'img_timestamp';
}
- function getStartBody() {
- # Do a link batch query for user pages
- if ( $this->mResult->numRows() ) {
- $lb = new LinkBatch;
- $this->mResult->seek( 0 );
- foreach ( $this->mResult as $row ) {
- if ( $row->img_user ) {
- $lb->add( NS_USER, str_replace( ' ', '_', $row->img_user_text ) );
- }
- }
- $lb->execute();
+ function doBatchLookups() {
+ $userIds = array();
+ $this->mResult->seek( 0 );
+ foreach ( $this->mResult as $row ) {
+ $userIds[] = $row->img_user;
}
-
- return parent::getStartBody();
+ # Do a link batch query for names and userpages
+ UserCache::singleton()->doQuery( $userIds, array( 'userpage' ), __METHOD__ );
}
function formatValue( $field, $value ) {
}
case 'img_user_text':
if ( $this->mCurrentRow->img_user ) {
+ $name = User::whoIs( $this->mCurrentRow->img_user );
$link = Linker::link(
- Title::makeTitle( NS_USER, $value ),
- htmlspecialchars( $value )
+ Title::makeTitle( NS_USER, $name ),
+ htmlspecialchars( $name )
);
} else {
$link = htmlspecialchars( $value );
return Html::openElement( 'form',
array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listfiles-form' ) ) .
Xml::fieldset( $this->msg( 'listfiles' )->text() ) .
+ Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
Xml::buildForm( $inputForm, 'table_pager_limit_submit' ) .
- $this->getHiddenFields( array( 'limit', 'ilsearch', 'user' ) ) .
+ $this->getHiddenFields( array( 'limit', 'ilsearch', 'user', 'title' ) ) .
Html::closeElement( 'fieldset' ) .
Html::closeElement( 'form' ) . "\n";
}
}
return $queries;
}
+
+ function getTitle() {
+ return SpecialPage::getTitleFor( 'Listfiles' );
+ }
}
* @param $context IContextSource
* @param $par null|array
*/
- function __construct( IContextSource $context = null, $par = null ) {
+ function __construct( IContextSource $context = null, $par = null, $including = null ) {
if ( $context ) {
$this->setContext( $context );
}
}
$this->editsOnly = $request->getBool( 'editsOnly' );
$this->creationSort = $request->getBool( 'creationSort' );
+ $this->including = $including;
$this->requestedUser = '';
if ( $un != '' ) {
$lang = $this->getLanguage();
+ $groups = '';
$groups_list = self::getGroups( $row->user_id );
- if( count( $groups_list ) > 0 ) {
+ if( !$this->including && count( $groups_list ) > 0 ) {
$list = array();
foreach( $groups_list as $group )
$list[] = self::buildGroupLink( $group, $userName );
$groups = $lang->commaList( $list );
- } else {
- $groups = '';
}
$item = $lang->specialList( $ulinks, $groups );
$item = "<span class=\"deleted\">$item</span>";
}
+ $edits = '';
global $wgEdititis;
- if ( $wgEdititis ) {
+ if ( !$this->including && $wgEdititis ) {
$edits = ' [' . $this->msg( 'usereditcount' )->numParams( $row->edits )->escaped() . ']';
- } else {
- $edits = '';
}
$created = '';
# Some rows may be NULL
- if( $row->creation ) {
+ if( !$this->including && $row->creation ) {
$user = $this->getUser();
$d = $lang->userDate( $row->creation, $user );
$t = $lang->userTime( $row->creation, $user );
*/
public function __construct() {
parent::__construct( 'Listusers' );
+ $this->mIncludable = true;
}
/**
$this->setHeaders();
$this->outputHeader();
- $up = new UsersPager( $this->getContext(), $par );
+ $up = new UsersPager( $this->getContext(), $par, $this->including() );
# getBody() first to check, if empty
$usersbody = $up->getBody();
- $s = $up->getPageHeader();
+ $s = '';
+ if ( !$this->including() ) {
+ $s = $up->getPageHeader();
+ }
+
if( $usersbody ) {
$s .= $up->getNavigationBar();
$s .= Html::rawElement( 'ul', array(), $usersbody );
private function getRevisionButton( $formcontents ) {
# If the user doesn't have the ability to delete log entries, don't bother showing him/her the button.
- if ( !$this->getUser()->isAllowed( 'deletelogentry' ) ) {
+ if ( !$this->getUser()->isAllowedAll( 'deletedhistory', 'deletelogentry' ) ) {
return $formcontents;
}
* @param $res DatabaseResult
*/
function preprocessResults( $db, $res ) {
+ if ( !$res->numRows() ) {
+ return;
+ }
+
$batch = new LinkBatch;
foreach ( $res as $row ) {
$batch->add( NS_CATEGORY, $row->title );
$batch->execute();
// Back to start for display
- if ( $db->numRows( $res ) > 0 ) {
- // If there are no rows we get an error seeking.
- $db->dataSeek( $res, 0 );
- }
+ $res->seek( 0 );
}
/**
function formatResult( $skin, $result ) {
global $wgContLang;
- $nt = Title::makeTitle( NS_CATEGORY, $result->title );
+ $nt = Title::makeTitleSafe( NS_CATEGORY, $result->title );
+ if ( !$nt ) {
+ return Html::element( 'span', array( 'class' => 'mw-invalidtitle' ),
+ Linker::getInvalidTitleDescription( $this->getContext(), NS_CATEGORY, $result->title ) );
+ }
+
$text = $wgContLang->convert( $nt->getText() );
$plink = Linker::link( $nt, htmlspecialchars( $text ) );
* @param $res ResultWrapper
*/
public function preprocessResults( $db, $res ) {
+ if ( !$res->numRows() ) {
+ return;
+ }
+
$batch = new LinkBatch();
foreach ( $res as $row ) {
$batch->add( $row->namespace, $row->title );
}
$batch->execute();
- if( $db->numRows( $res ) > 0 )
- $db->dataSeek( $res, 0 );
+
+ $res->seek( 0 );
}
/**
* @return String
*/
public function formatResult( $skin, $result ) {
- $title = Title::makeTitle( $result->namespace, $result->title );
+ $title = Title::makeTitleSafe( $result->namespace, $result->title );
+ if ( !$title ) {
+ return Html::element( 'span', array( 'class' => 'mw-invalidtitle' ),
+ Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) );
+ }
return $this->getLanguage()->specialList(
Linker::link( $title ),
function __construct( IContextSource $context, $par = null ) {
$this->like = $context->getRequest()->getText( 'like' );
$this->showbots = $context->getRequest()->getBool( 'showbots' , 0 );
+ if ( is_numeric( $par ) ) {
+ $this->setLimit( $par );
+ }
parent::__construct( $context );
}
),
'limit' => array(
'type' => 'hidden',
- 'default' => $this->getRequest()->getText( 'limit' ),
+ 'default' => $this->mLimit,
'name' => 'limit',
),
'offset' => array(
$user->setNewpassword( $password );
$user->saveSettings();
$passwords[] = $this->msg( 'passwordreset-emailelement', $user->getName(), $password
- )->inLanguage( $userLanguage )->plain(); // We'll escape the whole thing later
+ )->inLanguage( $userLanguage )->text(); // We'll escape the whole thing later
}
$passwordBlock = implode( "\n\n", $passwords );
*/
function makeOptionsLink( $title, $override, $options, $active = false ) {
$params = $override + $options;
+
+ // Bug 36524: false values have be converted to "0" otherwise
+ // wfArrayToCgi() will omit it them.
+ foreach ( $params as &$value ) {
+ if ( $value === false ) {
+ $value = '0';
+ }
+ }
+ unset( $value );
+
$text = htmlspecialchars( $title );
if ( $active ) {
$text = '<strong>' . $text . '</strong>';
$query = array( 'wpCookieCheck' => $type );
if ( $this->mReturnTo ) {
$query['returnto'] = $this->mReturnTo;
+ $query['returntoquery'] = $this->mReturnToQuery;
}
$check = $titleObj->getFullURL( $query );
}
function getPageHeader() {
- global $wgScript, $wgMiserMode;
+ global $wgScript;
- # Do not show useless input form if wiki is running in misermode
- if( $wgMiserMode ) {
+ # Do not show useless input form if special page is cached
+ if( $this->isCached() ) {
return '';
}
const WINDOWS_NONASCII_FILENAME = 13;
const FILENAME_TOO_LONG = 14;
+ /**
+ * @param $error int
+ * @return string
+ */
public function getVerificationErrorCode( $error ) {
$code_to_status = array(self::EMPTY_FILE => 'empty-file',
self::FILE_TOO_LARGE => 'file-too-large',
/**
* Check whether a request if valid for this handler
+ * @param $request
* @return bool
*/
public static function isValidRequest( $request ) {
* @param $tempPath string the temporary path
* @param $fileSize int the file size
* @param $removeTempFile bool (false) remove the temporary file?
- * @return null
+ * @throws MWException
*/
public function initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile = false ) {
$this->mDesiredDestName = $name;
/**
* @param $srcPath String: the source path
- * @return stringthe real path if it was a virtual URL
+ * @return string the real path if it was a virtual URL
*/
function getRealPath( $srcPath ) {
wfProfileIn( __METHOD__ );
/**
* Return the local file and initializes if necessary.
*
- * @return LocalFile
+ * @return LocalFile|null
*/
public function getLocalFile() {
if( is_null( $this->mLocalFile ) ) {
* earlier pseudo-'extensions' to determine type and execute
* scripts, so the blacklist needs to check them all.
*
+ * @param $filename string
* @return array
*/
public static function splitExtensions( $filename ) {
return false;
}
+ /**
+ * @param $filename string
+ * @return bool
+ */
protected function detectScriptInSvg( $filename ) {
$check = new XmlTypeCheck( $filename, array( $this, 'checkSvgScriptCallback' ) );
return $check->filterMatch;
/**
* @todo Replace this with a whitelist filter!
+ * @param $element string
+ * @param $attribs array
* @return bool
*/
public function checkSvgScriptCallback( $element, $attribs ) {
return false; //No scripts detected
}
+ /**
+ * @param $name string
+ * @return string
+ */
private function stripXmlNamespace( $name ) {
// 'http://www.w3.org/2000/svg:script' -> 'script'
$parts = explode( ':', strtolower( $name ) );
/**
* Helper function that checks whether the filename looks like a thumbnail
+ * @param $filename string
* @return bool
*/
public static function isThumbName( $filename ) {
return $info;
}
-
+ /**
+ * @param $error array
+ * @return Status
+ */
public function convertVerifyErrorToStatus( $error ) {
$code = $error['status'];
unset( $code['status'] );
return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
}
+ /**
+ * @param $forType null|string
+ * @return int
+ */
public static function getMaxUploadSize( $forType = null ) {
global $wgMaxUploadSize;
*/
class UploadFromChunks extends UploadFromFile {
protected $mOffset, $mChunkIndex, $mFileKey, $mVirtualTempPath;
-
+
/**
* Setup local pointers to stash, repo and user ( similar to UploadFromStash )
- *
+ *
* @param $user User
* @param $stash UploadStash
* @param $repo FileRepo
// Output a copy of this first to chunk 0 location:
$status = $this->outputChunk( $this->mLocalFile->getPath() );
-
+
// Update db table to reflect initial "chunk" state
$this->updateChunkStatus();
return $this->mLocalFile;
}
-
+
/**
* Continue chunk uploading
*/
$this->mUpload = $webRequestUpload;
// Get the chunk status form the db:
$this->getChunkStatus();
-
+
$metadata = $this->stash->getMetadata( $key );
$this->initializePathInfo( $name,
$this->getRealPath( $metadata['us_path'] ),
false
);
}
-
+
/**
* Append the final chunk and ready file for parent::performUpload()
* @return FileRepoStatus
}
return $status;
}
-
+
/**
* Update the chunk db table with the current status:
*/
private function updateChunkStatus(){
wfDebug( __METHOD__ . " update chunk status for {$this->mFileKey} offset:" .
$this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
-
+
$dbw = $this->repo->getMasterDb();
$dbw->update(
'uploadstash',
__METHOD__
);
}
+
/**
* Get the chunk db state and populate update relevant local values
*/
$this->mVirtualTempPath = $row->us_path;
}
}
+
/**
* Get the current Chunk index
* @return Integer index of the current chunk
}
return 0;
}
-
+
/**
* Gets the current offset in fromt the stashedupload table
* @return Integer current byte offset of the chunk file set
}
return 0;
}
-
+
/**
* Output the chunk to disk
- *
+ *
* @param $chunkPath string
+ * @throws UploadChunkFileException
* @return FileRepoStatus
*/
private function outputChunk( $chunkPath ){
}
return $storeStatus;
}
+
private function getChunkFileKey( $index = null ){
if( $index === null ){
$index = $this->getChunkIndex();
/**
* @param $request WebRequest
- * @return null
*/
function initializeFromRequest( &$request ) {
- $upload = $request->getUpload( 'wpUploadFile' );
+ $upload = $request->getUpload( 'wpUploadFile' );
$desiredDestName = $request->getText( 'wpDestFile' );
- if( !$desiredDestName )
+ if( !$desiredDestName ) {
$desiredDestName = $upload->getName();
-
- return $this->initialize( $desiredDestName, $upload );
+ }
+
+ $this->initialize( $desiredDestName, $upload );
}
/**
* Initialize from a filename and a WebRequestUpload
* @param $name
* @param $webRequestUpload
- * @return null
*/
function initialize( $name, $webRequestUpload ) {
$this->mUpload = $webRequestUpload;
- return $this->initializePathInfo( $name,
+ $this->initializePathInfo( $name,
$this->mUpload->getTempName(), $this->mUpload->getSize() );
}
$this->stash = new UploadStash( $this->repo, $this->user );
}
-
- return true;
}
/**
*
* @param $user User
*
- * @return true|string
+ * @return bool|string
*/
public static function isAllowed( $user ) {
if ( !$user->isAllowed( 'upload_by_url' ) ) {
* @param $async mixed Whether the download should be performed
* asynchronous. False for synchronous, async or async-leavemessage for
* asynchronous download.
+ * @throws MWException
*/
public function initialize( $name, $url, $async = false ) {
global $wgAllowAsyncCopyUploads;
/**
* Wrapper around the parent function in order to defer checking protection
* until we are sure that the file can actually be uploaded
+ * @param $user User
* @return bool|mixed
*/
public function verifyTitlePermissions( $user ) {
/**
* Wrapper around the parent function in order to defer uploading to the
* job queue for asynchronous uploads
+ * @param $comment string
+ * @param $pageText string
+ * @param $watch bool
+ * @param $user User
* @return Status
*/
public function performUpload( $comment, $pageText, $watch, $user ) {
}
/**
- * @param $comment
- * @param $pageText
- * @param $watch
- * @param $user User
- * @return
+ * @param $comment
+ * @param $pageText
+ * @param $watch
+ * @param $user User
+ * @return String
*/
protected function insertJob( $comment, $pageText, $watch, $user ) {
$sessionKey = $this->stashSession();
}
}
// at this point, $error should contain the single "most important" error, plus any parameters.
- throw new UploadStashFileException( "Error storing file in '$path': " . wfMessage( $error )->text() );
+ $errorMsg = array_shift( $error );
+ throw new UploadStashFileException( "Error storing file in '$path': " . wfMessage( $errorMsg, $error )->text() );
}
$stashPath = $storeStatus->value;
*
* @param $opposite Boolean Get the direction mark opposite to your language
* @return string
+ * @since 1.20
*/
function getDirMarkEntity( $opposite = false ) {
if ( $opposite ) { return $this->isRTL() ? '‎' : '‏'; }
* Take a list of strings and build a locale-friendly comma-separated
* list, using the local comma-separator message.
* The last two strings are chained with an "and".
+ * NOTE: This function will only work with standard numeric array keys (0, 1, 2…)
*
* @param $l Array
* @return string
function listToText( array $l ) {
$s = '';
$m = count( $l ) - 1;
- if ( $m == 1 ) {
+
+ if ( $m === 0 ) {
+ return $l[0];
+ } elseif ( $m === 1 ) {
return $l[0] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $l[1];
} else {
for ( $i = $m; $i >= 0; $i-- ) {
'shi',
'sr',
'tg',
+ 'uz',
'zh',
);
class ConverterRule {
var $mText; // original text in -{text}-
var $mConverter; // LanguageConverter object
- var $mManualCodeError = '<strong class="error">code error!</strong>';
var $mRuleDisplay = '';
var $mRuleTitle = false;
var $mRules = '';// string : the text of the rules
}
}
if ( $this->mRuleDisplay === false ) {
- $this->mRuleDisplay = $this->mManualCodeError;
+ $this->mRuleDisplay = '<span class="error">'
+ . wfMessage( 'converter-manual-rule-error' )->inContentLanguage()->escaped()
+ . '</span>';
}
$this->generateConvTable();
'z' => 'з', 'Z' => 'З',
'j' => 'ж', 'J' => 'Ж',
'o‘' => 'ў', 'O‘' => 'Ў', 'oʻ' => 'ў', 'Oʻ' => 'Ў',
+ 'yo‘' => 'йў', 'Yo‘' => 'Йў', 'yoʻ' => 'йў', 'Yoʻ' => 'Йў',
'ts' => 'ц', 'Ts' => 'Ц',
'q' => 'қ', 'Q' => 'Қ',
'yo' => 'ё', 'Yo' => 'Ё',
'revdelete-hide-image' => 'أخف محتوى الملف',
'revdelete-hide-name' => 'أخف الفعل والهدف',
'revdelete-hide-comment' => 'أخف تعليق التعديل',
+'revdelete-hide-user' => 'أخف اسم/آيبي المستخدم',
+'revdelete-hide-restricted' => 'أخف البيانات عن الإداريين إضافة إلى الآخرين',
+'revdelete-radio-same' => '(لا تغير)',
+'revdelete-radio-set' => 'نعم',
+'revdelete-radio-unset' => 'لا',
+'revdelete-suppress' => 'أخف البيانات عن مديري النظام والبقية',
+'revdelete-unsuppress' => 'إزالة الضوابط من المراجعات المسترجعة',
+'revdelete-log' => 'السبب:',
+'revdelete-submit' => 'طبق على {{PLURAL:$1||المراجعة المختارة|المراجعتين المختارتين|المراجعات المختارة}}',
+'revdelete-success' => "'''تم تحديث رؤية المراجعات بنجاح.'''",
+'revdelete-failure' => "'''تعذر تحديث رؤية المراجعة:'''
+$1",
+'logdelete-success' => "'''تم ضبط رؤية السجلات بنجاح.'''",
+'logdelete-failure' => "'''تعذر ضبط رؤية السجل:'''
+$1",
'revdel-restore' => 'تغيير الرؤية',
'revdel-restore-deleted' => 'مراجعات محذوفة',
'revdel-restore-visible' => 'مراجعات مرئية',
+'pagehist' => 'تاريخ الصفحة',
+'deletedhist' => 'التاريخ المحذوف',
+'revdelete-hide-current' => 'خطأ عند إحفاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.
+لا يمكن إخفاؤها.',
+'revdelete-show-no-access' => 'خطأ في إظهار العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك"مقيد".
+ليس لك صلاحية الوصول إليه.',
+'revdelete-modify-no-access' => 'خطأ في تعديل العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك"مقيد".
+ليس لك صلاحية الوصول إليه.',
+'revdelete-modify-missing' => 'خطأ في تعديل العنصر ذا الهوية $1: العنصر مفقود من قاعدة البيانات!',
+'revdelete-no-change' => "'''تحذير:''' العنصر ذو التاريخ $2 $1 لديه أصلا إعدادات الظهور المطلوبة.",
+'revdelete-concurrent-change' => 'خطأ في تعديل العنصر ذي التاريخ $2 $1: تظهر حالته أن شخصا آخر عدله أثناء محاولتك تعديله.
+من فضلك راجع السجلات.',
+'revdelete-only-restricted' => 'خطأ إخفاء العنصر المؤرخ $2, $1: لا تستطيع تنحية العناصر من عرض الإداريين بدون أن تحدد أيضا إحدى خيارات التنحية الأخرى.',
+'revdelete-reason-dropdown' => '* أسباب حذف عامة
+** خرق لحقوق النشر
+** معلومات شخصية غير ملائمة
+**معلومات تشهيرية محتملة',
+'revdelete-otherreason' => 'سبب آخر/إضافي:',
+'revdelete-reasonotherlist' => 'سبب آخر',
+'revdelete-edit-reasonlist' => 'عدل أسباب الحذف',
+'revdelete-offender' => 'مؤلف المراجعة:',
+
+# Suppression log
+'suppressionlog' => 'سجل الإخفاء',
+'suppressionlogtext' => 'بالأسفل قائمة بعمليات الحذف والمنع التي تتضمن محتوى مخفيا عن الإداريين.
+انظر [[Special:BlockList|قائمة منع الآيبي]] لترى عمليات المنع القائمة الآن.',
+
+# History merging
+'mergehistory' => 'دمج تواريخ الصفحة',
+'mergehistory-header' => 'هذه الصفحة تسمح لك بدمج نسخ تاريخ صفحة ما إلى صفحة أخرى.
+تأكد من أن هذا التغيير سيحافظ على استمرار تاريخ الصفحة.',
+'mergehistory-box' => 'دمج مراجعات صفحتين:',
+'mergehistory-from' => 'الصفحة المصدر:',
+'mergehistory-into' => 'الصفحة الهدف:',
+'mergehistory-list' => 'تاريخ التعديل القابل للدمج',
+'mergehistory-merge' => 'المراجعات التالية من [[:$1]] يمكن دمجها إلى [[:$2]].
+استخدم عامود الصناديق لدمج المراجعات التي تم إنشاؤها في وقبل الوقت المحدد.
+لاحظ أن استخدام وصلات التصفح سيعيد ضبط هذا العامود.',
+'mergehistory-go' => 'عرض التعديلات القابلة للدمج',
+'mergehistory-submit' => 'دمج المراجعات',
+'mergehistory-empty' => 'لا مراجعات يمكن دمجها.',
+'mergehistory-success' => '$3 {{PLURAL:$3|مراجعة|مراجعة}} من [[:$1]] تم دمجها بنجاح في [[:$2]].',
+'mergehistory-fail' => 'غير قادر على عمل دمج التاريخ، من فضلك أعد التحقق من محددات الصفحة والزمن.',
+'mergehistory-no-source' => 'الصفحة المصدر $1 غير موجودة.',
+'mergehistory-no-destination' => 'الصفحة الهدف $1 غير موجودة.',
+'mergehistory-invalid-source' => 'الصفحة المصدر يجب أن تكون عنوانا صحيحا.',
+'mergehistory-invalid-destination' => 'الصفحة الهدف يجب أن تكون عنوانا صحيحا.',
+'mergehistory-autocomment' => 'دمج [[:$1]] في [[:$2]]',
+'mergehistory-comment' => 'دمج [[:$1]] في [[:$2]]: $3',
+'mergehistory-same-destination' => 'صفحتا المصدر والهدف لا يمكن أن تكونا نفس الشيء',
+'mergehistory-reason' => 'السبب:',
# Merge log
+'mergelog' => 'سجل الدمج',
+'pagemerge-logentry' => 'دمج [[$1]] إلى [[$2]] (المراجعات حتى $3)',
'revertmerge' => 'إلغاء الدمج',
+'mergelogpagetext' => 'بالأسفل قائمة بأحدث عمليات الدمج لتاريخ صفحة ما إلى أخرى.',
# Diffs
'history-title' => ' «$1»: تاريخ المراجعة',
+'difference-title' => '«$1»: الفرق بين المراجعتين',
+'difference-title-multipage' => '«$1» و«$2»: الفرق بين الصفحتين',
+'difference-multipage' => '(الفرق بين الصفحتين)',
'lineno' => 'سطر $1:',
'compareselectedversions' => 'قارن بين النسختين المختارتين',
+'showhideselectedversions' => 'أظهر/أخف المراجعات المختارة',
'editundo' => 'تراجع',
'diff-multi' => '({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتين أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} {{PLURAL:$2||مستخدم واحد|مستخدمان|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)',
+'diff-multi-manyusers' => '({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتان أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} أكثر من {{PLURAL:$2||مستخدم واحد|مستخدمين|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)',
# Search results
'searchresults' => 'el resultats',
'searchresults-title' => 'نتائج البحث عن "$1"',
+'searchresulttext' => 'للمزيد من المعلومات حول البحث في {{SITENAME}}، انظر [[{{MediaWiki:Helppage}}|{{int:help}}]].',
'prevn' => '{{PLURAL:$1|$1}} السابقة',
'nextn' => '{{PLURAL:$1|$1}} التالية',
'prevn-title' => '$1 {{PLURAL:$1|نتيجة|نتيجة}} سابقة',
'tog-hidepatrolled' => 'Versteek gepatrolleerde wysigings in onlangse wysigingslys',
'tog-newpageshidepatrolled' => 'Versteek gepatrolleerde wysigings van nuwe bladsy lys',
'tog-extendwatchlist' => 'Brei dophoulys uit om alle wysigings te wys, nie slegs die nuutste nie',
-'tog-usenewrc' => 'Verbeterde onlangse wysigingslys (benodig JavaScript)',
+'tog-usenewrc' => 'Groepeer wysigings per bladsy in onlangse wysigings en dophoulys (benodig JavaScript)',
'tog-numberheadings' => 'Nommer opskrifte outomaties',
'tog-showtoolbar' => 'Wys redigeergereedskap (benodig JavaScript)',
'tog-editondblclick' => 'Dubbelkliek om blaaie te wysig (benodig JavaScript)',
'tog-editsectiononrightclick' => 'Wysig afdeling met regskliek op afdeling se titel (JavaScript)',
'tog-showtoc' => 'Wys inhoudsopgawe (by bladsye met meer as drie opskrifte)',
'tog-rememberpassword' => 'Onthou dat ek op hierdie rekenaar ingeteken het (vir \'n maksimum van $1 {{PLURAL:$|dag|dae}})',
-'tog-watchcreations' => 'Voeg bladsye wat ek skep by my dophoulys',
-'tog-watchdefault' => 'Lys nuwe en gewysigde bladsye.',
-'tog-watchmoves' => 'Voeg die bladsye wat ek skuif by my dophoulys',
-'tog-watchdeletion' => 'Voeg bladsye wat ek verwyder by my dophoulys',
+'tog-watchcreations' => 'Voeg bladsye wat ek skep en lêers wat ek oplaai by my dophoulys',
+'tog-watchdefault' => 'Voeg bladsye en lêers wat ek wysig by my dophoulys',
+'tog-watchmoves' => 'Voeg bladsye en lêers wat ek skuif by my dophoulys',
+'tog-watchdeletion' => 'Voeg bladsye en lêers wat ek skrap by my dophoulys',
'tog-minordefault' => 'Merk alle wysigings automaties as klein by verstek.',
'tog-previewontop' => 'Wys voorskou bo wysigingsboks.',
'tog-previewonfirst' => 'Wys voorskou met eerste wysiging',
'tog-nocache' => 'Deaktiveer blaaier se bladsykas',
-'tog-enotifwatchlistpages' => 'Stuur vir my e-pos met bladsyveranderings',
+'tog-enotifwatchlistpages' => "Stuur my e-pos as 'n bladsye of lêer op my dophoulys verander",
'tog-enotifusertalkpages' => 'Stuur vir my e-pos as my eie besprekingsblad verander word',
-'tog-enotifminoredits' => 'Stuur ook e-pos vir klein bladsywysigings',
+'tog-enotifminoredits' => 'Stuur my ook e-pos vir klein wysigings aan bladsye en lêers',
'tog-enotifrevealaddr' => 'Stel my e-posadres bloot in kennisgewingspos',
'tog-shownumberswatching' => 'Wys die aantal gebruikers wat dophou',
'tog-oldsig' => 'Bestaande handtekening:',
'youhavenewmessages' => 'U het $1 (sien $2).',
'newmessageslink' => 'nuwe boodskappe',
'newmessagesdifflink' => 'die laaste wysiging',
+'youhavenewmessagesfromusers' => "U het $1 van {{PLURAL:$3|'n ander gebruiker|$3 gebruikers}} ($2).",
+'youhavenewmessagesmanyusers' => 'U het $1 van baie gebruikers ($2).',
+'newmessageslinkplural' => "{{PLURAL:$1|'n nuwe boodskap|nuwe boodskappe}}",
+'newmessagesdifflinkplural' => 'laaste {{PLURAL:$1|wysiging|wysigings}}',
'youhavenewmessagesmulti' => 'U het nuwe boodskappe op $1',
'editsection' => 'wysig',
'editold' => 'wysig',
'cannotdelete' => 'Die bladsy of lêer "$1" kon nie skrap word nie.
Iemand anders het dit moontlik reeds geskrap.',
'cannotdelete-title' => 'Bladsy "$1" kan nie verwyder word nie',
+'delete-hook-aborted' => "Die wysiging is deur 'n hoek gekanselleer.
+Geen verduideliking is verskaf nie.",
'badtitle' => 'Ongeldige titel',
'badtitletext' => "Die bladsytitel waarvoor gevra is, is ongeldig, leeg, of
'n verkeerd geskakelde tussen-taal of tussen-wiki titel.",
'ns-specialprotected' => 'Spesiale bladsye kan nie geredigeer word nie.',
'titleprotected' => "Hierdie titel is beskerm teen skepping deur [[User:$1|$1]].
Die rede gegee is ''$2''.",
-'filereadonlyerror' => 'Dit was nie moontlik om die lêer "$1" te wysig nie lêerstoor tans lees-alleen is.
+'filereadonlyerror' => 'Dit was nie moontlik om die lêer "$1" te wysig nie omdat die lêerstoor "$2" tans lees-alleen is.
Die rede hiervoor is "\'\'$3\'\'".',
'invalidtitle-knownnamespace' => 'Ongeldige titel met naamruimte "$2" en teks "$3"',
'remembermypassword' => 'Onthou dat ek op hierdie rekenaar ingeteken het (vir \'n maksimum van $1 {{PLURAL:$|dag|dae}})',
'securelogin-stick-https' => 'Bly verbind met HTTPS na aanmelding',
'yourdomainname' => 'U domein:',
+'password-change-forbidden' => 'U kan nie wagwoorde op hierdie wiki verander nie.',
'externaldberror' => "'n Databasis fout het voorgekom tydens aanmelding of u het nie toestemming om u eksterne rekening op te dateer nie.",
'login' => 'Teken in',
'nav-login-createaccount' => 'Teken in',
of [{{fullurl:{{FULLPAGENAME}}|action=edit}} hierdie bladsy wysig]</span>.',
'noarticletext-nopermission' => 'Daar is tans geen teks in hierdie bladsy nie. U kan vir die bladsytitel [[Special:Search/{{PAGENAME}}|in ander bladsye soek]] of
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} die verwante logboeke deursoek]</span>.',
+'missing-revision' => 'Die weergawe #$1 van die bladsy "{{PAGENAME}} bestaan nie.
+
+Dit word meestal veroorsaak deur die volg van \'n verouderde verwysing na \'n bladsy wat verwyder is.
+Meer gegewens kan moontlik in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.',
'userpage-userdoesnotexist' => 'U is besig om \'n gebruikersblad wat nie bestaan nie te wysig (gebruiker "<nowiki>$1</nowiki>"). Maak asseblief seker of u die bladsy wil skep/ wysig.',
'userpage-userdoesnotexist-view' => 'Die gebruiker "$1" is nie geregistreer nie.',
'blocked-notice-logextract' => 'Hierdie gebruiker is tans geblokkeer.
Die laaste inskrywing in die blokkeerlogboek word hieronder vertoon:',
'clearyourcache' => "'''Let wel''': Na die voorkeure gestoor is, moet u blaaier se kasgeheue verfris word om die veranderinge te sien:
-* '''Firefox / Safari:''' hou ''Shift'' en kliek ''Reload'', of druk ''Ctrl-F5'' of ''Ctrl-R'' (''Command-R'' op 'n Mac)
-* '''Google Chrome:''' druk ''Ctrl-Shift-R'' (''Command-Shift-R'' op 'n Mac)
+* '''Firefox / Safari:''' hou ''Shift'' en kliek ''Reload'', of druk ''Ctrl-F5'' of ''Ctrl-R'' (''⌘-R'' op 'n Mac)
+* '''Google Chrome:''' druk ''Ctrl-Shift-R'' (''⌘-Shift-R'' op 'n Mac)
* '''Internet Explorer:''' hou ''Ctrl'' en kliek ''Refresh'', of druk ''Ctrl-F5''
* '''Konqueror:''' kliek ''Reload'' of druk ''F5''
* '''Opera:''' maak die kas skoon by ''Tools → Preferences''",
'expansion-depth-exceeded-warning' => 'Die bladsy bevat te veel sjablone',
'parser-unstrip-loop-warning' => '\'n "Unstrip"-lus is bespreur.',
'parser-unstrip-recursion-limit' => 'Die rekursielimiet ($1) vir "unstrip" is oorskry',
+'converter-manual-rule-error' => "'n Fout is in 'n handmatig toegevoegde taalomskalelingsreël gevind.",
# "Undo" feature
'undo-success' => 'Die wysiging kan ongedaan gemaak word.
'rev-deleted-text-unhide' => "Hierdie weergawe van die bladsy is '''verwyder'''.
Details kan in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.
U kan steeds na [$1 die weergawe kyk] as u wil voortgaan.",
-'rev-suppressed-text-unhide' => "Hierdie weergawe van die blad word '''onderdruk'''.
-Details kan moontlik in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logboek van onderdrukte weergawes] gesien word.
-As administrateur kan u, as u wil, na [$1 die verskille kyk].",
+'rev-suppressed-text-unhide' => "Hierdie weergawe van die bladsy word '''onderdruk'''.
+Details kan in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logboek van onderdrukte weergawes] gevind word.
+As u wil voortgaan kan u steeds [$1 die weergawe sien].",
'rev-deleted-text-view' => "Hierdie weergawe is '''verwyder'''.
-As administrateur kan u dit wel sien.
-Details kan moontlik in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] aanwesig wees.",
+U dit wel sien. Details kan in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.",
'rev-suppressed-text-view' => "Hierdie weergawe van die bladsy word '''onderdruk'''.
-As administrateur kan u dit sien.
-Details kan moontlik in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logboek van onderdrukte weergawes] gesien word.",
+U kan dit wel sien. Details kan in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logboek van onderdrukte weergawes] gevind word.",
'rev-deleted-no-diff' => "U kan nie die verskille sien nie omdat een van die weergawes '''verwyder''' is.
Details kan moontlik in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] aanwesig wees.",
'rev-suppressed-no-diff' => "U kan nie hierdie verskil sien nie omdat een van die weergawes '''geskrap''' is.",
-'rev-deleted-unhide-diff' => "Een van die weergawes vir hierdie verskil wat u aangevra het is '''verwyder'''.
-Meer details mag moontlik in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwyderingslogboek] aanwesig wees.
-As administrateur kan u steeds [$1 die verskille sien] as u wil voortgaan.",
-'rev-suppressed-unhide-diff' => "Een van die weergawes vir hierdie verskil wat u aangevra het is '''onderdruk'''.
-Meer details mag moontlik in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} verbergingslogboek] aanwesig wees.
-As administrateur kan u steeds [$1 hierdie weergawe sien] as u wil voortgaan.",
+'rev-deleted-unhide-diff' => "Een van die weergawes vir die verskil wat u aangevra het is '''verwyder'''.
+Details kan in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.
+U kan steeds [$1 die verskille sien] as u wil voortgaan.",
+'rev-suppressed-unhide-diff' => "Een van die weergawes vir die verskil wat u aangevra het is '''onderdruk'''.
+Details kan in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} verbergingslogboek] gesien word.
+U kan steeds [$1 hierdie weergawe sien] as u wil voortgaan.",
'rev-deleted-diff-view' => "Een van die weergawes van die verskil wat u aangevra het is '''verwyder'''.
-As administrateur kan u hierdie verskil sien. Meer details mag moontlik is die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwyderingslogboek] beskikbaar wees.",
+U kan steeds hierdie verskil sien. Details kan in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.",
'rev-suppressed-diff-view' => "Een van die weergawes vir die verskil wat u aangevra het is '''onderdruk'''.
-As administrateur kan u hierdie verskil sien. Meer details mag moontlik in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} verbergingslogboek] beskikbaar wees.",
+U kan wel hierdie verskil sien. Meer details kan in die [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} verbergingslogboek] gevind word.",
'rev-delundel' => 'wys/versteek',
'rev-showdeleted' => 'wys',
'revisiondelete' => 'Verwyder/herstel weergawes',
# Suppression log
'suppressionlog' => 'Verbergingslogboek',
-'suppressionlogtext' => 'Die onderstaande lys bevat verwyderings en blokkades wat vir administrateurs verborge is.
-Kyk na die [[Special:IPBlockList|IP-blokkeerlys]] om die huidige blokkades te sien.',
+'suppressionlogtext' => 'Die onderstaande lys bevat verwyderings en blokkades wat vir administrateurs versteek is.
+Kyk na die [[Special:BlockList|blokkeerlys]] om die huidige blokkades te sien.',
# History merging
'mergehistory' => 'Geskiedenis van bladsy samesmeltings',
'editundo' => 'maak ongedaan',
'diff-multi' => '({{PLURAL:$1|Een tussenin wysiging|$1 tussenin wysigings}} deur {{PLURAL:$2|een gebruiker|$2 gebruikers}} word nie gewys nie)',
'diff-multi-manyusers' => '({{PLURAL:$1|Een tussenin wysiging|$1 tussenin wysigings}} deur meer as $2 {{PLURAL:$2|gebruiker|gebruikers}} nie gewys nie)',
+'difference-missing-revision' => "{{PLURAL:$2|'n Weergawe|$2 weergawes}} van die verskil ($1) {{PLURAL:$2|is|is}} nie gevind nie.
+
+Dit word meestal veroorsaak deur die volg van 'n verouderde verwysing na 'n bladsy wat verwyder is.
+Details kan in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.",
# Search results
'searchresults' => 'soekresultate',
'userrights-no-interwiki' => 'U het nie toestemming om gebruikersregte op ander wikis te verander nie.',
'userrights-nodatabase' => 'Databasis $1 bestaan nie of is nie hier beskikbaar nie.',
'userrights-nologin' => "U moet [[Special:UserLogin|aanteken]] as 'n administrateur om gebruikersregte te mag toeken.",
-'userrights-notallowed' => 'U het nie die toestemming om gebruikersregte toe te ken nie.',
+'userrights-notallowed' => 'U het nie magtiging om gebruikersregte by te sit of weg te neem nie.',
'userrights-changeable-col' => 'Groepe wat u kan verander',
'userrights-unchangeable-col' => 'Groepe wat u nie kan verander nie',
'right-writeapi' => 'Bewerkings m.b.v. die API',
'right-delete' => 'Vee bladsye uit',
'right-bigdelete' => 'Skrap bladsye met groot geskiedenisse',
+'right-deletelogentry' => 'Skrap en terugplaas van spesifieke logboekreëls',
'right-deleterevision' => 'Skrap en ontskrap spesifieke hersienings van bladsye',
'right-deletedhistory' => 'Wys geskrapte geskiedenis-inskrywings, sonder hul teks',
'right-deletedtext' => 'Wys verwyderde teks en veranderings tussen geskrapte weergawes',
'upload-too-many-redirects' => 'Die URL bevat te veel aansture',
'upload-unknown-size' => 'Onbekende grootte',
'upload-http-error' => "'n HTTP-fout het voorgekom: $1",
+'upload-copy-upload-invalid-domain' => 'Gekopieerde oplaaie word nie vanuit die domein toegelaat nie.',
# File backend
'backend-fail-stream' => 'Kon nie die lêer $1 uitstroom nie.',
'backend-fail-backup' => "Kon nie 'n rugsteunkopie van die lêer $1 maak nie.",
'backend-fail-notexists' => 'Die lêer $1 bestaan nie.',
+'backend-fail-hashes' => 'Kon nie "hashes" vir die lêer vir die vergelyking kry nie.',
'backend-fail-notsame' => "'n Nie-identiese lêer bestaan al reeds by $1.",
'backend-fail-invalidpath' => "$1 is nie 'n geldige stoorplek nie.",
'backend-fail-delete' => 'Die lêer $1 kon nie geskrap word nie.',
'backend-fail-closetemp' => 'Kon nie tydelike lêer sluit nie.',
'backend-fail-read' => 'Kon nie lêer $1 lees nie.',
'backend-fail-create' => 'Kon nie lêer $1 uitskryf nie.',
+'backend-fail-maxsize' => 'Kon nie lêer "$1" uitskryf nie omdat dit groter as {{PLURAL:$2|een greep|$2 grepe}} is.',
+'backend-fail-readonly' => 'Die agterliggende stoorspasie "$1" is lees-alleen. Die rede verskaf is: "\'\'$2\'\'"',
+'backend-fail-synced' => 'Die lêer "$1" is tans in \'n onbestendige toestand in die interne stoorspasie.',
+'backend-fail-connect' => 'Kon nie aan die agterliggende stoorspasie konnekteer nie "$1".',
+'backend-fail-internal' => "'n Onbekende fout het in die agterliggende stoorspasie $1 voorgekom.",
+'backend-fail-contenttype' => 'Kon nie die inhoudstipe van die lêer bepaal om na "$1" te stoor nie.',
+'backend-fail-batchsize' => "Die agterliggende stoorspasie het 'n groep van $1 {{PLURAL:$1|operasie|operasies}} ontvang; die limiet is $2 {{PLURAL:$2|operasie|operasies}}.",
+'backend-fail-usable' => 'Kon nie na die lêer "$1" skryf nie vanweë onvoldoende regte of gidse wat nie bestaan nie.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Kon nie na die joernaal-databasis op die agterliggende stoorspasie "$1" konnekteer nie.',
+'filejournal-fail-dbquery' => 'Kon nie die joernaal-databasis op die agterliggende stoorspasie "$1" bywerk nie.',
+
+# Lock manager
+'lockmanager-notlocked' => 'Kon nie "$1" ontgrendel nie omdat dit nie gesluit is nie.',
+'lockmanager-fail-closelock' => 'Kon nie die slotlêer vir "$1" te sluit nie.',
+'lockmanager-fail-deletelock' => 'Kon nie die slotlêer vir "$1" skrap nie.',
+'lockmanager-fail-acquirelock' => 'Kon nie "$1" vergrendel nie.',
+'lockmanager-fail-openlock' => 'Kon nie die slotlêer vir "$1" oopmaak nie.',
+'lockmanager-fail-releaselock' => 'Kon nie "$1" ontgrendel nie.',
+'lockmanager-fail-db-bucket' => 'Dit was nie moontlik om voldoende vergrendel-databasisse in die houer $1 te kontak nie.',
+'lockmanager-fail-db-release' => "Kon nie 'n vergrendeling op databasis $1 ophef nie.",
+'lockmanager-fail-svr-acquire' => "Kon nie 'n vergrendeling op bediener $1 verkry nie.",
+'lockmanager-fail-svr-release' => 'Kon nie vergrandeling op bediener $1 ophef nie.',
# ZipDirectoryReader
'zip-file-open-error' => "'n Fout het voorgekom met die opening van die lêer vir ZIP toetsing.",
'img-auth-nopathinfo' => 'PATH_INFO word vermis.
U bediener is nie ingestel om hierdie inligting deur te stuur nie.
Miskien gebruik dit CGI, waartydens img_auth nie ondersteun word nie.
-[https://www.mediawiki.org/wiki/Manual:Image_Authorization Sien regte vir beelde] vir meer inligting.',
+Sien https://www.mediawiki.org/wiki/Manual:Image_Authorization vir meer inligting.',
'img-auth-notindir' => 'Die aangevraagde pad is nie die ingestelde oplaaigids nie.',
'img-auth-badtitle' => 'Dit was nie moontlik om \'n geldige bladsynaam van "$1" te maak nie.',
'img-auth-nologinnWL' => 'U is nie aangeteken en "$1" is nie op die witlys nie.',
Sien die [$2 lêer se beskrywingsblad] vir meer inligting.',
'sharedupload-desc-here' => 'Hierdie lêer kom vanaf $1 en kan ook in ander projekte gebruik word.
Die beskrywing op die [$2 lêer se inligtingsblad] word hieronder weergegee.',
+'sharedupload-desc-edit' => 'Hierdie lêer kom van $1 en kan ook in ander projekte gebruik word.
+Miskien wil u eerder die beskrywing daar op die [$2 lêerbeskrywing] bywerk.',
+'sharedupload-desc-create' => 'Hierdie lêer kom van $1 en kan ook in ander projekte gebruik word.
+Miskien wil u eerder die beskrywing daar op die [$2 lêerbeskrywing] bywerk.',
'filepage-nofile' => "Daar bestaan nie 'n lêer met die naam nie.",
'filepage-nofile-link' => "Daar bestaan nie 'n lêer met die naam nie, maar u kan een [$1 oplaai].",
'uploadnewversion-linktext' => "Laai 'n nuwe weergawe van hierdie lêer",
'disambiguations' => 'Bladsye wat na dubbelsinnigheidsbladsye skakel',
'disambiguationspage' => 'Template:Dubbelsinnig',
-'disambiguations-text' => "Die volgende bladsye skakel na '''dubbelsinnigheidsbladsye'''.
-Die bladsye moet gewysig word om eerder direk na die regte onderwerpe te skakel.<br />
+'disambiguations-text' => "Die volgende bladsye het ten minste een skakel na 'n '''dubbelsinnigheidsbladsy'''.
+Hulle moet gewysig word om eerder direk na die regte onderwerpe te skakel.<br />
'n Bladsy word beskou as 'n dubbelsinnigheidsbladsy as dit 'n sjabloon bevat wat geskakel is vanaf [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Dubbele aansture',
'wantedpages' => 'Begeerde bladsye',
'wantedpages-badtitle' => 'Ongeldige bladsynaam in resultate: $1',
'wantedfiles' => 'Begeerde lêers',
+'wantedfiletext-cat' => 'Die volgende lêers word gebruik, maar bestaan nie. Lêers van eksterne biblioteke kan, ondanks die feit dat hulle wel bestaan, ook hier gelys wees. Hierdie vals positiewes word as <del>deurgehaal aangedui</del>. Bladsye met lêers wat nie bestaan nie word aangegee by [[:$1]].',
+'wantedfiletext-nocat' => 'Die volgende lêers word gebruik, maar bestaan nie. Lêers van eksterne biblioteke kan, ondanks die feit dat hulle wel bestaan, ook hier gelys wees. Hierdie vals positiewes word as <del>deurgehaal aangedui</del>.',
'wantedtemplates' => 'Begeerde sjablone',
'mostlinked' => 'Bladsye met meeste skakels daarheen',
'mostlinkedcategories' => 'Kategorieë met die meeste skakels daarheen',
U kan die resultate vernou deur 'n boekstaaftipe, gebruikersnaam (kas-sensitief) of spesifieke blad (ook kas-sensitief) te kies.",
'logempty' => 'Geen inskrywings in die logboek voldoen aan die kriteria nie.',
'log-title-wildcard' => 'Soek bladsye wat met die naam begin',
+'showhideselectedlogentries' => 'Wys/versteek gekose logboekreëls',
# Special:AllPages
'allpages' => 'Alle bladsye',
'linksearch-pat' => 'Soekpatroon:',
'linksearch-ns' => 'Naamruimte:',
'linksearch-ok' => 'Soek',
-'linksearch-text' => 'Patrone soos "*.wikipedia.org" of "*.org" kan gebruik word.<br />
-Ondersteunde protokolle: <tt>$1</tt>',
+'linksearch-text' => 'Patrone soos "*.wikipedia.org" kan gebruik word.<br />
+Benodig ten minste een topvlakdomein, soos byvoorbeeld "*.org".<br />
+Ondersteunde protokolle: <tt>$1</tt> (moenie hierdie in u soektog gebruik nie)',
'linksearch-line' => '$1 geskakel vanaf $2',
'linksearch-error' => 'Patrone kan slegs aan die begin van die rekenaarnaam geplaas word.',
'rollback' => 'Rol veranderinge terug',
'rollback_short' => 'Rol terug',
'rollbacklink' => 'Rol terug',
+'rollbacklinkcount' => 'rol {{PLURAL:$1|een wysiging|$1 wysigings}} terug',
+'rollbacklinkcount-morethan' => 'rol meer as {{PLURAL:$1|een wysiging|$1 wysigings}} terug',
'rollbackfailed' => 'Terugrol onsuksesvol',
'cantrollback' => 'Kan nie na verandering terug keer nie; die laaste bydraer is die enigste outer van hierdie bladsy.',
'alreadyrolled' => "Dit is nie moontlik om die laaste wysiging van die bladsy [[:$1]] deur [[User:$2|$2]] ([[User talk:$2|bespreking]]{{int:pipe-separator}}[[Special:Contributions/$2|bydraes]]) ongedaan te maak nie.
'ipb-confirm' => 'Bevestig blokkade',
'badipaddress' => 'Die IP-adres is nie in die regte formaat nie.',
'blockipsuccesssub' => 'Blokkering het geslaag',
-'blockipsuccesstext' => "[[Special:Contributions/$1|$1]] is geblokkeer.<br />
-Sien die [[Special:BlockList|IP-bloklys]] vir 'n oorsig van blokkerings.",
+'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] is geblokkeer.<br />
+Sien die [[Special:BlockList|bloklys]] vir onlangse blokkades.',
'ipb-blockingself' => 'U is besig om uself te blokkeer! Is u seker u wil dit doen?',
'ipb-confirmhideuser' => 'U staan op die punt om \'n "versteekte gebruiker" te blokkeer. Dit sal die gebruiker se naam vanaf alle lyste en logboeke onderdruk. Is u seker u wil om dit te doen?',
'ipb-edit-dropdown' => 'Werk lys van redes by',
Die verbergingslogboek word hieronder ter verwysing weergegee:',
'blocklogentry' => '"[[$1]]" is vir \'n periode van $2 $3 geblok',
'reblock-logentry' => 'het die instellings vir die blokkade vir [[$1]] gewysig. Dit verval nou op $2 om $3',
-'blocklogtext' => "Hier is 'n lys van onlangse blokkeer en deblokkeer aksies. Outomaties geblokkeerde IP-adresse word nie vertoon nie.
-Sien die [[Special:BlockList|IP-bloklys]] vir geblokkeerde adresse.",
+'blocklogtext' => "Hierdie is 'n lys van onlangse blokkeer- en deblokkeer-aksies.
+Outomaties geblokkeerde IP-adresse word nie vertoon nie.
+Sien die [[Special:BlockList|bloklys]] vir tans geblokkeerde adresse.",
'unblocklogentry' => 'blokkade van $1 is opgehef:',
'block-log-flags-anononly' => 'anonieme gebruikers alleenlik',
'block-log-flags-nocreate' => 'registrasie van gebruikers geblokkeer',
'javascripttest-pagetext-frameworks' => 'Kies een van die volgende toetsraamwerke: $1',
'javascripttest-pagetext-skins' => "Kies 'n omslag waarmee die toets uitgevoer moet word:",
'javascripttest-qunit-intro' => 'Sien die [$1 toetsdokumentasie] op mediawiki.org.',
+'javascripttest-qunit-heading' => 'QUnit toetssuite vir MediaWiki JavaScript',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'U gebruikerbladsy',
* <span class="mw-specialpagecached">Spesiale bladsye met gegewens uit die kas (kan verouderd wees).</span>',
'specialpages-group-maintenance' => 'Onderhoud verslae',
'specialpages-group-other' => 'Ander spesiale bladsye',
-'specialpages-group-login' => 'Inteken / aansluit',
+'specialpages-group-login' => 'Meld aan / registreer',
'specialpages-group-changes' => 'Onlangse wysigings en boekstawings',
'specialpages-group-media' => 'Media verslae en oplaai',
'specialpages-group-users' => 'Gebruikers en regte',
'logentry-delete-event-legacy' => '$1 het die sigbaarheid van logboekreëls van $3 gewysig',
'logentry-delete-revision-legacy' => '$1 het die sigbaarheid van weergawes van die bladsy $3 gewysig.',
'logentry-suppress-delete' => '$1 het die bladsy $3 onderdruk',
+'logentry-suppress-event' => "$1 het in die geheim die sigbaarheid van {{PLURAL:$5|'n logboekreël|$5 logboekreëls}} van $3 gewysig: $4",
'logentry-suppress-revision' => "$1 het in die geheim die sigbaarheid van {{PLURAL:$5|'n weergawe|$5 weergawes}} van die bladsy $3 gewysig: $4",
'logentry-suppress-event-legacy' => '$1 het in die geheim die sigbaarheid van logboekreëls van $3 gewysig',
'logentry-suppress-revision-legacy' => '$1 het in die geheim die sigbaarheid van weergawes van die bladsy $3 gewysig',
'newuserlog-byemail' => 'wagwoord is per e-pos versend',
# Feedback
+'feedback-bugornote' => 'As u reg is om \'n tegniese probleem in detail te beskryf, [$1 rapporteer \'n fout].
+Anders kan u die eenvoudige vorm hieronder gebruik. U kommentaar sal by die bladsy "[$3 $2]", saam met u gebruikersnaam en die webblaaier wat u gebruik gevoeg word.',
'feedback-subject' => 'Onderwerp:',
'feedback-message' => 'Boodskap:',
'feedback-cancel' => 'Kanselleer',
'api-error-badaccess-groups' => 'U word nie toegelaat om lêers te laai op hierdie wiki.',
'api-error-badtoken' => 'Interne fout: slegte teken.',
'api-error-copyuploaddisabled' => 'Oplaai via URL is gedeaktiveer op hierdie bediener.',
+'api-error-duplicate' => "Daar {{PLURAL:$1|is al [$2 'n lêer]|is al [$2 lêers]}} met dieselfde inhoud op die wiki.",
+'api-error-duplicate-archive' => "Daar {{PLURAL:$1|was [$2 'n ander lêer]|was [$2 ander lêers]}} op hierdie webtuiste met dieselfde inhoud, maar {{PLURAL:$1|dit is|dit is}} geskrap.",
'api-error-duplicate-archive-popup-title' => 'Duplikaat {{PLURAL:$1|lêer|lêers}} wat al verwyder is.',
'api-error-duplicate-popup-title' => 'Duplikaat {{PLURAL:$1|lêer|lêers}}',
'api-error-empty-file' => 'Die lêer wat u probeer oplaai is leeg.',
'api-error-emptypage' => 'Die skep van leë nuwe bladsye word nie toegelaat nie.',
'api-error-fetchfileerror' => 'Interne fout: Iets het verkeerd gegaan met die haal van die lêer.',
+'api-error-fileexists-forbidden' => 'Daar is reeds \'n lêer met die naam "$1" wat nie oorskryf kan word nie.',
+'api-error-fileexists-shared-forbidden' => 'Daar is reeds \'n lêer met die naam "$1" in die gedeelde lêerstoor, en kan nie oorskryf word nie.',
'api-error-file-too-large' => 'Die lêer wat u probeer oplaai is te groot.',
'api-error-filename-tooshort' => 'Die lêernaam is te kort.',
'api-error-filetype-banned' => 'Hierdie tipe lêer is verban en word nie toegelaat nie.',
'duration-centuries' => '$1 {{PLURAL:$1|eeu|eeue}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
+# Unknown messages
+'api-error-filetype-banned-type' => 'Die {{PLURAL:$4|lêertipe|lêertipes}} $1 word nie toegelaat nie. Toelaatbare {{PLURAL:$3|lêertipes|lêertipes}} is $2.',
);
'api-error-uploaddisabled' => 'ፋይል መላክ በዚህ ውኪ ላይ አልተፈቀደም።',
'api-error-verification-error' => 'ይህ ፋይል የተበላሸ ወይም ትክክል ያልሆነ ቅጥያ ያለው ሊሆን ይችላል።',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 ያልተፈቀደ ፋይል አይነት ነው። የተፈቀዱት ፋይል አይነቶች $2 ናቸው።',
);
'api-error-uploaddisabled' => 'As cargas de fichers son desactivadas en iste wiki.',
'api-error-verification-error' => 'Iste fichero puede estar danyau, u tiene una extensión incorrecta.',
+# Unknown messages
+'api-error-filetype-banned-type' => "{{PLURAL:$4|Os fichers de tipo $1|Os fichers d'os tipos $1}} no se permiten. {{PLURAL:$3|Nomás s'admeten os fichers d'o tipo|Nomás s'admeten os fichers d'os tipos}} $2.",
);
'tog-editsection' => 'مكن تعديل الأقسام عن طريق وصلات [عدل]',
'tog-editsectiononrightclick' => 'فعل تعديل الأقسام بواسطة كبسة الفأرة اليمين على عناوين الأقسام (جافاسكريبت)',
'tog-showtoc' => 'اعرض فهرس المحتويات (للصفحات التي تحتوي على أكثر من 3 عناوين)',
-'tog-rememberpassword' => 'تذÙ\83ر دخÙ\88Ù\84Ù\8a عÙ\84Ù\89 Ù\87ذا اÙ\84Ù\85تصÙ\81Ø (Ø¥Ù\84Ù\89 {{PLURAL:$1||Ù\8aÙ\88Ù\85 Ù\88اØد|Ù\8aÙ\88Ù\85Ù\8aÙ\86|$1 Ø£Ù\8aاÙ\85|$1 Ù\8aÙ\88Ù\85Ù\8bا|$1 Ù\8aÙ\88Ù\85}} Ù\83Øد Ø£Ù\82صÙ\89)',
+'tog-rememberpassword' => 'تذÙ\83ر دخÙ\88Ù\84Ù\8a بÙ\87ذا اÙ\84Ù\85تصÙ\81Ø (Ù\84Ù\85دة Ø£Ù\82صاÙ\87ا {{PLURAL:$1||Ù\8aÙ\88Ù\85 Ù\88اØد|Ù\8aÙ\88Ù\85اÙ\86|$1 Ø£Ù\8aاÙ\85|$1 Ù\8aÙ\88Ù\85ا|$1 Ù\8aÙ\88Ù\85}})',
'tog-watchcreations' => 'أضف الصفحات التي أنشئها والملفات التي أرفعها إلى قائمة مراقبتي.',
'tog-watchdefault' => '!!أضف الصفحات والملفات التي أعدلها إلى قائمة مراقبتي',
'tog-watchmoves' => 'أضف الصفحات والملفات التي أنقلها إلى قائمة مراقبتي',
'about' => 'عن',
'article' => 'صفحة محتوى',
'newwindow' => '(تفتح في نافذة جديدة)',
-'cancel' => 'Ø£Ù\84غÙ\90',
+'cancel' => 'Ø¥Ù\84غاء',
'moredotdotdot' => 'المزيد...',
'mypage' => 'صفحتي',
'mytalk' => 'نقاشي',
# Cologne Blue skin
'qbfind' => 'جد',
'qbbrowse' => 'تصفح',
-'qbedit' => 'عدل',
+'qbedit' => 'تعدÙ\8aل',
'qbpageoptions' => 'هذه الصفحة',
'qbpageinfo' => 'سياق النص',
'qbmyoptions' => 'صفحاتي',
# Vector skin
'vector-action-addsection' => 'أضف موضوعا',
-'vector-action-delete' => 'اØØ°Ù\81',
+'vector-action-delete' => 'حذف',
'vector-action-move' => 'انقل',
'vector-action-protect' => 'احم',
'vector-action-undelete' => 'استرجع الحذف',
'vector-action-unprotect' => 'غير الحماية',
'vector-simplesearch-preference' => 'مكّن مقترحات البحث المُحسّنة (لواجهة فكتور فقط)',
'vector-view-create' => 'أنشئ',
-'vector-view-edit' => 'عدل',
+'vector-view-edit' => 'تعدÙ\8aل',
'vector-view-history' => 'اعرض التاريخ',
'vector-view-view' => 'اقرأ',
'vector-view-viewsource' => 'اعرض المصدر',
'tagline' => 'من {{SITENAME}}',
'help' => 'مساعدة',
'search' => 'بحث',
-'searchbutton' => 'ابØØ«',
+'searchbutton' => 'بحث',
'go' => 'اذهب',
'searcharticle' => 'اذهب',
'history' => 'تاريخ الصفحة',
'permalink' => 'وصلة دائمة',
'print' => 'اطبع',
'view' => 'عرض',
-'edit' => 'عدل',
+'edit' => 'تعدÙ\8aل',
'create' => 'أنشئ',
-'editthispage' => 'عدل هذه الصفحة',
+'editthispage' => 'تعدÙ\8aل هذه الصفحة',
'create-this-page' => 'أنشئ هذه الصفحة',
-'delete' => 'اØØ°Ù\81',
+'delete' => 'حذف',
'deletethispage' => 'احذف هذه الصفحة',
'undelete_short' => 'استرجاع {{PLURAL:$1|تعديل واحد|تعديلين|$1 تعديلات|$1 تعديل|$1 تعديلا}}',
'viewdeleted_short' => 'عرض {{PLURAL:$1|تعديل محذوف|$1 تعديلات محذوفة}}',
'protectedpage' => 'صفحة محمية',
'jumpto' => 'اذهب إلى:',
'jumptonavigation' => 'تصفح',
-'jumptosearch' => 'اÙ\84بØØ«',
+'jumptosearch' => 'بحث',
'view-pool-error' => 'عذرا، الخوادم منهكة حاليا.
يحاول مستخدمون كثر الوصول إلى هذه الصفحة.
من فضلك انتظر قليلا قبل أن تحاول الوصول إلى هذه الصفحة مجددا.
'youhavenewmessages' => 'توجد لديك $1 ($2).',
'newmessageslink' => 'رسائل جديدة',
'newmessagesdifflink' => 'آخر تغيير',
+'youhavenewmessagesfromusers' => 'لديك $1 من {{PLURAL:$3|مستخدم آخر|$3 مستخدمين}} ($2).',
+'youhavenewmessagesmanyusers' => 'لديك $1 من مستخدمين كثر ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|رسالة جديدة|رسائل جديدة}}',
+'newmessagesdifflinkplural' => 'آخر {{PLURAL:$1|تغيير|تغييرات}}',
'youhavenewmessagesmulti' => 'لديك رسائل جديدة على $1',
-'editsection' => 'عدل',
-'editold' => 'عدل',
+'editsection' => 'تعدÙ\8aل',
+'editold' => 'تعدÙ\8aل',
'viewsourceold' => 'اعرض المصدر',
-'editlink' => 'عدل',
+'editlink' => 'تعدÙ\8aل',
'viewsourcelink' => 'اعرض المصدر',
'editsectionhint' => 'حرر القسم: $1',
'toc' => 'محتويات',
'cannotdelete' => 'تعذر حذف الصفحة أو الملف "$1".
ربما حذفها شحص آخر.',
'cannotdelete-title' => 'لا يمكن حذف الصفحة "$1"',
+'delete-hook-aborted' => 'إحباط الحذف من قبل هوك.
+لم يقدم أي توضيح.',
'badtitle' => 'عنوان سيء',
'badtitletext' => 'عنوان الصفحة المطلوب إما غير صحيح أو فارغ، وربما الوصلة بين اللغات أو بين المشاريع خاطئة.
ومن الممكن وجود رموز لا تصلح للاستخدام في العناوين.',
المدير الذي قام بغلقه قدم التفسير التالي: "$3".',
'invalidtitle-knownnamespace' => 'عنوان غير صالح في النطاق «$2» مع نص «$3»',
'invalidtitle-unknownnamespace' => 'عنوان غير صالح ذو نطاق غير معروف رقم $1 ونص «$2»',
+'exception-nologin' => 'غير مسجل الدخول',
+'exception-nologin-text' => 'تتطلب هذه الصفحة أو الفعل منك القيام بتسجيل الدخول على هذه الويكي أولا.',
# Virus scanner
'virus-badscanner' => "ضبط سيء: ماسح فيروسات غير معروف: ''$1''",
'yourname' => 'اسم المستخدم:',
'yourpassword' => 'كلمة السر:',
'yourpasswordagain' => 'أعد كتابة كلمة السر:',
-'remembermypassword' => 'تذÙ\83ر دخÙ\88Ù\84Ù\8a عÙ\84Ù\89 Ù\87ذا اÙ\84ØاسÙ\88ب (Ø¥Ù\84Ù\89 {{PLURAL:$1||Ù\8aÙ\88Ù\85 Ù\88اØد|Ù\8aÙ\88Ù\85Ù\8aÙ\86|$1 Ø£Ù\8aاÙ\85|$1 Ù\8aÙ\88Ù\85Ù\8bا|$1 Ù\8aÙ\88Ù\85}} Ù\83Øد Ø£Ù\82صÙ\89)',
+'remembermypassword' => 'تذÙ\83ر دخÙ\88Ù\84Ù\8a بÙ\87ذا اÙ\84Ù\85تصÙ\81Ø (Ù\84Ù\85دة Ø£Ù\82صاÙ\87ا {{PLURAL:$1||Ù\8aÙ\88Ù\85 Ù\88اØد|Ù\8aÙ\88Ù\85اÙ\86|$1 Ø£Ù\8aاÙ\85|$1 Ù\8aÙ\88Ù\85ا|$1 Ù\8aÙ\88Ù\85}})',
'securelogin-stick-https' => 'ابقَ في اتصال HTTPS بعد الدخول.',
'yourdomainname' => 'نطاقك:',
+'password-change-forbidden' => 'أنت لا يمكنك تغيير كلمات السر على هذا الويكي.',
'externaldberror' => 'هناك إما خطأ في دخول قاعدة البيانات الخارجية أو أنه غير مسموح لك بتحديث حسابك الخارجي.',
-'login' => 'ادخل',
-'nav-login-createaccount' => 'ادخÙ\84 / Ø£Ù\86شئ Øسابا',
+'login' => 'تسجÙ\8aÙ\84 اÙ\84دخÙ\88ل',
+'nav-login-createaccount' => 'دخÙ\88Ù\84 / Ø¥Ù\86شاء Øساب',
'loginprompt' => 'يجب أن تكون الكوكيز لديك مفعلة لتسجل الدخول إلى {{SITENAME}}.',
'userlogin' => 'دخول / إنشاء حساب',
-'userloginnocreate' => 'دخول',
+'userloginnocreate' => 'تسجÙ\8aÙ\84 اÙ\84دخÙ\88Ù\84',
'logout' => 'اخرج',
'userlogout' => 'اخرج',
'notloggedin' => 'غير مسجل الدخول',
-'nologin' => "ألا تمتلك حسابا؟ '''$1'''.",
+'nologin' => "ليس لديك حساب؟ '''$1'''.",
'nologinlink' => 'أنشئ حسابا',
'createaccount' => 'أنشئ حسابا',
-'gotaccount' => "تمتلك حسابا بالفعل؟ '''$1'''.",
-'gotaccountlink' => 'لُج',
-'userlogin-resetlink' => 'أنسيت بيانات الولوج؟',
+'gotaccount' => "لديك حساب؟ '''$1'''.",
+'gotaccountlink' => 'تسجيل الدخول',
+'userlogin-resetlink' => 'نسيت تفاصيل الدخول؟',
'createaccountmail' => 'بواسطة البريد الإلكتروني',
'createaccountreason' => 'السبب:',
'badretype' => 'كلمات السر التي أدخلتها لا تتطابق.',
'resetpass_forbidden' => 'كلمات السر لا يمكن تغييرها',
'resetpass-no-info' => 'يجب أن تكون مسجل الدخول للوصول إلى هذه الصفحة مباشرة.',
'resetpass-submit-loggedin' => 'تغيير كلمة السر',
-'resetpass-submit-cancel' => 'Ø£Ù\84غÙ\90',
+'resetpass-submit-cancel' => 'Ø¥Ù\84غاء',
'resetpass-wrong-oldpass' => 'كلمة سر حالية أو مؤقتة غير صحيحة.
ربما تكون غيرت كلمة السر الخاصة بك بنجاح أو طلبت كلمة سر مؤقتة جديدة.',
'resetpass-temp-password' => 'كلمة سر مؤقتة:',
{{PLURAL:$3|كلمة المرور المؤقتة|كلمات المرور المؤقة}}سينتهي في {{PLURAL:$5|يوم|ايام$5 }}
من الافضل ان تسجل الدخول وتختار كلمة مرور جديدة الان .
إذا قام شخص آخر بهذا الطلب، أو إذا تذكرت كلمة المرور الأصلية الخاصة بك،ولم تعد ترغب في تغييره، يمكنك تجاهل هذه الرسالة ومتابعة استخدام كلمة المرورالقديمة.',
-'passwordreset-emailelement' => 'اسم المستخدم: $1
+'passwordreset-emailelement' => 'اسم {{GENDER:$1|المستخدم|المستخدمة}}: $1
كلمة السر المؤقتة: $2',
'passwordreset-emailsent' => 'أرسل بريد إلكتروني تذكيري',
'passwordreset-emailsent-capture' => 'أرسل بريد إلكتروني تذكيري وهو معروض بالأسفل.',
'changeemail-newemail' => 'عنوان البريد الإلكتروني الجديد:',
'changeemail-none' => '(لا شيء)',
'changeemail-submit' => 'غيّر البريد الإلكتروني',
-'changeemail-cancel' => 'Ø£Ù\84غÙ\90',
+'changeemail-cancel' => 'Ø¥Ù\84غاء',
# Edit page toolbar
'bold_sample' => 'نص غليظ',
'minoredit' => 'هذا تعديل طفيف',
'watchthis' => 'راقب هذه الصفحة',
'savearticle' => 'احفظ الصفحة',
-'preview' => 'معاينة',
+'preview' => 'عرض مسبق',
'showpreview' => 'أظهر معاينة',
'showlivepreview' => 'عرض مباشر',
-'showdiff' => 'أظÙ\87ر التغييرات',
+'showdiff' => 'عرض التغييرات',
'anoneditwarning' => "'''تحذير:''' لم تقم بالدخول.
سيسجل عنوان الآيبي خاصتك في تاريخ هذه الصفحة.",
'anonpreviewwarning' => "''أنت غير مسجل الدخول. الحفظ سيسجل عنوان الأيبي الخاص بك في تاريخ هذه الصفحة.''",
'nosuchsectiontext' => 'لقد حاولت تحرير قسم غير موجود.
ربما يكون قد تم نقله أو حذفه أثناء مشاهدتك للصفحة.',
'loginreqtitle' => 'تسجيل الدخول مطلوب',
-'loginreqlink' => 'اÙ\84Ù\88Ù\84Ù\88ج',
+'loginreqlink' => 'تسجÙ\8aÙ\84 اÙ\84دخÙ\88Ù\84',
'loginreqpagetext' => 'يجب عليك $1 لتشاهد صفحات أخرى.',
'accmailtitle' => 'تم إرسال كلمة السر.',
'accmailtext' => "كلمة سر مولدة عشوائيا ل [[User talk:$1|$1]] تم إرسالها إلى $2.
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة]،
أو [{{fullurl:{{FULLPAGENAME}}|action=edit}} تعديل هذه الصفحة]</span>.',
'noarticletext-nopermission' => 'لا يوجد حاليا أي نص في هذه الصفحة.يمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى,أو <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} بحث السجلات المتصلة]</span>.',
+'missing-revision' => 'المراجعة #$1 من الصفحة المسماة "{{PAGENAME}}" غير موجودة.
+
+هذا يحدث عادة عن طريق اتباع وصلة تاريخ قديمة لصفحة تم حذفها.
+التفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].',
'userpage-userdoesnotexist' => 'حساب المستخدم "<nowiki>$1</nowiki>" غير مسجل.
من فضلك تأكد أنك تريد إنشاء/تعديل هذه الصفحة.',
'userpage-userdoesnotexist-view' => 'حساب المستخدم "$1" غير مسجل.',
'expansion-depth-exceeded-warning' => 'الصفحة تجاوزت عمق التوسيع',
'parser-unstrip-loop-warning' => 'حلقة معراة تم الكشف عنها',
'parser-unstrip-recursion-limit' => 'تعدى حد العودية Unstrip ($1)',
+'converter-manual-rule-error' => 'خطأ تم اكتشافه في قاعدة تحويل اللغة اليدوية',
# "Undo" feature
'undo-success' => 'يمكن استرجاع التعديل.
'lineno' => 'سطر $1:',
'compareselectedversions' => 'قارن بين النسختين المختارتين',
'showhideselectedversions' => 'أظهر/أخف المراجعات المختارة',
-'editundo' => 'تراجع',
+'editundo' => 'رجÙ\88ع',
'diff-multi' => '({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتين أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} {{PLURAL:$2||مستخدم واحد|مستخدمان|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)',
'diff-multi-manyusers' => '({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتان أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} أكثر من {{PLURAL:$2||مستخدم واحد|مستخدمين|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)',
+'difference-missing-revision' => '{{PLURAL:$2|مراجعة واحدة|$2 مراجعات}} لهذا الفرق ($1) {{PLURAL:$2|لم|لم}} يتم إيجادها.
+
+هذا يحدث عادة عن طريق اتباع وصلة فرق قديمة لصفحة تم حذفها.
+التفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].',
# Search results
'searchresults' => 'نتائج البحث',
'preferences' => 'تفضيلات',
'mypreferences' => 'تفضيلاتي',
'prefs-edits' => 'عدد التعديلات:',
-'prefsnologin' => 'غير مسجل',
+'prefsnologin' => 'غير مسجل الدخول',
'prefsnologintext' => 'يجب أن تكون <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} مسجل الدخول]</span> حتى تتمكن من تعديل تفضيلات المستخدم.',
'changepassword' => 'غير كلمة السر',
'prefs-skin' => 'واجهة',
-'skin-preview' => 'عاÙ\8aÙ\86',
+'skin-preview' => 'عرض Ù\85سبÙ\82',
'datedefault' => 'لا تفضيل',
'prefs-beta' => 'مزايا بيتا',
'prefs-datetime' => 'وقت وتاريخ',
'prefs-edit-boxsize' => 'حجم نافذة التحرير.',
'rows' => 'صفوف:',
'columns' => 'أعمدة:',
-'searchresultshead' => 'ابØØ«',
+'searchresultshead' => 'بحث',
'resultsperpage' => 'عدد النتائج في الصفحة:',
'stub-threshold' => 'الحد لتنسيق <a href="#" class="stub">وصلة البذرة</a>:',
'stub-threshold-disabled' => 'معطل',
'right-writeapi' => 'استخدام API للكتابة',
'right-delete' => 'حذف الصفحات',
'right-bigdelete' => 'حذف الصفحات ذات التواريخ الكبيرة',
+'right-deletelogentry' => 'حذف والغاء حذف إدخالات سجل معين',
'right-deleterevision' => 'حذف واسترجاع مراجعات معينة من الصفحات',
'right-deletedhistory' => 'رؤية مدخلات التاريخ المحذوفة، بدون نصوصها المصاحبة',
'right-deletedtext' => 'عرض النص المحذوف والتغييرات بين المراجعات المحذوفة',
'uploadbtn' => 'ارفع الملف',
'reuploaddesc' => 'إلغاء الرفع والرجوع إلى استمارة الرفع',
'upload-tryagain' => 'أرسل وصف ملف معدل',
-'uploadnologin' => 'لم تقم بتسجيل الدخول',
+'uploadnologin' => 'غير مسجل الدخول',
'uploadnologintext' => 'يجب أن تكون [[Special:UserLogin|مسجلا الدخول]] لتتمكن من رفع الملفات.',
'upload_directory_missing' => 'مجلد الرفع ($1) مفقود ولم يمكن إنشاؤه بواسطة خادوم الوب.',
'upload_directory_read_only' => 'مجلد الرفع ($1) لا يمكن الكتابة عليه بواسطة خادوم الوب.',
'lockmanager-fail-releaselock' => 'تعذر تحرير التأمين لـ "$1"..',
'lockmanager-fail-db-bucket' => 'تعذر الإتصال بعدد كافي من قواعد تأمين البيانات في الحزمة $1.',
'lockmanager-fail-db-release' => 'تعذر تحرير الأقفال في قاعدة البيانات $1.',
+'lockmanager-fail-svr-acquire' => 'لم يمكن فرض أقفال على المخدم $1.',
'lockmanager-fail-svr-release' => 'تعذر تحرير الأقفال على الخادم $1.',
# ZipDirectoryReader
'filedelete-intro' => "أنت على وشك حذف الملف '''[[Media:$1|$1]]''' مع كل تاريخه.",
'filedelete-intro-old' => "أنت تحذف نسخة '''[[Media:$1|$1]]''' بتاريخ [$4 $3، $2].",
'filedelete-comment' => 'السبب:',
-'filedelete-submit' => 'اØØ°Ù\81',
+'filedelete-submit' => 'حذف',
'filedelete-success' => "'''$1''' تم حذفه.",
'filedelete-success-old' => "نسخة '''[[Media:$1|$1]]''' بتاريخ $3، $2 تم حذفها.",
'filedelete-nofile' => "'''$1''' غير موجود.",
'disambiguations' => 'الصفحات التي ترتبط بصفحات توضيح',
'disambiguationspage' => 'Template:توضيح',
-'disambiguations-text' => "اÙ\84صÙ\81Øات اÙ\84تاÙ\84Ù\8aØ© تصل إلى '''صفحة توضيح'''.
-ينبغي في المقابل أن تصل إلى الصفحة الملائمة. <br />
+'disambiguations-text' => "اÙ\84صÙ\81Øات اÙ\84تاÙ\84Ù\8aØ© تØتÙ\88Ù\8a عÙ\84Ù\89 Ù\88صÙ\84Ø© Ù\88اØدة عÙ\84Ù\89 اÙ\84Ø£Ù\82ل إلى '''صفحة توضيح'''.
+ربما ينبغي أن تصل إلى صفحة أكثر ملائمة. <br />
تعامل الصفحة كصفحة توضيح إذا كان بها قالب موجود في [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'تحويلات مزدوجة',
'brokenredirects' => 'تحويلات مكسورة',
'brokenredirectstext' => 'التحويلات التالية تصل لصفحات غير موجودة:',
-'brokenredirects-edit' => 'عدل',
+'brokenredirects-edit' => 'تعدÙ\8aل',
'brokenredirects-delete' => 'حذف',
'withoutinterwiki' => 'صفحات بدون وصلات لغات أخرى',
'linksearch' => 'بحث في الوصلات الخارجية',
'linksearch-pat' => 'نمط البحث:',
'linksearch-ns' => 'النطاق:',
-'linksearch-ok' => 'ابØØ«',
+'linksearch-ok' => 'بحث',
'linksearch-text' => 'Wildcards مثل "*.wikipedia.org" يمكن استخدامها.
تحتاج على الأقل إلى نطاق ذو مستوى أعلى، كمثال "*.org".<br />
البروتوكولات المدعومة: <tt>$1</tt> (لا تقم بإضافة أي من هذه إلى بحثك).',
'emailnotarget' => 'المستلم ليس مستخدمًا موجودًا أو ليس مستخدمًا صحيحًا.',
'emailtarget' => 'أدخل اسم مستخدم المستلم',
'emailusername' => 'اسم المستخدم:',
-'emailusernamesubmit' => 'أرسل',
+'emailusernamesubmit' => 'إرسال',
'email-legend' => 'إرسال بريد إلكتروني إلى مستخدم {{SITENAME}} آخر',
'emailfrom' => 'من:',
'emailto' => 'إلى:',
'watchlistfor2' => 'ل$1 $2',
'nowatchlist' => 'لا توجد مدخلات في قائمة مراقبتك.',
'watchlistanontext' => 'الرجاء $1 لعرض أو تعديل الصفحات في قائمة مراقبتك.',
-'watchnologin' => 'غير مسجل',
+'watchnologin' => 'غير مسجل الدخول',
'watchnologintext' => 'يجب أن تكون [[Special:UserLogin|مسجل الدخول]] لتعدل قائمة مراقبتك.',
'addwatch' => 'إضافة إلى قائمة المراقبة',
'addedwatchtext' => "أضيفت الصفحة \"[[:\$1]]\" إلى [[Special:Watchlist|قائمة مراقبتك]].
'exblank' => 'الصفحة كانت فارغة',
'delete-confirm' => 'حذف "$1"',
'delete-legend' => 'حذف',
-'historywarning' => "'''تحذير:''' الصفحة التي توشك على حذفها لها تاريخ فيه {{PLURAL:$1||مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}} تقريباً:",
+'historywarning' => "'''تحذير:''' الصفحة التي توشك على حذفها لها تاريخ فيه {{PLURAL:$1||مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}} تقريبا:",
'confirmdeletetext' => 'أنت على وشك أن تقوم بحذف صفحة بالإضافة إلى كل تاريخها.
من فضلك التأكد من عزمك على الحذف، وبأنك مدرك للعواقب، وبأنك تقوم بهذا بالتوافق مع [[{{MediaWiki:Policy-url}}|السياسة]].',
'actioncomplete' => 'انتهاء العملية',
'rollback' => 'استرجاع التعديلات',
'rollback_short' => 'استرجع',
'rollbacklink' => 'استرجع',
+'rollbacklinkcount' => 'استرجع {{PLURAL:$1|لا تعديلات|تعديلاً واحداً|تعديلين|$1 تعديلات|$1 تعديل}}',
+'rollbacklinkcount-morethan' => 'استرجاع أكثر من $1 {{PLURAL:$1|تعديل|تعديلات}}',
'rollbackfailed' => 'لم ينجح الاسترجاع',
'cantrollback' => 'لم يمكن استرجاع التعديل؛
آخر مساهم هو المؤلف الوحيد لهذه الصفحة.',
'pagesize' => '(بايت)',
# Restrictions (nouns)
-'restriction-edit' => 'اÙ\84تعدÙ\8aÙ\84',
+'restriction-edit' => 'تعديل',
'restriction-move' => 'النقل',
'restriction-create' => 'الإنشاء',
-'restriction-upload' => 'اÙ\84رÙ\81ع',
+'restriction-upload' => 'رفع',
# Restriction levels
'restriction-level-sysop' => 'حماية كاملة',
'undelete-search-title' => 'البحث في الصفحات المحذوفة',
'undelete-search-box' => 'ابحث في الصفحات المحذوفة',
'undelete-search-prefix' => 'عرض الصفحات التي تبدأ بـ:',
-'undelete-search-submit' => 'ابØØ«',
+'undelete-search-submit' => 'بحث',
'undelete-no-results' => 'لم يتم العثور على صفحات مطابقة في أرشيف المحذوفات.',
'undelete-filename-mismatch' => 'لم يمكن استرجاع مراجعة الملف بتاريخ $1: اسم الملف لا يطابق',
'undelete-bad-store-key' => 'لم يمكن استرجاع مراجعة الملف بتاريخ $1: الملف كان مفقوداً قبل الحذف',
'sp-contributions-search' => 'بحث عن مساهمات',
'sp-contributions-username' => 'عنوان أيبي أو اسم مستخدم:',
'sp-contributions-toponly' => 'أظهر أعلى المراجعات فقط',
-'sp-contributions-submit' => 'ابØØ«',
+'sp-contributions-submit' => 'بحث',
# What links here
'whatlinkshere' => 'ماذا يصل هنا',
'blocklist-by' => 'حظر المشرف',
'blocklist-params' => 'معطيات المنع',
'blocklist-reason' => 'السبب',
-'ipblocklist-submit' => 'ابØØ«',
+'ipblocklist-submit' => 'بحث',
'ipblocklist-localblock' => 'المنع المحلي',
'ipblocklist-otherblocks' => '{{PLURAL:$1||المنع الآخر|المنعان الآخران|المنوعات الأخرى}}',
'infiniteblock' => 'لا نهائي',
وفي هذه الحالات، يجب عليك نقل أو دمج محتويات الصفحة يدويا، إذا رغب في ذلك.",
'movearticle' => 'انقل الصفحة:',
'moveuserpage-warning' => "'''تحذير: أنت على وشك نقل صفحة مستخدم. من فضلك لاحظ أن الصفحة وحدها سوف تنقل وأن المستخدم لن يعاد تسميته.'''",
-'movenologin' => 'غير مسجل',
+'movenologin' => 'غير مسجل الدخول',
'movenologintext' => 'يجب أن تكون مستخدماً مسجلاً وأن [[Special:UserLogin|تسجل دخولك]] لكي تنقل صفحة.',
'movenotallowed' => 'أنت لا تمتلك الصلاحية لنقل الصفحات.',
'movenotallowedfile' => 'أنت لا تمتلك الصلاحية لنقل الملفات.',
'spambot_username' => 'تنظيف سبام ميدياويكي',
'spam_reverting' => 'استرجاع آخر نسخة ليس بها وصلات إلى $1',
'spam_blanking' => 'كل النسخ احتوت على وصلات ل $1، إفراغ',
+'spam_deleting' => 'جميع النسخ تحوي رابطا إلى $1، يتم الحذف',
# Info page
'pageinfo-title' => 'المعلومات ل"$1"',
'newimages-label' => 'اسم الملف (أو جزء منه):',
'showhidebots' => '($1 بوتات)',
'noimages' => 'لا شيء للعرض.',
-'ilsubmit' => 'ابØØ«',
+'ilsubmit' => 'بحث',
'bydate' => 'حسب التاريخ',
'sp-newimages-showfrom' => 'أظهر الملفات الجديدة بدءا من $2، $1',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
'video-dims' => '$1، $2×$3',
-'seconds-abbrev' => '$1s',
-'minutes-abbrev' => '$1m',
-'hours-abbrev' => '$1h',
+'seconds-abbrev' => '$1ث',
+'minutes-abbrev' => '$1ق',
+'hours-abbrev' => '$1س',
+'days-abbrev' => '$1ي',
'seconds' => '{{PLURAL:$1||ثانية واحدة|ثانيتين|$1 ثوانٍ|$1 ثانية}}',
'minutes' => '{{PLURAL:$1||دقيقة واحدة|دقيقتين|$1 دقائق|$1 دقيقة}}',
'hours' => '{{PLURAL:$1||ساعة واحدة|ساعتين|$1 ساعات|$1 ساعة}}',
'exif-compression-2' => 'CCITT المجموعة 3 -1 تعديل طول تشغيل ترميز هوفمان البعدي',
'exif-compression-3' => 'ترميز فاكس المجموعة 3 CCITT',
'exif-compression-4' => 'ترميز فاكس المجموعة 4 CCITT',
+'exif-compression-5' => 'إل زد دبليو',
'exif-compression-6' => 'JPEG (قديم)',
'exif-compression-7' => 'جيه بي إي جي',
'exif-compression-8' => 'Deflate (أدوبي)',
'size-kilobytes' => '$1 كيلوبايت',
'size-megabytes' => '$1 ميجابايت',
'size-gigabytes' => '$1 جيجابايت',
+'size-terabytes' => '$1 تيرابايت',
+'size-petabytes' => '$1 بيتابايت',
+'size-exabytes' => '$1 إكسابايت',
+'size-zetabytes' => '$1 زيتابايت',
+'size-yottabytes' => '$1 يوتابايت',
+
+# Bitrate units
+'bitrate-bits' => '$1بيت لكل ثانية',
+'bitrate-kilobits' => '$1كيلوبيت لكل ثانية',
+'bitrate-megabits' => '$1ميجابيت لكل ثانية',
+'bitrate-gigabits' => '$1جيجابيت لكل ثانية',
+'bitrate-terabits' => '$1تيرابيت لكل ثانية',
+'bitrate-petabits' => '$1بيتابيت لكل ثانية',
+'bitrate-exabits' => '$1إكسابيت لكل ثانية',
+'bitrate-zetabits' => '$1زيتابيت لكل ثانية',
+'bitrate-yottabits' => '$1يوتابيت لكل ثانية',
# Live preview
-'livepreview-loading' => 'يُحمّل...',
+'livepreview-loading' => 'جاري التحميل...',
'livepreview-ready' => 'يُحمّل… جاهز!',
'livepreview-failed' => 'فشل العرض المباشر!
حاول تجربة العرض العادي.',
'version-variables' => 'المتغيرات',
'version-antispam' => 'منع البريد المزعج',
'version-skins' => 'واجهات',
+'version-api' => 'إيه بي آي',
'version-other' => 'أخرى',
'version-mediahandlers' => 'متحكمات الميديا',
'version-hooks' => 'الخطاطيف',
'version-entrypoints' => 'نقطة دخول روابط المواقع',
'version-entrypoints-header-entrypoint' => 'تقطة دخول',
'version-entrypoints-header-url' => 'المسار',
+'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath مسار المقالات]',
+'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath مسار السكريبت]',
# Special:FilePath
'filepath' => 'مسار ملف',
'fileduplicatesearch-summary' => 'ابحث عن الملفات المكررة بناء على قيم الهاش.',
'fileduplicatesearch-legend' => 'بحث عن مكرر',
'fileduplicatesearch-filename' => 'اسم الملف:',
-'fileduplicatesearch-submit' => 'ابØØ«',
+'fileduplicatesearch-submit' => 'بحث',
'fileduplicatesearch-info' => '$1 × $2 بكسل<br />حجم الملف: $3<br />نوع MIME: $4',
'fileduplicatesearch-result-1' => 'الملف "$1" ليس له تكرار مطابق.',
'fileduplicatesearch-result-n' => 'الملف "$1" له {{PLURAL:$2|1 تكرار مطابق|$2 تكرار مطابق}}.',
* <span class="mw-specialpagecached">صفحات خاصة لبيانات مخزنة فقط (قد تكون مهجورة).</span>',
'specialpages-group-maintenance' => 'تقارير الصيانة',
'specialpages-group-other' => 'صفحات خاصة أخرى',
-'specialpages-group-login' => 'دخÙ\88Ù\84 / تسجÙ\8aÙ\84',
+'specialpages-group-login' => 'دخÙ\88Ù\84 / Ø¥Ù\86شاء Øساب',
'specialpages-group-changes' => 'السجلات وأحدث التغييرات',
'specialpages-group-media' => 'تقارير الميديا وعمليات الرفع',
'specialpages-group-users' => 'المستخدمون والصلاحيات',
'tags-display-header' => 'الظهور في قوائم التغييرات',
'tags-description-header' => 'وصف كامل للمعنى',
'tags-hitcount-header' => 'تغييرات موسومة',
-'tags-edit' => 'عدل',
+'tags-edit' => 'تعدÙ\8aل',
'tags-hitcount' => '{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}',
# Special:ComparePages
'htmlform-int-toolow' => 'القيمة التي حددتها أقل من الحد الأدنى وهو $1',
'htmlform-int-toohigh' => 'القيمة التي حددتها أكبر من الحد الأقصى وهو $1',
'htmlform-required' => 'هذه القيمة مطلوبة',
-'htmlform-submit' => 'أرسل',
+'htmlform-submit' => 'إرسال',
'htmlform-reset' => 'الرجوع عن التغييرات',
'htmlform-selectorother-other' => 'أخرى',
'sqlite-no-fts' => '$1 بدون دعم البحث في كامل النص',
# New logging system
-'logentry-delete-delete' => '{{GENDER:$2|حذف|حذفت}} $1 صفحة $3',
+'logentry-delete-delete' => '$1 حذف الصفحة $3',
'logentry-delete-restore' => 'استعاد $1 صفحة $3',
-'logentry-delete-event' => '{{GENDER:$2|غيّر|غيّرت}} $1 إمكانية مشاهدة {{PLURAL:$5||حدث|حدثين|$5 أحداث|$5 حدثًا|$5 حدث}} في سجل $3: $4',
+'logentry-delete-event' => '$1 غير خاصية العرض لـ {{PLURAL:$5|مدخل السجل|$5 مدخلات السجل}} في $3: $4',
'logentry-delete-revision' => 'غيّر $1 إمكانية مشاهدة {{PLURAL:$5||مراجعة واحدة|مراجعتين|$5 مراجعات|$5 مراجعة}} في صفحة $3: $4',
'logentry-delete-event-legacy' => 'غيّر $1 إمكانية رؤية أحداث سجل $3',
'logentry-delete-revision-legacy' => 'غيّر $1 إمكانية رؤية مراجعات صفحة $3',
بخلاف ذلك، يمكنك أستخدام الطريقة الأسهل أسفله، سيتم إضافة تعليقك للصفحة "[$3 $2]"، بالإضافة إلى اسم المستخدم و نوع المتصفح الذي تستخدمه حاليا.',
'feedback-subject' => 'الموضوع:',
'feedback-message' => 'الرسالة:',
-'feedback-cancel' => 'Ø£Ù\84غÙ\90',
+'feedback-cancel' => 'Ø¥Ù\84غاء',
'feedback-submit' => 'أرسل الملاحظات',
'feedback-adding' => 'إضافة تعليقات إلى الصفحة...',
'feedback-error1' => 'خطأ: لا يمكن التعرف عليها من API',
'duration-centuries' => '{{PLURAL: $1||قرن واحد|قرنان|$1 قرون|$1 قرنًا|$1 قرن}}',
'duration-millennia' => '{{PLURAL: $1||ألفية واحدة|ألفيتان|$1 ألفيات|$1 ألفية}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ليس نوع ملف مسموح به|ليست أنواع ملفات مسموح بها}}. {{PLURAL:$3|نوع الملف المسموح به هو|أنواع الملفات المسموح بها هي}} $2.',
);
'badtitle' => 'ܟܘܢܝܐ ܠܐ ܛܒܐ',
'perfcached' => 'ܓܠܝܬ̈ܐ ܗܠܝܢ ܐܣܢܝܢ ܐܢܘܢ ܘܡܬܡܨܝܢܬܐ ܐܝܬܝܗܝ ܕܠܐ ܢܗܘܢ ܚܘ̈ܕܬܐ. ܡܬܚܐ ܥܠܝܐ ܕ {{PLURAL:$1|ܚܕ ܦܠܛܐ|$1 ܦܠܛ̈ܐ}} ܐܝܬ ܒܐܣܢܐ.',
'perfcachedts' => 'ܓܠܝܬ̈ܐ ܗܠܝܢ ܐܣܢܝܢ ܐܢܘܢ ܘܚܘܕܬܐ ܐܚܪܝܐ ܗܘܐ ܒ $1. ܡܬܚܐ ܥܠܝܐ ܕ {{PLURAL:$4|ܚܕ ܦܠܛܐ|$4 ܦܠܛ̈ܐ}} ܐܝܬ ܒܐܣܢܐ.',
+'querypage-no-updates' => 'ܚܘܕ̈ܬܐ ܕܗܕܐ ܦܐܬܐ ܠܐ ܙܪ̈ܝܙܐ ܐܢܘܢ.
+ܝܕ̈ܥܬܐ ܗܪܟܐ ܠܐ ܡܬܚܕܬܝܢ ܗܫܐ.',
'viewsource' => 'ܚܙܝ ܡܒܘܥܐ',
'viewsource-title' => 'ܚܙܝ ܡܒܘܥܐ ܕ $1',
'actionthrottled' => 'ܠܐ ܡܬܡܨܝܢܬܐ ܐܝܬܝܗܝ ܠܡܥܒܕ ܝܬܝܪ ܡܢ ܗܢܐ ܥܒܕܐ',
'resetpass-temp-password' => 'ܡܠܬܐ ܕܥܠܠܐ ܙܒܢܢܝܬܐ:',
# Special:PasswordReset
+'passwordreset' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
+'passwordreset-legend' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
'passwordreset-username' => 'ܫܡܐ ܕܡܦܠܚܢܐ:',
+# Special:ChangeEmail
+'changeemail' => 'ܫܚܠܦ ܒܝܠܕܪܐ ܐܠܩܛܪܘܢܝܐ',
+
# Edit page toolbar
'bold_sample' => 'ܟܬܒܬܐ ܥܒܝܬܐ',
'bold_tip' => 'ܟܬܒܬܐ ܥܒܝܬܐ',
'extlink_tip' => 'ܐܣܘܪܐ ܒܪܝܐ (ܕܟܘܪ http:// ܩܕܡܝܬܐ)',
'headline_sample' => 'ܨܚܚܐ ܕܦܪܫܓܢܐ ܪܫܝܐ',
'nowiki_sample' => 'ܣܢܘܦ ܟܬܒܬܐ ܕܠܐ ܣܕܝܪܘܬܐ ܗܪܟܐ',
+'nowiki_tip' => 'ܒܣܝ ܣܕܝܪܘܬܐ ܕܘܝܩܝ',
'image_tip' => 'ܠܦܦܐ ܛܡܝܪܐ',
'media_tip' => 'ܐܣܘܪܐ ܕܠܦܦܐ',
'sig_tip' => 'ܪܡܝ ܐܝܕܟ ܥܡ ܙܒܢܐ ܘܣܝܩܘܡܐ',
'unusedimages' => 'ܠܦܦ̈ܐ ܠܐ ܦܠܝܚ̈ܐ',
'popularpages' => 'ܦܐܬܬ̈ܐ ܡܫܡܗܬ̈ܐ',
'wantedcategories' => 'ܣܕܪ̈ܐ ܒܥܝ̈ܐ',
-'wantedpages' => 'ܦܐܬܬ̈ܐ ܒܥܝܬ̈ܐ',
-'wantedfiles' => 'ܠܦܦ̈ܐ ܒܥܝ̈ܐ',
-'wantedtemplates' => 'ܩܠܒ̈ܐ ܒܥܝ̈ܐ',
+'wantedpages' => 'ܦܐܬܬ̈ܐ ܣܢܝܩܬ̈ܐ',
+'wantedfiles' => 'ܠܦܦ̈ܐ ܣܢܝܩ̈ܐ',
+'wantedtemplates' => 'ܩܠܒ̈ܐ ܣܢܝܩ̈ܐ',
+'mostlinked' => 'ܦܐܬܬ̈ܐ ܐܣܝܪ̈ܬܐ ܝܬܝܪ ܡܢ ܟܠ',
+'mostlinkedcategories' => 'ܣܕܪ̈ܐ ܐܣܝܪ̈ܐ ܝܬܝܪ ܡܢ ܟܠ',
+'mostlinkedtemplates' => 'ܩܠܒ̈ܐ ܐܣܝܪ̈ܐ ܝܬܝܪ ܡܢ ܟܠ',
+'mostcategories' => 'ܦܐܬܬ̈ܐ ܣܕܝܪܐ ܝܬܝܪ ܡܢ ܟܠ',
+'mostimages' => 'ܠܦܦ̈ܐ ܐܣܝܪ̈ܐ ܝܬܝܪ ܡܢ ܟܠ',
+'mostrevisions' => 'ܦܐܬܬ̈ܐ ܥܡ ܫܘܚܠܦ̈ܐ ܝܬܝܪ ܡܢ ܟܠ',
'prefixindex' => 'ܟܠ ܦܐܬܬ̈ܐ ܥܡ ܫܪܘܝܐ',
+'prefixindex-namespace' => 'ܟܠ ܦܐܬܬ̈ܐ ܥܡ ܫܪܘܝܐ ($1 ܚܩܠܐ)',
'shortpages' => 'ܦܐܬܬ̈ܐ ܟܪ̈ܝܬܐ',
'longpages' => 'ܦܐܬܬ̈ܐ ܐܪ̈ܝܟܬܐ',
'deadendpages' => 'ܦܐܬܬ̈ܐ ܥܡ ܚܪܬܐ ܡܝܬܬܐ',
'unblockip' => 'ܫܩܘܠ ܚܪܡܐ ܡܢ ܡܦܠܚܢܐ',
'ipusubmit' => 'ܫܩܘܠ ܚܪܡܐ ܗܢܐ',
'unblocked' => 'ܐܫܬܩܠ ܚܪܡܐ ܡܢ [[User:$1|$1]]',
+'blocklist' => 'ܡܦܠܚܢ̈ܐ ܡܚܪ̈ܡܐ',
'ipblocklist' => 'ܡܦܠܚܢ̈ܐ ܡܚܪ̈ܡܐ',
'ipblocklist-legend' => 'ܐܫܟܚ ܡܦܠܚܢܐ ܡܚܪܡܐ',
'ipblocklist-submit' => 'ܒܨܝ',
# Special:SpecialPages
'specialpages' => 'ܦܐܬܬ̈ܐ ܕ̈ܝܠܢܝܬܐ',
+'specialpages-note' => '----
+* ܦܐܬܬ̈ܐ ܕ̈ܝܠܢܝܬܐ ܥܝܕ̈ܝܬܐ.
+* <span class="mw-specialpagerestricted">ܦܐܬܬ̈ܐ ܕ̈ܝܠܢܝܬܐ ܕܩܝܘܡ̈ܐ ܒܠܚܘܕ.</span>',
'specialpages-group-maintenance' => 'ܬܫܪܪܐ ܕܚܕܬܘܬܐ',
'specialpages-group-other' => 'ܦܐܬܬ̈ܐ ܕ̈ܝܠܢܝܬܐ ܐܚܪ̈ܢܝܬܐ',
'specialpages-group-login' => 'ܥܘܠ / ܒܪܝ',
'specialpages-group-changes' => 'ܫܘܚܠܦ̈ܐ ܚܕ̈ܬܐ ܘܣܓܠ̈ܐ',
'specialpages-group-users' => 'ܡܦܠܚܢ̈ܐ ܘܙܕ̈ܩܐ',
+'specialpages-group-highuse' => 'ܦܐܬܬ̈ܐ ܕܡܬܚܫܚܢܘܬܐ ܥܠܝܬܐ',
'specialpages-group-pages' => 'ܡܟܬܒܘܬ̈ܐ ܕܦܐܬܬ̈ܐ',
'specialpages-group-pagetools' => 'ܡܐܢ̈ܐ ܕܦܐܬܐ',
'specialpages-group-wiki' => 'ܓܠܝܬ̈ܐ ܘܡܐܢ̈ܐ ܕܘܝܩܝ',
'tags-hitcount' => '$1 {{PLURAL:$1|ܫܘܚܠܦܐ|ܫܘܚܠܦ̈ܐ}}',
# Special:ComparePages
+'comparepages' => 'ܦܚܘܡ ܒܝܢܝ ܦܐܬܬ̈ܐ',
+'compare-selector' => 'ܦܚܘܡ ܒܝܢܝ ܬܢܝܬ̈ܐ ܕܦܐܬܬ̈ܐ',
'compare-page1' => 'ܦܐܬܐ 1',
'compare-page2' => 'ܦܐܬܐ 2',
'compare-rev1' => 'ܬܢܝܬܐ 1',
'right-writeapi' => 'ৰাইট এ.পি.আই.ৰ ব্যৱহাৰ',
'right-delete' => 'পৃষ্ঠাসমূহ বিলোপ কৰক',
'right-bigdelete' => 'অতিৰিক্ত ইতিহাস থকা পৃষ্ঠাসমূহ বিলোপ কৰক',
+'right-deletelogentry' => "নিৰ্দিষ্ট ল'গ ভুক্তি বিলোপ কৰক বা ঘূৰাই অনক।",
'right-deleterevision' => 'পৃষ্ঠাসমূহৰ নিৰ্দিষ্ট সংশোধনী বিলোপ আৰু পুনৰুদ্ধাৰ কৰক',
'right-deletedhistory' => 'বিলোপ কৰা ইতিহাসৰ ভৰ্তি সংশ্লিষ্ট লেখা অবিহনে চাওক',
'right-deletedtext' => 'বিলোপ কৰা লেখা আৰু বিলোপ কৰা সংশোধনসমূহৰ মাজত হোৱা সালসলনি চাওক',
'api-error-emptypage' => 'নতুন, খালী পৃষ্ঠা সৃষ্টি কৰিবলৈ অনুমতি নাই।',
'api-error-fetchfileerror' => 'আভ্যন্তৰীণ ত্ৰুটি: ফাইলটো অনাত কিবা সমস্যা হৈছে।',
'api-error-fileexists-forbidden' => '"$1" নামৰ এখন নথি আগৰ পৰাই উপলদ্ধ আৰু ইয়াৰ পুনৰ লিখন অসম্ভৱ ।',
+'api-error-fileexists-shared-forbidden' => 'উমৈহতীয়া ফাইল ভঁৰালত "$1" নামৰ ফাইল এটা আছেই, ইয়াৰ ওপৰত লিখিব নোৱাৰি।',
'api-error-file-too-large' => 'আপুনি দাখিল কৰা ফাইলখন বৰ ডাঙৰ ।',
'api-error-filename-tooshort' => 'ফাইলৰ নামটো অতি চুটি।',
'api-error-filetype-banned' => 'এই ধৰণৰ ফাইল নিষিদ্ধ।',
'duration-centuries' => '$1 {{PLURAL:$1|শতাব্দী|শতাব্দী}}',
'duration-millennia' => '$1 {{PLURAL:$1|সহস্ৰাব্দ|সহস্ৰাব্দ}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|অনুমোদিত ফাইল প্ৰকাৰ নহয়|সমূহ অনুমোদিত ফাইল প্ৰকাৰ নহয়}} । {{PLURAL:$3|অনুমোদিত ফাইল প্ৰকাৰ হ’ল|অনুমোদিত ফাইল প্ৰকাৰসমূহ হ’ল}} $2 ।',
);
'remembermypassword' => "Recordar la mio identificación nesti restolador (un máximu {{PLURAL:$1|d'un día|de $1 díes}})",
'securelogin-stick-https' => "Siguir coneutáu al HTTPS dempués d'identificase",
'yourdomainname' => 'El to dominiu:',
+'password-change-forbidden' => 'Nun se pueden camudar les contraseñes nesta wiki.',
'externaldberror' => "O hebo un fallu d'autenticación de la base de datos o nun tienes permisu p'anovar la to cuenta esterna.",
'login' => 'Identificase',
'nav-login-createaccount' => 'Identificase / crear una cuenta',
'noarticletext-nopermission' => 'Nestos momentos nun hai testu nesta páxina.
Pues [[Special:Search/{{PAGENAME}}|guetar esti títulu de páxina]] n\'otres páxines,
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} guetar los rexistros rellacionaos]</span>.',
+'missing-revision' => 'La revisión #$1 de la páxina llamada "{{PAGENAME}}" nun esiste.
+
+De vezu la causa d\'esto ye siguir un enllaz antiguu del historial a una páxina que se desanició.
+Se puen alcontrar más detalles nel [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rexistru de desanicios].',
'userpage-userdoesnotexist' => "La cuenta d'usuariu «$1» nun ta rexistrada.
Por favor comprueba si quies crear/editar esta páxina.",
'userpage-userdoesnotexist-view' => "La cuenta d'usuariu «$1» nun ta rexistrada.",
'editundo' => 'esfacer',
'diff-multi' => "({{PLURAL:$1|Nun s'amuesa 1 revisión intermedia|Nun s'amuesen $1 revisiones intermedies}} {{PLURAL:$2|d'un usuariu|de $2 usuarios}} )",
'diff-multi-manyusers' => "({{PLURAL:$1|Nun s'amuesa una revisión intermedia|Nun s'amuesen $1 revisiones intermedies}} de más de $2 {{PLURAL:$2|usuariu|usuarios}})",
+'difference-missing-revision' => "{{PLURAL:$2|Nun s'alcontró|Nun s'alcontraron}} {{PLURAL:$2|una revisión|$2 revisiones}} d'esta diferencia ($1).
+
+De vezu la causa d'esto ye siguir un enllaz de diferencia antiguu a una páxina que se desanició.
+Se puen alcontrar más detalles nel [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rexistru de desanicios].",
# Search results
'searchresults' => 'Resultaos de la gueta',
'right-writeapi' => "Usar l'API d'escritura",
'right-delete' => 'Esborrar páxines',
'right-bigdelete' => 'Esborrar páxines con historiales grandes',
+'right-deletelogentry' => 'Desaniciar y restaurar entraes del rexistru determinaes',
'right-deleterevision' => 'Eliminar y restaurar revisiones específiques de les páxines',
'right-deletedhistory' => 'Ver entraes eliminaes del historial ensin testu asociáu',
'right-deletedtext' => 'Ver el testu desaniciáu y los cambeos ente versiones desaniciaes',
'disambiguations' => "Páxines qu'enllacen con páxines de dixebra",
'disambiguationspage' => 'Template:dixebra',
-'disambiguations-text' => "Les siguientes páxines enllacien a una '''páxina de dixebra'''. En cuenta d'ello habríen enllaciar al artículu apropiáu.<br />Una páxina considérase de dixebra si usa una plantía que tea enllaciada dende [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Les siguientes páxines contienen polo menos un enllaz a una '''páxina de dixebra'''. En cuenta d'ello habríen enllaciar a una páxina más apropiada.<br />
+Una páxina tratase como una páxina de dixebra si usa una plantía que tea enllaciada dende [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Redireiciones dobles',
'doubleredirectstext' => 'Esta páxina llista páxines que redireicionen a otres páxines de redireición.
'rollback' => 'Revertir ediciones',
'rollback_short' => 'Revertir',
'rollbacklink' => 'revertir',
+'rollbacklinkcount' => 'revertir $1 {{PLURAL:$1|edición|ediciones}}',
+'rollbacklinkcount-morethan' => 'revertir más de $1 {{PLURAL:$1|edición|ediciones}}',
'rollbackfailed' => 'Falló la reversión',
'cantrollback' => "Nun se pue revertir la edición; el postrer collaborador ye l'únicu autor d'esta páxina.",
'alreadyrolled' => 'Nun se pue revertir la postrer edición de [[:$1]] fecha por [[User:$2|$2]] ([[User talk:$2|alderique]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
'duration-centuries' => '$1 {{PLURAL:$1|sieglu|sieglos}}',
'duration-millennia' => '$1 {{PLURAL:$1|mileniu|milenios}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|nun ye una triba de ficheru permitida|nun son tribes de ficheru permitíes}}. {{PLURAL:$3|La triba de ficheru permitida ye|Les tribes de ficheru permitíes son}} $2.',
);
'tog-editsection' => 'Hər bir bölmə üçün [redaktə]ni mümkün et',
'tog-editsectiononrightclick' => 'Bölmələrin redaktəsini başlıqların üzərində sağ klik etməklə mümkün et (JavaScript)',
'tog-showtoc' => 'Mündəricat siyahısını göstər (3 başlıqdan artıq olan səhifələrdə)',
-'tog-rememberpassword' => 'Məni bu kompüterdə xatırla (maksimum $1 {{PLURAL:$1|gün|gün}})',
+'tog-rememberpassword' => 'Məni bu kompyuterdə xatırla (maksimum $1 {{PLURAL:$1|gün|gün}})',
'tog-watchcreations' => 'Yaratdığım səhifələri izlədiyim səhifələrə əlavə et',
'tog-watchdefault' => 'Redaktə etdiyim səhifələri izlədiyim səhifələrə əlavə et',
'tog-watchmoves' => 'Adlarını dəyişdiyim səhifələri izlədiyim səhifələrə əlavə et',
'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-'about' => 'İzah',
+'about' => 'Haqqında',
'article' => 'Mündəricat',
'newwindow' => '(yeni pəncərədə açılır)',
'cancel' => 'Ləğv et',
'yourname' => 'İstifadəçi adı',
'yourpassword' => 'Parol:',
'yourpasswordagain' => 'Parolu təkrar yazın:',
-'remembermypassword' => 'Məni bu kompüterdə xatırla (maksimum $1 {{PLURAL:$1|gün|gün}})',
+'remembermypassword' => 'Məni bu kompyuterdə xatırla (maksimum $1 {{PLURAL:$1|gün|gün}})',
'securelogin-stick-https' => 'Daxil olduqdan sonra HTTPS-lə əlaqədə qal',
'yourdomainname' => 'Sizin domain',
'externaldberror' => 'Verilənlər bazasının doğruluğunu yoxlamada xəta baş verib və yaxud sizin xarici istifadəçi qeydiyyatını yeniləmək hüququnuz yoxdur.',
'last' => 'son',
'page_first' => 'birinci',
'page_last' => 'sonuncu',
-'histlegend' => "Fərqə bax: müqayisə etmək istədiyiniz versiyaların yanındakı dairələri işarələyin və \"Enter\"ə və ya \"müqayisə et\" düyməsinə basın.<br />
-Açıqlama: '''(hh)''' — hal-hazırkı versiya ilə aradakı fərq, '''(son)''' — əvvəlki versiya ilə aradakı fərq, '''k''' — kiçik redaktə.",
+'histlegend' => "<div id=\"histlegend\"><span style=\"white-space:nowrap;\">Aşağıda sadalanan hər hansı bir versiyası görmək üçün, tarixinin üzərinə tıklayın.</span> <span style=\"white-space:nowrap;\">Daha çox kömək üçün, [[Kömək:Səhifə keçmişi|səhifə keçmişi]] səhifəsinə baxın.</span><br /><span style=\"white-space:nowrap;\">Xarici vasitələr: <!-- [http://toolserver.org/~tparis/articleinfo/index.php?article={{FULLPAGENAMEE}}&lang=tr&wiki=wikipedia Təftiş keçmişinin statistikası] '''·'''</span> <span style=\"white-space:nowrap;\"> -->[http://wikipedia.ramselehof.de/wikiblame.php?lang=tr&article={{FULLPAGENAMEE}} Təftiş keçmişini axtarmaq] '''·'''</span> <span style=\"white-space:nowrap;\">[http://toolserver.org/~daniel/WikiSense/Contributors.php?wikilang=tr&wikifam=.wikipedia.org&grouped=on&page={{FULLPAGENAMEE}} Töhfəsi olanlar] '''·'''</span> <span style=\"white-space:nowrap;\">[http://toolserver.org/~mzmcbride/cgi-bin/watcher.py?db=trwiki_p&titles={{FULLPAGENAMEE}} İzləyənlərin sayı] '''·'''</span> <span style=\"white-space:nowrap;\">[http://stats.grok.se/tr/latest/{{FULLPAGENAMEE}} Səhifəyə baxılma statistikası]</span></div>
+----
+Fərqləri seçmə və göstərmə: müqayisə etmək istədiyiniz versiyaların yanındakı radio qutularına işarə qoyun və daxil etmə düyməsinə (enter+a) və ya \"müqayisə et\" düyməsinə vurun.
+
+Açıqlama: '''(hh)''' = hal-hazırkı versiya ilə olan fərqlər, '''(son)''' = əvvəlki versiya ilə olan fərqlər, '''k''' = kiçik redaktələr.</span>",
'history-fieldset-title' => 'Tarixçəni nəzərdən keçir',
'history-show-deleted' => 'Yalnız silinənlər',
'histfirst' => 'Ən əvvəlki',
'filedelete-reason-otherlist' => 'Başqa səbəb',
'filedelete-reason-dropdown' => '*Əsas silmə səbəbi
** Müəllif hüququ pozuntusu
-** Dublikat fayl',
+** Dublikat fayl
+** Keyfiyyətsiz şəkil
+** İstifadəsiz fayl
+** Qeyri-ensiklopedik şəxs
+** Lisenziyasız fayl
+** Azad şəkillə əvəz olundu',
'filedelete-edit-reasonlist' => 'Silmə səbəblərini redaktə et',
# MIME search
'currentevents-url' => 'Project:Ағымдағы ваҡиғалар',
'disclaimers' => 'Яуаплылыҡтан баш тартыу',
'disclaimerpage' => 'Project:Яуаплылыҡтан баш тартыу',
-'edithelp' => 'Ð\9cÓ©Ñ\85Ó\99Ñ\80иÑ\80лÓ\99ү белешмәһе',
+'edithelp' => 'ТөÒ\99Ó\99Ñ\82еү белешмәһе',
'edithelppage' => 'Help:Төҙәтеү белешмәһе',
'helppage' => 'Help:Белешмә',
'mainpage' => 'Баш бит',
'youhavenewmessages' => 'Яңы $1 бар ($2).',
'newmessageslink' => 'яңы хәбәр',
'newmessagesdifflink' => 'һуңғы үҙгәртеү',
+'youhavenewmessagesfromusers' => 'Һеҙгә {{PLURAL:$3|башҡа ҡатнашыусынан|$3 ҡатнашыусынан}} $1 бар ($2).',
+'youhavenewmessagesmanyusers' => 'Һеҙгә күп ҡатнашыусынан $1 бар ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|яңы хәбәр|яңы хәбәр}}',
+'newmessagesdifflinkplural' => 'һуңғы {{PLURAL:$1|үҙгәртеү|үҙгәртеү}}',
'youhavenewmessagesmulti' => 'Һеҙгә яңы хәбәрҙәр бар: $1',
'editsection' => 'үҙгәртергә',
'editold' => 'төҙәтеү',
'filereadonlyerror' => "«$1» файлын үҙгәртеп булмай, сөнки «$2» һаҡлағысы «уҡыу өсөн генә» тәртибендә.
Был сикләүҙе индергән хаким биргән аңлатма:«''$3''».",
+'invalidtitle-knownnamespace' => '"$2" исем арауығы һәм "$3" тексты исем өсөн ярамай',
+'invalidtitle-unknownnamespace' => '"$2" тексты һәм "$1" арауыҡ өсөн билдәһеҙ номерлы исем ярамай',
+'exception-nologin' => 'Танылмағанһығыҙ',
+'exception-nologin-text' => 'Был битте ҡарар йәки һоратылған ғәмәлде башҡарыр өсөн системала танылыу кәрәк.',
# Virus scanner
'virus-badscanner' => "Көйләү хатаһы: Билдәһеҙ вирустар сканеры: ''$1''",
'remembermypassword' => 'Был компьютерҙа паролемде иҫләргә ($1 {{PLURAL:$1|көндән|көндән}} күп түгел)',
'securelogin-stick-https' => 'Танылғандан һуң HTTPS менән бәйләнеште ҡалдырырға',
'yourdomainname' => 'Һеҙҙең домен',
+'password-change-forbidden' => 'Был викила паролегеҙҙе үҙгәртә алмайһығыҙ.',
'externaldberror' => 'Тышҡы мәғлүмәт базаһы менән танылғанда хата барлыҡҡа килде йәки тышҡы үҙ көйләүҙәрегеҙҙе үҙгәртер өсөн хоҡуҡтарығыҙ етәрле түгел.',
'login' => 'Танышыу йәки теркәлеү',
'nav-login-createaccount' => 'Танышыу йәки теркәлеү',
'hr_tip' => 'Горизонталь һыҙыҡ (бик йыш ҡулланмағыҙ)',
# Edit pages
-'summary' => 'Үҙгәртеүҙең ҡыҫҡаса тасуирламаһы:',
+'summary' => 'Үҙгәртеү аңлатмаһы:',
'subject' => 'Тема/исем:',
'minoredit' => 'Әҙ генә үҙгәрештәр',
-'watchthis' => 'Ð\91Ñ\8bл биÑ\82Ñ\82е күÒ\99Ó\99Ñ\82еүÒ\99Ó\99Ñ\80 иÑ\81емлегенÓ\99 индеÑ\80еÑ\80гә',
+'watchthis' => 'Ð\9aÒ¯Ò\99Ó\99Ñ\82еү иÑ\81емлегенә',
'savearticle' => 'Яҙҙырып ҡуйырға',
'preview' => 'Ҡарап сығыу',
'showpreview' => 'Ҡарап сығырға',
'noarticletext-nopermission' => 'Хәҙерге ваҡытта был биттә текст юҡ.
Һеҙ башҡа мәҡәләләрҙә [[Special:Search/{{PAGENAME}}|был исемде]] йәки
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} журналдағы яҙмаларҙы] эҙләй алаһығыҙ.</span>',
+'missing-revision' => '"{{PAGENAME}}" исемле биттең $1 номерлы өлгөһө юҡ.
+
+Был хәл, ғәҙәттә, юйылған биткә яһалған һылтанманын ваҡыты үтеүенән барлыҡҡа килә.
+Тулыраҡ мәғлүмәт өсөн ҡарағыҙ: [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} юйыу яҙмалары].',
'userpage-userdoesnotexist' => '«<nowiki>$1</nowiki>» иҫәп яҙыуы юҡ. Һеҙ бындай бит яһарға йәки битте үҙгәртергә теләһәгеҙ яңынан тикшерегеҙ.',
'userpage-userdoesnotexist-view' => '«$1» исемле иҫәп яҙыуы юҡ.',
'blocked-notice-logextract' => 'Хәҙергә был ҡатнашыусы ябылған. Һуңғы ҡулланыусы ябыу яҙмаһы:',
'parser-template-loop-warning' => 'Төйөн табылған ҡалыптар: [[$1]]',
'parser-template-recursion-depth-warning' => '($1) ҡалыбын рекурсия итеп ҡулланыу тәрәнлеге рөхсәт ителгәндән артып киткән',
'language-converter-depth-warning' => 'Телдәрҙе үҙгәртеү тәрәнлегенең сиге үткән ($1)',
+'node-count-exceeded-category' => 'Төйөндәр һаны артҡан биттәр',
+'node-count-exceeded-warning' => 'Биттә төйөндәр һаны артып киткән',
+'expansion-depth-exceeded-category' => 'Асылыу тәрәнлеге артып киткән биттәр',
+'expansion-depth-exceeded-warning' => 'Биттә һалыныу тәрәнлеге сиге үтеп кителгән',
+'parser-unstrip-loop-warning' => 'Ябылмаған pre табылды',
+'parser-unstrip-recursion-limit' => '($1) рекурсия сиге үтеп кителгән',
+'converter-manual-rule-error' => 'Тел әйлендереү ҡағиҙәһендә хата табылды',
# "Undo" feature
'undo-success' => 'Был үҙгәртеүҙе кире алып була. Зинһар, улар һеҙҙе ҡыҙыҡһындырған үҙгәртеүҙәр булыуынан шикләнмәҫ өсөн версияларҙы сағыштырыуҙы ҡарағыҙ һәм үҙгәртеүҙәрҙе ғәмәлғә керетер өсөн «Битте һаҡларға» төймәһенә баҫығыҙ.',
# Diffs
'history-title' => '$1 битенең үҙгәртеү тарихы',
+'difference-title' => '$1 — версиялар араһындағы айырма',
+'difference-title-multipage' => '«$1» һәм «$2» биттәре араһындағы айырма',
'difference-multipage' => '(Биттәр араһындағы айырма)',
'lineno' => '$1 юл:',
'compareselectedversions' => 'Һайланған версияларҙы сағыштырыу',
'editundo' => 'кире алыу',
'diff-multi' => '({{PLURAL:$2|$2 ҡатнашыусының}} {{PLURAL:$1|ваҡытлы версияһы}} күрһәтелмәгән)',
'diff-multi-manyusers' => '(Кәмендә {{PLURAL:$2|$2 ҡатнашыусының}} {{PLURAL:$1|ваҡытлы версияһы}} күрһәтелмәгән)',
+'difference-missing-revision' => '$1 айырмаһының {{PLURAL:$2|бер өлгөһө|$2 өлгөһө}} табылманы.
+
+Был хәл, ғәҙәттә, юйылған биткә яһалған айырма һылтанмаһының ваҡыты үтеүенән барлыҡҡа килә.
+Тулыраҡ мәғлүмәт өсөн ҡарағыҙ: [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} юйыу яҙмалары].',
# Search results
'searchresults' => 'Эҙләү һөҙөмтәләре',
'right-writeapi' => 'Яҙҙырыу өсөн API ҡулланыу',
'right-delete' => 'Биттәрҙе юйырға',
'right-bigdelete' => 'Тарихы оҙон булған биттәрҙе юйыу',
+'right-deletelogentry' => 'Журналдың билдәле яҙмаларын юйыу һәм тергеҙеү.',
'right-deleterevision' => 'Биттәрҙең күрһәтелгән өлгөләрен юйыу һәм тергеҙеү',
'right-deletedhistory' => 'Биттәрҙең юйылған тарих яҙмаларын текстһыҙ ҡарау',
'right-deletedtext' => 'Биттең юйылған өлгөләре араһындағы юйылған текстты һәм үҙгәртеүҙәрҙе ҡарау',
'backend-fail-closetemp' => 'Ваҡытлы файлды ябып булмай.',
'backend-fail-read' => '«$1» файлын уҡып булмай.',
'backend-fail-create' => '«$1» файлын яҙып булмай.',
+'backend-fail-maxsize' => '$1 файлын яҙып булманы, сөнки уның күләме {{PLURAL:$2|$2 байттан|$2 байттан}} күп.',
'backend-fail-readonly' => '$1 һаҡлағысы әлегә уҡыу өсөн генә асыҡ. Сәбәбе: $2',
'backend-fail-synced' => '$1 файлы эске һаҡлағыста ярашһыҙ хәлдә тора.',
'backend-fail-connect' => '"$1" һаҡлағысы менән бәйләнеш яһап булманы.',
'backend-fail-internal' => '$1 һаҡлағысында билдәһеҙ хата килеп сыҡты',
'backend-fail-contenttype' => 'Файлды $1 адресына һаҡлар өсөн уның эстәлеге төрөн билдәләп булманы.',
+'backend-fail-batchsize' => 'Һаҡлағыс $1 {{PLURAL:$1|файл операцияһынан|файл операцияһынан}} бер блок алды, сикләү һаны: $2 {{PLURAL:$1|операция|операция}}.',
+'backend-fail-usable' => 'Хоҡуҡтар етмәгәнлектән йәки кәрәкле папкалар булмағанлыҡтан $1 файлын яҙып булманы.',
# File journal errors
'filejournal-fail-dbconnect' => '"$1" мәғлүмәт базаһы журналына тоташып булманы.',
'lockmanager-fail-releaselock' => '"$1" асҡысының биген асып булманы.',
'lockmanager-fail-db-bucket' => '$1 сегментында етәрле күләмдә бикләү базаһы менән бәйләнеп булманы.',
'lockmanager-fail-db-release' => '$1 мәғлүмәттәр базаһы биген сисеп булманы.',
+'lockmanager-fail-svr-acquire' => '$1 серверындағы биктәрҙе алып булманы.',
'lockmanager-fail-svr-release' => '$1 серверы биктәрен сисеп булманы.',
# ZipDirectoryReader
Тулыраҡ мәғлүмәтте [$2 файл тасуирламаһы битендә] ҡарарға мөмкин.',
'sharedupload-desc-here' => 'Был файл $1 базаһынан һәм башҡа проекттарҙа ҡулланыла ала.
[$2 файл тасуирламаһы битенән] тулыраҡ мәғлүмәт түбәндә килтерелгән.',
+'sharedupload-desc-edit' => 'Файл килгән урын: $1. Был файл башҡа сайттарҙа ҡулланылырға мөмкин.
+Тасуиламаһын [$2 кәрәкле биттә] үҙгәртергә була.',
+'sharedupload-desc-create' => 'Файл килгән урын: $1. Был файл башҡа сайттарҙа ҡулланылырға мөмкин.
+Тасуиламаһын [$2 кәрәкле биттә] үҙгәртергә була.',
'filepage-nofile' => 'Бындай исемле файл юҡ.',
'filepage-nofile-link' => 'Бындай исемле файл юҡ. Һеҙ уны [$1 тейәй алаһығыҙ].',
'uploadnewversion-linktext' => 'Был файлдың яңы версияһын тейәргә',
'disambiguations' => 'Күп мәғәнәле төшөнсәләр биттәренә һылтанған биттәр',
'disambiguationspage' => 'Template:Күп_мәғәнәлелек',
-'disambiguations-text' => "ТүбÓ\99ндÓ\99ге биÑ\82Ñ\82Ó\99Ñ\80Ò\99Ó\99н '''күп мÓ\99Ò\93Ó\99нÓ\99ле биÑ\82Ñ\82Ó\99Ñ\80гÓ\99''' Ò»Ñ\8bлÑ\82анма Ñ\8fһалÒ\93ан.
+'disambiguations-text' => "Ð\9aилÓ\99һе биÑ\82Ñ\82Ó\99Ñ\80Ò\99Ó\99 кÓ\99мендÓ\99 беÑ\80 '''күп мÓ\99Ò\93Ó\99нÓ\99ле биÑ\82Ñ\82Ó\99Ñ\80гÓ\99''' Ò»Ñ\8bлÑ\82анма баÑ\80.
Бының урынына улар фәҡәт үҙенә кәрәкле мәҡәләгә һылтанырға тейеш.<br />
Әгәр биттә исеме [[MediaWiki:Disambiguationspage]] битендә күрһәтелгән ҡалып ҡулланылһа, ул күп мәғәнәле тип иҫәпләнә.",
'wantedpages' => 'Кәрәкле биттәр',
'wantedpages-badtitle' => 'Һорау һөҙөмтәләрендә дөрөҫ булмаған исем: $1',
'wantedfiles' => 'Кәрәкле файлдар',
+'wantedfiletext-cat' => 'Киләһе файлдарҙы улар булмаған хәлдә ҡулланырға тырышыла. Тыш һаҡлағыстарҙа булған файлдар был исемлеккә яңылыш эләгеүе мөмкин. Бындай хаталы белдереүҙәр <del>һыҙыҡ</del> менән күрһәтеләсәк. Шулай уҡ, булмаған файлдарҙы алған биттәр киләһе исемлектә күрһәтелгән: [[:$1]]',
+'wantedfiletext-nocat' => 'Киләһе файлдарҙы улар булмаған хәлдә ҡулланырға тырышыла. Тыш һаҡлағыстарҙа булған файлдар был исемлеккә яңылыш эләгеүе мөмкин. Бындай хаталы белдереүҙәр <del>һыҙыҡ</del> менән күрһәтеләсәк.',
'wantedtemplates' => 'Кәрәкле ҡалыптар',
'mostlinked' => 'Иң күп һылтанма яһалған биттәр',
'mostlinkedcategories' => 'Иң күп һылтанма яһалған категориялар',
'alllogstext' => '{{SITENAME}} проектының дөйөм яҙмалар журналы исемлеге. Һеҙ һөҙөмтәләрҙе журнал төрө буйынса, ҡатаншыусы исеме буйынса (ҙур/бәләкәй хәрефкә һиҙгер) йәки ҡағылған бит исеме буйынса (шулай уҡ ҙур/бәләкәй хәрефкә һиҙгер) һайлап ала алаһығыҙ.',
'logempty' => 'Журнал яҙмаларында һайланған юлдар юҡ.',
'log-title-wildcard' => 'Керетелгән хәрефтәр менән башланған исемдәрҙе табырға',
+'showhideselectedlogentries' => 'Журналдың һайланған яҙмаларын күрһәтергә/йәшерергә.',
# Special:AllPages
'allpages' => 'Бөтә биттәр',
'allpages-hide-redirects' => 'Йүнәлтеүҙәрҙе йәшерергә',
# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Һеҙ биттең кэшланған өлгөһөн ҡарайһығыҙ. Уның $1 элек яңыртылыуы мөмкин.',
+'cachedspecial-viewing-cached-ts' => 'Һеҙ биттең кэшланған өлгөһөн ҡарайһығыҙ. Уның хәҙерге өлгөнән бик ныҡ айырылыуы мөмкин.',
'cachedspecial-refresh-now' => 'Һуңғы версияны ҡарарға',
# Special:Categories
'rollback' => 'Үҙгәртеүҙәрҙе кире ҡайтарырға',
'rollback_short' => 'Кире ҡайтарырға',
'rollbacklink' => 'кире алырға',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|төҙәтеүҙе|төҙәтеүҙе}} кире алырға',
+'rollbacklinkcount-morethan' => '$1 {{PLURAL:$1|төҙәтеүҙән|төҙәтеүҙән}} күберәк кире алырға',
'rollbackfailed' => 'Кире ҡайтарырғанда барлыҡҡа килгән хата',
'cantrollback' => 'Үҙгәртеүҙәрҙе кире алыу мөмкин түгел. Битте һуңғы үҙгәртеүсе ҡатнашыусы уның берҙән-бер авторы булып тора.',
'alreadyrolled' => '[[User:$2|$2]] ([[User talk:$2|фекер алышыу]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) кереткән [[:$1]] һуңғы үҙгәртеүҙәрҙе кире алыу мөмкин түгел; башҡа ҡатнашыусы был битте мөхәррирләгән йәки үҙгәртеүҙәрҙе кире алған инде.
'spambot_username' => 'Спамдан таҙартыусы',
'spam_reverting' => '$1 һылтанмаһыҙ һуңғы өлгөгә ҡайтарыу',
'spam_blanking' => 'Бөтә өлгөләрҙә лә $1 һылтанмаһы бар, таҙартыу',
+'spam_deleting' => 'Бөтә өлгөләрҙә лә $1 һылтанма бар, таҙартыу бара',
# Info page
'pageinfo-title' => '«$1» буйынса мәғлүмәт',
'api-error-empty-file' => 'Һеҙ ебәргән файл буш.',
'api-error-emptypage' => 'Яңы буш биттәр яһау тыйыла.',
'api-error-fetchfileerror' => 'Эске хата: файлды күсергән ваҡытта хата китте',
+'api-error-fileexists-forbidden' => '«$1» исемле файл бар һәм өҫтөнә яҙып булмай.',
+'api-error-fileexists-shared-forbidden' => '«$1» исемле файл уртаҡ файлдар һаҡлағысында бар һәм өҫтөнә яҙып булмай.',
'api-error-file-too-large' => 'Һеҙ ебәргән файл үтә ҙур.',
'api-error-filename-tooshort' => 'Файл исеме бик ҡыҫҡа.',
'api-error-filetype-banned' => 'Был файл төрө тыйылған.',
'duration-centuries' => '$1 {{PLURAL:$1|быуат|быуаттар}}',
'duration-millennia' => '$1 {{PLURAL:$1|меңйыллыҡ|меңйыллыҡтар}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|тыйылған файл төрө|тыйылған файл төрҙәре}}. Рөхсәт ителгән {{PLURAL:$3|файл төрө|файл төрҙәре}} $2.',
);
'prefs-help-realname' => 'Opsyonal an totoong pangaran asin kun itatao mo ini, gagamiton ini yangarig an mga sinurat mo maatribuir saimo.',
'prefs-help-email' => 'Opsyonal an e-koreo, alagad pwede ka na masosog kan iba sa paagi kan saimong pahina o pahina nin olay na dai kinakaipuhan na ipabisto an identidad mo.',
'prefs-help-email-required' => 'Kaipuhan an e-koreo.',
+'prefs-advancedrendering' => 'Abantidong mga pagpipilian',
+'prefs-advancedsearchoptions' => 'Abantidong mga pagpipilian',
+'prefs-advancedwatchlist' => 'Abantidong mga pagpipilian',
+'prefs-displayrc' => 'Ihayag an mga pagpipilian',
+'prefs-displaysearchoptions' => 'Ipahiling ang mga pagpipilian',
+'prefs-displaywatchlist' => 'Ipahiling ang mga pagpipilian',
+'prefs-diffs' => 'Diffs',
# User preference: e-mail validation using jQuery
+'email-address-validity-valid' => 'An e-koreo nagpapahiling na balido',
'email-address-validity-invalid' => 'Magkaag nin sarong balidong e-koreong address',
# User rights
'right-writeapi' => 'Gamit kan pagsurat sa API',
'right-delete' => 'Puraon an mga pahina',
'right-bigdelete' => 'Puraon an mga pahina na igwang darakulang mga historiya',
+'right-deletelogentry' => 'Puraon asin dae pagpuran an espesipikong mga entrada sa log',
'right-deleterevision' => 'Puraon asin dae puraon an espisipikong pagbabago kan mga pahina',
'right-deletedhistory' => 'Tanawon an pinagpurang mga entradang historiya, na dae kan saindang asosyadong teksto',
'right-deletedtext' => 'Tanawon an pinagpurang teksto asin mga karibay sa tahaw kan mga pagbabagong pinagpura na',
'windows-nonascii-filename' => 'Ining wiki dae tabi nagsusuporta kan mga pangaran kan sagunson na igwang espesyal na mga karakter.',
'fileexists' => "Igwa nang ''file'' na may parehong pangaran sa ini, sosogon tabî an '''<tt>[[:$1]]</tt>''' kun dai ka seguradong ribayan ini.
[[$1|thumb]]",
+'filepageexists' => "An pahinang pandeskripsyon kaining sagunson pinagmukna na tabi sa '''<tt>[[:$1]]</tt>''', alagad mayong sagunson na igwa kaining pangaran sa ngunyan nag-eeksister.
+An sumaryong na saimong ipinaglaog dae minaluwas sa pahina kan deskription.
+Tanganing gibohon na an saimong sumaryo magluwas duman, kaipohan mong manwal na pagliliwat kaini.
+[[$1|thumb]]",
'fileexists-extension' => "May ''file'' na may parehong pangaran: [[$2|thumb]]
* Pangaran kan pigkakargang ''file'': '''<tt>[[:$1]]</tt>'''
* Pangaran kan yaon nang ''file'': '''<tt>[[:$2]]</tt>'''
'fileexists-thumbnail-yes' => "An ''file'' garo ladawan kan pinasadit ''(thumbnail)''. [[$1|thumb]]
Sosogon tabî an ''file'' '''<tt>[[:$1]]</tt>'''.
Kun an sinosog na ''file'' iyo an parehong ladawan na nasa dating sokol, dai na kaipuhan magkarga nin iba pang retratito.",
-'file-thumbnail-no' => "An ''filename'' nagpopoon sa '''<tt>$1</tt>'''. Garo ladawan na pinasadit ini ''(thumbnail)''.
-Kun igwa ka nin ladawan na may resolusyón na maximo ikarga tabî ini, kun dai, bâgohon tabî an pangaran nin ''file''.",
-'fileexists-forbidden' => "Igwa nang ''file'' na may parehong pangaran; bumalik tabi asin ikarga an ''file'' sa bâgong pangaran [[File:$1|thumb|center|$1]]",
-'fileexists-shared-forbidden' => "Igwa nang ''file'' na may parehong pangaran sa repositoryo nin mga bakas na ''file''; bumalik tabî asin ikarga an ''file'' sa bâgong pangaran. [[File:$1|thumb|center|$1]]",
+'file-thumbnail-no' => "An sagunson minapoon sa '''<tt>$1</tt>'''.
+Garo baga ini sarong imaheng pinasadit an sukol ''(thumbnail)''.
+Kun igwa ka kaining imahe sa kabilogang resolusyon ikarga ini, kun laen pakiribayi an ngaran kan sagunson.",
+'fileexists-forbidden' => 'May sagunson na sa arog kaining ngaran, asin dae puwedeng mapapatungan.
+Kun gusto mo pang ipagkarga an saimong sagunson, pakibalik lang asin gumamit nin bagong ngaran.
+[[File:$1|thumb|center|$1]]',
+'fileexists-shared-forbidden' => 'May sagunson na sa arog kaining ngaran sa repositoryo kan pinagheras na sagunson.
+Kun gusto mo pang ipagkarga an saimong sagunson, pakibalik lang asin gumamit nin bagong ngaran.
+[[File:$1|thumb|center|$1]]',
+'file-exists-duplicate' => 'An sagunson na ini sarong duplikado kan minasunod na {{PLURAL:$1|file|files}}:',
+'file-deleted-duplicate' => 'Sarong sagunson na kapareho kaini ([[:$1]]) na dati nang pinagpura.
+Kaipuhan mong aramon an historiya kan pagpura bago ka man magpadagos sa pagkarga kaini giraray.',
'uploadwarning' => 'Patanid sa pagkarga',
+'uploadwarning-text' => 'Pakibaguha tabi an deskripsyon kan sagunson sa ibaba asin paki-otroha giraray.',
'savefile' => "Itagama an ''file''",
'uploadedimage' => 'Ikinarga "[[$1]]"',
'overwroteimage' => 'kinarga an bagong bersión kan "[[$1]]"',
'uploaddisabled' => 'Pigpopondó an mga pagkargá',
-'uploaddisabledtext' => "Pigpopogolan an pagkarga nin mga ''file'' o sa ining wiki.",
+'copyuploaddisabled' => 'An pagkarga sa paagi kan kilyawan pinagpondo.',
+'uploadfromurl-queued' => 'An saimong pagkarga pinagpahalat.',
+'uploaddisabledtext' => 'An pagkarga kan mga sagunson pinagpondo tabi.',
'uploadscripted' => "Ining ''file'' igwang HTML o kodang eskritura na pwede ser na salang mainterpretar kan ''browser''.",
'uploadvirus' => "May virus an ''file''! Mga detalye: $1",
'sourcefilename' => 'Ginikanan kan pangaran nin sagunson:',
'api-error-uploaddisabled' => 'Загрузка ў гэтую вікі адключаная.',
'api-error-verification-error' => 'Гэты файл можа быць пашкоджаны, ці мае няслушнае пашырэнне.',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|належыць да недазволенага тыпу файлаў|належаць да недазволеных тыпаў файлаў}}. Пералік дазволеных тыпаў складаецца з {{PLURAL:$3|тыпа|тыпаў}}: $2.',
);
'ok' => 'Добра',
'pagetitle' => '$1 — {{SITENAME}}',
'retrievedfrom' => 'Атрымана з «$1»',
-'youhavenewmessages' => 'Ð\92Ñ\8b маеÑ\86е $1 ($2).',
+'youhavenewmessages' => 'Ð\92Ñ\8b аÑ\82Ñ\80Ñ\8bмалÑ\96 $1 ($2).',
'newmessageslink' => 'новыя паведамленьні',
'newmessagesdifflink' => 'апошняя зьмена',
+'youhavenewmessagesfromusers' => 'Вы атрымалі $1 ад {{PLURAL:$3|іншага ўдзельніка|$3 удзельнікаў}} ($2).',
+'youhavenewmessagesmanyusers' => 'Вы атрымалі $1 ад некалькіх удзельнікаў ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|новае паведамленьне|новыя паведамленьні}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|апошняя зьмена|апошнія зьмены}}',
'youhavenewmessagesmulti' => 'Вы атрымалі новыя паведамленьні на $1',
'editsection' => 'рэдагаваць',
'editold' => 'рэдагаваць',
'remembermypassword' => 'Запомніць мяне на гэтым кампутары (ня больш за $1 {{PLURAL:$1|дзень|дні|дзён}})',
'securelogin-stick-https' => 'Утрымліваць злучэньне праз HTTPS пасьля ўваходу ў сыстэму',
'yourdomainname' => 'Ваш дамэн:',
+'password-change-forbidden' => 'Вы ня можаце зьмяняць паролі ў гэтай вікі.',
'externaldberror' => 'Адбылася памылка аўтэнтыфікацыі з дапамогай вонкавай базы зьвестак, ці Вам не дазволена абнаўляць свой рахунак.',
'login' => 'Увайсьці',
'nav-login-createaccount' => 'Уваход / стварэньне рахунку',
альбо [{{fullurl:{{NAMESPACE}}:{{PAGENAME}}|action=edit}} рэдагаваць гэтую старонку]</span>.',
'noarticletext-nopermission' => 'Цяпер на гэтай старонцы тэкст адсутнічае.
Вы можаце [[Special:Search/{{PAGENAME}}|пашукаць назву гэтай старонкі]] на іншых старонках, альбо <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} пашукаць зьвязаныя запісы ў журналах]</span>.',
+'missing-revision' => 'Вэрсія старонкі №$1 з назвай «{{PAGENAME}}» не існуе.
+
+Звычайна гэта здараецца з-за перахода па састарэлай спасылцы на старонку, якая была выдаленая.
+Падрабязнасьці можна знайсьці ў [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленьняў].',
'userpage-userdoesnotexist' => 'Рахунак удзельніка «<nowiki>$1</nowiki>» не зарэгістраваны. Калі ласка, удакладніце, ці жадаеце Вы стварыць/рэдагаваць гэтую старонку.',
'userpage-userdoesnotexist-view' => 'Рахунак «$1» ня створаны.',
'blocked-notice-logextract' => 'Гэты ўдзельнік у дадзены момант заблякаваны.
'expansion-depth-exceeded-warning' => 'Старонка перавысіла дазволеную глыбіню ўключэньняў',
'parser-unstrip-loop-warning' => 'Вызначаная незачыненая пятля',
'parser-unstrip-recursion-limit' => 'Перавышанае абмежаваньне глыбіні рэкурсіі ($1)',
+'converter-manual-rule-error' => 'Знойдзеная памылка ў ручным правіле моўнага канвэртара',
# "Undo" feature
'undo-success' => 'Рэдагаваньне можа быць адмененае. Калі ласка, параўнайце адрозьненьні паміж вэрсіямі, каб упэўніцца, што гэта адпаведныя зьмены, а потым запішыце зьмены для сканчэньня рэдагаваньня.',
'editundo' => 'скасаваць',
'diff-multi' => '($1 {{PLURAL:$1|прамежная вэрсія|прамежныя вэрсіі|прамежных вэрсіяў}} $2 {{PLURAL:$2|удзельніка|удзельнікаў|удзельнікаў}} {{PLURAL:$1|не паказаная|не паказаныя|не паказаныя}})',
'diff-multi-manyusers' => '($1 {{PLURAL:$1|прамежная вэрсія|прамежныя вэрсіі|прамежных вэрсіяў}} $2 {{PLURAL:$2|удзельніка|удзельнікаў|удзельнікаў}} {{PLURAL:$1|не паказаная|не паказаныя|не паказаныя}})',
+'difference-missing-revision' => '{{PLURAL:$2|Адна вэрсія|$2 вэрсіі}} з гэтымі адрозьненьнямі ($1) {{PLURAL:$2|не была|не былі}} знойдзеныя.
+
+Звычайна гэта здараецца з-за перахода па састарэлай спасылцы на старонку, якая была выдаленая.
+Падрабязнасьці можна знайсьці ў [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленьняў].',
# Search results
'searchresults' => 'Вынікі пошуку',
'right-writeapi' => 'выкарыстаньне API для запісаў',
'right-delete' => 'выдаленьне старонак',
'right-bigdelete' => 'Выдаленьне старонак зь вялікімі гісторыямі',
+'right-deletelogentry' => 'Выдаленьне і аднаўленьне асобных запісаў журналу',
'right-deleterevision' => 'выдаленьне і аднаўленьне асобных вэрсіяў старонак',
'right-deletedhistory' => 'прагляд выдаленай гісторыі старонак без доступу да выдаленага тэксту',
'right-deletedtext' => 'прагляд выдаленага тэксту і зьменаў паміж выдаленымі вэрсіямі старонак',
'lockmanager-fail-releaselock' => 'Немагчыма зьняць блякаваньне для «$1».',
'lockmanager-fail-db-bucket' => 'Немагчыма скантактавацца з дастатковай колькасьцю базаў блякавньняў на ўчастку $1.',
'lockmanager-fail-db-release' => 'Немагчыма зьняць блякаваньні для базы зьвестак $1.',
+'lockmanager-fail-svr-acquire' => 'Немагчыма запытаць блякаваньні на сэрвэры $1.',
'lockmanager-fail-svr-release' => 'Немагчыма зьняць блякаваньні для сэрвэра $1.',
# ZipDirectoryReader
'rollback' => 'Адкаціць рэдагаваньні',
'rollback_short' => 'Адкат',
'rollbacklink' => 'адкат',
+'rollbacklinkcount' => 'адкаціць $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}',
+'rollbacklinkcount-morethan' => 'адкаціць больш за $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}',
'rollbackfailed' => 'Памылка адкату',
'cantrollback' => 'Немагчыма адкаціць зьмену; апошні рэдактар — адзіны аўтар гэтай старонкі.',
'alreadyrolled' => 'Немагчыма адкаціць апошнюю зьмену [[:$1]], якую {{GENDER:$2|зрабіў|зрабіла}} [[User:$2|$2]] ([[User talk:$2|гутаркі]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); нехта іншы ўжо зьмяніў старонку альбо адкаціў зьмены.
'duration-millennia' => '$1 {{PLURAL:$1|тысячагодзьдзе|тысячагодзьдзі|тысячагодзьдзяў}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Немагчыма запытаць блякаваньні на сэрвэры $1.',
+'api-error-filetype-banned-type' => '$1 — {{PLURAL:$4|забаронены тып файлаў|забароненыя тыпы файлаў}}. {{PLURAL:$3|Дазволены тып файлаў|Дазволеныя тыпы файлаў}}: $2.',
);
'tog-hidepatrolled' => 'Скриване на патрулираните редакции от списъка с последните промени',
'tog-newpageshidepatrolled' => 'Скриване на патрулираните редакции от списъка на новите страници',
'tog-extendwatchlist' => 'Разширяване на списъка, така че да показва всички промени, не само най-скорошните',
-'tog-usenewrc' => 'Ð\9fодобÑ\80Ñ\8fване на поÑ\81ледниÑ\82е пÑ\80омени (изисква Джаваскрипт)',
+'tog-usenewrc' => 'Ð\93Ñ\80Ñ\83пиÑ\80ане на поÑ\81ледниÑ\82е пÑ\80омени и Ñ\81пиÑ\81Ñ\8aка за наблÑ\8eдение по Ñ\81Ñ\82Ñ\80аниÑ\86и (изисква Джаваскрипт)',
'tog-numberheadings' => 'Номериране на заглавията',
'tog-showtoolbar' => 'Помощна лента за редактиране (изисква Джаваскрипт)',
'tog-editondblclick' => 'Редактиране при двойно щракване (изисква Джаваскрипт)',
'tog-showtoc' => 'Показване на съдържание (за страници с повече от три раздела)',
'tog-rememberpassword' => 'Запомяне на паролата ми в този браузър (за не повече от $1 {{PLURAL:$1|ден|дни}})',
'tog-watchcreations' => 'Добавяне на създадените от мен страници и качените от мен файлове към списъка ми за наблюдение',
-'tog-watchdefault' => 'Ð\94обавÑ\8fне на Ñ\80едакÑ\82иÑ\80аниÑ\82е оÑ\82 мен Ñ\81Ñ\82Ñ\80аниÑ\86и кÑ\8aм списъка ми за наблюдение',
+'tog-watchdefault' => 'Ð\94обавÑ\8fне на Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е, коиÑ\82о Ñ\80едакÑ\82иÑ\80ам, в списъка ми за наблюдение',
'tog-watchmoves' => 'Добавяне на преместените от мен страници към списъка ми за наблюдение',
'tog-watchdeletion' => 'Добавяне на изтритите от мен страници към списъка ми за наблюдение',
'tog-minordefault' => 'Отбелязване на всички промени като малки по подразбиране',
'edit-no-change' => 'Вашата редакция беше игнорирана, тъй като не съдържа промени по текста.',
'edit-already-exists' => 'Не можа да се създаде нова страница.
Такава вече съществува.',
+'defaultmessagetext' => 'Текст на съобщението по подразбиране',
# Parser/template warnings
'expensive-parserfunction-warning' => 'Внимание: Тази страница прекалено много пъти използва ресурсоемки парсерни функции.
# Diffs
'history-title' => 'Преглед на историята на „$1“',
'difference-title' => 'Разлика между версии на „$1“',
+'difference-title-multipage' => 'Разлики между страниците „$1“ и „$2“',
'difference-multipage' => '(Разлики между страниците)',
'lineno' => 'Ред $1:',
'compareselectedversions' => 'Сравнение на избраните версии',
'import-logentry-interwiki-detail' => '{{PLURAL:$1|една версия|$1 версии}} на $2 бяха внесени',
# JavaScriptTest
-'javascripttest-disabled' => 'Тази функция не е достъпна в това уики.',
+'javascripttest-disabled' => 'Тази функционалност не е активирана в това уики.',
'javascripttest-pagetext-noframework' => 'Тази страница е запазена за изпълнение на Джаваскрипт тестове.',
'javascripttest-qunit-intro' => 'Вижте [$1 тестовата документация] на mediawiki.org.',
'duration-centuries' => '$1 {{PLURAL:$1|век|века}}',
'duration-millennia' => '$1 {{PLURAL:$1|хилядолетие|хилядолетия}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 не {{PLURAL:$4|е позволен файлов формат|са позволени файлови формати}}. {{PLURAL:$3|Позволеният файлов формат е|Позволените файлови формати са}} $2.',
);
'youhavenewmessages' => 'আপনার $1 ($2) এসেছে৷',
'newmessageslink' => 'নতুন বার্তা',
'newmessagesdifflink' => 'সর্বশেষ পরিবর্তন',
+'youhavenewmessagesfromusers' => 'আপনি {{PLURAL:$3|অন্য ব্যবহারকারী|$3 ব্যবহারকারী}} ($2) থেকে $1পেয়েছেন।',
+'youhavenewmessagesmanyusers' => 'আপনি অনেক ব্যবহারকারী ($2) থেকে $1 পেয়েছেন।',
+'newmessageslinkplural' => '{{PLURAL:$1|একটি নতুন বার্তা|নতুন বার্তা}}',
+'newmessagesdifflinkplural' => '$1 {{PLURAL:$1|পরিবর্তন|পরিবর্তনসমূহ}}',
'youhavenewmessagesmulti' => 'আপনার $1টি নতুন বার্তা এসেছে',
'editsection' => 'সম্পাদনা',
'editold' => 'সম্পাদনা করুন',
'cannotdelete' => '"$1" পাতা বা ফাইলটি মোছা সম্ভব না।
সম্ভবত অন্য কেউ আগেই এটিকে মুছে ফেলেছেন।',
'cannotdelete-title' => '"$1" পৃষ্ঠা মুছে ফেলা যাচ্ছে না।',
+'delete-hook-aborted' => 'হূক দ্বারা সম্পাদনা পরিত্যক্ত হয়েছে।
+এর কোন ব্যাখ্যা নাই।',
'badtitle' => 'শিরোনামটি গ্রহনযোগ্য নয়।',
'badtitletext' => 'অনুরোধকৃত পাতার শিরোনামটি অবৈধ, খালি কিংবা কোন ভুল আন্তঃভাষা বা আন্তঃউইকি শিরোনাম সংযোগ ছিল। এটিতে সম্ভবত এমন এক (একাধিক) ক্যারেক্টার আছে, যা (যেগুলি) শিরোনামে ব্যবহারযোগ্য নয়।',
'perfcached' => 'নিচের উপাত্তগুলো ক্যাশ থেকে নেয়া এবং সম্পূর্ণ হালনাগাদকৃত না-ও হতে পারে। সর্বোচ্চ {{PLURAL:$1|একটি ফলাফল|$1 টি ফলাফল}} ক্যাশে থাকতে পারে।',
একজন প্রশাসক যিনি এটাকে লকড করেছেন তার যৌক্তিকতা দেওয়া হল: "$3"',
'invalidtitle-knownnamespace' => 'অবৈধ শিরনাম, যেখানে নামস্থান "$2" এবং লেখা হয়েছে "$3"',
'invalidtitle-unknownnamespace' => 'অবৈধ শিরনাম, যেখানে ব্যবহৃত হয়েছে অপরিচিত নামস্থান সংখ্যা $1 এবং লেখা হয়েছে "$2"',
+'exception-nologin' => 'লগইন করা হয়নি',
+'exception-nologin-text' => 'এই কাজটি করার জন্য উইকিতে লগইন করা প্রয়োজন।',
# Virus scanner
'virus-badscanner' => "ভুল কনফিগারেশন: অজ্ঞাত ভাইরাস স্কেনার: ''$1''",
'remembermypassword' => 'একাধিক সেশনের জন্য শব্দচাবি মনে রাখা হোক (সর্বোচ্চ $1 {{PLURAL:$1|দিনের|দিনের}} জন্য)',
'securelogin-stick-https' => 'লগইনের পর এইচটিটিপিএস-এর সাথে সংযোগকৃত থাকুন',
'yourdomainname' => 'আপনার ডোমেইন',
+'password-change-forbidden' => 'আপনি এই উইকিতে পাসওয়ার্ড পরিবর্তন করতে পারবেন না।',
'externaldberror' => 'হয় কোন বহিঃস্থ যাচাইকরণ ডাটাবেজ ত্রুটি ঘটেছে অথবা আপনার বহিঃস্থ অ্যাকাউন্ট হালনাগাদ করার অনুমতি নেই।',
'login' => 'প্রবেশ করুন',
'nav-login-createaccount' => 'প্রবেশ/নতুন অ্যাকাউন্ট',
'duration-centuries' => '$1 {{PLURAL:$1|শতাব্দী|শতাব্দী}}',
'duration-millennia' => '$1 {{PLURAL:$1|সহস্রাব্দ|সহস্রাব্দ}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ফাইল ধরনটি অনুমোদিত নয়|ফাইল ধরনগুলো অনুমোদিত নয়}}। অনুমোদিত {{PLURAL:$3|ফাইলের ধরন|ফাইলের ধরনগুলো}} হল $2।',
);
'nosuchsectiontext' => "Klasket hoc'h eus kemmañ ur rann n'eus ket anezhi.
Moarvat ez eo bet dilerc'hiet pe dilamet abaoe ma oa bet lennet ganeoc'h.",
'loginreqtitle' => 'Anv implijer rekis',
-'loginreqlink' => 'Kevreañ',
+'loginreqlink' => 'kevreañ',
'loginreqpagetext' => "Ret eo deoc'h $1 evit gwelet pajennoù all.",
'accmailtitle' => 'Ger-tremen kaset.',
'accmailtext' => "Kaset ez eus bet ur ger-tremen dargouezhek evit [[User talk:$1|$1]] da $2.
'lineno' => 'Linenn $1:',
'compareselectedversions' => 'Keñveriañ ar stummoù diuzet',
'showhideselectedversions' => 'Diskouez/Kuzhat ar stummoù diuzet',
-'editundo' => 'disteuler',
+'editundo' => 'dizober',
'diff-multi' => "({{PLURAL:$1|Ur reizhadenn da c'hortoz|$1 reizhadenn da c'hortoz}} gant {{PLURAL:$2|un implijer|$2 implijer}} kuzhet.)",
'diff-multi-manyusers' => "({{PLURAL:$1|Ur reizhadenn da c'hortoz|$1 reizhadenn da c'hortoz}} gant muioc'h eget $2 {{PLURAL:$2|implijer|implijer}} kuzhet.)",
'lockmanager-fail-releaselock' => 'Dibosupl leuskel ar prenn digor evit "$1".',
'lockmanager-fail-db-bucket' => "Dibosupl mont e darempred gant diazoù roadennoù a-walc'h evit ar c'helornad $1.",
'lockmanager-fail-db-release' => 'Dibosupl da leuskel ar prennoù digor war an diaz roadennoù $1.',
+'lockmanager-fail-svr-acquire' => 'Dibosupl eo bet tapout ar prennoù war ar servijer $1.',
'lockmanager-fail-svr-release' => 'Dibosupl da leuskel ar prennoù digor war ar servijer $1.',
# ZipDirectoryReader
# Special:ActiveUsers
'activeusers' => 'Roll an implijerien oberiant',
'activeusers-intro' => 'Setu aze ur roll eus an implijerien zo bet oberiant mui pe vui e-pad an $1 {{PLURAL:$1|deiz|deiz}} diwezhañ.',
-'activeusers-count' => '$1 {{PLURAL:$1|degasadenn}} abaoe an {{PLURAL:$3|deiz}} diwezhañ',
+'activeusers-count' => '$1 {{PLURAL:$1|degasadenn}} abaoe an {{PLURAL:$3|deiz|$3 deiz}} diwezhañ',
'activeusers-from' => 'Diskouez an implijerien adal :',
'activeusers-hidebots' => 'Kuzhat ar robotoù',
'activeusers-hidesysops' => 'Kuzhat ar verourien',
'duration-millennia' => '$1 {{PLURAL:$1|milvloaz|milvoaz}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Dibosupl eo bet tapout ar prennoù war ar servijer $1.',
+'api-error-filetype-banned-type' => "'''N'eo ket $1 {{PLURAL:$4|ur seurt restr aotreet|seurtoù restroù aotreet}}. $2 eo {{PLURAL:$3|ar seurt restroù|ar seurtoù restroù}} degemeret.",
);
'duration-seconds' => '$1 {{PLURAL:$1|sekunda|sekunde}}',
'duration-days' => '$1 {{PLURAL:$1|dan|dana}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 nije dopuštena vrsta datoteke. {{PLURAL:$3|Dopuštena vrsta datoteke je|Dopuštene vrste datoteka su}} $2.',
);
* @file
*
* @author Aleator
+ * @author Arnaugir
* @author Avm99963
* @author BroOk
* @author Cedric31
'index-category' => 'Pàgines indexades',
'noindex-category' => 'Pàgines no indexades',
'broken-file-category' => 'Pàgines amb enllaços a fitxer trencats',
+'categoryviewer-pagedlinks' => '($1) ($2)',
'about' => 'Quant a',
'article' => 'Pàgina de contingut',
'unprotect' => 'Desprotecció',
'unprotectthispage' => 'Desprotegeix aquesta pàgina',
'newpage' => 'Pàgina nova',
-'talkpage' => 'Discussió de la pàgina',
+'talkpage' => 'Discussió',
'talkpagelinktext' => 'Discussió',
'specialpage' => 'Pàgina especial',
'personaltools' => "Eines de l'usuari",
'youhavenewmessages' => 'Tens $1 ($2).',
'newmessageslink' => 'nous missatges',
'newmessagesdifflink' => 'últims canvis',
+'youhavenewmessagesfromusers' => "Tens $1 {{PLURAL:$3|d'un altre usuari|de $3 usuaris}} ($2).",
+'youhavenewmessagesmanyusers' => 'Tens $1 de molts usuaris ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un nou missatge|nous missatges}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|darrer canvi|darrers canvis}}',
'youhavenewmessagesmulti' => 'Teniu nous missatges a $1',
'editsection' => 'modifica',
'editold' => 'modifica',
'remembermypassword' => 'Recorda la contrasenya entre sessions (per un màxim de $1 {{PLURAL:$1|dia|dies}})',
'securelogin-stick-https' => "Roman connectat via HTTPS desprès d'autenticar-se",
'yourdomainname' => 'El vostre domini',
+'password-change-forbidden' => 'No podeu canviar les contrasenyes en aquest wiki.',
'externaldberror' => "Hi ha hagut una fallida en el servidor d'autenticació externa de la base de dades i no teniu permís per a actualitzar el vostre compte d'accès extern.",
'login' => 'Inici de sessió',
'nav-login-createaccount' => 'Inicia una sessió / crea un compte',
o [{{fullurl:{{FULLPAGENAME}}|action=edit}} crear-la ara]</span>.',
'noarticletext-nopermission' => 'Actualment no hi ha text en aquesta pàgina.
Podeu [[Special:Search/{{PAGENAME}}|cercar aquest títol]] en altres pàgines o bé <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cercar en els registres relacionats]</span>.',
+'missing-revision' => 'La revisió # $1 de la pàgina anomenada "{{PAGENAME}}" no existeix.
+
+Això és normalment provocat per després d\'un enllaç d\'història antiquat a una pàgina que s\'ha suprimit.
+Detalls es poden trobar en el [{{fullurl: {{# especial: registre}} / delete|page = {{FULLPAGENAMEE}}}} registre de supressió].',
'userpage-userdoesnotexist' => "Atenció: El compte d'usuari «<nowiki>$1</nowiki>» no està registrat. En principi no hauríeu de crear ni editar aquesta pàgina.",
'userpage-userdoesnotexist-view' => 'El compte d\'usuari "$1" no està registrat.',
'blocked-notice-logextract' => "En aquests moments aquest compte d'usuari es troba blocat.
'expansion-depth-exceeded-warning' => "La pàgina ha excedit la profunditat d'expansió",
'parser-unstrip-loop-warning' => "S'ha detectat un bucle no desmuntable",
'parser-unstrip-recursion-limit' => "S'ha excedit el límit ($1) de recursivitat no desmuntable",
+'converter-manual-rule-error' => 'Error detectat a la norma de conversió de llengua manual',
# "Undo" feature
'undo-success' => "Pot desfer-se la modificació. Si us plau, reviseu la comparació de sota per a assegurar-vos que és el que voleu fer; llavors deseu els canvis per a finalitzar la desfeta de l'edició.",
'editundo' => 'desfés',
'diff-multi' => '({{PLURAL:$1|Hi ha una revisió intermèdia |Hi ha $1 revisions intermèdies}} sense mostrar fetes per {{PLURAL:$2|un usuari|$2 usuaris}})',
'diff-multi-manyusers' => "({{PLURAL:$1|Hi ha una revisió intermèdia|Hi ha $1 revisions intermèdies}} sense mostrar fetes per més {{PLURAL:$2|d'un usuari|de $2 usuaris}})",
+'difference-missing-revision' => "{{PLURAL:$2|Una revisió|$2 revisions}} d'aquesta diferència ($1) no {{PLURAL:$2|s'ha|s'han}} trobat.
+
+Això passa generalment en seguir un enllaç obsolet de diferències a una pàgina que ha estat esborrada.
+Es pot trobar més informació en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registre de supressions].",
# Search results
'searchresults' => 'Resultats de la cerca',
'right-writeapi' => "Fer servir l'escriptura a l'API",
'right-delete' => 'Esborrar pàgines',
'right-bigdelete' => 'Esborrar pàgines amb historials grans',
+'right-deletelogentry' => "Suprimeix o desfés la supressió d'entrades de registre específiques",
'right-deleterevision' => 'Esborrar i restaurar versions específiques de pàgines',
'right-deletedhistory' => 'Veure els historials esborrats sense consultar-ne el text',
'right-deletedtext' => 'Veure el text esborrat i els canvis entre revisions esborrades',
'lockmanager-fail-releaselock' => "No s'ha pogut alliberar el bloqueig de «$1».",
'lockmanager-fail-db-bucket' => "No s'han pogut contactar un nombre suficient de bases de bloqueig en el cubell $1.",
'lockmanager-fail-db-release' => "No s'han pogut alliberar els bloquejos a la base de dades $1.",
+'lockmanager-fail-svr-acquire' => "No s'han pogut aconseguir els bloquejos al servidor $1.",
'lockmanager-fail-svr-release' => "No s'han pogut alliberar els bloquejos al servidor $1.",
# ZipDirectoryReader
'disambiguations' => 'Pàgines que enllacen a pàgines de desambiguació',
'disambiguationspage' => 'Template:Desambiguació',
-'disambiguations-text' => "Les següents pàgines enllacen a una '''pàgina de desambiguació'''.
-Per això, caldria que enllacessin al tema apropiat.<br />
-Una pàgina es tracta com de desambiguació si utilitza una plantilla que està enllaçada a [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Les següents pàgines tenen algun enllaç a una '''pàgina de desambiguació'''.
+És possible que hagin d'enllaçar a una altra pàgina més apropiada.<br />
+Una pàgina es tracta com de desambiguació si utilitza una plantilla que està enllaçada a [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redireccions dobles',
'doubleredirectstext' => 'Aquesta pàgina llista les pàgines que redirigeixen a altres pàgines de redirecció.
'rollback' => 'Reverteix edicions',
'rollback_short' => 'Revoca',
'rollbacklink' => 'Reverteix',
+'rollbacklinkcount' => 'reverteix $1 {{PLURAL:$1|edició|edicions}}',
+'rollbacklinkcount-morethan' => 'reverteix més de $1 {{PLURAL:$1|edició|edicions}}',
'rollbackfailed' => "No s'ha pogut revocar",
'cantrollback' => "No s'ha pogut revertir les edicions; el darrer coŀlaborador és l'únic autor de la pàgina.",
'alreadyrolled' => "No es pot revertir la darrera modificació de [[:$1]]
Això significa que si reanomeneu per equivocació una pàgina amb el seu nom anterior no ho podreu fer, ja que no es pot sobreescriure una pàgina existent.
-'''Avís:''' Això pot ser un canvi dràstic i inesperat per una pàgina popular; si us plau, assegureu-vos que sabeu el que féu abans de continuar.",
+'''Avís:''' Això pot ser un canvi dràstic i inesperat per una pàgina popular; si us plau, assegureu-vos que sabeu el que feu abans de continuar.",
'movepagetalktext' => "La pàgina de discussió associada, si existeix, serà traslladada automàticament '''tret dels següents casos''':
* Ja hi existeix una pàgina de discussió no buida amb el nou nom, o si
* la opció de davall es troba desactivada
'duration-millennia' => '$1 {{PLURAL:$1|mil·leni|mil·lenis}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => "No s'han pogut aconseguir els bloquejos al servidor $1.",
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|no és un tipus de fitxer permès|no són tipus de fitxer permesos}}. {{PLURAL:$3|El tipus de fitxer permès és|Els tipus de fitxer permesos són}} $2.',
);
* @author Aras Noori
* @author Arastein
* @author Asoxor
+ * @author Calak
* @author Cyrus abdi
+ * @author Diyar se
* @author Haval
* @author Marmzok
* @author رزگار
# User preference toggles
'tog-underline' => 'ھێڵ ھێنان بەژێر بەستەرەکان:',
'tog-justify' => 'پەرەگرافەکان پڕاوپر نیشان بدە',
-'tog-hideminor' => 'دەستکارییە بچوکەکان بشارەوە لە دوا گۆڕانکارییەکاندا',
+'tog-hideminor' => 'دەستکارییە بچووکەکان بشارەوە لە دوایین گۆڕانکارییەکاندا',
'tog-hidepatrolled' => 'لە دوایین گۆڕانکاریەکان، دەستکاریە پارێزراوەکان داشارە',
'tog-newpageshidepatrolled' => 'لە لیستی لاپەڕە نوێکان، لاپەڕە پارێزراوەکان داشارە',
'tog-extendwatchlist' => 'لیستی چاودێری درێژبکەرەوە بۆ نیشان دانی ھەموو گۆڕانکارییەکان، نەک تەنھا دوایینەکان.',
-'tog-usenewrc' => 'دوا گۆڕانکارییە پەرە پێدراوەکان بەکار ببە (پێویستی بە جاڤاسکریپتە)',
+'tog-usenewrc' => 'گۆڕانکارییەکان لە دوایین گۆڕانکارییەکان و لیستی چاودێریدا بە پێی پەڕە پۆلێن بکە (پێویستی بە جاڤاسکریپتە)',
'tog-numberheadings' => 'ژمارەکردنی خۆکاری سەردێڕەکان',
'tog-showtoolbar' => 'شریتی ئامرازەکانی دەستکاری نیشان بدە (JavaScript پێویستە)',
'tog-editondblclick' => 'دەستکاریی پەڕە بە دووکلیک لەسەر دەق (JavaScript پێویستە)',
'tog-editsectiononrightclick' => 'ڕێگە بدە بۆ دەستکاری کردنی بەشەکان لە ڕێگەی کلیکی ڕاست کردن لەسەر سەردێڕی بەشەکان (JavaScript پێویستە)',
'tog-showtoc' => 'پێرستی ناوەرۆک نیشان بدە (بۆ ئەو پەڕانە کە زیاتر لە ٣ سەردێڕیان تێدایە)',
'tog-rememberpassword' => 'چوونە ژوورەوەم لەسەر ئەم وێبگەڕە پاشەکەوت بکە (ئەو پەڕی $1 {{PLURAL:$1|ڕۆژ|ڕۆژ}}ە)',
-'tog-watchcreations' => 'ئەو پەڕانە کە من دروستم کردوون زیاد بکە بە لیستی چاودێڕییەکەم',
-'tog-watchdefault' => 'ئەو پەڕانە کە من دەستکاریم کردوون زیاد بکە بە لیستی چاودێڕییەکەم',
-'tog-watchmoves' => 'ئەو پەڕانە کە من گواستومنەتەوە زیاد بکە بە لیستی چاودێڕییەکەم',
-'tog-watchdeletion' => 'ئەو پەڕانە کە من سڕیومنەتەوە زیاد بکە بە لیستی چاودێڕییەکەم',
+'tog-watchcreations' => 'ئەو پەڕانەی من دروستم کردوون و ئەو پەڕگانە من بارم کردوون زیاد بکە بە لیستی چاودێڕییەکەم',
+'tog-watchdefault' => 'ئەو پەڕانە و ئەو پەڕگانە من دەستکاریان دەکەم زیاد بکە بە لیستی چاودێڕییەکەم',
+'tog-watchmoves' => 'ئەو پەڕانە و ئەو پەڕگانە کە من گواستومنەتەوە زیاد بکە بە لیستی چاودێڕییەکەم',
+'tog-watchdeletion' => 'ئەو پەڕانە و ئەو پەڕگانە من سڕیومنەتەوە زیاد بکە بە لیستی چاودێڕییەکەم',
'tog-minordefault' => 'ھەموو دەستکارییەکان بە ورد نیشان بکە لە حاڵەتی دیفاڵت',
'tog-previewontop' => 'پێشبینین بەرلە چوارچێوەی دەستکاری نیشان بدە',
'tog-previewonfirst' => 'لە یەکەم دەستکاری دا پێشبینین نیشان بدە',
'tog-nocache' => 'کاشکردنی پەڕەکانی وێبگەڕەکە لەکاربخە',
-'tog-enotifwatchlistpages' => 'ئÛ\95Ú¯Û\95ر Ù¾Û\95Ú\95Û\95Û\8cÛ\95Ú©Û\8c Ù\84Û\95 Ù\84Û\8cستÛ\8c Ú\86اÙ\88دÛ\8eÚ\95Û\8cÛ\8cÛ\95کاÙ\86Ù\85 Ú¯Û\86Ú\95درا ئÛ\8cÙ\85Û\95Û\8cÙ\84Ù\85 بÛ\86 بÙ\86Û\8eرÛ\95',
+'tog-enotifwatchlistpages' => 'ئÛ\8cÙ\85Û\95Û\8cÙ\84Ù\85 بÛ\86 بÙ\86Û\8eرÛ\95 کاتÛ\8eÚ© Ù¾Û\95Ú\95Û\95Û\8cÛ\95Ú© Û\8cاÙ\86 Ù¾Û\95Ú\95Ú¯Û\95Û\8cÛ\95Ú© Ù\84Û\95 Ù\84Û\8cستÛ\8c Ú\86اÙ\88دÛ\8eÚ\95Û\8cÛ\8cÛ\95کاÙ\86Ù\85دا Ú¯Û\86Ú\95درا',
'tog-enotifusertalkpages' => 'ئەگەر پەڕەی وتووێژەکەم گۆڕدرا ئیمەیلم بۆ بنێرە',
-'tog-enotifminoredits' => 'بۆ گۆڕانکارییە بچووکەکانی پەڕەکانیش ئیمەیلم بۆ بنێرە',
+'tog-enotifminoredits' => 'بۆ گۆڕانکارییە بچووکەکانی پەڕەکان و پەڕگەکانیش ئیمەیلم بۆ بنێرە',
'tog-enotifrevealaddr' => 'ئەدرەسی ئیمەیلەکەم لە ئیمەیلە ئاگاداریدەرەکان دا نیشان بدە',
'tog-shownumberswatching' => 'ژمارەی بەکارھێنەرە چاودێڕەکان نیشان بدە',
'tog-oldsig' => 'واژۆی ئێستا:',
'category-file-count' => '{{PLURAL:$2|ئەم هاوپۆلە تەنها ئەم پەڕگەی لەخۆ گرتووە.|ئەم {{PLURAL:$1|پەڕگەیە}} کە بەشێکە لە هەموو $2پەڕگەی ئەم هاوپۆلە دەیبینی.}}',
'category-file-count-limited' => 'ئەم {{PLURAL:$1|پەڕگە|پەڕگانە}} لەم هاوپۆلەدایە.',
'listingcontinuesabbrev' => '(درێژە)',
+'index-category' => 'پەڕە پێرستەکراوەکان',
'noindex-category' => 'پەڕە پێرستنەکراوەکان',
'about' => 'سەبارەت',
'vector-action-undelete' => 'سڕینەوە بگەڕێنەوە',
'vector-action-unprotect' => 'پاراستنی بگۆڕە',
'vector-simplesearch-preference' => 'ڕێگە بدە بە پێشنیارەکانی گەڕانی پێشکەوتوو (تەنیا بۆ پێستەی ڤێکتۆر)',
-'vector-view-create' => 'درووستکردن',
+'vector-view-create' => 'دروستکردن',
'vector-view-edit' => 'دەستکاریی بکە',
'vector-view-history' => 'مێژووەکەی ببینە',
'vector-view-view' => 'بیخوێنەوە',
'userexists' => 'ئەو ناوەی تۆ داوتە پێشتر بەکارھێنراوە.
ناوێکی دیکە ھەڵبژێرە.',
'loginerror' => 'ھەڵەی چوونەژوورەوە',
-'nocookiesnew' => 'هەژماری بەکارهێنەر درووستکرا، بەڵام بە سەرکەوتوویی نەچوویتەوە ژوورەوە.
-{{SITENAME}} بÛ\86 Ú\86Ù\88Ù\88Ù\86Û\95Ù\88Û\95 Ú\98Ù\88Ù\88رÛ\8c بÛ\95کارÙ\87Û\8eÙ\86Û\95ر Ù\84Û\95 Ø´Û\95کرÛ\86Ú©Û\95 Ú©Û\95ÚµÚ© Ù\88Û\95ر دەگرێت.
-تÛ\86 بÛ\95کارâ\80\8cÙ\87Û\8eÙ\86اÙ\86Û\8c Ø´Û\95کرÛ\86Ú©Û\95ت Ù\84Û\95کارخستە.
-تکایە شەکرۆکە کارا بکە و بە ناو و وشەی تێپەڕبوونی بەکارهێنەر بچۆ ژوورەوە.',
+'nocookiesnew' => 'ھەژماری بەکارھێنەر دروستکرا، بەڵام بە سەرکەوتوویی نەچوویتەوە ژوورەوە.
+{{SITENAME}} بÛ\86 Ú\86Ù\88Ù\88Ù\86Û\95Ù\88Û\95 Ú\98Ù\88Ù\88رÛ\95Ù\88Û\95Û\8c بÛ\95کارھÛ\8eÙ\86Û\95ر Ù\84Û\95 Ø´Û\95کرÛ\86Ú©Û\95 Ú©Û\95ÚµÚ© Ù\88Û\95ردەگرێت.
+تÛ\86 Ø´Û\95کرÛ\86Ú©Û\95Ú©Û\95ت Ù\84Û\95کارخستÙ\88Ù\88ە.
+تکایە شەکرۆکەکە کارا بکە و پاشان بە ناوی بەکارھێنەر و تێپەڕوشە بچۆ ژوورەوە.',
'nocookieslogin' => '{{SITENAME}} بۆ چوونەژوورەوە لە کووکییەکان کەڵک وەرئەگرێت.
ڕێگەت نەداوە بە کووکییەکان.
ڕێگەیان پێ بدەو و دیسان تێبکۆشە.',
'''هێشتا پاشەکەوت نەبووه !'''",
'sitecsspreview' => "'''لهیادت بێ که ئێسته تهنها پێشبینینی ئەم CSS دهکهی.'''
'''هێشتا پاشهکهوت نهکراوە !'''",
-'sitejspreview' => "'''لهیادت بێ که ئێسته تهنها پێشبینینی ئەم کۆدی جاڤاسکریپتە دهکهی.'''
-'''هێشتا پاشهکهوت نهکراوە !'''",
+'sitejspreview' => "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینی ئەم کۆدەی جاڤاسکریپتە.'''
+'''گۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!'''",
'userinvalidcssjstitle' => "'''ئاگادارکردنەوە:''' پێست نیە بۆ \"\$1\".
لەیادت بێ کە لاپەڕەکانی .css و .js لە بابەت بە پیتی بچووک کەڵک وەر ئەگرن. وەک {{ns:user}}:Foo/vector.css نە وەک {{ns:user}}:Foo/Vector.css .",
'updated' => '(نوێکراوە)',
'note' => "'''تێبینی:'''",
-'previewnote' => "'''لە بیرت بێت کە ئەمە تەنھا پێشبینینە.'''
+'previewnote' => "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینە.'''
گۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!",
'previewconflict' => 'ئەم پێشبینینە بە تۆ نیشان ئەدات ئەو دەقەی لە شوێنی دەستکاری سەرەوە داتناوە چۆن بەرچاو ئەکەوێت ئەگەر پاشەکەوتی بکەیت.',
'session_fail_preview' => "'''ببوورە! ناتوانین دەستکارییەکەت پێواژۆ بکەین بە ھۆی لەدەستدانی session data.'''
تکایە چاو لەو هەڵسەنگاندنەی خوارەوە بکە تا دڵنیا بیت ئەمە ئەوەیە کە دەتویست بیکەی و دواتر گۆڕانکارییەکانی خوارەوە پاشەکەوت بکە بۆ تەواوکردنی پووچەڵکردنەوەکە.',
'undo-failure' => 'لەبەر کێشەی دەستتێوەردان، ناتوانی دەستکاریەکە ئەنجامنەدراو بکەیت.',
'undo-norev' => 'ناتوانی دەستکاریەکە ئەنجامنەدراو بکەی لەبەر ئەوەی بوونی نیە یا سڕدراوەتەوە.',
-'undo-summary' => 'پووچەڵکرنەوەی پیاچوونەوەی $1 بەدەستی [[Special:Contributions/$2|$2]] ([[User talk:$2|وتووێژ]])',
+'undo-summary' => 'گەڕاندنەوەی پێداچوونەوەی $1 لە لایەن [[Special:Contributions/$2|$2]] ([[User talk:$2|وتووێژ]])',
# Account creation failure
'cantcreateaccounttitle' => 'ناتوانرێت هەژمار دروست بکرێت',
'lineno' => 'ھێڵی $1:',
'compareselectedversions' => 'پیاچوونەوە ھەڵبژێردراوەکان ھەڵسەنگێنە',
'showhideselectedversions' => 'پیاچوونەوە ھەڵبژێردراوەکان نیشانبدە/بشارەوە',
-'editundo' => 'پووچەڵکردنەوە',
+'editundo' => 'گەڕاندنەوە',
'diff-multi' => '({{PLURAL:$1|پیاچوونەوەیەکی نێوانی|$1 پیاچوونەوەی نێوانی}}ی {{PLURAL:$2|بەکارھێنەرێک|$2 بەکارھێنەر}} نیشان نەدراوە)',
# Search results
'recentchanges-legend' => 'ھەڵبژاردەکانی دوایین گۆڕانکارییەکان',
'recentchanges-summary' => 'لەم پەڕەدا بە دوای دوایین گۆڕانکارییەکان لەم ویکیەدا بکەوە.',
'recentchanges-feed-description' => 'دوای دوایین گۆڕانکارییەکانی ئەم ویکیە بکەوە لەم «فید»ەوە.',
-'recentchanges-label-newpage' => 'ئÛ\95Ù\85 دÛ\95ستکارÛ\8cÛ\95 Ù\84اپÛ\95Ú\95Û\95Û\8cÛ\95Ú©Û\8c Ù\86Ù\88Û\8eÛ\8c درÙ\88Ù\88ستâ\80\8cکرد',
+'recentchanges-label-newpage' => 'ئÛ\95Ù\85 دÛ\95ستکارÛ\8cÛ\8cÛ\95 Ù\84اپÛ\95Ú\95Û\95Û\8cÛ\95Ú©Û\8c Ù\86Ù\88Û\8eÛ\8c درÙ\88ستکرد',
'recentchanges-label-minor' => 'ئەمە دەستکاریەکی بچووکە',
'recentchanges-label-bot' => 'ئەم دەستکاریە لە لایەن بۆتەوە پێک هاتووە',
'recentchanges-label-unpatrolled' => 'ئەم دەستکاریە هێشتا نەڕۆشتەتە ژێر چاودێری',
'backend-fail-delete' => 'نەکرا پەڕگەی $1 بسڕدرێتەوە.',
'backend-fail-copy' => 'نەکرا پەڕگەی $1 کۆپی بکرێت بۆ $2.',
'backend-fail-move' => 'نەکرا پەڕگەی $1 بگوازرێتەوە بۆ $2.',
-'backend-fail-create' => 'Ù\86Û\95کرا Ù¾Û\95Ú\95Ú¯Û\95Û\8c $1 درÙ\88ستبکرÛ\8eت.',
+'backend-fail-create' => 'Ù\86Û\95کرا Ù¾Û\95Ú\95Ú¯Û\95Û\8c $1 بÙ\86Ù\88Ù\88سرÛ\8eت',
# img_auth script messages
'img-auth-accessdenied' => 'تێپهربوون رهتکرایهوه',
[[Special:WantedCategories|پۆلە خوازراوەکان]]یش ببینە.',
'categoriesfrom' => 'نیشاندانی پۆلەکان بە دستپێکردن لە:',
'special-categories-sort-count' => 'ڕیز کردن بە پێی ژمارە',
-'special-categories-sort-abc' => 'ڕیز کردن بە پێی ئەلفابێت',
+'special-categories-sort-abc' => 'ڕیزکردن بە پێی ئەلفوبێ',
# Special:DeletedContributions
'deletedcontributions' => 'بەشدارییە سڕاوەکان',
'activeusers-noresult' => 'هیچ بەکارهێنەرێک نەدۆزرایەوە',
# Special:Log/newusers
-'newuserlogpage' => 'لۆگی دروست کردنی بەکارھێنەر',
-'newuserlogpagetext' => 'ئەمە لۆگێکی درووستکردنی بەکارهێنەرە.',
+'newuserlogpage' => 'لۆگی دروستکردنی بەکارھێنەر',
+'newuserlogpagetext' => 'ئەمە لۆگێکی دروستکردنی بەکارھێنەرە.',
# Special:ListGroupRights
'listgrouprights' => 'مافەکانی گرووپە بەکارھێنەرییەکان',
'skinname-monobook' => 'مۆنۆ',
'skinname-myskin' => 'پێستی خۆم',
'skinname-chick' => 'جووچک',
-'skinname-simple' => 'ساده',
+'skinname-simple' => 'ساکار',
'skinname-modern' => 'مۆدێڕن',
'skinname-vector' => 'ڤێکتۆر',
'exif-gpsspeed-k' => 'کیلۆمەتر هەر کاتژمێر',
'exif-gpsspeed-m' => 'مایل هەر کاتژمێر',
+'exif-iimcategory-hth' => 'تەندروستی',
+'exif-iimcategory-sci' => 'زانست و تەکنۆلۆژیا',
+'exif-iimcategory-soi' => 'بابەتە کۆمەڵایەتییەکان',
+'exif-iimcategory-spo' => 'وەرزشەکان',
+
+'exif-urgency-normal' => 'ئاسایی ($1)',
+
# External editor support
'edit-externally' => 'دەستکاریی ئەم پەڕەیە بکە بە بەکارھێنانی پڕۆگرامێکی دەرەکی',
'edit-externally-help' => '(بۆ زانیاریی زیاتر سەیری [//www.mediawiki.org/wiki/Manual:External_editors ڕێنماییەکانی دامەزراندن] بکە)',
# Core parser functions
'unknown_extension_tag' => 'تاگی درێژکراوەی نەناسراو "$1"',
+'duplicate-defaultsort' => "'''ئاگاداری''' کلیلی پۆلێنکردنی \"\$2'' چووەتە شوێنی کلیلی پۆلێنکردنی \"\$1\"",
# Special:Version
'version' => 'وەشان',
'duration-centuries' => '$1 {{PLURAL:$1|سەدە|سەدە}}',
'duration-millennia' => '$1 {{PLURAL:$1|ھەزارە|ھەزارە}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|جۆرە پەڕگەی ڕێگە پێنەدراوە|جۆرە پەڕگە ڕێگە پێنەدراوەکانن}}. $2، ئەو جۆرە {{PLURAL:$3|پەڕگەیە کە ڕێگەی|پەڕگانەیە کە ڕێگەیان}} پێدراوە.',
);
* @ingroup Language
* @file
*
+ * @author Chmee2
* @author Danny B.
* @author Dontlietome7
* @author Helix84
* @author Tchoř
* @author Urhixidur
* @author Utar
+ * @author Vks
* @author לערי ריינהארט
*/
'youhavenewmessages' => 'Máte $1 ($2).',
'newmessageslink' => 'nové zprávy',
'newmessagesdifflink' => 'rozdíl oproti předchozí verzi',
+'youhavenewmessagesfromusers' => 'Máte $1 od {{PLURAL:$3|jiného uživatele|$3 jiných uživatelů}} ($2).',
+'youhavenewmessagesmanyusers' => 'Máte $1 od mnoha dalších uživatelů ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|novou zprávu|nové zprávy}}',
+'newmessagesdifflinkplural' => 'poslední {{PLURAL:$1|změna|změny}}',
'youhavenewmessagesmulti' => 'Na $1 máte nové zprávy',
'editsection' => 'editovat',
'editold' => 'editovat',
'remembermypassword' => 'Zapamatovat si mé přihlášení na tomto počítači (maximálně $1 {{PLURAL:$1|den|dny|dní}})',
'securelogin-stick-https' => 'Zůstat po přihlášení připojen přes HTTPS',
'yourdomainname' => 'Vaše doména',
+'password-change-forbidden' => 'Na této wiki nemůžete měnit hesla.',
'externaldberror' => 'Buď nastala chyba externí autentizační databáze, nebo nemáte dovoleno měnit svůj externí účet.',
'login' => 'Přihlaste se',
'nav-login-createaccount' => 'Přihlášení / vytvoření účtu',
Můžete [[Special:Search/{{PAGENAME}}|zkusit tento název vyhledat]] na jiných stránkách, <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} prohlédnout si příslušné protokolovací záznamy] nebo [{{fullurl:{{FULLPAGENAME}}|action=edit}} tuto stránku založit]</span>.',
'noarticletext-nopermission' => 'Tato stránka zatím neobsahuje žádný text.
Můžete [[Special:Search/{{PAGENAME}}|zkusit tento název vyhledat]] na jiných stránkách nebo <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} si prohlédnout příslušné protokolovací záznamy]</span>.',
+'missing-revision' => 'Revize #$1 stránky s názvem „{{PAGENAME}}“ neexistuje.
+
+Toto je obvykle způsobeno tím, že jste následovali zastaralý odkaz historickou verzi stránky, jež byla smazána.
+Podrobnosti mohou být uvedeny v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} knize smazaných stránek].',
'userpage-userdoesnotexist' => 'Uživatelský účet „<nowiki>$1</nowiki>“ není zaregistrován. Zkontrolujte zda skutečně chcete vytvořit či editovat tuto stránku.',
'userpage-userdoesnotexist-view' => 'Uživatelský účet „$1“ není zaregistrován.',
'blocked-notice-logextract' => 'Tento uživatel je momentálně zablokován.
'expansion-depth-exceeded-warning' => 'Stránka překročila hloubku expanze',
'parser-unstrip-loop-warning' => 'Detekováno zacyklení unstrip',
'parser-unstrip-recursion-limit' => 'Překročen limit rekurze unstrip ($1)',
+'converter-manual-rule-error' => 'Detekována chyba v pravidlech manuální jazykové konverze',
# "Undo" feature
'undo-success' => 'Editace může být zrušena. Zkontrolujte a pak potvrďte změny zobrazené níže.',
'editundo' => 'zrušit editaci',
'diff-multi' => '({{PLURAL:$1|Není zobrazena 1 mezilehlá verze|Nejsou zobrazeny $1 mezilehlé verze|Není zobrazeno $1 mezilehlých verzí}} od {{PLURAL:$2|1 uživatele|$2 uživatelů}}.)',
'diff-multi-manyusers' => '(Není zobrazeno $1 mezilehlých verzí od více než $2 {{PLURAL:$2|uživatele|uživatelů}}.)',
+'difference-missing-revision' => '{{PLURAL:$2|Jedna z revizí|$2 revize|$2 revizí}} k požadovanému porovnání ($1) {{PLURAL:$2|neexistuje|neexistují|neexistuje}}.
+
+Toto je obvykle způsobeno tím, že jste následovali zastaralý odkaz historickou verzi stránky, jež byla smazána.
+Podrobnosti mohou být uvedeny v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} knize smazaných stránek].',
# Search results
'searchresults' => 'Výsledky hledání',
'disambiguations' => 'Stránky odkazující na rozcestníky',
'disambiguationspage' => 'Template:Rozcestník',
-'disambiguations-text' => 'Odkazy na následujících stránkách vedou na rozcestníky (stránky obsahující některou ze šablon uvedených na [[MediaWiki:Disambiguationspage|seznamu rozcestníkových šablon]]) místo na příslušný článek.',
+'disambiguations-text' => "Následující stránky obsahují nejméně jeden odkaz na '''rozcestník'''.
+Asi by místo toho měly odkazovat na konkrétnější stránku.<br />
+Stránka je považována za rozcestník, pokud používá některou ze šablon odkazovaných na [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dvojitá přesměrování',
'doubleredirectstext' => 'Na této stránce je seznam přesměrování vedoucích na další přesměrování.
'rollback' => 'Vrátit zpět editace',
'rollback_short' => 'Vrátit zpět',
'rollbacklink' => 'vrácení zpět',
+'rollbacklinkcount' => 'vrácení $1 {{PLURAL:$1|editace|editací}} zpět',
+'rollbacklinkcount-morethan' => 'vrácení více než $1 {{PLURAL:$1|editace|editací}} zpět',
'rollbackfailed' => 'Nešlo vrátit zpět',
'cantrollback' => 'Nelze vrátit zpět poslední editaci, neboť poslední přispěvatel je jediným autorem této stránky.',
'alreadyrolled' => 'Nelze vrátit zpět poslední editaci [[:$1]] od uživatele [[User:$2|$2]] ([[User talk:$2|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), protože někdo jiný již stránku editoval nebo vrátil tuto změnu zpět.
* <span class="mw-specialpagecached">Speciální stránky z cache (mohou být zastaralé)</span>',
'specialpages-group-maintenance' => 'Údržba',
'specialpages-group-other' => 'Ostatní',
-'specialpages-group-login' => 'Přihlašování / registrace',
+'specialpages-group-login' => 'Přihlášení / vytvoření účtu',
'specialpages-group-changes' => 'Poslední změny a záznamy',
'specialpages-group-media' => 'Média',
'specialpages-group-users' => 'Uživatelé a skupiny',
'duration-centuries' => '$1 {{PLURAL:$1|století}}',
'duration-millennia' => '$1 {{PLURAL:$1|tisíciletí}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|je nedovolený formát souborů|jsou nedovolené formáty souborů}}. {{PLURAL:$3|Povolený formát souborů je|Povolené formáty souborů jsou}} $2.',
);
'statistics-articles' => 'истиньнꙑ члѣни',
'statistics-pages' => 'страницѧ',
'statistics-files' => 'положєнꙑ дѣла',
+'statistics-users-active' => 'дѣꙗтєльнꙑ польꙃєватєлє',
'disambiguations' => 'страницѧ ижє съвѧꙁи съ мъногосъмꙑслиꙗ имѫтъ',
'disambiguationspage' => 'Template:мъногосъмꙑслиѥ',
'tog-editsectiononrightclick' => 'Galluogi golygu adran drwy dde-glicio ar bennawd yr adran (JavaScript)',
'tog-showtoc' => 'Dangos y daflen gynnwys (ar gyfer tudalennau sydd â mwy na 3 pennawd)',
'tog-rememberpassword' => "Y porwr hwn i gofio'r manylion mewngofnodi (hyd at $1 {{PLURAL:$1||diwrnod|ddiwrnod|diwrnod|diwrnod|diwrnod}})",
-'tog-watchcreations' => 'Ychwanegu tudalennau at fy rhestr wylio wrth i mi eu creu',
-'tog-watchdefault' => 'Ychwanegu tudalennau at fy rhestr wylio wrth i mi eu golygu',
-'tog-watchmoves' => 'Ychwanegu tudalennau at fy rhestr wylio wrth i mi eu symud',
-'tog-watchdeletion' => 'Ychwanegu tudalennau at fy rhestr wylio wrth i mi eu dileu',
+'tog-watchcreations' => 'Ychwanegu tudalennau at fy rhestr wylio wrth i mi eu creu a ffeiliau wrth i mi eu huwchlwytho.',
+'tog-watchdefault' => 'Ychwanegu tudalennau a ffeiliau at fy rhestr wylio wrth i mi eu golygu',
+'tog-watchmoves' => 'Ychwanegu tudalennau a ffeiliau at fy rhestr wylio wrth i mi eu symud',
+'tog-watchdeletion' => 'Ychwanegu tudalennau a ffeiliau at fy rhestr wylio wrth i mi eu dileu',
'tog-minordefault' => 'Marcio pob golygiad fel un bach yn ddiofyn',
'tog-previewontop' => 'Dangos y rhagolwg cyn y blwch golygu',
'tog-previewonfirst' => 'Dangos rhagolwg ar y golygiad cyntaf',
'tog-nocache' => 'Analluogi storio tudalennau yng nghelc y porydd',
-'tog-enotifwatchlistpages' => 'Gyrru e-bost ataf pan fo newid i dudalen ar fy rhestr wylio',
+'tog-enotifwatchlistpages' => 'Gyrru e-bost ataf pan fo newid i dudalen neu ffeil ar fy rhestr wylio',
'tog-enotifusertalkpages' => "Gyrru e-bost ataf fy hunan pan fo newid i'm tudalen sgwrs",
-'tog-enotifminoredits' => 'Gyrru e-bost ataf hefyd ar gyfer golygiadau bychain i dudalennau',
+'tog-enotifminoredits' => 'Gyrru e-bost ataf hefyd ar gyfer golygiadau bychain i dudalennau a ffeiliau',
'tog-enotifrevealaddr' => 'Datguddio fy nghyfeiriad e-bost mewn e-byst hysbysu',
'tog-shownumberswatching' => "Dangos y nifer o ddefnyddwyr sy'n gwylio",
'tog-oldsig' => 'Llofnod cyfredol:',
'youhavenewmessages' => 'Mae gennych chi $1 ($2).',
'newmessageslink' => 'Neges(eueon) newydd',
'newmessagesdifflink' => 'y newid diweddaraf',
+'youhavenewmessagesfromusers' => 'Mae gennych $1 oddi wrth {{PLURAL:$3||ddefnyddiwr arall|$2 ddefnyddiwr arall|$3 defnyddiwr arall|$3 defnyddiwr arall|$3 defnyddiwr arall}} ($2).',
+'youhavenewmessagesmanyusers' => 'Mae gennych $1 oddi wrth lu o ddefnyddwyr eraill ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|neges newydd|neges newydd|negeseuon newydd}}',
+'newmessagesdifflinkplural' => 'y {{PLURAL:$1||newid diweddaraf|newidiadau diweddaraf}}',
'youhavenewmessagesmulti' => 'Mae negeseuon newydd gennych ar $1',
'editsection' => 'golygu',
'editold' => 'golygu',
'cannotdelete' => "Mae'n amhosib dileu'r dudalen neu'r ddelwedd \"\$1\".
Efallai fod rhywun arall eisoes wedi'i dileu.",
'cannotdelete-title' => "Ni ellir dileu'r dudalen '$1'",
+'delete-hook-aborted' => 'Terfynwyd y dilead cyn pryd gan fachyn.
+Ni roddodd eglurhad.',
'badtitle' => 'Teitl gwael',
'badtitletext' => "Mae'r teitl a ofynnwyd amdano yn annilys, yn wag, neu cysylltu'n anghywir rhwng ieithoedd neu wicïau. Gall fod ynddo un nod neu ragor na ellir eu defnyddio mewn teitlau.",
'perfcached' => "Mae'r wybodaeth ganlynol yn gopi cadw; mae'n bosib nad y fersiwn diweddaraf ydyw. Cedwir hyd at {{PLURAL:$1||un canlyniad yn unig|ddau ganlyniad|dri canlyniad|$1 chanlyniad|$1 canlyniad}} yn y copi cadw.",
'remembermypassword' => "Y porwr hwn i gofio'r manylion mewngofnodi (am hyd at $1 {{PLURAL:$1||diwrnod|ddiwrnod|diwrnod|diwrnod|diwrnod}})",
'securelogin-stick-https' => "Cadw'r cyswllt â HTTPS ar ôl mewngofnodi",
'yourdomainname' => 'Eich parth',
+'password-change-forbidden' => 'Ni allwch newid cyfrineiriau ar y wici hwn.',
'externaldberror' => "Naill ai: cafwyd gwall dilysu allanol ar databas neu: ar y llaw arall efallai nad oes hawl gennych chi i ddiwygio'ch cyfrif allanol.",
'login' => 'Mewngofnodi',
'nav-login-createaccount' => 'Mewngofnodi',
Gallwch [[Special:Search/{{PAGENAME}}|chwilio am y teitl hwn]] ar dudalennau eraill, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} chwilio drwy'r logiau perthnasol], neu [{{fullurl:{{FULLPAGENAME}}|action=edit}} golygu'r dudalen]</span>.",
'noarticletext-nopermission' => 'Mae\'r dudalen hon yn wag ar hyn o bryd.
Gallwch [[Special:Search/{{PAGENAME}}|chwilio am y teitl hwn]] ar dudalennau eraill, neu gallwch <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} chwilio drwy\'r logiau perthnasol]</span>.',
+'missing-revision' => 'Nid yw\'r diwygiad #$1 o\'r dudalen "{{PAGENAME}}" ar gael.
+
+Fel arfer, fe ddigwydd hyn wrth ddilyn hen gyswllt i dudalen sydd wedi ei dileu.
+Gallwch weld y manylion yn y [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} lòg dileu].',
'userpage-userdoesnotexist' => 'Nid oes defnyddiwr a\'r enw "<nowiki>$1</nowiki>" yn bod. Gwnewch yn siwr eich bod am greu/golygu\'r dudalen hon.',
'userpage-userdoesnotexist-view' => 'Nid yw\'r cyfrif defnyddiwr "$1" wedi ei gofrestri.',
'blocked-notice-logextract' => "Mae'r defnyddiwr hwn wedi ei flocio ar hyn o bryd.
'expansion-depth-exceeded-warning' => "Mae dyfnder ehangu'r dudalen y tu hwnt i'r terfyn",
'parser-unstrip-loop-warning' => 'Wedi darganfod dolen dad-blicio (unstrip loop)',
'parser-unstrip-recursion-limit' => 'Wedi mynd dros ben y terfyn ar ddychweliad dad-blicio (unstrip recursion) ($1)',
+'converter-manual-rule-error' => 'Cafwyd hyd i wall yn y rheol trosi iaith â llaw',
# "Undo" feature
'undo-success' => "Gellir dadwneud y golygiad. Byddwch gystal â gwirio'r gymhariaeth isod i sicrhau mai dyma sydd arnoch eisiau gwneud, ac yna rhowch y newidiadau ar gadw i gwblhau'r gwaith o ddadwneud y golygiad.",
'right-writeapi' => "Defnyddio'r API i ysgrifennu a thrin y tudalennau",
'right-delete' => 'Dileu tudalennau',
'right-bigdelete' => 'Dileu tudalennau a hanes llwythog iddynt',
+'right-deletelogentry' => 'Dileu a dad-ddileu cofnodion lòg penodedig',
'right-deleterevision' => 'Dileu a dad-ddileu golygiadau arbennig o dudalennau',
'right-deletedhistory' => 'Gweld cofnodion fersiynau sydd wedi eu dileu, heb y testun ynddynt',
'right-deletedtext' => 'Gweld ysgrifen sydd wedi ei ddileu a newidiadau rhwng fersiynau ar ôl eu dileu',
'lockmanager-fail-releaselock' => 'Wedi methu agor y clo ar "$1".',
'lockmanager-fail-db-bucket' => 'Methwyd cysylltu â digon o gronfeydd data cloi yn y bwced $1.',
'lockmanager-fail-db-release' => 'Wedi methu agor y cloion ar y gronfa ddata $1.',
+'lockmanager-fail-svr-acquire' => 'Wedi methu sicrhau cloion ar y gweinydd $1.',
'lockmanager-fail-svr-release' => 'Wedi methu agor y cloion ar y gweinydd $1.',
# ZipDirectoryReader
'disambiguations' => "Tudalennau sy'n cysylltu â thudalennau gwahaniaethu",
'disambiguationspage' => 'Template:Gwahaniaethu',
-'disambiguations-text' => "Mae'r tudalennau canlynol yn cynnwys un neu ragor o gysylltau wici, sydd yn cysylltu â '''thudalennau gwahaniaethu'''. Yn hytrach dylent arwain yn syth at yr erthygl briodol.<br />
+'disambiguations-text' => "Mae'r tudalennau canlynol yn cynnwys un neu ragor o gysylltau, sydd yn arwain at '''dudalennau gwahaniaethu'''. Hwyrach y byddai'n hwylusach petai'r cyswllt yn arwain yn syth at y dudalen briodol.<br />
Diffinir tudalen yn dudalen gwahaniaethu pan mae'n cynnwys un o'r nodiadau '[[MediaWiki:Disambiguationspage|tudalen gwahaniaethu]]'.",
'doubleredirects' => 'Ailgyfeiriadau dwbl',
'rollback' => 'Gwrthdroi golygiadau',
'rollback_short' => 'Gwrthdroi',
'rollbacklink' => 'gwrthdroi',
+'rollbacklinkcount' => 'gwrthdröer $1 {{PLURAL:$1||golygiad|olygiad|golygiad|golygiad|golygiad}}',
+'rollbacklinkcount-morethan' => 'gwrthdröer mwy na $1 {{PLURAL:$1||golygiad|olygiad|golygiad|golygiad|golygiad}}',
'rollbackfailed' => 'Methodd y gwrthdroi',
'cantrollback' => "Wedi methu gwrthdroi'r golygiad; y cyfrannwr diwethaf oedd unig awdur y dudalen hon.",
'alreadyrolled' => "Nid yw'n bosib dadwneud y golygiad diwethaf i'r dudalen [[:$1|$1]] gan [[User:$2|$2]] ([[User talk:$2|Sgwrs]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
'api-error-empty-file' => "Mae'r ffeil a gyflwynwyd gennych yn wag.",
'api-error-emptypage' => "Ni chaniateir dechrau tudalen newydd, a honno'n wag.",
'api-error-fetchfileerror' => "Gwall mewnol: aeth rhywbeth o'i le tra'n cywain y ffeil.",
+'api-error-fileexists-forbidden' => 'Mae ffeil o\'r enw "$1" ar gael yn barod, ac ni ellir ei throsysgrifo.',
+'api-error-fileexists-shared-forbidden' => 'Mae ffeil o\'r enw "$1" eisoes ar gael yn y storfa ffeiliau gyfrannol, ac ni ellir ei throsysgrifo.',
'api-error-file-too-large' => "Mae'r ffeil a gyflwynwyd gennych yn rhy fawr.",
'api-error-filename-tooshort' => "Mae enw'r ffeil yn rhy fyr.",
'api-error-filetype-banned' => "Mae'r math hwn o ffeil wedi ei wahardd.",
'duration-millennia' => '$1 {{PLURAL:$1|milflwydd|filflwydd|filflwydd|milflwydd|milflwydd|milflwydd}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Wedi methu sicrhau cloion ar y gweinydd $1.',
+'api-error-filetype-banned-type' => "Ni chaniateir ffeiliau o'r {{PLURAL:$4|math|math|mathau|mathau|mathau|mathau}} $1. $2 yw'r {{PLURAL:$3|math|math|mathau|mathau|mathau|mathau}} o ffeil a ganiateir.",
);
'print' => 'Udskriv',
'view' => 'Vis',
'edit' => 'Redigér',
-'create' => 'opret',
+'create' => 'Opret',
'editthispage' => 'Redigér side',
-'create-this-page' => 'opret ny side',
+'create-this-page' => 'Opret denne side',
'delete' => 'Slet',
'deletethispage' => 'Slet side',
'undelete_short' => 'Fortryd sletning af {{PLURAL:$1|$1 version|$1 versioner}}',
'jumptonavigation' => 'Navigation',
'jumptosearch' => 'Søgning',
'view-pool-error' => 'Beklager, men serverne er i øjeblikket overbelastede.
-For mange brugere prøver at vise denne side.
-Vent et øjeblik, før du prøver at vise denne side ige.
+For mange brugere prøver at se denne side.
+Vent et øjeblik, før du prøver at besøge denne side igen.
$1',
'pool-timeout' => 'Timeout mens man venter på låsningen',
'youhavenewmessages' => 'Du har $1 ($2).',
'newmessageslink' => 'nye beskeder',
'newmessagesdifflink' => 'ændringer siden sidste visning',
+'youhavenewmessagesfromusers' => 'Du har $1 fra {{PLURAL:$3|en anden bruger| $3 brugere}} ($2).',
+'youhavenewmessagesmanyusers' => 'Du har $1 fra mange brugere ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|en ny besked|nye beskeder}}',
+'newmessagesdifflinkplural' => 'seneste {{PLURAL:$1|ændring|ændringer}}',
'youhavenewmessagesmulti' => 'Du har nye beskeder på $1',
'editsection' => 'redigér',
'editold' => 'redigér',
"$1"
fra funktionen "$2".
Databasen returnerede fejlen "$3: $4"',
-'laggedslavemode' => 'Bemærk: Den viste side indeholder muligvis ikke de nyeste ændringer.',
+'laggedslavemode' => "'''Bemærk:''' Den viste side indeholder muligvis ikke de nyeste ændringer.",
'readonly' => 'Databasen er skrivebeskyttet',
'enterlockreason' => 'Skriv en begrundelse for skrivebeskyttelsen, med samt en vurdering af, hvornår skrivebeskyttelsen ophæves igen',
'readonlytext' => 'Databasen er midlertidigt skrivebeskyttet. Forsøg venligst senere.
'remembermypassword' => 'Husk mit brugernavn på denne computer (højst $1 {{PLURAL:$1|dag|dage}})',
'securelogin-stick-https' => 'Behold forbindelsen til HTTPS efter login',
'yourdomainname' => 'Dit domænenavn',
+'password-change-forbidden' => 'Du kan ikke ændre adgangskoder på denne wiki.',
'externaldberror' => 'Der er opstået en fejl i en ekstern adgangsdatabase, eller du har ikke rettigheder til at opdatere denne.',
'login' => 'Log på',
'nav-login-createaccount' => 'Opret en konto eller log på',
'loginprompt' => 'Du skal have cookies slået til for at kunne logge på {{SITENAME}}.',
'userlogin' => 'Opret en konto eller log på',
-'userloginnocreate' => 'Log ind',
+'userloginnocreate' => 'Log på',
'logout' => 'Log af',
'userlogout' => 'Log af',
'notloggedin' => 'Ikke logget på',
'summary' => 'Beskrivelse:',
'subject' => 'Emne/overskrift:',
'minoredit' => 'Dette er en mindre ændring.',
-'watchthis' => 'Overvåg denne artikel',
+'watchthis' => 'Overvåg denne side',
'savearticle' => 'Gem side',
'preview' => 'Forhåndsvisning',
'showpreview' => 'Forhåndsvisning',
'noarticletext-nopermission' => 'Der er i øjeblikket ikke noget tekst på denne side.
Du kan [[Special:Search/{{PAGENAME}}|søge efter denne sides titel]] på andre sider,
eller <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} se de relaterede loglister]</span>.',
+'missing-revision' => 'Revision #$1 af siden med navnet "{{PAGENAME}}" eksisterer ikke.
+
+Dette skyldes normalt at et forældet historik-link er fulgt til en side der er slettet.
+Detaljer kan findes i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} sletningsloggen].',
'userpage-userdoesnotexist' => 'Brugerkontoen "<nowiki>$1</nowiki>" findes ikke. Overvej om du ønsker at oprette eller redigere denne side.',
'userpage-userdoesnotexist-view' => 'Brugerkontoen "$1" er ikke oprettet.',
'blocked-notice-logextract' => 'Denne bruger er i øjeblikket blokeret.
'parser-template-loop-warning' => 'Skabelonløkke fundet: [[$1]]',
'parser-template-recursion-depth-warning' => 'En skabelon er rekursivt inkluderet for mange gange ($1)',
'language-converter-depth-warning' => 'Dybdegrænse for sprogkonvertering overskredet ($1)',
+'node-count-exceeded-category' => 'Sider hvor antal noder er overskredet',
+'node-count-exceeded-warning' => 'Sider der har overskredet antallet af noder',
'expansion-depth-exceeded-category' => 'Sider, der overskrider ekspansionsdybden',
'expansion-depth-exceeded-warning' => 'Siden overskred ekspansionsdybden',
+'parser-unstrip-loop-warning' => 'Unstrip-loop opdaget',
+'parser-unstrip-recursion-limit' => 'Unstrip rekursionsgrænse er nået ($1)',
+'converter-manual-rule-error' => 'Fejl opdaget i manuel sprogkonvertingsregel',
# "Undo" feature
'undo-success' => 'Redigeringen kan fjernes.
'editundo' => 'fjern redigering',
'diff-multi' => '({{PLURAL:$1|En mellemliggende version|$1 mellemliggende versioner}} af {{PLURAL:$2|en bruger|$2 brugere}} ikke vist)',
'diff-multi-manyusers' => '({{PLURAL:$1|En mellemliggende version|$1 mellemliggende versioner}} af mere end $2 {{PLURAL:$2|bruger|brugere}} ikke vist)',
+'difference-missing-revision' => '{{PLURAL:$2|En revision|$2 revisioner}} af denne forskel ($1) {{PLURAL:$2|blev|blev}} ikke fundet.
+
+Dette skyldes normalt et forældet diff link til en side der er slettet.
+Detaljer kan findes i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} sletningsloggen].',
# Search results
'searchresults' => 'Søgeresultater',
'right-writeapi' => 'Bruge redigeringsdelen af API',
'right-delete' => 'Slette sider',
'right-bigdelete' => 'Slette sider med mange versioner',
+'right-deletelogentry' => 'Slet og gendan specifikke log-poster',
'right-deleterevision' => 'Slette og gendanne enkelte versioner af sider',
'right-deletedhistory' => 'Se slettede verioner, uden at vise versionens indhold.',
'right-deletedtext' => 'Vise slettet tekst og ændringer i slettede revisioner',
'backend-fail-internal' => 'En ukendt fejl opstod i filbackend "$1".',
'backend-fail-contenttype' => 'Kunne ikke bestemme typen af indhold i filen, der skal gemmes på "$1".',
'backend-fail-batchsize' => 'Lagringsbackend gav en batch på $1 fil{{PLURAL:$1|operation|operationer}}; grænsen er $2 {{PLURAL:$2|operation|operationer}}.',
+'backend-fail-usable' => 'Kunne ikke skrive til filen "$1" på grund af manglende rettigheder eller manglende mapper/containere.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Kunne ikke tilslutte til journal databasen for lager backenden "$1".',
+'filejournal-fail-dbquery' => 'Kunne ikke opdatere journal databasen for lager backenden "$1".',
# Lock manager
'lockmanager-notlocked' => 'Kunne ikke låse "$1" op, da den ikke er låst.',
'lockmanager-fail-releaselock' => 'Kunne ikke frigive låsen for "$1".',
'lockmanager-fail-db-bucket' => 'Kunne ikke kontakte nok låsedatabaser i bøtten $1.',
'lockmanager-fail-db-release' => 'Kunne ikke frigive lås til databasen $1.',
+'lockmanager-fail-svr-acquire' => 'Kunne ikke hente lås på serveren $1.',
'lockmanager-fail-svr-release' => 'Kunne ikke frigive låse til serveren $1.',
# ZipDirectoryReader
'disambiguations' => 'Sider, der henviser til flertydige titler',
'disambiguationspage' => 'Template:Flertydig',
-'disambiguations-text' => 'De følgende sider henviser til en flertydig titel. De bør henvise direkte til det passende emne i stedet. En side behandles som en side med en flertydig titel hvis den bruger en skabelon som er henvist til fra [[MediaWiki:Disambiguationspage]].',
+'disambiguations-text' => "De følgende sider henviser til mindst en side med en '''flertydig titel'''.
+De bør henvise direkte til et mere passende emne i stedet.<br />
+En side behandles som en side med en flertydig titel hvis den bruger en skabelon som der er henvist til fra [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dobbelte omdirigeringer',
'doubleredirectstext' => 'Dette er en liste over sider som omdirigerer til andre omdirigeringssider.
'rollback' => 'Fjern redigeringer',
'rollback_short' => 'Fjern redigering',
'rollbacklink' => 'rul tilbage',
+'rollbacklinkcount' => 'tilbagefør $1 {{PLURAL:$1|redigering|redigeringer}}',
+'rollbacklinkcount-morethan' => 'tilbagefør mere end $1 {{PLURAL:$1|redigering|redigeringer}}',
'rollbackfailed' => 'Kunne ikke fjerne redigeringen',
'cantrollback' => 'Kan ikke fjerne redigering; den sidste bruger er den eneste forfatter.',
'alreadyrolled' => 'Kan ikke fjerne den seneste redigering af [[:$1]] foretaget af [[User:$2|$2]] ([[User talk:$2|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
'api-error-emptypage' => 'Det er ikke tilladt at oprette nye, tomme sider.',
'api-error-fetchfileerror' => 'Intern fejl: noget gik galt under hentningen af filen.',
'api-error-fileexists-forbidden' => 'En fil med navnet "$1" findes allerede, og den kan ikke overskrives.',
+'api-error-fileexists-shared-forbidden' => 'En fil med navnet "$1" eksisterer allerede i det delte filsystem og kan ikke overskrives.',
'api-error-file-too-large' => 'Den fil du indsendte var for stor.',
'api-error-filename-tooshort' => 'Filnavnet er for kort.',
'api-error-filetype-banned' => 'Denne type fil er ikke tilladt.',
'duration-centuries' => '$1 {{PLURAL:$1|århundrede|århundreder}}',
'duration-millennia' => '$1 {{PLURAL:$1|årtusind|årtusinder}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|er ikke en tilladt filtype|er ikke tilladte filtyper}}. Tilladt {{PLURAL:$3|filtype er|filtyper er}} $2.',
);
'youhavenewmessages' => 'Du hast $1 ($2).',
'newmessageslink' => 'neue Nachrichten',
'newmessagesdifflink' => 'Letzte Änderung',
+'youhavenewmessagesfromusers' => 'Du hast $1 von {{PLURAL:$3|einem anderen Benutzer|$3 Benutzern}} ($2).',
+'youhavenewmessagesmanyusers' => 'Du hast $1 von vielen Benutzern ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|eine neue Nachricht|neue Nachrichten}}',
+'newmessagesdifflinkplural' => 'letzte {{PLURAL:$1|Änderung|Änderungen}}',
'youhavenewmessagesmulti' => 'Du hast neue Nachrichten: $1',
'editsection' => 'Bearbeiten',
'editold' => 'Bearbeiten',
'remembermypassword' => 'Mit diesem Browser dauerhaft angemeldet bleiben (maximal $1 {{PLURAL:$1|Tag|Tage}})',
'securelogin-stick-https' => 'Nach dem Anmelden mit HTTPS verbunden bleiben',
'yourdomainname' => 'Deine Domain:',
+'password-change-forbidden' => 'Du kannst auf diesem Wiki keine Passwörter ändern.',
'externaldberror' => 'Entweder es liegt ein Fehler bei der externen Authentifizierung vor oder du darfst dein externes Benutzerkonto nicht aktualisieren.',
'login' => 'Anmelden',
'nav-login-createaccount' => 'Anmelden / Benutzerkonto erstellen',
'noarticletext-nopermission' => 'Diese Seite enthält momentan noch keinen Text.
Du kannst ihren Titel auf anderen Seiten [[Special:Search/{{PAGENAME}}|suchen]]
oder die zugehörigen <span class="plainlinks">[{{fullurl:{{#special:Log}}|page={{FULLPAGENAMEE}}}} Logbücher betrachten].</span>',
+'missing-revision' => 'Die Version $1 der Seite namens „{{PAGENAME}}“ ist nicht vorhanden.
+
+Dieser Fehler wird normalerweise von einem veralteten Link zur Versionsgeschichte einer Seite verursacht, die zwischenzeitlich gelöscht wurde.
+Einzelheiten sind im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lösch-Logbuch] einsehbar.',
'userpage-userdoesnotexist' => 'Das Benutzerkonto „<nowiki>$1</nowiki>“ ist nicht vorhanden. Bitte prüfe, ob du diese Seite wirklich erstellen/bearbeiten willst.',
'userpage-userdoesnotexist-view' => 'Das Benutzerkonto „$1“ ist nicht vorhanden.',
'blocked-notice-logextract' => '{{GENDER:$1|Dieser Benutzer|Diese Benutzerin|Dieser Benutzer}} ist zurzeit gesperrt.
'expansion-depth-exceeded-warning' => 'Die Seite hat die Expansionstiefe überschritten.',
'parser-unstrip-loop-warning' => 'Zirkelbezug festgestellt',
'parser-unstrip-recursion-limit' => 'Rekursionsgrenze beim Auflösen überschritten ($1)',
+'converter-manual-rule-error' => 'Bei der manuellen Sprachkonvertierungsregel wurde ein Fehler entdeckt.',
# "Undo" feature
'undo-success' => 'Die Bearbeitung kann rückgängig gemacht werden.
'editundo' => 'rückgängig machen',
'diff-multi' => '({{PLURAL:$1|Eine dazwischenliegende Version|$1 dazwischenliegende Versionen}} von {{PLURAL:$2|einem Benutzer|$2 Benutzern}} {{PLURAL:$1|wird|werden}} nicht angezeigt)',
'diff-multi-manyusers' => '({{PLURAL:$1|$1 dazwischenliegende Versionen}} von mehr als {{PLURAL:$2|$2 Benutzern}}, die nicht angezeigt werden)',
+'difference-missing-revision' => '{{PLURAL:$2|Eine Version|$2 Versionen}} dieser Unterschiedsanzeige ($1) {{PLURAL:$2|wurde|wurden}} nicht gefunden.
+
+Dieser Fehler wird normalerweise von einem veralteten Link zur Versionsgeschichte einer Seite verursacht, die zwischenzeitlich gelöscht wurde.
+Einzelheiten sind im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lösch-Logbuch] vorhanden.',
# Search results
'searchresults' => 'Suchergebnisse',
'right-ipblock-exempt' => 'Ausnahme von IP-Sperren, automatischen Sperren und Rangesperren',
'right-proxyunbannable' => 'Ausnahme von automatischen Proxysperren',
'right-unblockself' => 'Sich selbst entsperren',
-'right-protect' => 'Seitenschutzstatus ändern',
+'right-protect' => 'Seitenschutzstatus ändern und geschützte Seiten bearbeiten',
'right-editprotected' => 'Geschützte Seiten bearbeiten (ohne Kaskadenschutz)',
'right-editinterface' => 'Benutzeroberfläche bearbeiten',
'right-editusercssjs' => 'Fremde CSS- und JavaScript-Dateien bearbeiten',
'disambiguations' => 'Seiten die auf Begriffsklärungsseiten verlinken',
'disambiguationspage' => 'Template:Begriffsklärung',
-'disambiguations-text' => 'Die folgenden Seiten verlinken auf eine Seite zur Begriffsklärung. Sie sollten statt dessen auf die eigentlich gemeinte Seite verlinken.
+'disambiguations-text' => "Die folgenden Seiten enthalten mindestens einen Link zur einer '''Begriffsklärungsseite'''. Sie sollten möglicherweise auf die eigentlich gemeinte Seite verlinken.
-Eine Seite gilt als Begriffsklärungsseite, wenn sie eine der in [[MediaWiki:Disambiguationspage]] aufgeführte(n) Vorlage(n) einbindet.<br />
-Links aus Namensräumen werden hier nicht aufgelistet.',
+Eine Seite gilt als Begriffsklärungsseite, wenn sie mindestens eine der auf [[MediaWiki:Disambiguationspage]] aufgeführten Vorlagen enthält.",
'doubleredirects' => 'Doppelte Weiterleitungen',
'doubleredirectstext' => 'Diese Liste enthält Weiterleitungen, die auf Weiterleitungen verlinken.
'rollback' => 'Zurücksetzen der Änderungen',
'rollback_short' => 'Zurücksetzen',
'rollbacklink' => 'Zurücksetzen',
+'rollbacklinkcount' => '{{PLURAL:$1|Eine Version|$1 Versionen}} zurücksetzen',
+'rollbacklinkcount-morethan' => 'Mehr als {{PLURAL:$1|eine Version|$1 Versionen}} zurücksetzen',
'rollbackfailed' => 'Zurücksetzen gescheitert',
'cantrollback' => 'Die Änderung kann nicht zurückgesetzt werden, da es keine früheren Autoren gibt.',
'alreadyrolled' => 'Das Zurücksetzen der Änderungen von [[User:$2|$2]] ([[User talk:$2|Diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) an [[:$1]] ist gescheitert, da in der Zwischenzeit ein anderer Benutzer die Seite geändert hat.
'duration-centuries' => '$1 {{PLURAL:$1|Jahrhundert|Jahrhunderte}}',
'duration-millennia' => '$1 {{PLURAL:$1|Jahrtausend|Jahrtausende}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ist ein nicht zulässiger Dateityp|sind nicht zulässige Dateitypen}}. {{PLURAL:$3|Ein zulässiger Dateityp ist|Zulässige Dateitypen sind}} $2.',
);
'tog-enotifminoredits' => 'Vurnayışanê qıckekanê pelan u dosyeyan de zi mı rê e-poste bırışe',
'tog-enotifrevealaddr' => 'Adresa e-posteyê mı posteyê xeberan de bımocne',
'tog-shownumberswatching' => 'Amarê karberanê seyrkerdoğan bımocne',
-'tog-oldsig' => 'İmza mewcude:',
+'tog-oldsig' => 'İmzaya şıma:',
'tog-fancysig' => 'İmza rê mameley wikimeqaley bıke (bê gıreyo otomatik)',
'tog-externaleditor' => 'Editorê teberi standard bıxebetne (tenya seba ekspertano, komputerê şıma de eyarê xısusiy lazımê. [//www.mediawiki.org/wiki/Manual:External_editors Melumato vêşêr.])',
'tog-externaldiff' => 'Têverşanayışan pê programê teberi vıraze (tenya seba ekspertano, komputerê şıma de eyarê xısusiy lazımê. [//www.mediawiki.org/wiki/Manual:External_editors Melumato vêşêr.])',
# Font style option in Special:Preferences
'editfont-style' => 'Cayê vurnayışi de tipê nuştışi:',
-'editfont-default' => 'Fereziya cıgeyrayoği',
+'editfont-default' => 'Hesıbyayiya rovıteri',
'editfont-monospace' => 'Tipê nustey sabıtcagırewtoği',
'editfont-sansserif' => 'Tipê nustey Sans-serifi',
'editfont-serif' => 'Tipê nustey Serifi',
'about' => 'Heqa',
'article' => 'Wesiqe',
-'newwindow' => '(zerrey pençereyê dê newey de beno a)',
+'newwindow' => '(Teqa da newi de abêno)',
'cancel' => 'Bıterkne',
'moredotdotdot' => 'Vêşêri...',
'mypage' => 'Pela mı',
-'mytalk' => 'Werênayışê mı',
+'mytalk' => 'Werênayışi',
'anontalk' => 'Pela werênayışê nê IPy',
'navigation' => 'Pusula',
'and' => ' u',
# Cologne Blue skin
'qbfind' => 'Bıvêne',
-'qbbrowse' => 'Çım ra viyarne',
+'qbbrowse' => 'Rovete',
'qbedit' => 'Bıvurne',
'qbpageoptions' => 'Ena pele',
'qbpageinfo' => 'Gıre',
'print' => 'Çap ke',
'view' => 'Bıvêne',
'edit' => 'Bıvurnên',
-'create' => 'Vırazê',
+'create' => 'Vıraze',
'editthispage' => 'Ena pele bıvurne',
'create-this-page' => 'Na pele bınuse',
'delete' => 'Besterne',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
'aboutsite' => 'Heqa {{SITENAME}} de',
-'aboutpage' => 'Project:Heqa',
+'aboutpage' => 'Project:Heqdê cı',
'copyright' => 'Zerrek bınê $1 dero.',
'copyrightpage' => '{{ns:project}}:Heqa telifi',
'currentevents' => 'Veng u vac',
'youhavenewmessages' => 'To rê estê $1 ($2).',
'newmessageslink' => 'mesacê newey',
'newmessagesdifflink' => 'vurnayışo peyên',
+'youhavenewmessagesfromusers' => 'Zey $1 ra {{PLURAL:$3|zewbi karber|$3 karberi}} ($2) esto.',
+'youhavenewmessagesmanyusers' => '$1 ra tay karberi ($2) dı estê.',
+'newmessageslinkplural' => '{{PLURAL:$1|yew mesac|mesacê newey}}',
+'newmessagesdifflinkplural' => 'peyni {{PLURAL:$1|vurnayış|vurnayışi}}',
'youhavenewmessagesmulti' => '$1 mesaco newe esto',
'editsection' => 'bıvurne',
'editsection-brackets' => '[$1]',
'thisisdeleted' => 'Bıvêne ya zi $1 peyser bia?',
'viewdeleted' => '$1 bıvêne?',
'restorelink' => '{{PLURAL:$1|yew vurnayışo esterıte|$1 vurnayışê esterıtey}}',
-'feedlinks' => 'Cı resne:',
+'feedlinks' => 'Warikerdış:',
'feed-invalid' => 'Qeydey cıresnayışê beğşi nêvêreno.',
'feed-unavailable' => 'Cıresnayışê şebekey çıniyê',
'site-rss-feed' => '$1 Cıresnayışê RSSi',
'remembermypassword' => 'Parola mı nê cıgeyraoği de biya xo viri (heta $1 {{PLURAL:$1|roc|roci}}).',
'securelogin-stick-https' => "Dekewtış kerdışi dıma HTTPS'i dı grêdaye bıman",
'yourdomainname' => 'Nameyê şıma yo meydani',
+'password-change-forbidden' => 'Şıma na wiki de nêşenê parola bıvurnê.',
'externaldberror' => 'Ya database de xeta esta ya zi heqê şıma çino şıma no hesab bıvurni.',
'login' => 'Cı kewe',
'nav-login-createaccount' => 'Dekew de / hesab vıraze',
'loginprompt' => "Cıkewtena {{SITENAME}}i rê gani ''cookies'' akerdey bê.",
'userlogin' => 'Cı kewe / hesab vıraze',
'userloginnocreate' => 'Cı kewe',
-'logout' => 'Bıveciyên',
+'logout' => 'Veciyayış',
'userlogout' => 'Bıveciyên',
'notloggedin' => 'Hesab akerde niyo',
'nologin' => "Hesabê şıma çıniyo? '''$1'''.",
'resetpass-temp-password' => 'parolayo muweqet:',
# Special:PasswordReset
-'passwordreset' => 'Parola ancia bınuse',
+'passwordreset' => 'Parola reset ke',
'passwordreset-text' => 'Nê formi melumatê hesab dê şıma birê şıma viri deye pırkerê.',
-'passwordreset-legend' => 'Parola ancia bınuse',
+'passwordreset-legend' => 'Parola reset ke',
'passwordreset-disabled' => 'Parola reset kerdış ena viki sera qefılneyayo.',
'passwordreset-pretext' => '{{PLURAL:$1||Enê cerenan ra jeweri defiye de}}',
'passwordreset-username' => 'Namey karberi:',
'changeemail-text' => 'Şıma qayılê ke e-postay xo bıvurnê se enê formi pırkerê. Qandê araşt kerdışi zi parolay xo şıma de bınusnê',
'changeemail-no-info' => 'Resayışê ena pela rê Dekewtış icab keno.',
'changeemail-oldemail' => 'E-postay şımaya newki:',
-'changeemail-newemail' => 'E-postay şımayê newe:',
+'changeemail-newemail' => 'E-posta adresiyo newe:',
'changeemail-none' => '(Çıno)',
'changeemail-submit' => 'E-postay xo bıvurne',
'changeemail-cancel' => 'Bıterkne',
# Edit pages
'summary' => 'Xulasa:',
'subject' => 'Mewzu/serrêze:',
-'minoredit' => 'Vurnayışo qıckeko',
+'minoredit' => 'Vurnayışo werdı',
'watchthis' => 'Ena pele seyr ke',
-'savearticle' => 'Ena pele qeyd ke',
+'savearticle' => 'Peler qeyd ke',
'preview' => 'Verqayt',
-'showpreview' => 'Verqayti bımocne',
-'showlivepreview' => 'Live preview',
-'showdiff' => 'Vurnayışan bımocne',
+'showpreview' => 'Verqayti bıvin',
+'showlivepreview' => 'Verqayto cıwın',
+'showdiff' => 'Vurnayışa bıvin',
'anoneditwarning' => 'Teme!: Şıma bı hesabê xo nıkewtê cı. Hurêndiya namey şıma dı IP-adresa şıma qeyd bena u asena.',
'anonpreviewwarning' => "''Ti hama nicikewte. Qeyd kerdiş zerre tarixê pele de adresê IP yê tu keyd keno.''",
'missingsummary' => "'''DİQET:''' Şıma kılmnuşte nıkerd.
* Dest pê kerdışê musade nêdayiş: $8
* Qedyayişê musade nêdayiş: $6
-* Muddetê musade nêdayiş: $7
+* Oyo ke cı rê musade nêdeyêno: $7
-Eke şıma sebebê musade nêdayiş ri itiraz keni, $1 de ya zi yewna [[{{MediaWiki:Grouppage-sysop}}|xızmkar]] de şıma eşkeni na mesela de qıse bıkeri. [[Special:Preferences|Tercihlerim]] eke şıma na qısme de pey yew e-postayo raşt nêkewte cı, şıma xususiyetê "Karber ri e-posta bışaw" ra n3eeşkeni istifade bıkeri, eke şıma tercihanê xo bıerz zerreyê e-postayê xo şıma hıni eşkeni e-posta bışawi.
-<br />IP adresê şıma yo nıkayın $3, numrayo musade nêdayiş #$5.
-<br />Eke şıma qayile yew xızmkar çiko bıpers, no malumatan not bıkere ney şıma ri lazım beni.',
-'autoblockedtext' => 'IP adresê şıma otomotikmen verniya grewya, çunke yewna ten $1 no numra şuxulnayene u no numra zi verniye gırewte bi.
-Sebeb noyo:
+Eke şıma sebebê musade nêdayiş ri itiraz keni, $1 de ya zi yewna [[{{MediaWiki:Grouppage-sysop}}|xızmetkar]] de şıma eşkeni na mesela de qıse bıkeri. [[Special:Preferences|Tercihê]] eke şıma na qısme de pey yew e-postayo raşt nêkewte cı, şıma xususiyetê "Karber ri e-posta bırışê" ra nêeşkeni istifade bıkeri, eke şıma tercihanê xo bıerz zerreyê e-postayê xo şıma hıni şenê ep-posta bırışê.
+<br />IP adresê şıma yo nıkayın $3, numreya musade nêdayiş #$5.
+<br />Eke şıma qayile yew xızmkar çiko bıpers, no malumatan not bıkere ney şıma rê lazım beni.',
+'autoblockedtext' => 'IP adresê şıma otomotikmen kerda kılit, çıkı $1 verniya nê hesabi grota.
+Sebebê cı zi:
:\'\'$2\'\'
-* Dest pê kerdışê musade nêdayiş: $8
-* Qedyayişê musade nêdayiş: $6
-* Qayili bloke bıbo: $7
+* Dest pê kerdışê verni grotışi: $8
+* Qedyayişê verni grotışi: $6
+* Qayile ke bloqe bıbo: $7
-Şıma qayile qey weri kewtışê na mesela, pê $1 ya zi [[{{MediaWiki:Grouppage-sysop}}|serkaran ra]] yew de eşkeni irtibat bıkeri.
+Şıma qayile qey weri kewtışê na mesela, $1 ya na [[{{MediaWiki:Grouppage-sysop}}|serkaran ra]] yewi ra şenê irtibat kewê.
-Not, [[Special:Preferences|Tercihê karberi]] eke şıma yew e-postayo raşt nênuşt şıma nêeşkeni na xususiyet ra "karber ri e-posta bışıraw" istifade bıkeri.
+Not, [[Special:Preferences|Tercihê karberi]] eke şıma yew e-postayo raşt nênuşt se şıma nêşenê na xususiyet ra "karber rê e-posta bırışê" istifade bıkeri.
-IP numre şıma yo nıkayın $3 u ID şıma yo ke musade nêdaye #$5. Eke şıma yew tehqiqat vırazeni malumatê corınan xo vir ra mekere.',
+IP adresiya şıma yo nıkayên $3 u ID şıma yo ke musade nêdaye #$5. Eke şıma yew tehqiqat vırazeni malumatê corênan xo vira mekerê.',
'blockednoreason' => 'sebeb nidaniyo',
'whitelistedittext' => 'Eka ti wazene binusi ti gani $1.',
'confirmedittext' => 'Eka ti wazene binusi, adresê xo e-maili confirme bike.
'noarticletext-nopermission' => 'No pel nıka veng o.
No sernuşte şıma [[Special:Search/{{PAGENAME}}|pelanê binan de eşkeni bıgeri]]
ya zi <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} itara eşekeni bıgere].</span>',
+'missing-revision' => 'Rewizyonê name dê pela da #$1 "{{PAGENAME}}" dı çıniyo.
+
+No normal de tarix dê pelanê besterneyan dı ena xırabin asena.
+Detayê besternayışi [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} tiya dı] aseno.',
'userpage-userdoesnotexist' => 'Hesabê karberi "<nowiki>$1</nowiki>" qeyd nêbiyo.
Kerem ke, tı ke wazenay na pele bafernê/bıvurnê, qontrol ke.',
'userpage-userdoesnotexist-view' => 'Hesabê karberi "$1" qeyd nêbiyo.',
'nonunicodebrowser' => "'''DİQET: Browserê şıma u unicode yewbini nêgeni. Qey izin dayişê vurnayişê pelan: Karakteri ke ASCII niyê; zerreyê qutiyê vurnayişi de kodi (cod) şiyes-şiyes aseni.'''",
'editingold' => "'''DİQET: Şıma pelo revizebiyaye de vurnayiş keni. Eke şıma qayd bıkeri vurnayişi ke pelo revizebiyayiş ra heta ewro biyê, pêroyê ey beni vini.'''",
'yourdiff' => 'pêverronayiş',
-'copyrightwarning' => "'''Teme''': Ena {{SITENAME}} dı iştirakanê hemi bınê \$2 (qe informasyonê zafyeri: \$1) vêrenê. Eg tı nıwazen ser nuşteyan tı karberan bıhewit ya zi bıcebiri/bıvurni, itiya dı nınusi.<br />
-Tı ma ri soz dano ki tı nuşte xo oricinali nuşt, ya zi tı yew dokumantê umumi ra kopya keno.
-'''İTİYA DI METARİYALÊ \"COPYRIGHT\"İ NINUSİ!.'''",
+'copyrightwarning' => "'''Recayê ikazi:''' Sita da {{SITENAME}} ra iştıraqi pêro umışin da $2 zerredeyo (teferruata rê $1'i bıvinê).
+İştıraqê şıma, şıma kayıl niyê ke yewna merdumi kerpeyina bıvurnê yana yewna caya ra vılakerê se, iştıraq mekewê.<br />
+Fına zi qayılê ke iştıraq kewê, Şıma qayılê kê şar vaco eno nuşte felani nuşnayo yana resmi meqeman ra zanayışê cı u malumatê cı esto/ Xoseri cayan ra groti rê şıma qerenti danê. '''Tiya dı, şıma wêrê telifira mısade nêgroto se eserê cı tiya vıla mekerê! '''",
'copyrightwarning2' => 'Ney bızane ke nuşteyê ke şıma ruşneni (şaweni) keyepelê {{SITENAME}} herkes eşkeno nê nuşteyanê şıma ser kay bıkero. Eke şıma qayil niye kes bıvurno, nuşetyanê xo meerze ita. <br />
Wexta ke şıma nuşte zi erzeni ita; şıma gani taahhud bıde koti ra ardo (qey teferruati referans: $1).',
'longpageerror' => "'''Xırab: Dergeya nuşte dê şıma nezdi {{PLURAL:$1|kilobayto|$1 kilobayto}}, feqet {{PLURAL:$2|kilobayt|$2 kilobayt}} ra vêşiyo. Qeyd biyayişê cı nêbeno'''",
'edit-no-change' => 'Vurnayişê şıma qebul nêbı, çunke nuşte de yew vurnayiş n3evıraziya.',
'edit-already-exists' => 'Pelo newe nêvıraziyeno.
Pel ca ra esto.',
-'defaultmessagetext' => 'Normal metnê nuşti',
+'defaultmessagetext' => 'Hesıbyaye metne mesaci',
# Parser/template warnings
'expensive-parserfunction-warning' => 'Hişyari: No pel de fonksiyoni zaf esti.
'expansion-depth-exceeded-warning' => 'Ravêriya pela xori herayêna',
'parser-unstrip-loop-warning' => 'Unstrip lete vineya',
'parser-unstrip-recursion-limit' => 'Sinorê limit dê qayış dê ($1) ravêrya',
+'converter-manual-rule-error' => 'Rehberê zıwan açarnayışi dı xırabin tesbit biya',
# "Undo" feature
'undo-success' => 'No vurnayiş tepeye geryeno. pêverronayişêyê cêrıni kontrol bıkeri.',
# Diffs
'history-title' => 'Rewizyonê $1:',
-'difference-title' => 'Ferkê revizyonan de "$1"',
+'difference-title' => 'Pela "$1" ferqê çım ra viyarnayışan',
'difference-title-multipage' => 'Ferkê pelan dê "$1" u "$2"',
'difference-multipage' => '(Ferqê pelan)',
'lineno' => 'Rêza $1i:',
'editundo' => 'peyser bia',
'diff-multi' => '({{PLURAL:$1|Yew revizyono miyanên|$1 revizyonê miyanêni}} terefê {{PLURAL:$2|yew karberi|$2 karberan}} nêmocno)',
'diff-multi-manyusers' => '({{PLURAL:$1|jew timar kerdışo qıckeko|$1 timar kerdışo qıckeko}} timar kerdo, $2 {{PLURAL:$2|Karber|karberi}} memocne)',
+'difference-missing-revision' => 'Ferqê {{PLURAL:$2|Yew rewizyonê|$2 rewizyonê}} {{PLURAL:$2|dı|dı}} ($1) sero çıniyo.
+
+No normal de werênayış dê pelanê besterneyan dı ena xırabin asena.
+Detayê besternayışi [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} tiya dı] aseno.',
# Search results
'searchresults' => 'Neticeyê geyrayışi',
'searchprofile-images-tooltip' => 'Dosya cı geyr',
'searchprofile-everything-tooltip' => 'Tedeestey hemine cı geyre (pelanê mınaqeşey zi tey)',
'searchprofile-advanced-tooltip' => 'qe cayê nimeyî bigêre',
-'search-result-size' => '$1 ({{PLURAL:$2|1 kelime|$2 kelimey}})',
+'search-result-size' => '$1 ({{PLURAL:$2|1 çekuyo|$2 çekuyê}})',
'search-result-category-size' => '{{PLURAL:$1|1 eza|$1 ezayan}} ({{PLURAL:$2|1 ketegoriyê bini|$2 kategiryanê binan}}, {{PLURAL:$3|1 dosya|$3 dosyayan}})',
'search-result-score' => 'Eleqa: $1%',
'search-redirect' => '(ber $1)',
'showingresultsheader' => "{{PLURAL:$5|Neticeyê '''$1''' of '''$3'''|Neticeyanê '''$1 - $2''' of '''$3'''}} qe '''$4'''",
'nonefound' => "'''Teme''': Teyna tay namecayan cıgeyro beno.
Pe verbendi ''all:'', vaceyê xo bıvurni ki contenti hemi cıgeyro (pelanê mınaqeşe, templatenan, ucb.) ya zi cıgeyro ser namecay ki tı wazeni.",
-'search-nonefound' => 'Ser ena çekuye de netice çino',
+'search-nonefound' => 'Zey cıgeyrayış de şıma netice nêvineya',
'powersearch' => 'Cıgeyrayışo hera',
'powersearch-legend' => 'Cıgeyrayışo hera',
'powersearch-ns' => 'Cayanê nameyan de cıgeyrayış:',
'prefs-rendering' => 'Asayış',
'saveprefs' => 'Star ke',
'resetprefs' => 'Vurnayışê ke qeyd nêbiy, pak ke',
-'restoreprefs' => 'Sazanê standardan pêron newe dere barke',
+'restoreprefs' => 'Sazanê hesıbyaya pêron newe dere barke',
'prefs-editing' => 'Cay pela nustısi',
'prefs-edit-boxsize' => 'Ebatê pencereyê vurnayîşî.',
'rows' => 'Xeti:',
'stub-threshold-disabled' => 'Dezge ra vıcyaya',
'recentchangesdays' => 'Rocê ke vurnayışanê peyênan de bıasê:',
'recentchangesdays-max' => 'Tewr zaf $1 {{PLURAL:$1|roc|roci}}',
-'recentchangescount' => 'Amarê vurnayışiê ke standard bıasê:',
+'recentchangescount' => 'Amarê vurnayışê ke hesıbyaye deye bımocneyê:',
'prefs-help-recentchangescount' => 'Ney de vurnayışê peyêni, tarixê pelan u cıkewteni asenê.',
'prefs-help-watchlist-token' => 'Eke no ca pê kılito dızdeni/miyanki pırr bo, lista şımawa seyrkerdışi rê yew cıresnayışê RSSi vıraziyeno.
Her kamo ke nê kılitê nê cay zaneno, şeno lista şımawa seyrkerdışi ki bıwano, coke ra yewo sağlem weçine.
'timezoneregion-europe' => 'Ewrope',
'timezoneregion-indian' => 'Okyanuso Hind',
'timezoneregion-pacific' => 'Okyanuso Pasifik',
-'allowemail' => 'Karberanê binan ra e-mail qebul bıke',
+'allowemail' => 'Karberê bini wa bışê mı rê e-posta bırışê.',
'prefs-searchoptions' => 'Tercihê cıgeyrayışi',
'prefs-namespaces' => 'Caê namey',
'defaultns' => 'Eke heni, enê cayanê namey de cı geyre (sae ke):',
-'default' => 'default',
+'default' => 'hesıbyaye',
'prefs-files' => 'Dosyey',
'prefs-custom-css' => 'CSSê xasi',
'prefs-custom-js' => 'JSê xasi',
'badsig' => 'Îmzayê tu raşt niyo.
Etiketê HTMLî kontrol bike.',
'badsiglength' => 'İmzayê şıma zaf dergo.
-$1 gani bınê no {{PLURAL:$1|karakter|karakter}}de bıbo.',
+$1 gani bınê no {{PLURAL:$1|karakter|karakter}} de bıbo.',
'yourgender' => 'Neri makey:',
'gender-unknown' => 'Cınsiyet nêvato',
'gender-male' => 'cıwamêrd',
# Groups
'group' => 'Grup:',
'group-user' => 'Karberi',
-'group-autoconfirmed' => 'Karbero ke xob xo biyê araşt',
+'group-autoconfirmed' => 'Karberê ke xob xo biyê araşt',
'group-bot' => 'Boti',
'group-sysop' => 'İdarekari',
'group-bureaucrat' => 'Burokrati',
-'group-suppress' => 'Kontroli',
+'group-suppress' => 'Çımpawıteni',
'group-all' => '(pêro)',
'group-user-member' => '{{GENDER:$1|karber}}',
-'group-autoconfirmed-member' => '{{GENDER:$1|Karbero ke xob xo biyê araşt}}',
+'group-autoconfirmed-member' => '{{GENDER:$1|Karberê ke xob xo biyê araşt}}',
'group-bot-member' => '{{GENDER:$1|bot}}',
'group-sysop-member' => '{{GENDER:$1|İdarekar}}',
'group-bureaucrat-member' => '{{GENDER:$1|buroqrat}}',
'group-suppress-member' => '{{GENDER:$1|Temaşekar}}',
'grouppage-user' => '{{ns:project}}:Karberi',
-'grouppage-autoconfirmed' => '{{ns:project}}:Karbero ke xob xo biyê araşt',
+'grouppage-autoconfirmed' => '{{ns:project}}:Karberê ke xob xo biyê araşt',
'grouppage-bot' => '{{ns:project}}:Boti',
'grouppage-sysop' => '{{ns:project}}:İdarekeri',
'grouppage-bureaucrat' => '{{ns:project}}:Burokrati',
'right-writeapi' => 'İstıfadey APIyê nuştey',
'right-delete' => 'Pele bestere',
'right-bigdelete' => 'Pelanê be tarixanê dergan bestere',
+'right-deletelogentry' => 'besternayış u mebesternayışa re qeyde definayışê xısusi',
'right-deleterevision' => 'Vurnayışê xısusiyê ke ê pelanê, inan bestere ya peyser bia',
'right-deletedhistory' => 'Qeydanê tarixanê esterıteyan de qayt ke, bê nuştey inan',
'right-deletedtext' => 'Mabênê newede vurnayışanê esterıtiyan de qaytê nuştey esterıtey u vurnayışan ke',
'license' => 'Lisans:',
'license-header' => 'Lisans',
-'nolicense' => 'Çik niweçiyeyo',
+'nolicense' => 'Theba nêweçineya',
'license-nopreview' => '(verqeydî çin o)',
'upload_source_url' => '(yew URLê raştî, şar rê akerde yo)',
'upload_source_file' => '(komputerê ti de yew dosya)',
# File reversion
'filerevert' => '$1 reyna biyere',
-'filerevert-legend' => 'Dosya reyna biyere',
+'filerevert-legend' => 'Dosya ber weziyet do verên',
'filerevert-intro' => "Ti ho ena dosyayê '''[[Media:$1|$1]]'''î [$4 versiyonê $3, $2] rê reyna anî.",
'filerevert-comment' => 'Sebeb:',
'filerevert-defaultcomment' => 'Versiyonê $2, $1 rê reyna ard',
# MIME search
'mimesearch' => 'MIME bigêre',
'mimesearch-summary' => 'no pel, no tewır dosyayan MIME kontrol kena. kewteye: tipa zerreyi/tipa bıni, e.g. <tt>resim/jpeg</tt>.',
-'mimetype' => 'Tipê MIME:',
+'mimetype' => 'Babetê NIME',
'download' => 'bar ke',
# Unwatched pages
'unusedtemplateswlh' => 'linkanê binî',
# Random page
-'randompage' => 'Kamci pele ke raşt amê',
+'randompage' => 'Peleya ke raştamê',
'randompage-nopages' => 'Ena {{PLURAL:$2|cayêname|cayênameyî}} de enê pelan çin o: $1.',
# Random redirect
'disambiguations' => 'Pelayê ke maneyo bini rê gırey cı esto',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => 'satıro ewwil de ke peli ca genî; gıreyê pelê ciya-manayi mocneni. İkinci sırada <br />tiya de [[MediaWiki:Disambiguationspage]] gani heme gıreyê şablonê ciya-manayan re gıre bıdiyo',
+'disambiguations-text' => "Peleyê ke satır da sıteyên dı pelanê '''maneo bin'''i rê esteyina zeregri mocnenê. Nara satırda dıdın dı zi <br />tiya de [[MediaWiki:Disambiguationspage|Pelaya Maneo do bini ]] gani heme gıreyê şablonê ciya-manayan re gıre dayış icab keno.",
'doubleredirects' => 'Hetenayışê dıletıni',
'doubleredirectstext' => 'no pel pelê ray motışani liste keno.
'nopagetext' => 'pelê hedefi ke şıma nişane kerdo çin o.',
'pager-newer-n' => '{{PLURAL:$1|newiyer 1|newiyer $1}}',
'pager-older-n' => '{{PLURAL:$1|Kıhanyer 1|Kıhanyer $1}}',
-'suppress' => 'Kontrol',
+'suppress' => 'Çımpawıten',
'querypage-disabled' => 'Na pelaya xısusi,sebeb de performansi ra qefılneyê.',
# Book sources
'specialloguserlabel' => 'Kerdoğ:',
'speciallogtitlelabel' => 'Menzil (sernuşte yana karber):',
'log' => 'Qeydi',
-'all-logs-page' => 'Loganê umum yê hemî',
+'all-logs-page' => 'Umumi qeydi pêro',
'alllogstext' => 'qey {{SITENAME}}i mocnayişê heme rocaneyani.
tipa rocaneyi, nameyê karberi (herfa pil u qıci re hessas a), ya zi peli (reyna hessasiyê herfa pil u qıciyi) bıweçine u esayiş qıc kerê.',
'logempty' => 'qaydi de weina yew malumat çino',
'allarticles' => 'Peli pêro',
'allinnamespace' => 'Peli pênro ( $1 cayênameyî)',
'allnotinnamespace' => 'Pelanê hemî ($1 cayênameyî de niyo)',
-'allpagesprev' => 'Verêni',
+'allpagesprev' => 'Verên',
'allpagesnext' => 'ver şo',
-'allpagessubmit' => 'Şo',
+'allpagessubmit' => 'Biya',
'allpagesprefix' => 'herfê ke şıma tiya de nuşti, pê ney herfan pelê ke destpêkenê liste ker:',
'allpagesbadtitle' => 'pel o ke şıma kewenî cı, nameyê no peli de gıreyê zıwanan u wikiyi re elaqa esto, ê ra cıkewtış qebul niyo. ya zi sernameyan de karakterê qedexeyi tede esto.',
'allpages-bad-ns' => '{{SITENAME}} keyepel de wina "$1" yew nameyê cayi çino.',
'special-categories-sort-abc' => 'alfabetik rêz ker',
# Special:DeletedContributions
-'deletedcontributions' => 'Îştirakê karberî wederna',
+'deletedcontributions' => 'İştiraqê karberan de besternayına',
'deletedcontributions-title' => 'Îştirakê karberî wederna',
-'sp-deletedcontributions-contribs' => 'îştirakî',
+'sp-deletedcontributions-contribs' => 'iştıraqi',
# Special:LinkSearch
'linksearch' => 'Gıreyê teberi cı geyrê',
'email-legend' => 'karberê {{SITENAME}} binan re e-posta bıerşaw',
'emailfrom' => 'Rışten:',
'emailto' => 'Geren:',
-'emailsubject' => 'behs/mesela:',
+'emailsubject' => 'Mersel:',
'emailmessage' => 'Mesaj',
'emailsend' => 'bıerşawê/bıruşnê',
'emailccme' => 'kopyayekê mesaji mı re bıerşaw',
'emailccsubject' => '$2 kopyaya mesaj a ke şıma erşawıto/a $1:',
-'emailsent' => 'e-mail erşawiya/ruşiya',
+'emailsent' => 'E-posta bırşê',
'emailsenttext' => 'e-mailê şıma erşawiya/ruşiya',
'emailuserfooter' => 'na e-posta hetê ıney ra $1 erşawiya $2 no/na karberi/e re. pê fonksiyonê "Karberi/e re e-posta bıerşaw" no {{SITENAME}} keyepeli erşawiya.',
'removewatch' => 'Listedê mınê seyr kerdışi ra hewad',
'removedwatchtext' => 'Ena pela "[[:$1]]" biya wedariya [[Special:Watchlist|listeyê seyr-kerdışi şıma]].',
'watch' => 'bıgê seyr-kerdış',
-'watchthispage' => 'Ena pele seyr ke',
-'unwatch' => 'Endi seyr meke',
+'watchthispage' => 'Peler seyr ke',
+'unwatch' => 'Seyr meke',
'unwatchthispage' => 'temaşa kerdışê peli vındarn.',
'notanarticle' => 'mebhesê peli niyo',
'notvisiblerev' => 'Revizyon esteriyayo',
# Delete
'deletepage' => 'Pele bıestere',
-'confirm' => 'Konfirme bike',
+'confirm' => 'Testiq ke',
'excontent' => "behso kêm: '$1'",
'excontentauthor' => "behso kêm: '$1' no/na ('[[Special:Contributions/$2|$2]]' karber/e têna paşt dayo/a)",
'exbeforeblank' => "behsê verê esteriyayişi: '$1'",
'rollback' => 'vurnayişan tepiya bıger',
'rollback_short' => 'Peyser bia',
'rollbacklink' => 'peyser bia',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|vurnayış|vurnayışi}} peyd gıroti',
+'rollbacklinkcount-morethan' => '$1 {{PLURAL:$1|vurnayış|vuranyışi}} tewr peyd gırot',
'rollbackfailed' => 'Peyserardış nêbi',
'cantrollback' => 'karbero peyin têna paşt dayo, no semedi ra vuriyayiş tepiya nêgeriyeni.',
'alreadyrolled' => '[[User:$2|$2]] ([[User talk:$2|Talk]]{{int:pipe-separator}} hetê [[Special:Contributions/$2|{{int:contribslink}}]]) ra pelê ıney[[:$1]] de vurnayiş biyo u no vurnayiş tepiya nêgeriyeno;
# Restrictions (nouns)
'restriction-edit' => 'Bıvurne',
'restriction-move' => 'Ahûln',
-'restriction-create' => 'Viraze',
+'restriction-create' => 'Vıraze',
'restriction-upload' => 'Barke',
# Restriction levels
'sp-contributions-blocked-notice-anon' => 'Eno adresê IPi bloke biyo.
Cıkewtışo tewr peyêno ke bloke biyo, cêr seba referansi belikerdeyo:',
'sp-contributions-search' => 'Ser iştariqi bıgeyr',
-'sp-contributions-username' => 'Adresa IPy ya ki namey karberi:',
-'sp-contributions-toponly' => 'Teyna revizyonanê tewr peniyan bimocne',
+'sp-contributions-username' => 'Adresa IP yana namey karberi:',
+'sp-contributions-toponly' => 'Tenya rewizyonanê tewr peyniyan bimocne',
'sp-contributions-submit' => 'Cı geyre',
# What links here
'whatlinkshere-hidetrans' => 'Açarnayışê $1',
'whatlinkshere-hidelinks' => '$1 greyan',
'whatlinkshere-hideimages' => 'gireyê resimî $1',
-'whatlinkshere-filters' => 'Parzumi',
+'whatlinkshere-filters' => 'Avrêci',
# Block/unblock
'autoblockid' => 'Otomatik vındarnayış #$1',
'blockip-title' => 'Karberi kılit ke',
'blockip-legend' => 'Karber blok bike',
'blockiptext' => 'pê şuxulnayişê formê cêrıni, şıma eşkeni verniyê vurnayişkerdışê yew karberi ya zi yew IPyi bıgêrî. No têna qey verni-gırewtışê vandalizmiyo u gani şıma [[{{MediaWiki:Policy-url}}|qaydeyan]] re diqqet bıkeri. cêr de muheqqeq sebebê verni-grewtışi bınusi. (mesela: -nê- pelani de vandalizm kerdo).',
-'ipadressorusername' => 'Adresê IPî ya zi namayê karberî',
+'ipadressorusername' => 'Adresa IP yana namey karberi:',
'ipbexpiry' => 'Qedyayış:',
'ipbreason' => 'Sebeb:',
'ipbreasonotherlist' => 'Sebebê bini',
'blocklink' => 'kılit ke',
'unblocklink' => 'ake',
'change-blocklink' => 'kılit-kerdışi bıvurne',
-'contribslink' => 'iştıraki',
+'contribslink' => 'iştıraqi',
'emaillink' => 'e-poste bırışe',
'autoblocker' => 'Şıma otomatikmen kılit biy, çıke adresa şımawa \'\'IP\'\'y terefê "[[User:$1|$1]]" gureniyena.
Sebebê kılit-biyayışê $1\'i: "$2"o',
'proxyblocker' => 'blokarê proxyi',
'proxyblocker-disabled' => 'Eno fonksiyon nêxebetiyeno.',
'proxyblockreason' => 'IPadresa şıma yew proxyo akerdeyo u ey ra verniyê ey geriya.',
-'proxyblocksuccess' => 'Kar bi temam.',
+'proxyblocksuccess' => 'Qeyd ke.',
'sorbs' => 'DNSBL',
'sorbsreason' => 'IP adresa şıma, hetê no {{SITENAME}} keyepeli ra DNSBL de proxy hesibyayo u liste biyo.',
'sorbs_create_account_reason' => 'IP adresa şıma, hetê no translatewiki.net keyepeli ra DNSBL de proxy hesibyayo u liste biyo.
'cant-move-user-page' => 'desturê şıma çino, şıma pelanê karberani bıkırışi (bê pelê cerıni).',
'cant-move-to-user-page' => 'desturê şıma çino, şıma yew peli bıkırışi pelê yew karberi.',
'newtitle' => 'Nameyê newi:',
-'move-watch' => 'Ena pele seyr ke',
+'move-watch' => 'Peler seyr ke',
'movepagebtn' => 'Pele bere',
'pagemovedsub' => 'Berdışi kerd temam',
'movepage-moved' => '\'\'\'"$1" berd "$2"\'\'\'',
# Namespace 8 related
'allmessages' => 'Mesacê sistemi',
'allmessagesname' => 'Name',
-'allmessagesdefault' => 'nuşteyo orjinal',
+'allmessagesdefault' => 'Hesıbyaye metnê mesaci',
'allmessagescurrent' => 'nuşte yo ke Karyayo',
'allmessagestext' => 'na liste, listeya mesajê cayê nameyê wikimedya yo.
eke şıma qayili paşt bıdi mahalli kerdışê wikimedyayi, kerem kerê pelê [//www.mediawiki.org/wiki/Localisation mahalli kerdışê wikimedyayi] u [//translatewiki.net translatewiki.net] ziyaret bıkerê.',
'allmessagesnotsupportedDB' => "'''\$wgUseDatabaseMessages''' qefelnaye yo u ey ra '''{{ns:special}}:Allmessages''' karkerdışi re akerde niyo.",
-'allmessages-filter-legend' => 'Filitre',
+'allmessages-filter-legend' => 'Avrêc',
'allmessages-filter' => 'goreyê xususi kerdışi re filtre bıker',
'allmessages-filter-unmodified' => 'Nivurnaye',
'allmessages-filter-all' => 'Pêro',
'import-interwiki-source' => 'Çime wîkî/pel:',
'import-interwiki-history' => 'Qe eno pel, revizyonê tarixê hemî kopya bike',
'import-interwiki-templates' => 'Şablonê hemî dehil bike',
-'import-interwiki-submit' => 'Import bike',
+'import-interwiki-submit' => 'Azare de',
'import-interwiki-namespace' => 'Destinasyonê canameyî:',
'import-upload-filename' => 'Nameyê dosyayi:',
-'import-comment' => 'Xulasa:',
+'import-comment' => 'Vatış:',
'importtext' => 'Kerem ke dosyay, çımeyê wiki ra pê [[Special:Export|kırıştışê teberdayişi]] bıdê teber, Komputerê xo de qeyd kerê u bar kerê tiya.',
'importstart' => 'Pelan împort kenî',
'import-revision-count' => '$1 {{PLURAL:$1|revizyon|revizyon}}',
'tooltip-pt-anontalk' => 'vurnayiş ê ke no Ipadresi ra biyo muneqeşa bıker',
'tooltip-pt-preferences' => 'Tercihê to',
'tooltip-pt-watchlist' => 'Lista pelanê ke to gırewtê seyrkerdış',
-'tooltip-pt-mycontris' => 'Lista iştırakanê to',
+'tooltip-pt-mycontris' => 'Listey iştıraqa',
'tooltip-pt-login' => 'Seba cıkewtışi şıma rê dewato; labelê, no zeruri niyo',
'tooltip-pt-anonlogin' => 'Seba cıkewtışi şıma rê dewato; labelê, no zeruri niyo',
'tooltip-pt-logout' => 'Bıveciyên',
'newimages' => 'Galeriya dosyayan dê newan',
'imagelisttext' => "Cêr de yew listeyê '''$1''' esto {{PLURAL:$1|dosya|dosyayi}} veçiniya $2.",
'newimages-summary' => 'Ena pela xasi dosyayi ke peni de bar biyayeyi mocnane.',
-'newimages-legend' => 'Filtre',
+'newimages-legend' => 'Avrêc',
'newimages-label' => 'Nameyê dosya ( ya zi parçe ey)',
'showhidebots' => '(bota $1)',
'noimages' => 'Çik çini yo.',
'exif-imageuniqueid' => 'şınasnameyê resmê xususiyi',
'exif-gpsversionid' => 'revizyonê GPSyi',
'exif-gpslatituderef' => 'paralelê zıme û veroci',
-'exif-gpslatitude' => 'paralel',
+'exif-gpslatitude' => 'Heralem',
'exif-gpslongituderef' => 'meridyenê rocvetış û rocawavi',
-'exif-gpslongitude' => 'meridyen',
+'exif-gpslongitude' => 'Lemen',
'exif-gpsaltituderef' => 'çımeyê berziyi',
'exif-gpsaltitude' => 'berzî',
'exif-gpstimestamp' => "Wextê GPSyi (se'eta atomiki)",
'exif-attributionurl' => 'No nuşte çı wext karyayo, şıma ra reca gre dekerê de',
'exif-preferredattributionname' => 'No nuşte çı wext karyayo, Şıma ra reca morkerê',
'exif-pngfilecomment' => "Vatışê dosyada PNG'i",
-'exif-disclaimer' => 'Reddê mesuliyeti',
+'exif-disclaimer' => 'Redê mesuliyeti',
'exif-contentwarning' => 'İkazê zerreki',
'exif-giffilecomment' => "vatena dosya da GIF'i",
'exif-intellectualgenre' => 'Babeta çêki',
'exif-dc-contributor' => 'İştırakdari',
'exif-dc-coverage' => 'Heruna yana wextin grotışa medya',
'exif-dc-date' => 'Tarix(i)',
-'exif-dc-publisher' => 'Wılakar',
+'exif-dc-publisher' => 'Hesrekar',
'exif-dc-relation' => 'Medyay cı',
'exif-dc-rights' => 'Heqi',
'exif-dc-source' => 'Medyay çımi',
'limitall' => 'hemi',
# E-mail address confirmation
-'confirmemail' => 'Adresê emaîlî xo konfirme bike',
+'confirmemail' => 'Adresê e-posta tesdiq ker',
'confirmemail_noemail' => 'Yew emaîlê tu raştîyê çin o ke [[Special:Preferences|tercihê karberî]] ayar bike.',
'confirmemail_text' => 'Qey gurweyayışê e-postayê wikiyi gani veror e-postayê şıma araşt bıbo.
Adresa şıma re qey erşawıtışê e-postayê araştin, butonê cêrıni pıploxnê.
* <strong class="mw-specialpagerestricted">Peleya xısusiya ke grota verhefıza.</strong>',
'specialpages-group-maintenance' => 'Raporê pak tepiştîşî',
'specialpages-group-other' => 'Pelê xasiyê bini',
-'specialpages-group-login' => 'Cıkewtış / Hesab vıraştış',
+'specialpages-group-login' => 'Cı kewe / hesab vıraze',
'specialpages-group-changes' => 'Vurnayişê peni u logan',
'specialpages-group-media' => 'Raporê medya u bar kerdîşî',
'specialpages-group-users' => 'Karber u heqqî',
# Special:Tags
'tags' => 'Etiketê vurnayîşê raştî',
'tag-filter' => '[[Special:Tags|Tag]] filitre:',
-'tag-filter-submit' => 'Filitre',
+'tag-filter-submit' => 'Avrêc',
'tags-title' => 'Etiketan',
'tags-intro' => 'Eno pel de listeyê eyiketî este ke belki software pê ey edit kenî.',
'tags-tag' => 'Nameyê etiketi',
'logentry-move-move_redir' => '$1 pela $3 pela da $4 sera hetenayış ra ahulnê',
'logentry-move-move_redir-noredirect' => '$1 hetenayışê qeydê pela da $3 ahulnê $4 sero hetenayış vıraşt',
'logentry-patrol-patrol' => '$1 revizyonê pela da $4 $3 ke kontrol',
-'logentry-patrol-patrol-auto' => '$1 Otomatik revizyonê pela da $4 $3 ke kontrol',
+'logentry-patrol-patrol-auto' => "$1 pelay $3'i rewizyon dê $4 ya kontrol ke",
'logentry-newusers-newusers' => '$1 deye namey karberi vıraziya',
'logentry-newusers-create' => '$1 deye namey karberi vıraziya',
'logentry-newusers-create2' => "$1'i $3 rê hesab vıraşt",
# Feedback
'feedback-bugornote' => 'Jew mersela teferruato teknik esta şıma reca malumatê şıma hazıro se [ $1 jew xırab rapor] bıvinê.Zewbi zi, formê cerê xo rê şenê karfiyê. Vatışê xo pela da "[ $3 $2 ]", namey karber dê xoya piya u wasteriya karfiye.',
-'feedback-subject' => 'Muhtewa:',
+'feedback-subject' => 'Mersel:',
'feedback-message' => 'Mesac:',
'feedback-cancel' => 'Bıterkne',
'feedback-submit' => 'Peyxeberdar Bırşe',
'duration-centuries' => '$1 {{PLURAL:$1|seserre|seserri}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenyum|milenyumi}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|Dosya qebul ne vinena|dosya qebul ne vinena|Ena babeta dosya qebul ne vinena}}. Eke cırê izin deyayo se {{PLURAL:$3|Babatan dosyayan|babeta dosyayan}} de $2 bıvin.',
);
# User preference toggles
'tog-underline' => 'Wótkaze pódšmarnuś:',
'tog-justify' => 'Tekst do bloka zrownaś',
-'tog-hideminor' => 'Małe změny schowaś',
+'tog-hideminor' => 'Małe změny w aktualnych změnach schowaś',
'tog-hidepatrolled' => 'Doglědowane změny w aktualnych změnach schowaś',
'tog-newpageshidepatrolled' => 'Doglědowane boki z lisćiny nowych bokow schowaś',
'tog-extendwatchlist' => 'Wobglědowańku wócyniś, aby wšě změny pokazał, nic jano nejnowše',
'tog-usenewrc' => 'Kupkowe změny pó boku w aktualnych změnach a wobglědowanjach (trjeba JavaScript)',
'tog-numberheadings' => 'Nadpisma awtomatiski numerěrowaś',
-'tog-showtoolbar' => 'Wobźěłańsku lejstwu pokazaś (JavaScript)',
-'tog-editondblclick' => 'Boki z dwójnym kliknjenim wobźěłaś (JavaScript)',
+'tog-showtoolbar' => 'Wobźěłańsku lejstwu pokazaś (pótrjebujo JavaScript)',
+'tog-editondblclick' => 'Boki z dwójnym kliknjenim wobźěłaś (pótrjebujo JavaScript)',
'tog-editsection' => 'Wobźěłanje wótstawkow pśez wótkaze [wobźěłaś] zmóžniś',
'tog-editsectiononrightclick' => 'Wobźěłanje wótstawkow pśez kliknjenje z pšaweju tastu myški zmóžniś (JavaScript)',
'tog-showtoc' => 'Wopśimjeśe pokazaś, jolic ma bok wěcej nježli 3 nadpisma',
'youhavenewmessages' => 'Maš $1 ($2).',
'newmessageslink' => 'nowe powěsći',
'newmessagesdifflink' => 'slědna změna',
+'youhavenewmessagesfromusers' => 'Maš $1 wót {{PLURAL:$3|drugego wužywarja|$3 wužywarjowu|$3 wužywarjow|$3 wužywarjow}} ($2).',
+'youhavenewmessagesmanyusers' => 'Maš $1 wót wjele wužywarjow ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|nowa powěsć|nowej powěsći|nowe powěsći|nowe powěsći}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|slědna změna|slědnej změnje|slědne změny|slědne změny}}',
'youhavenewmessagesmulti' => 'Maš nowe powěsći: $1',
'editsection' => 'wobźěłaś',
'editold' => 'wobźěłaś',
'remembermypassword' => 'Na toś tom licadle pśizjawjony wóstaś (za maksimalnje $1 {{PLURAL:$1|źeń|dnja|dny|dnjow}})',
'securelogin-stick-https' => 'Pó pśizjawjenju z HTTPS zwězany wóstaś',
'yourdomainname' => 'Twója domejna',
+'password-change-forbidden' => 'Njamóžoš gronidła w toś tom wikiju změniś.',
'externaldberror' => 'Abo jo wustupiła eksterna zmólka awtentifikacije datoweje banki, abo njesmějoš swójo eksterne wužywarske konto aktualizěrowaś.',
'login' => 'Pśizjawiś se',
'nav-login-createaccount' => 'Pśizjawiś se/Konto załožyś',
'noarticletext-nopermission' => 'Tuchylu njejo žeden tekst na toś tom boku.
Móžoš [[Special:Search/{{PAGENAME}}|toś ten bokowy titel]] na drugich bokach pytaś
abo <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} wótpowědne protokole pytaś]</span>.',
+'missing-revision' => 'Wersija #$1 boka z mjenim "{{PAGENAME}}" njeeksistěrujo.
+
+Pśicyna jo zwětšego zestarjony wótkaz w historiji k bokoju, kótaryž jo se wulašował.
+Drobnostki móžoš w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolu wulašowanjow] namakaś.',
'userpage-userdoesnotexist' => 'Wužywarske konto "<nowiki>$1</nowiki>" njejo zregistrěrowane. Pšosym pśeglědaj, lěc coš toś ten bok wopšawdu napóraś/wobźěłaś.',
'userpage-userdoesnotexist-view' => 'Wužywarske konto "$1" njejo zregistrowane.',
'blocked-notice-logextract' => 'Toś ten wužywaŕ jo tuchylu blokěrowany.
'expansion-depth-exceeded-warning' => 'Bok jo ekspansisku dłymokosć pśekšocył',
'parser-unstrip-loop-warning' => 'Njeskóńcna kokula namakana',
'parser-unstrip-recursion-limit' => 'Rekursiska granica pśekšocona ($1)',
+'converter-manual-rule-error' => 'Zmólka w manuelnem pšawidle rěcnego konwertěrowanja namakana',
# "Undo" feature
'undo-success' => 'Wobźěłanje móžo se wótpóraś. Pšosym pśeglěduj dołojcne pśirownowanje aby se wěsty był, až to wót wěrnosći coš, a pón składuj změny, aby se wobźěłanje doskóńcnje wótpórało.',
'editundo' => 'wótwrośiś',
'diff-multi' => '({{PLURAL:$1|Jadna mjazywersija|$1 mjazywersiji|$1 mjazywersije|$1 mjazywersijow}} wót {{PLURAL:$2|jadnogo wužywarja|$2 wužywarjowu|$2 wužywarjow|$2 wužywarjow}} {{PLURAL:$1|njepokazana|njepokazanej|njepokazane|njepokazane}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Jadna mjazywersija|$1 mjazywersiji|$1 mjazywersije|$1 mjazywersijow}} wót wěcej ako {{PLURAL:$2|jadnogo wužywarja|$2 wužywarjowu|$2 wužywarjow|$2 wužywarjow}} {{PLURAL:$1|njepokazana|njepokazanej|njepokazane|njepokazane}})',
+'difference-missing-revision' => '{{PLURAL:$2|Jadna wersija|$2 wersiji|$2 wersije|$2 wersijow}} toś togo rozdźěla ($1) {{PLURAL:$2|njejo se namakała|njejstej se namakałej|njejsu namakali|njejo se namakało}}.
+
+Pśicyna jo zwětšego zestarjony diferencny wótkaz k bokoju, kótaryž jo se wulašował.
+Drobnostki móžoš w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolu wulašowanjow] namakaś.',
# Search results
'searchresults' => 'Wuslědki pytanja',
'right-writeapi' => 'writeAPI wužywaś',
'right-delete' => 'Boki wulašowaś',
'right-bigdelete' => 'lašowaś boki, kótarež maju wjelike stawizny',
+'right-deletelogentry' => 'Jadnotliwe protokolowe zapiski wulašowaś a wótnowiś',
'right-deleterevision' => 'Specifiske boki lašowaś a wótnowiś',
'right-deletedhistory' => 'Wulašowane wersiji w stawiznach se bśez pśisłušnego teksta wobglědaś',
'right-deletedtext' => 'Wulašowany tekst a změny mjazy wulašowanymi wersijami se woglědaś',
'lockmanager-fail-releaselock' => 'Zastajenje za "$1" njedajo se dopušćiś.',
'lockmanager-fail-db-bucket' => 'W zběrniku $1 njedajo se dosć zastajeńskich datowych bankow kontaktěrowaś',
'lockmanager-fail-db-release' => 'Zastajenja na datowu banku $1 njedaju se dopušćiś.',
+'lockmanager-fail-svr-acquire' => 'Zastajenja na serwer $1 njedaju se wótwołaś.',
'lockmanager-fail-svr-release' => 'Zastajenja na serwer $1 njedaju se dopušćiś.',
# ZipDirectoryReader
'disambiguations' => 'Boki, kótarež wótkazuju na boki wěcejzmysłowosći',
'disambiguationspage' => 'Template:Rozjasnjenje zapśimjeśow',
-'disambiguations-text' => 'Slědujuce boki wótkazuju na bok za rozjasnjenje zapśimjeśow.
-Wótkazujśo lubjej na pótrjefjony bok.<br />
-Bok wobjadnawa se ako bok wujasnjenja zapśimjeśa, gaž wótkazujo na nju [[MediaWiki:Disambiguationspage]].',
+'disambiguations-text' => 'Slědujuce boki wopśimuju nanejmjenjej jaden wótkaz k bokoju rozjasnjenja zapśimjeśow. Wóne by dejali město togo ku gódnjejšemu bokoju wótkazaś.<br />
+Maju bok za bok rozjasnjenja zapśimjeśow, gaž wužywa pśedłogu, na kótaruž wótkazujo se wót [[MediaWiki:Disambiguationspage]].',
'doubleredirects' => 'Dwójne dalejpósrědnjenja',
'doubleredirectstext' => 'Toś ten bok nalicujo boki, kótarež dalej pósrědnjaju na druge dalejpósrědnjenja.
'rollback' => 'Wobźěłanja slědk wześ',
'rollback_short' => 'anulěrowaś',
'rollbacklink' => 'anulěrowaś',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|změnu|změnje|změny|změnow}} slědk wześ',
+'rollbacklinkcount-morethan' => 'wěcej ako $1 {{PLURAL:$1|změnu|změnje|změny|změnow}} slědk wześ',
'rollbackfailed' => 'Slědkwześe njejo se raźiło.',
'cantrollback' => 'Njejo móžno změnu slědk wześ, slědny pśinosowaŕ jo jadnučki awtor boka.',
'alreadyrolled' => 'Njejo móžno slědnu změnu w nastawku [[:$1]] wót [[User:$2|$2]] ([[User talk:$2|diskusija]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) slědk wześ; drugi wužywaŕ jo mjaztym bok změnił abo južo slědk stajił .
* <span class="mw-specialpagerestricted">Specialne boki z wobgranicowanym pśistupom.</span>',
'specialpages-group-maintenance' => 'Wótwardowańske lisćiny',
'specialpages-group-other' => 'Druge specialne boki',
-'specialpages-group-login' => 'Pśizjawjenje',
+'specialpages-group-login' => 'Pśizjawiś/Konto załožyś',
'specialpages-group-changes' => 'Slědne změny a protokole',
'specialpages-group-media' => 'Medije',
'specialpages-group-users' => 'Wužywarje a pšawa',
'duration-millennia' => '$1 {{PLURAL:$1|lěttysac|lěttysaca|lěttysace|lěttysacow}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Zastajenja na serwer $1 njedaju se wótwołaś.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|njejo dowólony datajowy typ|njejstej dowólenej datajowej typa|njejsu dowólone datajowe typy|njejsu dowólone datajowe typy}}. {{PLURAL:$3|Dowólony datajowy typ jo|Dowólenej datajowej typa stej|Dowólone datajowe typy su}} $2.',
);
'tog-editsectiononrightclick' => 'Ενεργοποίηση επεξεργασίας τμήματος με δεξί κλικ στους τίτλους των τμημάτων (JavaScript)',
'tog-showtoc' => 'Εμφάνιση πίνακα περιεχομένων (για σελίδες με περισσότερες από τρεις κεφαλίδες)',
'tog-rememberpassword' => 'Διατήρηση της σύνδεσης μου σε αυτόν τον browser (για ένα μέγιστο $1 {{PLURAL:$1|ημέρας|ημερών}})',
-'tog-watchcreations' => 'Πρόσθεσε τις σελίδες που δημιουργώ στη λίστα παρακολούθησής μου',
-'tog-watchdefault' => 'Î Ï\81οÏ\83θήκη Ï\84Ï\89ν Ï\83ελίδÏ\89ν Ï\80οÏ\85 εÏ\80εξεÏ\81γάζεÏ\83Ï\84ε στη λίστα παρακολούθησης.',
+'tog-watchcreations' => 'Πρόσθεσε τις σελίδες που δημιουργώ και αρχεία που ανεβάζω στη λίστα παρακολούθησής μου',
+'tog-watchdefault' => 'Î Ï\81οÏ\83θήκη Ï\84Ï\89ν Ï\83ελίδÏ\89ν Ï\80οÏ\85 εÏ\80εξεÏ\81γάζομαι στη λίστα παρακολούθησης.',
'tog-watchmoves' => 'Πρόσθεσε τις σελίδες που μετακινώ στη λίστα παρακολούθησής μου',
-'tog-watchdeletion' => 'Πρόσθεσε τις σελίδες που διαγράφω στη λίστα παρακολούθησής μου',
+'tog-watchdeletion' => 'Πρόσθεσε τις σελίδες και αρχεία που διαγράφω στη λίστα παρακολούθησής μου',
'tog-minordefault' => 'Προκαθορίστε να χαρακτηρίζονται όλες οι αλλαγές "μικρής κλίμακας".',
'tog-previewontop' => 'Εμφάνιση προεπισκόπησης πριν από το πλαίσιο επεξεργασίας και όχι μετά',
'tog-previewonfirst' => 'Εμφάνιση προεπισκόπησης κατά την πρώτη επεξεργασία',
'passwordreset' => 'Κωδικός επαναφοράς',
'passwordreset-text' => 'Συμπληρώστε αυτή τη φόρμα για να λάβετε ένα e-mail υπενθύμιση του λογαριασμού σας.',
'passwordreset-legend' => 'Επαναφορά κωδικού πρόσβασης',
-'passwordreset-disabled' => 'Î\97 ανάκÏ\84ηÏ\83η κÏ\89δικοÏ\8d Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 ÎÏ\87οÏ\85ν αÏ\80ενεÏ\81γοÏ\80οιηθεί Ï\83ε αÏ\85Ï\84Ï\8c Ï\84ο βίκι',
+'passwordreset-disabled' => 'Î\97 εÏ\80αναÏ\86οÏ\81ά κÏ\89δικοÏ\8d Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 ÎÏ\87ει αÏ\80ενεÏ\81γοÏ\80οιηθεί Ï\83ε αÏ\85Ï\84Ï\8c Ï\84ο wiki',
'passwordreset-pretext' => '{{PLURAL:$1||Εισάγεται ένα από τα στοιχεία δεδομένων που βλέπετε παρακάτω}}',
'passwordreset-username' => 'Όνομα χρήστη:',
'passwordreset-domain' => 'Domain:',
'nosuchsectiontitle' => 'Δεν υπάρχει τέτοιο τμήμα',
'nosuchsectiontext' => 'Προσπαθήσατε να επεξεργαστείτε μια ενότητα που δεν υπάρχει. Μπορεί να έχει μετακινηθεί ή διαγραφεί, ενώ βλέπατε τη σελίδα.',
'loginreqtitle' => 'Απαιτείται η σύνδεση του χρήστη.',
-'loginreqlink' => 'είσοδος',
+'loginreqlink' => 'συνδεθείτε',
'loginreqpagetext' => 'Πρέπει να $1 για να δείτε άλλες σελίδες.',
'accmailtitle' => 'Ο κωδικός έχει σταλεί.',
'accmailtext' => "Ο τυχαία παρηγμένος κωδικός για τον/την [[User talk:$1|$1]] έχει σταλεί στο $2.
'shown-title' => 'Εμφάνιση $1 {{PLURAL:$1|αποτελέσματος|αποτελεσμάτων}} ανά σελίδα',
'viewprevnext' => 'Εμφάνιση ($1 {{int:pipe-separator}} $2) ($3).',
'searchmenu-legend' => 'Επιλογές αναζήτησης',
-'searchmenu-exists' => "'''Υπάρχει μια σελίδα που ονομάζεται \"[[:\$1]]\" σε αυτό το βίκι'''",
-'searchmenu-new' => "'''Δημιουργήστε τη σελίδα \"[[:\$1]]\" σε αυτό το βίκι!'''",
+'searchmenu-exists' => "'''Υπάρχει μια σελίδα που ονομάζεται \"[[:\$1]]\" σε αυτό το wiki'''",
+'searchmenu-new' => "'''Δημιουργήστε τη σελίδα \"[[:\$1]]\" σε αυτό το wiki!'''",
'searchhelp-url' => 'Help:Περιεχόμενα',
'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Πλοηγηθείτε σε σελίδες με αυτό το πρόθεμα]]',
'searchprofile-articles' => 'Σελίδες περιεχομένων',
'action-unwatchedpages' => 'να εμφανίσετε τον κατάλογο μη παρακολουθούμενων σελίδων',
'action-mergehistory' => 'να συγχωνεύσετε το ιστορικό αυτής της σελίδας',
'action-userrights' => 'να επεξεργαστείτε όλα τα δικαιώματα χρηστών',
-'action-userrights-interwiki' => 'να εÏ\80εξεÏ\81γαÏ\83Ï\84είÏ\84ε Ï\84α δικαιÏ\8eμαÏ\84α Ï\87Ï\81ηÏ\83Ï\84Ï\8eν Ï\83ε άλλα βίκι',
+'action-userrights-interwiki' => 'εÏ\80εξεÏ\81γαÏ\83Ï\84είÏ\84ε Ï\84α δικαιÏ\8eμαÏ\84α Ï\87Ï\81ηÏ\83Ï\84Ï\8eν Ï\83ε άλλα wiki',
'action-siteadmin' => 'να κλειδώσετε ή ξεκλειδώσετε τη βάση δεδομένων',
'action-sendemail' => 'αποστείλετε μηνύματα ηλεκτρονικού ταχυδρομείου',
'backend-fail-internal' => 'Παρουσιάστηκε ένα άγνωστο σφάλμα στην αποθήκευση παρασκηνίου "$1".',
'backend-fail-contenttype' => 'Δεν μπόρεσε να προσδιοριστεί ο τύπος περιεχομένου του αρχείου για την αποθήκευση σε "$1".',
+# Lock manager
+'lockmanager-notlocked' => 'Δεν μπορέσατε να ξεκλειδώσετε το " $1 ". Δεν είναι κλειδωμένο.',
+'lockmanager-fail-closelock' => 'Δεν μπόρεσε να κλείσει το κλειδωμένο αρχείο για " $1 ".',
+
# ZipDirectoryReader
'zip-file-open-error' => 'Παρουσιάστηκε σφάλμα κατά το άνοιγμα του αρχείου για ZIP ελέγχους.',
'zip-wrong-format' => 'Το καθορισμένο αρχείο δεν ήταν αρχείο ZIP.',
'disambiguations' => 'Σελίδες με συνδέσμους σε σελίδες αποσαφήνισης',
'disambiguationspage' => 'Project:Σύνδεσμοι_προς_τις_σελίδες_αποσαφήνισης',
-'disambiguations-text' => "Οι ακόλουθες σελίδες συνδέουν σε μια '''σελίδα αποσαφήνισης'''.
-Αντιθέτως πρέπει να συνδέουν στο κατάλληλο θέμα.<br />
-Î\9cια Ï\83ελίδα μεÏ\84αÏ\87ειÏ\81ίζεται ως σελίδα αποσαφήνισης αν χρησιμοποιεί ένα πρότυπο το οποίο συνδέεται από το [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Οι ακόλουθες σελίδες οδηγούν σε μια '''σελίδα αποσαφήνισης'''.
+Αντιθέτως θα έπρεπε να κατευθύνουν στο κατάλληλο θέμα.<br />
+Î\9cια Ï\83ελίδα ανÏ\84ιμεÏ\84Ï\89Ï\80ίζεται ως σελίδα αποσαφήνισης αν χρησιμοποιεί ένα πρότυπο το οποίο συνδέεται από το [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Διπλές ανακατευθύνσεις',
'doubleredirectstext' => 'Αυτή η σελίδα συγκαταλέγει σελίδες οι οποίες ανακατευθύνουν σε άλλες σελίδες ανακατεύθυνσης. Κάθε σειρά περιέχει συνδέσμους προς την πρώτη και τη δεύτερη σελίδα ανακατεύθυνσης, όπως επίσης και την πρώτη αράδα του κειμένου στη δεύτερη σελίδα ανακατεύθυνσης η οποία και είναι, κανονικά, ο πραγματικός προορισμός της ανακατεύθυνσης -εκεί δηλαδή όπου θα έπρεπε να είχατε οδηγηθεί από την αρχή. Τα <del>διεγραμμένα</del> λήμματα έχουν επιλυθεί.',
'emailtarget' => 'Εισαγάγετε το όνομα χρήστη του παραλήπτη',
'emailusername' => 'Όνομα χρήστη:',
'emailusernamesubmit' => 'Υποβολή',
-'email-legend' => 'Αποστολή μηνύματος ηλ. ταχυδρομείου σε έναν άλλο χρήστη του βίκι {{SITENAME}}',
+'email-legend' => 'Αποστολή μηνύματος ηλ. ταχυδρομείου σε έναν άλλο χρήστη του wiki {{SITENAME}}',
'emailfrom' => 'Από:',
'emailto' => 'Προς:',
'emailsubject' => 'Θέμα:',
# Export
'export' => 'Εξαγωγή σελίδων',
-'exporttext' => 'Μπορείτε να κάνετε εξαγωγή του κειμένου και του ιστορικού επεξεργασίας μιας συγκεκριμένης σελίδας (ή περισσοτέρων σελίδων που έχουν ομαδοποιηθεί με χρήση XML). Μπορείτε να κάνετε εισαγωγή αυτού σε ένα άλλο βίκι χρησιμοποιώντας MediaWiki μέσω τής [[Special:Import|σελίδας εισαγωγής]].
+'exporttext' => 'Μπορείτε να κάνετε εξαγωγή του κειμένου και του ιστορικού επεξεργασίας μιας συγκεκριμένης σελίδας (ή περισσοτέρων σελίδων που έχουν ομαδοποιηθεί με χρήση XML). Μπορείτε να κάνετε εισαγωγή αυτού σε ένα άλλο wiki χρησιμοποιώντας MediaWiki μέσω τής [[Special:Import|σελίδας εισαγωγής]].
Για την εξαγωγή ολόκληρων άρθρων, συμπληρώστε τους τίτλους στο παρακάτω πλαίσιο (ένα τίτλο σε κάθε σειρά) και επιλέξτε ανάμεσα από το να εξαγάγετε μόνο την τρέχουσα έκδοση (με τις πληροφορίες της πιο πρόσφατης επεξεργασίας) ή εναλλακτικά και τις παλιότερες εκδόσεις (με τις αντίστοιχες καταγραφές στη σελιδα του ιστορικού).
'import-interwiki-namespace' => 'Προορισμός στην περιοχή ονομάτων:',
'import-upload-filename' => 'Όνομα αρχείου:',
'import-comment' => 'Σχόλιο:',
-'importtext' => 'Παρακαλούμε εξάγετε το αρχείο από το πηγαίο βίκι (χρησιμοποιώντας το [[Special:Export|εργαλείο εξαγωγής]]), αποθηκεύστε το στον υπολογιστή σας και μεταφορτώστε το από εκεί.',
+'importtext' => 'Παρακαλούμε εξάγετε το αρχείο από το πηγαίο wiki (χρησιμοποιώντας το [[Special:Export|εργαλείο εξαγωγής]]), αποθηκεύστε το στον υπολογιστή σας και μεταφορτώστε το από εκεί.',
'importstart' => 'Η εισαγωγή των σελίδων είναι σε εξέλιξη...',
'import-revision-count' => '$1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}}',
'importnopages' => 'Δεν υπάρχουν σελίδες για εισαγωγή.',
'import-invalid-interwiki' => 'Δεν είναι δυνατή η εισαγωγή από το καθορισμένο wiki.',
'import-error-edit' => 'Η σελίδα "$1" δεν εισήχθηκε επειδή δεν επιτρέπεται να το επεξεργαστείτε.',
'import-error-create' => 'Η σελίδα "$1" δεν εισήχθηκε επειδή δεν επιτρέπεται να τη δημιουργήσετε.',
+'import-error-interwiki' => 'Η σελίδα " $1 " δεν έχει εισαχθεί, επειδή το όνομα της δεσμευμένο για εξωτερική σύνδεση (interwiki).',
# Import log
'importlogpage' => 'Αρχείο καταγραφής εισαγωγών',
'version-hook-subscribedby' => 'Υπογεγραμμένο από',
'version-version' => '(Έκδοση $1)',
'version-license' => 'Άδεια χρήσης',
-'version-poweredby-credits' => "Αυτό το βίκι λειτουργεί χάρις στο '''[//www.mediawiki.org/ MediaWiki]''', πνευματική ιδιοκτησία © 2001-$1 $2.",
+'version-poweredby-credits' => "Αυτό το wiki λειτουργεί με στο '''[//www.mediawiki.org/ MediaWiki]''', πνευματική ιδιοκτησία © 2001-$1 $2.",
'version-poweredby-others' => 'άλλοι',
'version-license-info' => "Το MediaWiki είναι ελεύθερο λογισμικό. Μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό τους όρους της άδειας GNU General Public License όπως αυτή εκδόθηκε από το Free Software Foundation· είτε της δεύτερης έκδοσης της άδειας, είτε (κατ' επιλογή σας) οποιασδήποτε επόμενης έκδοσης.
'compare-revision-not-exists' => 'Η αναθεώρηση που καθορίσατε δεν υπάρχει.',
# Database error messages
-'dberr-header' => 'Αυτό το βίκι έχει ένα πρόβλημα',
+'dberr-header' => 'Αυτό το wiki έχει ένα πρόβλημα',
'dberr-problems' => 'Λυπούμαστε! Αυτός ο ιστότοπος αντιμετωπίζει τεχνικές δυσκολίες.',
'dberr-again' => 'Δοκιμάστε να περιμενένετε λίγα λεπτά και να ανανεώσετε.',
'dberr-info' => '(Δεν μπορεί να επικοινωνήσει με τον εξυπηρετητή της βάσης δεδομένων: $1)',
'logentry-delete-delete' => 'Ο/η $1 διέγραψε τη σελίδα $3',
'logentry-delete-restore' => 'Ο/η $1 αποκατέστησε τη σελίδα $3',
'logentry-delete-event' => '$1 άλλαξε την ορατότητα σε {{PLURAL:$5|ένα γεγονός καταγραφής|$5 log events}} στο $3: $4',
+'logentry-delete-revision-legacy' => '$1 άλλαξε την ορατότητα των αναθεωρήσεων στη σελίδα $3',
'logentry-suppress-delete' => 'Ο $1 διέγραψε τη σελίδα $3',
+'logentry-suppress-revision-legacy' => '$1 κρυφά άλλαξαν την ορατότητα των αναθεωρήσεων στη σελίδα $3',
'revdelete-content-hid' => 'το περιεχόμενο αποκρύφθηκε',
'revdelete-summary-hid' => 'Η σύνοψη επεξεργασίας αποκρύφθηκε',
'revdelete-uname-hid' => 'Το όνομα χρήστη αποκρύφθηκε',
'duration-centuries' => '$1 {{PLURAL:$1|Αιώνα|αιώνες}}',
'duration-millennia' => '$1 {{PLURAL:$1|Χιλιετία|Χιλιετίες}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 δεν είναι {{PLURAL:$4|επιτρεπόμενος τύπος αρχείου|επιτρεπόμενοι τύποι αρχείων}}. {{PLURAL:$3|Επιτρεπόμενος τύπος αρχείων|Επιτρεπόμενοι τύποι αρχείων}} είναι $2.',
);
* This array can be modified at runtime with the LanguageGetMagic hook
*/
$magicWords = array(
-# ID CASE SYNONYMS
- 'redirect' => array( 0, '#REDIRECT' ),
- 'notoc' => array( 0, '__NOTOC__' ),
- 'nogallery' => array( 0, '__NOGALLERY__' ),
- 'forcetoc' => array( 0, '__FORCETOC__' ),
- 'toc' => array( 0, '__TOC__' ),
- 'noeditsection' => array( 0, '__NOEDITSECTION__' ),
- 'noheader' => array( 0, '__NOHEADER__' ),
- 'currentmonth' => array( 1, 'CURRENTMONTH', 'CURRENTMONTH2' ),
- 'currentmonth1' => array( 1, 'CURRENTMONTH1' ),
- 'currentmonthname' => array( 1, 'CURRENTMONTHNAME' ),
- 'currentmonthnamegen' => array( 1, 'CURRENTMONTHNAMEGEN' ),
- 'currentmonthabbrev' => array( 1, 'CURRENTMONTHABBREV' ),
- 'currentday' => array( 1, 'CURRENTDAY' ),
- 'currentday2' => array( 1, 'CURRENTDAY2' ),
- 'currentdayname' => array( 1, 'CURRENTDAYNAME' ),
- 'currentyear' => array( 1, 'CURRENTYEAR' ),
- 'currenttime' => array( 1, 'CURRENTTIME' ),
- 'currenthour' => array( 1, 'CURRENTHOUR' ),
- 'localmonth' => array( 1, 'LOCALMONTH', 'LOCALMONTH2' ),
- 'localmonth1' => array( 1, 'LOCALMONTH1' ),
- 'localmonthname' => array( 1, 'LOCALMONTHNAME' ),
- 'localmonthnamegen' => array( 1, 'LOCALMONTHNAMEGEN' ),
- 'localmonthabbrev' => array( 1, 'LOCALMONTHABBREV' ),
- 'localday' => array( 1, 'LOCALDAY' ),
- 'localday2' => array( 1, 'LOCALDAY2' ),
- 'localdayname' => array( 1, 'LOCALDAYNAME' ),
- 'localyear' => array( 1, 'LOCALYEAR' ),
- 'localtime' => array( 1, 'LOCALTIME' ),
- 'localhour' => array( 1, 'LOCALHOUR' ),
- 'numberofpages' => array( 1, 'NUMBEROFPAGES' ),
- 'numberofarticles' => array( 1, 'NUMBEROFARTICLES' ),
- 'numberoffiles' => array( 1, 'NUMBEROFFILES' ),
- 'numberofusers' => array( 1, 'NUMBEROFUSERS' ),
- 'numberofactiveusers' => array( 1, 'NUMBEROFACTIVEUSERS' ),
- 'numberofedits' => array( 1, 'NUMBEROFEDITS' ),
- 'numberofviews' => array( 1, 'NUMBEROFVIEWS' ),
- 'pagename' => array( 1, 'PAGENAME' ),
- 'pagenamee' => array( 1, 'PAGENAMEE' ),
- 'namespace' => array( 1, 'NAMESPACE' ),
- 'namespacee' => array( 1, 'NAMESPACEE' ),
- 'namespacenumber' => array( 1, 'NAMESPACENUMBER' ),
- 'talkspace' => array( 1, 'TALKSPACE' ),
- 'talkspacee' => array( 1, 'TALKSPACEE' ),
- 'subjectspace' => array( 1, 'SUBJECTSPACE', 'ARTICLESPACE' ),
- 'subjectspacee' => array( 1, 'SUBJECTSPACEE', 'ARTICLESPACEE' ),
- 'fullpagename' => array( 1, 'FULLPAGENAME' ),
- 'fullpagenamee' => array( 1, 'FULLPAGENAMEE' ),
- 'subpagename' => array( 1, 'SUBPAGENAME' ),
- 'subpagenamee' => array( 1, 'SUBPAGENAMEE' ),
- 'basepagename' => array( 1, 'BASEPAGENAME' ),
- 'basepagenamee' => array( 1, 'BASEPAGENAMEE' ),
- 'talkpagename' => array( 1, 'TALKPAGENAME' ),
- 'talkpagenamee' => array( 1, 'TALKPAGENAMEE' ),
- 'subjectpagename' => array( 1, 'SUBJECTPAGENAME', 'ARTICLEPAGENAME' ),
- 'subjectpagenamee' => array( 1, 'SUBJECTPAGENAMEE', 'ARTICLEPAGENAMEE' ),
- 'msg' => array( 0, 'MSG:' ),
- 'subst' => array( 0, 'SUBST:' ),
- 'safesubst' => array( 0, 'SAFESUBST:' ),
- 'msgnw' => array( 0, 'MSGNW:' ),
- 'img_thumbnail' => array( 1, 'thumbnail', 'thumb' ),
- 'img_manualthumb' => array( 1, 'thumbnail=$1', 'thumb=$1' ),
- 'img_right' => array( 1, 'right' ),
- 'img_left' => array( 1, 'left' ),
- 'img_none' => array( 1, 'none' ),
- 'img_width' => array( 1, '$1px' ),
- 'img_center' => array( 1, 'center', 'centre' ),
- 'img_framed' => array( 1, 'framed', 'enframed', 'frame' ),
- 'img_frameless' => array( 1, 'frameless' ),
- 'img_page' => array( 1, 'page=$1', 'page $1' ),
- 'img_upright' => array( 1, 'upright', 'upright=$1', 'upright $1' ),
- 'img_border' => array( 1, 'border' ),
- 'img_baseline' => array( 1, 'baseline' ),
- 'img_sub' => array( 1, 'sub' ),
- 'img_super' => array( 1, 'super', 'sup' ),
- 'img_top' => array( 1, 'top' ),
- 'img_text_top' => array( 1, 'text-top' ),
- 'img_middle' => array( 1, 'middle' ),
- 'img_bottom' => array( 1, 'bottom' ),
- 'img_text_bottom' => array( 1, 'text-bottom' ),
- 'img_link' => array( 1, 'link=$1' ),
- 'img_alt' => array( 1, 'alt=$1' ),
- 'int' => array( 0, 'INT:' ),
- 'sitename' => array( 1, 'SITENAME' ),
- 'ns' => array( 0, 'NS:' ),
- 'nse' => array( 0, 'NSE:' ),
- 'localurl' => array( 0, 'LOCALURL:' ),
- 'localurle' => array( 0, 'LOCALURLE:' ),
- 'articlepath' => array( 0, 'ARTICLEPATH' ),
- 'pageid' => array( 0, 'PAGEID' ),
- 'server' => array( 0, 'SERVER' ),
- 'servername' => array( 0, 'SERVERNAME' ),
- 'scriptpath' => array( 0, 'SCRIPTPATH' ),
- 'stylepath' => array( 0, 'STYLEPATH' ),
- 'grammar' => array( 0, 'GRAMMAR:' ),
- 'gender' => array( 0, 'GENDER:' ),
- 'notitleconvert' => array( 0, '__NOTITLECONVERT__', '__NOTC__' ),
- 'nocontentconvert' => array( 0, '__NOCONTENTCONVERT__', '__NOCC__' ),
- 'currentweek' => array( 1, 'CURRENTWEEK' ),
- 'currentdow' => array( 1, 'CURRENTDOW' ),
- 'localweek' => array( 1, 'LOCALWEEK' ),
- 'localdow' => array( 1, 'LOCALDOW' ),
- 'revisionid' => array( 1, 'REVISIONID' ),
- 'revisionday' => array( 1, 'REVISIONDAY' ),
- 'revisionday2' => array( 1, 'REVISIONDAY2' ),
- 'revisionmonth' => array( 1, 'REVISIONMONTH' ),
- 'revisionmonth1' => array( 1, 'REVISIONMONTH1' ),
- 'revisionyear' => array( 1, 'REVISIONYEAR' ),
- 'revisiontimestamp' => array( 1, 'REVISIONTIMESTAMP' ),
- 'revisionuser' => array( 1, 'REVISIONUSER' ),
- 'plural' => array( 0, 'PLURAL:' ),
- 'fullurl' => array( 0, 'FULLURL:' ),
- 'fullurle' => array( 0, 'FULLURLE:' ),
- 'canonicalurl' => array( 0, 'CANONICALURL:' ),
- 'canonicalurle' => array( 0, 'CANONICALURLE:' ),
- 'lcfirst' => array( 0, 'LCFIRST:' ),
- 'ucfirst' => array( 0, 'UCFIRST:' ),
- 'lc' => array( 0, 'LC:' ),
- 'uc' => array( 0, 'UC:' ),
- 'raw' => array( 0, 'RAW:' ),
- 'displaytitle' => array( 1, 'DISPLAYTITLE' ),
- 'rawsuffix' => array( 1, 'R' ),
- 'newsectionlink' => array( 1, '__NEWSECTIONLINK__' ),
- 'nonewsectionlink' => array( 1, '__NONEWSECTIONLINK__' ),
- 'currentversion' => array( 1, 'CURRENTVERSION' ),
- 'urlencode' => array( 0, 'URLENCODE:' ),
- 'anchorencode' => array( 0, 'ANCHORENCODE' ),
- 'currenttimestamp' => array( 1, 'CURRENTTIMESTAMP' ),
- 'localtimestamp' => array( 1, 'LOCALTIMESTAMP' ),
- 'directionmark' => array( 1, 'DIRECTIONMARK', 'DIRMARK' ),
- 'language' => array( 0, '#LANGUAGE:' ),
- 'contentlanguage' => array( 1, 'CONTENTLANGUAGE', 'CONTENTLANG' ),
- 'pagesinnamespace' => array( 1, 'PAGESINNAMESPACE:', 'PAGESINNS:' ),
- 'numberofadmins' => array( 1, 'NUMBEROFADMINS' ),
- 'formatnum' => array( 0, 'FORMATNUM' ),
- 'padleft' => array( 0, 'PADLEFT' ),
- 'padright' => array( 0, 'PADRIGHT' ),
- 'special' => array( 0, 'special', ),
- 'speciale' => array( 0, 'speciale', ),
- 'defaultsort' => array( 1, 'DEFAULTSORT:', 'DEFAULTSORTKEY:', 'DEFAULTCATEGORYSORT:' ),
- 'filepath' => array( 0, 'FILEPATH:' ),
- 'tag' => array( 0, 'tag' ),
- 'hiddencat' => array( 1, '__HIDDENCAT__' ),
- 'pagesincategory' => array( 1, 'PAGESINCATEGORY', 'PAGESINCAT' ),
- 'pagesize' => array( 1, 'PAGESIZE' ),
- 'index' => array( 1, '__INDEX__' ),
- 'noindex' => array( 1, '__NOINDEX__' ),
- 'numberingroup' => array( 1, 'NUMBERINGROUP', 'NUMINGROUP' ),
- 'staticredirect' => array( 1, '__STATICREDIRECT__' ),
- 'protectionlevel' => array( 1, 'PROTECTIONLEVEL' ),
- 'formatdate' => array( 0, 'formatdate', 'dateformat' ),
- 'url_path' => array( 0, 'PATH' ),
- 'url_wiki' => array( 0, 'WIKI' ),
- 'url_query' => array( 0, 'QUERY' ),
- 'defaultsort_noerror' => array( 0, 'noerror' ),
- 'defaultsort_noreplace' => array( 0, 'noreplace' ),
- 'pagesincategory_all' => array( 0, 'all' ),
- 'pagesincategory_pages' => array( 0, 'pages' ),
- 'pagesincategory_subcats' => array( 0, 'subcats' ),
- 'pagesincategory_files' => array( 0, 'files' ),
+# ID CASE SYNONYMS
+ 'redirect' => array( 0, '#REDIRECT' ),
+ 'notoc' => array( 0, '__NOTOC__' ),
+ 'nogallery' => array( 0, '__NOGALLERY__' ),
+ 'forcetoc' => array( 0, '__FORCETOC__' ),
+ 'toc' => array( 0, '__TOC__' ),
+ 'noeditsection' => array( 0, '__NOEDITSECTION__' ),
+ 'noheader' => array( 0, '__NOHEADER__' ),
+ 'currentmonth' => array( 1, 'CURRENTMONTH', 'CURRENTMONTH2' ),
+ 'currentmonth1' => array( 1, 'CURRENTMONTH1' ),
+ 'currentmonthname' => array( 1, 'CURRENTMONTHNAME' ),
+ 'currentmonthnamegen' => array( 1, 'CURRENTMONTHNAMEGEN' ),
+ 'currentmonthabbrev' => array( 1, 'CURRENTMONTHABBREV' ),
+ 'currentday' => array( 1, 'CURRENTDAY' ),
+ 'currentday2' => array( 1, 'CURRENTDAY2' ),
+ 'currentdayname' => array( 1, 'CURRENTDAYNAME' ),
+ 'currentyear' => array( 1, 'CURRENTYEAR' ),
+ 'currenttime' => array( 1, 'CURRENTTIME' ),
+ 'currenthour' => array( 1, 'CURRENTHOUR' ),
+ 'localmonth' => array( 1, 'LOCALMONTH', 'LOCALMONTH2' ),
+ 'localmonth1' => array( 1, 'LOCALMONTH1' ),
+ 'localmonthname' => array( 1, 'LOCALMONTHNAME' ),
+ 'localmonthnamegen' => array( 1, 'LOCALMONTHNAMEGEN' ),
+ 'localmonthabbrev' => array( 1, 'LOCALMONTHABBREV' ),
+ 'localday' => array( 1, 'LOCALDAY' ),
+ 'localday2' => array( 1, 'LOCALDAY2' ),
+ 'localdayname' => array( 1, 'LOCALDAYNAME' ),
+ 'localyear' => array( 1, 'LOCALYEAR' ),
+ 'localtime' => array( 1, 'LOCALTIME' ),
+ 'localhour' => array( 1, 'LOCALHOUR' ),
+ 'numberofpages' => array( 1, 'NUMBEROFPAGES' ),
+ 'numberofarticles' => array( 1, 'NUMBEROFARTICLES' ),
+ 'numberoffiles' => array( 1, 'NUMBEROFFILES' ),
+ 'numberofusers' => array( 1, 'NUMBEROFUSERS' ),
+ 'numberofactiveusers' => array( 1, 'NUMBEROFACTIVEUSERS' ),
+ 'numberofedits' => array( 1, 'NUMBEROFEDITS' ),
+ 'numberofviews' => array( 1, 'NUMBEROFVIEWS' ),
+ 'pagename' => array( 1, 'PAGENAME' ),
+ 'pagenamee' => array( 1, 'PAGENAMEE' ),
+ 'namespace' => array( 1, 'NAMESPACE' ),
+ 'namespacee' => array( 1, 'NAMESPACEE' ),
+ 'namespacenumber' => array( 1, 'NAMESPACENUMBER' ),
+ 'talkspace' => array( 1, 'TALKSPACE' ),
+ 'talkspacee' => array( 1, 'TALKSPACEE' ),
+ 'subjectspace' => array( 1, 'SUBJECTSPACE', 'ARTICLESPACE' ),
+ 'subjectspacee' => array( 1, 'SUBJECTSPACEE', 'ARTICLESPACEE' ),
+ 'fullpagename' => array( 1, 'FULLPAGENAME' ),
+ 'fullpagenamee' => array( 1, 'FULLPAGENAMEE' ),
+ 'subpagename' => array( 1, 'SUBPAGENAME' ),
+ 'subpagenamee' => array( 1, 'SUBPAGENAMEE' ),
+ 'basepagename' => array( 1, 'BASEPAGENAME' ),
+ 'basepagenamee' => array( 1, 'BASEPAGENAMEE' ),
+ 'talkpagename' => array( 1, 'TALKPAGENAME' ),
+ 'talkpagenamee' => array( 1, 'TALKPAGENAMEE' ),
+ 'subjectpagename' => array( 1, 'SUBJECTPAGENAME', 'ARTICLEPAGENAME' ),
+ 'subjectpagenamee' => array( 1, 'SUBJECTPAGENAMEE', 'ARTICLEPAGENAMEE' ),
+ 'msg' => array( 0, 'MSG:' ),
+ 'subst' => array( 0, 'SUBST:' ),
+ 'safesubst' => array( 0, 'SAFESUBST:' ),
+ 'msgnw' => array( 0, 'MSGNW:' ),
+ 'img_thumbnail' => array( 1, 'thumbnail', 'thumb' ),
+ 'img_manualthumb' => array( 1, 'thumbnail=$1', 'thumb=$1' ),
+ 'img_right' => array( 1, 'right' ),
+ 'img_left' => array( 1, 'left' ),
+ 'img_none' => array( 1, 'none' ),
+ 'img_width' => array( 1, '$1px' ),
+ 'img_center' => array( 1, 'center', 'centre' ),
+ 'img_framed' => array( 1, 'framed', 'enframed', 'frame' ),
+ 'img_frameless' => array( 1, 'frameless' ),
+ 'img_page' => array( 1, 'page=$1', 'page $1' ),
+ 'img_upright' => array( 1, 'upright', 'upright=$1', 'upright $1' ),
+ 'img_border' => array( 1, 'border' ),
+ 'img_baseline' => array( 1, 'baseline' ),
+ 'img_sub' => array( 1, 'sub' ),
+ 'img_super' => array( 1, 'super', 'sup' ),
+ 'img_top' => array( 1, 'top' ),
+ 'img_text_top' => array( 1, 'text-top' ),
+ 'img_middle' => array( 1, 'middle' ),
+ 'img_bottom' => array( 1, 'bottom' ),
+ 'img_text_bottom' => array( 1, 'text-bottom' ),
+ 'img_link' => array( 1, 'link=$1' ),
+ 'img_alt' => array( 1, 'alt=$1' ),
+ 'int' => array( 0, 'INT:' ),
+ 'sitename' => array( 1, 'SITENAME' ),
+ 'ns' => array( 0, 'NS:' ),
+ 'nse' => array( 0, 'NSE:' ),
+ 'localurl' => array( 0, 'LOCALURL:' ),
+ 'localurle' => array( 0, 'LOCALURLE:' ),
+ 'articlepath' => array( 0, 'ARTICLEPATH' ),
+ 'pageid' => array( 0, 'PAGEID' ),
+ 'server' => array( 0, 'SERVER' ),
+ 'servername' => array( 0, 'SERVERNAME' ),
+ 'scriptpath' => array( 0, 'SCRIPTPATH' ),
+ 'stylepath' => array( 0, 'STYLEPATH' ),
+ 'grammar' => array( 0, 'GRAMMAR:' ),
+ 'gender' => array( 0, 'GENDER:' ),
+ 'notitleconvert' => array( 0, '__NOTITLECONVERT__', '__NOTC__' ),
+ 'nocontentconvert' => array( 0, '__NOCONTENTCONVERT__', '__NOCC__' ),
+ 'currentweek' => array( 1, 'CURRENTWEEK' ),
+ 'currentdow' => array( 1, 'CURRENTDOW' ),
+ 'localweek' => array( 1, 'LOCALWEEK' ),
+ 'localdow' => array( 1, 'LOCALDOW' ),
+ 'revisionid' => array( 1, 'REVISIONID' ),
+ 'revisionday' => array( 1, 'REVISIONDAY' ),
+ 'revisionday2' => array( 1, 'REVISIONDAY2' ),
+ 'revisionmonth' => array( 1, 'REVISIONMONTH' ),
+ 'revisionmonth1' => array( 1, 'REVISIONMONTH1' ),
+ 'revisionyear' => array( 1, 'REVISIONYEAR' ),
+ 'revisiontimestamp' => array( 1, 'REVISIONTIMESTAMP' ),
+ 'revisionuser' => array( 1, 'REVISIONUSER' ),
+ 'plural' => array( 0, 'PLURAL:' ),
+ 'fullurl' => array( 0, 'FULLURL:' ),
+ 'fullurle' => array( 0, 'FULLURLE:' ),
+ 'canonicalurl' => array( 0, 'CANONICALURL:' ),
+ 'canonicalurle' => array( 0, 'CANONICALURLE:' ),
+ 'lcfirst' => array( 0, 'LCFIRST:' ),
+ 'ucfirst' => array( 0, 'UCFIRST:' ),
+ 'lc' => array( 0, 'LC:' ),
+ 'uc' => array( 0, 'UC:' ),
+ 'raw' => array( 0, 'RAW:' ),
+ 'displaytitle' => array( 1, 'DISPLAYTITLE' ),
+ 'rawsuffix' => array( 1, 'R' ),
+ 'newsectionlink' => array( 1, '__NEWSECTIONLINK__' ),
+ 'nonewsectionlink' => array( 1, '__NONEWSECTIONLINK__' ),
+ 'currentversion' => array( 1, 'CURRENTVERSION' ),
+ 'urlencode' => array( 0, 'URLENCODE:' ),
+ 'anchorencode' => array( 0, 'ANCHORENCODE' ),
+ 'currenttimestamp' => array( 1, 'CURRENTTIMESTAMP' ),
+ 'localtimestamp' => array( 1, 'LOCALTIMESTAMP' ),
+ 'directionmark' => array( 1, 'DIRECTIONMARK', 'DIRMARK' ),
+ 'language' => array( 0, '#LANGUAGE:' ),
+ 'contentlanguage' => array( 1, 'CONTENTLANGUAGE', 'CONTENTLANG' ),
+ 'pagesinnamespace' => array( 1, 'PAGESINNAMESPACE:', 'PAGESINNS:' ),
+ 'numberofadmins' => array( 1, 'NUMBEROFADMINS' ),
+ 'formatnum' => array( 0, 'FORMATNUM' ),
+ 'padleft' => array( 0, 'PADLEFT' ),
+ 'padright' => array( 0, 'PADRIGHT' ),
+ 'special' => array( 0, 'special' ),
+ 'speciale' => array( 0, 'speciale' ),
+ 'defaultsort' => array( 1, 'DEFAULTSORT:', 'DEFAULTSORTKEY:', 'DEFAULTCATEGORYSORT:' ),
+ 'filepath' => array( 0, 'FILEPATH:' ),
+ 'tag' => array( 0, 'tag' ),
+ 'hiddencat' => array( 1, '__HIDDENCAT__' ),
+ 'pagesincategory' => array( 1, 'PAGESINCATEGORY', 'PAGESINCAT' ),
+ 'pagesize' => array( 1, 'PAGESIZE' ),
+ 'index' => array( 1, '__INDEX__' ),
+ 'noindex' => array( 1, '__NOINDEX__' ),
+ 'numberingroup' => array( 1, 'NUMBERINGROUP', 'NUMINGROUP' ),
+ 'staticredirect' => array( 1, '__STATICREDIRECT__' ),
+ 'protectionlevel' => array( 1, 'PROTECTIONLEVEL' ),
+ 'formatdate' => array( 0, 'formatdate', 'dateformat' ),
+ 'url_path' => array( 0, 'PATH' ),
+ 'url_wiki' => array( 0, 'WIKI' ),
+ 'url_query' => array( 0, 'QUERY' ),
+ 'defaultsort_noerror' => array( 0, 'noerror' ),
+ 'defaultsort_noreplace' => array( 0, 'noreplace' ),
+ 'pagesincategory_all' => array( 0, 'all' ),
+ 'pagesincategory_pages' => array( 0, 'pages' ),
+ 'pagesincategory_subcats' => array( 0, 'subcats' ),
+ 'pagesincategory_files' => array( 0, 'files' ),
);
/**
'youhavenewmessages' => 'You have $1 ($2).',
'newmessageslink' => 'new messages',
'newmessagesdifflink' => 'last change',
+'youhavenewmessagesfromusers' => 'You have $1 from {{PLURAL:$3|another user|$3 users}} ($2).',
+'youhavenewmessagesmanyusers' => 'You have $1 from many users ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|a new message|new messages}}', # don't rely on the value of $1, it's 1 for singular and 2 for "more than one"
+'newmessagesdifflinkplural' => 'last {{PLURAL:$1|change|changes}}', # don't rely on the value of $1, it's 1 for singular and 2 for "more than one"
'youhavenewmessagesmulti' => 'You have new messages on $1',
'newtalkseparator' => ', ', # do not translate or duplicate this message to other languages
'editsection' => 'edit',
'remembermypassword' => 'Remember my login on this browser (for a maximum of $1 {{PLURAL:$1|day|days}})',
'securelogin-stick-https' => 'Stay connected to HTTPS after login',
'yourdomainname' => 'Your domain:',
+'password-change-forbidden' => 'You cannot change passwords on this wiki.',
'externaldberror' => 'There was either an authentication database error or you are not allowed to update your external account.',
'login' => 'Log in',
'nav-login-createaccount' => 'Log in / create account',
You can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages,
or <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>.',
'noarticletextanon' => '{{int:noarticletext}}', # do not translate or duplicate this message to other languages
+'missing-revision' => 'The revision #$1 of the page named "{{PAGENAME}}" does not exist.
+
+This is usually caused by following an outdated history link to a page that has been deleted.
+Details can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].',
'userpage-userdoesnotexist' => 'User account "$1" is not registered.
Please check if you want to create/edit this page.',
'userpage-userdoesnotexist-view' => 'User account "$1" is not registered.',
'expansion-depth-exceeded-warning' => 'Page exceeded the expansion depth',
'parser-unstrip-loop-warning' => 'Unstrip loop detected',
'parser-unstrip-recursion-limit' => 'Unstrip recursion limit exceeded ($1)',
+'converter-manual-rule-error' => 'Error detected in manual language conversion rule',
# "Undo" feature
'undo-success' => 'The edit can be undone.
'mergelogpagetext' => 'Below is a list of the most recent merges of one page history into another.',
# Diffs
-'history-title' => 'Revision history of "$1"',
-'difference-title' => 'Difference between revisions of "$1"',
-'difference-title-multipage' => 'Difference between pages "$1" and "$2"',
-'difference-multipage' => '(Difference between pages)',
-'lineno' => 'Line $1:',
-'compareselectedversions' => 'Compare selected revisions',
-'showhideselectedversions' => 'Show/hide selected revisions',
-'editundo' => 'undo',
-'diff-multi' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by {{PLURAL:$2|one user|$2 users}} not shown)',
-'diff-multi-manyusers' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by more than $2 {{PLURAL:$2|user|users}} not shown)',
+'history-title' => 'Revision history of "$1"',
+'difference-title' => 'Difference between revisions of "$1"',
+'difference-title-multipage' => 'Difference between pages "$1" and "$2"',
+'difference-multipage' => '(Difference between pages)',
+'lineno' => 'Line $1:',
+'compareselectedversions' => 'Compare selected revisions',
+'showhideselectedversions' => 'Show/hide selected revisions',
+'editundo' => 'undo',
+'diff-multi' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by {{PLURAL:$2|one user|$2 users}} not shown)',
+'diff-multi-manyusers' => '({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by more than $2 {{PLURAL:$2|user|users}} not shown)',
+'difference-missing-revision' => '{{PLURAL:$2|One revision|$2 revisions}} of this difference ($1) {{PLURAL:$2|was|were}} not found.
+
+This is usually caused by following an outdated diff link to a page that has been deleted.
+Details can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].',
# Search results
'search-summary' => '', # do not translate or duplicate this message to other languages
'hookaborted' => 'The modification you tried to make was aborted by an extension.',
'illegal-filename' => 'The filename is not allowed.',
'overwrite' => 'Overwriting an existing file is not allowed.',
-'unknown-error' => 'An unknown error occured.',
+'unknown-error' => 'An unknown error occurred.',
'tmp-create-error' => 'Could not create temporary file.',
'tmp-write-error' => 'Error writing temporary file.',
'large-file' => 'It is recommended that files are no larger than $1;
If the problem persists, contact an [[Special:ListUsers/sysop|administrator]].',
'upload-too-many-redirects' => 'The URL contained too many redirects',
'upload-unknown-size' => 'Unknown size',
-'upload-http-error' => 'An HTTP error occured: $1',
+'upload-http-error' => 'An HTTP error occurred: $1',
'upload-copy-upload-invalid-domain' => 'Copy uploads are not available from this domain.',
# File backend
-'backend-fail-stream' => 'Could not stream file $1.',
-'backend-fail-backup' => 'Could not backup file $1.',
+'backend-fail-stream' => 'Could not stream file "$1".',
+'backend-fail-backup' => 'Could not backup file "$1".',
'backend-fail-notexists' => 'The file $1 does not exist.',
'backend-fail-hashes' => 'Could not get file hashes for comparison.',
-'backend-fail-notsame' => 'A non-identical file already exists at $1.',
-'backend-fail-invalidpath' => '$1 is not a valid storage path.',
-'backend-fail-delete' => 'Could not delete file $1.',
-'backend-fail-alreadyexists' => 'The file $1 already exists.',
-'backend-fail-store' => 'Could not store file $1 at $2.',
-'backend-fail-copy' => 'Could not copy file $1 to $2.',
-'backend-fail-move' => 'Could not move file $1 to $2.',
+'backend-fail-notsame' => 'A non-identical file already exists at "$1".',
+'backend-fail-invalidpath' => '"$1" is not a valid storage path.',
+'backend-fail-delete' => 'Could not delete file "$1".',
+'backend-fail-alreadyexists' => 'The file "$1" already exists.',
+'backend-fail-store' => 'Could not store file "$1" at "$2".',
+'backend-fail-copy' => 'Could not copy file "$1" to "$2".',
+'backend-fail-move' => 'Could not move file "$1" to "$2".',
'backend-fail-opentemp' => 'Could not open temporary file.',
'backend-fail-writetemp' => 'Could not write to temporary file.',
'backend-fail-closetemp' => 'Could not close temporary file.',
-'backend-fail-read' => 'Could not read file $1.',
-'backend-fail-create' => 'Could not write file $1.',
-'backend-fail-maxsize' => 'Could not write file $1 because it is larger than {{PLURAL:$2|one byte|$2 bytes}}.',
+'backend-fail-read' => 'Could not read file "$1".',
+'backend-fail-create' => 'Could not write file "$1".',
+'backend-fail-maxsize' => 'Could not write file "$1" because it is larger than {{PLURAL:$2|one byte|$2 bytes}}.',
'backend-fail-readonly' => 'The storage backend "$1" is currently read-only. The reason given is: "\'\'$2\'\'"',
'backend-fail-synced' => 'The file "$1" is in an inconsistent state within the internal storage backends',
'backend-fail-connect' => 'Could not connect to storage backend "$1".',
'backend-fail-internal' => 'An unknown error occurred in storage backend "$1".',
'backend-fail-contenttype' => 'Could not determine the content type of the file to store at "$1".',
-'backend-fail-batchsize' => 'Storage backend given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.',
-'backend-fail-usable' => 'Could not write file $1 due to insufficient permissions or missing directories/containers.',
+'backend-fail-batchsize' => 'The storage backend was given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.',
+'backend-fail-usable' => 'Could not write file "$1" due to insufficient permissions or missing directories/containers.',
# File journal errors
'filejournal-fail-dbconnect' => 'Could not connect to the journal database for storage backend "$1".',
'disambiguations' => 'Pages linking to disambiguation pages',
'disambiguations-summary' => '', # do not translate or duplicate this message to other languages
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "The following pages link to a '''disambiguation page'''.
-They should link to the appropriate topic instead.<br />
+'disambiguations-text' => "The following pages contain at least one link to a '''disambiguation page'''.
+They may have to link to a more appropriate page instead.<br />
A page is treated as disambiguation page if it uses a template which is linked from [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Double redirects',
proceed with caution.',
# Rollback
-'rollback' => 'Roll back edits',
-'rollback_short' => 'Rollback',
-'rollbacklink' => 'rollback',
-'rollbackfailed' => 'Rollback failed',
-'cantrollback' => 'Cannot revert edit;
+'rollback' => 'Roll back edits',
+'rollback_short' => 'Rollback',
+'rollbacklink' => 'rollback',
+'rollbacklinkcount' => 'rollback $1 {{PLURAL:$1|edit|edits}}',
+'rollbacklinkcount-morethan' => 'rollback more than $1 {{PLURAL:$1|edit|edits}}',
+'rollbackfailed' => 'Rollback failed',
+'cantrollback' => 'Cannot revert edit;
last contributor is only author of this page.',
-'alreadyrolled' => 'Cannot rollback last edit of [[:$1]] by [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
+'alreadyrolled' => 'Cannot rollback last edit of [[:$1]] by [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
someone else has edited or rolled back the page already.
The last edit to the page was by [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
-'editcomment' => "The edit summary was: \"''\$1''\".",
-'revertpage' => 'Reverted edits by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) to last revision by [[User:$1|$1]]',
-'revertpage-nouser' => 'Reverted edits by (username removed) to last revision by [[User:$1|$1]]',
-'rollback-success' => 'Reverted edits by $1;
+'editcomment' => "The edit summary was: \"''\$1''\".",
+'revertpage' => 'Reverted edits by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) to last revision by [[User:$1|$1]]',
+'revertpage-nouser' => 'Reverted edits by (username removed) to last revision by [[User:$1|$1]]',
+'rollback-success' => 'Reverted edits by $1;
changed back to last revision by $2.',
# Edit tokens
Since you do not have the hideuser right, you cannot see or edit the user's block.",
'ipbblocked' => 'You cannot block or unblock other users, because you are yourself blocked',
'ipbnounblockself' => 'You are not allowed to unblock yourself',
+'ipb-default-expiry' => '', # do not translate or duplicate this message to other languages
# Developer tools
'lockdb' => 'Lock database',
* <span class="mw-specialpagerestricted">Restricted special pages.</span>',
'specialpages-group-maintenance' => 'Maintenance reports',
'specialpages-group-other' => 'Other special pages',
-'specialpages-group-login' => 'Login / sign up',
+'specialpages-group-login' => 'Login / create account',
'specialpages-group-changes' => 'Recent changes and logs',
'specialpages-group-media' => 'Media reports and uploads',
'specialpages-group-users' => 'Users and rights',
'api-error-file-too-large' => 'The file you submitted was too large.',
'api-error-filename-tooshort' => 'The filename is too short.',
'api-error-filetype-banned' => 'This type of file is banned.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|is not a permitted file type|are not permitted file types}}. Permitted {{PLURAL:$3|file type is|file types are}} $2.',
'api-error-filetype-missing' => 'The filename is missing an extension.',
'api-error-hookaborted' => 'The modification you tried to make was aborted by an extension.',
'api-error-http' => 'Internal error: Unable to connect to server.',
'tog-hidepatrolled' => 'Kaŝi patrolitajn redaktojn en lastaj ŝanĝoj',
'tog-newpageshidepatrolled' => 'Kaŝi patrolitajn paĝojn de listo de novaj paĝoj',
'tog-extendwatchlist' => 'Etendi la atentaron por montri ĉiujn ŝanĝojn, ne nur la plej lastajn',
-'tog-usenewrc' => 'Uzi progresan "Lastaj ŝanĝoj" (bezonas JavaSkripton)',
+'tog-usenewrc' => 'Grupigi ŝanĝoj laŭ paĝo en "Lastaj ŝanĝoj" kaj "Atentaro" (bezonas Ĝavaskripton)',
'tog-numberheadings' => 'Aŭtomate numerigi sekciojn',
'tog-showtoolbar' => 'Montri eldonilaron',
'tog-editondblclick' => 'Redakti per duobla alklako (JavaScript)',
'tog-editsectiononrightclick' => 'Ŝalti sekcian redaktadon per dekstra musklako de sekciaj titoloj (kun JavaScript)',
'tog-showtoc' => 'Montri enhavliston (por paĝoj kun pli ol 3 sekcioj)',
'tog-rememberpassword' => 'Memori mian ensalutadon ĉe ĉi tiu retumilo (daŭrante maksimume $1 {{PLURAL:$1|tagon|tagojn}})',
-'tog-watchcreations' => 'Aldoni de mi kreitajn paĝojn al mia atentaro',
+'tog-watchcreations' => 'Aldoni miajn kreatajn paĝojn al mia atentaro',
'tog-watchdefault' => 'Aldoni al mia atentaro paĝojn redaktitajn de mi',
'tog-watchmoves' => 'Aldoni paĝojn, kiujn mi movas, al mia atentaro',
'tog-watchdeletion' => 'Aldoni paĝojn, kiujn mi forigas, al mia atentaro',
'tog-previewontop' => 'Montri antaŭrigardon antaŭ redaktilo',
'tog-previewonfirst' => 'Montri antaŭrigardon je unua redakto',
'tog-nocache' => 'Malŝalti retumilan kaŝmemoron de paĝoj',
-'tog-enotifwatchlistpages' => 'Sendi al mi retmesaĝon kiam tiu paĝo estas ŝanĝita',
+'tog-enotifwatchlistpages' => 'Sendi al mi retmesaĝon kiam tiu paĝo en mia atentaro estas ŝanĝita',
'tog-enotifusertalkpages' => 'Sendi al mi retmesaĝon kiam mia diskutpaĝo estas ŝanĝita',
-'tog-enotifminoredits' => 'Sendi al mi ankaŭ retmesaĝojn por malgrandaj redaktoj de paĝoj',
+'tog-enotifminoredits' => 'Sendi al mi ankaŭ retmesaĝojn pro malgrandaj redaktoj de paĝoj',
'tog-enotifrevealaddr' => 'Malkaŝi mian retadreson en informaj retpoŝtaĵoj',
'tog-shownumberswatching' => 'Montri la nombron da priatentaj uzantoj',
'tog-oldsig' => 'Ekzistanta subskribo:',
'youhavenewmessages' => 'Por vi estas $1 ($2).',
'newmessageslink' => 'novaj mesaĝoj',
'newmessagesdifflink' => 'ŝanĝoj kompare kun antaŭlasta versio',
+'youhavenewmessagesfromusers' => 'Riceviĝis $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).',
+'youhavenewmessagesmanyusers' => 'Riceviĝis $1 de multaj uzantoj ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|nova mesaĝo|novaj mesaĝoj}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|lasta ŝanĝo|lastaj ŝanĝoj}}',
'youhavenewmessagesmulti' => 'Vi havas novajn mesaĝojn ĉe $1',
'editsection' => 'redakti',
'editold' => 'redakti',
'cannotdelete' => 'Ne eblis forigi la elektitan paĝon aŭ dosieron "$1".
Eble ĝi estis jam forigita de iu alia.',
'cannotdelete-title' => 'Ne eblas forigi paĝon "$1"',
+'delete-hook-aborted' => 'Forigo ĉesigis per hoko.
+Ĝi ne donis eksplikon.',
'badtitle' => 'Fuŝa titolo',
'badtitletext' => 'La petita paĝotitolo estis malvalida, malplena, aŭ malĝuste ligita interlingva aŭ intervikia titolo.
Ĝi eble enhavas unu aŭ pliaj signoj kiu ne povas esti uzata en titoloj.',
'ns-specialprotected' => 'Paĝoj en la {{ns:special}} nomspaco ne povas esti redaktataj.',
'titleprotected' => "Ĉi titolo estas protektita de kreado de [[User:$1|$1]].
La kialo donata estis ''$2''.",
+'filereadonlyerror' => 'La dosiero "$1" ne estas modifebla, ĉar la datumbazujo "$2" estas en nurlegebla modo.
+
+La administranto kiu ŝlosis ĝin proponis tiun klarigon: "$3".',
'invalidtitle-knownnamespace' => 'Nevalida titolo kun nomspaco "$2" kaj teksto "$3"',
'invalidtitle-unknownnamespace' => 'Nevalida titolo kun nekonata nomspaca numero $1 kaj teksto "$2"',
+'exception-nologin' => 'Ne ensalutita',
+'exception-nologin-text' => 'Vi devas ensaluti ĉi tiun vikion por fari ĉi tiun agon.',
# Virus scanner
'virus-badscanner' => "Malbona konfiguro: nekonata virusa skanilo: ''$1''",
'remembermypassword' => 'Memori mian ensalutadon ĉe ĉi tiu komputilo (daŭrante maksimume $1 {{PLURAL:$1|tagon|tagojn}})',
'securelogin-stick-https' => 'Resti konektita al HTTPS post ensalutado',
'yourdomainname' => 'Via domajno',
+'password-change-forbidden' => 'Ve ne povas ŝanĝi pasvortojn en ĉi tiu vikio.',
'externaldberror' => 'Aŭ estis datenbaza eraro rilate al ekstera aŭtentikigado, aŭ vi ne rajtas ĝisdatigi vian eksteran konton.',
'login' => 'Ensaluti',
'nav-login-createaccount' => 'Ensaluti / Krei novan konton',
'noarticletext-nopermission' => 'Estas neniom da teksto en ĉi tiu paĝo.
Vi povas [[Special:Search/{{PAGENAME}}|serĉi ĉi tiun paĝan titolon]] en aliaj paĝoj,
aŭ <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serĉi la rilatajn protokolojn]</span>.',
+'missing-revision' => 'La revizio n-ro $1 de la paĝo nomata "{{PAGENAME}}" ne ekzistas.
+
+La kutima kaŭzo estas sekvi malaktualan historio-ligilon al paĝo forviŝita.
+Detaloj troveblos en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de forviŝoj].',
'userpage-userdoesnotexist' => 'Uzantokonto "<nowiki>$1</nowiki>" ne estas registrita. Bonvolu konfirmi se vi volas krei/redakti ĉi tiun paĝon.',
'userpage-userdoesnotexist-view' => 'Uzanto-konto "$1" ne estas registrita.',
'blocked-notice-logextract' => 'Ĉi tiu uzanto estas ĉi-momente forbarita.
'edit-no-change' => 'Via redakto estis ignorita, ĉar neniu ŝanĝo estis farita al la teksto.',
'edit-already-exists' => 'Ne eblis krei novan paĝon.
Ĝi jam ekzistas.',
+'defaultmessagetext' => 'Defaŭlta teksto',
# Parser/template warnings
'expensive-parserfunction-warning' => 'Averto: Ĉi tiu paĝo enhavas tro da multekostaj sintaksaj funkcio-vokoj.
'node-count-exceeded-warning' => 'Paĝo preterpasis la nombron da nodoj.',
'expansion-depth-exceeded-category' => 'Paĝoj en kiuj la ekpansiprofundo estas preterpasita',
'expansion-depth-exceeded-warning' => 'Paĝo preterpasis la ekpansiprofundon.',
+'parser-unstrip-loop-warning' => 'Cirkloreferencon detektis',
+'parser-unstrip-recursion-limit' => 'Rikurlimiton de analizopoj ($1) superis',
+'converter-manual-rule-error' => 'Eraron detektis en mana lingvokonverta regulo',
# "Undo" feature
'undo-success' => 'La redakto estas malfarebla.
'editundo' => 'malfari',
'diff-multi' => '({{PLURAL:$1|Unu intermeza versio|$1 intermezaj versioj}} de {{PLURAL:$2|unu uzanto|$2 uzantoj}} ne estas {{PLURAL:$1|montrata|montrataj}}.)',
'diff-multi-manyusers' => '({{PLURAL:$1|Unu intermeza versio|$1 intermezaj versioj}} de pli ol {{PLURAL:$2|unu uzanto|$2 uzantoj}} ne estas {{PLURAL:$1|montrata|montrataj}}.)',
+'difference-missing-revision' => '{{PLURAL:$2|Unu revizio|$2 revizioj}} de ĉi tiu malsameco ($1) ne {{PLURAL:$2|estis|estis}} trovebla.
+
+La kutima kaŭzo estas sekvi malaktualan malsamo-ligilon al paĝo forviŝita.
+Detaloj troveblos en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de forviŝoj].',
# Search results
'searchresults' => 'Serĉrezultoj',
'prefs-beta' => 'Ecoj de Beta',
'prefs-datetime' => 'Dato kaj horo',
'prefs-labs' => 'Ecoj el Laboratorio',
-'prefs-user-pages' => 'Uzanto paĝoj',
+'prefs-user-pages' => 'Uzantopaĝoj',
'prefs-personal' => 'Uzanta profilo',
'prefs-rc' => 'Lastaj ŝanĝoj',
'prefs-watchlist' => 'Atentaro',
'right-writeapi' => 'Uzi la API por modifi la vikion',
'right-delete' => 'Forigi paĝojn',
'right-bigdelete' => 'Forigi paĝojn kun grandaj historioj',
+'right-deletelogentry' => 'Forigi kaj malforigi specifajn enmetojn en la registro.',
'right-deleterevision' => 'Forigi kaj malforigi specifajn versiojn de paĝoj',
'right-deletedhistory' => 'Rigardi listanojn de forigitaj historioj, sen ties asociaj tekstoj',
'right-deletedtext' => 'Rigardi forigitan tekston kaj ŝanĝojn inter forigitaj revizioj.',
'upload-too-many-redirects' => 'La URL-o enhavis tro multajn alidirektilojn',
'upload-unknown-size' => 'Nekonata grandeco',
'upload-http-error' => 'HTTP-eraro okazis: $1',
+'upload-copy-upload-invalid-domain' => 'Kopio-alŝutoj ne disponiĝas el ĉi tiu domajno.',
# File backend
'backend-fail-stream' => 'Ne povis fluigi dosieron $1.',
'backend-fail-closetemp' => 'Ne povis fermi provizoran dosieron.',
'backend-fail-read' => 'Ne povas legi dosieron "$1".',
'backend-fail-create' => 'Ne povas skribi dosieron $1.',
+'backend-fail-maxsize' => 'Ne povis skribi la dosieron "$1," ĉar ĝi estas pli granda ol {{plural: $2|bitoko|$2 bitokoj}}.',
+'backend-fail-readonly' => 'La interna konservujo "$1" nune estas nurlega. La indikata kialo estas: "\'\'$2\'\'"',
+'backend-fail-synced' => 'La dosiero "$1" estas en nekohera stato kun la internaj konservujoj',
+'backend-fail-connect' => 'Ne eblis konekti la internan konservujon "$1".',
+'backend-fail-internal' => 'Nekonata eraro okazis en interna konservujo "$1".',
+'backend-fail-contenttype' => 'Ne eblis determini la enhavo-tipo de la dosiero por konservi ĉe "$1".',
+'backend-fail-batchsize' => 'Interna konservujo estis donita komandaron de $1 {{PLURAL:$1|dosiera operacio|dosieraj operacioj}}; la limo estas $2 {{PLURAL:$2|operacio|operacioj}}.',
+'backend-fail-usable' => 'Ne eblis skribi dosieron "$1" pro malsufiĉaj permesoj aŭ mankantaj dosierujoj.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Ne eblis konekti la protokolan datumbazon por la ekstera konservujo "$1".',
+'filejournal-fail-dbquery' => 'Ne eblis ĝisdatigi la protokolan datumbazon por la ekstera konservujo "$1".',
# Lock manager
'lockmanager-notlocked' => 'Ne povis malŝlosi "$1"; ĝi ne estas ŝlosita.',
'lockmanager-fail-releaselock' => 'Ne povis liberigi ŝlosadon por "$1".',
'lockmanager-fail-db-bucket' => 'Ne povis kontakti sufiĉajn ŝlos-datumbazojn en ujo $1.',
'lockmanager-fail-db-release' => 'Ne povis liberigi ŝlosadojn de datumbazao $1.',
+'lockmanager-fail-svr-acquire' => 'Ne povis akiri ŝlosadojn de servilo $1.',
'lockmanager-fail-svr-release' => 'Ne povis liberigi ŝlosadojn de servilo $1.',
# ZipDirectoryReader
Bonvolu vidi la [$2 dosier-priskriban paĝon] por plua informo.',
'sharedupload-desc-here' => 'Ĉi tiu dosiero estas de $1 kaj estas uzebla de aliaj projektoj.
Jen la priskribo en ties [$2 dosier-priskriba paĝo].',
+'sharedupload-desc-edit' => 'Ĉi tiu dosiero estas el $1 kaj estas uzebla en aliaj projektoj.
+Eble vi volas redakti la priskribon ĉe ties [$2 dosier-priskriba paĝo].',
+'sharedupload-desc-create' => 'Ĉi tiu dosiero estas el $1 kaj estas uzebla en aliaj projektoj.
+Eble vi volas redakti la priskribon ĉe ties [$2 dosier-priskriba paĝo].',
'filepage-nofile' => 'Neniu dosiero de ĉi tiu nomo ekzistas.',
'filepage-nofile-link' => 'Neniu dosiero de ĉi tiu nomo ekzistas, sed vi povas [$1 alŝuti ĝin].',
'uploadnewversion-linktext' => 'Alŝuti novan version de ĉi tiu dosiero',
'wantedpages' => 'Dezirataj paĝoj',
'wantedpages-badtitle' => 'Malvalida titolo en rezulta aro: $1',
'wantedfiles' => 'Dezirataj dosieroj',
+'wantedfiletext-cat' => 'La jenaj dosieroj estas uzataj sed ne ekzistas. Dosieroj de eksteraj konservujoj eble estos listigita malgraŭ ne ekzistante. Tia malprave pozitivaj rezultoj estos <del>forstrekita</del>. Ankaŭ, paĝoj kiuj enmetas dosierojn kiuj ne ekzistas estas listigita en [[:$1]].',
+'wantedfiletext-nocat' => 'La jenaj dosieroj estas uzataj sed ne ekzistas. Dosieroj de eksteraj dosierujoj eble estas listigitaj malgraŭ eksistado. Tia malprave pozitiva rezulto estos <del>forstrekita</del>.',
'wantedtemplates' => 'Dezirataj ŝablonoj',
'mostlinked' => 'Plej ligitaj paĝoj',
'mostlinkedcategories' => 'Plej ligitaj kategorioj',
Vi povas plistrikti la mendon per selektado de protokola speco, la salutnomo (inkluzivante uskladon) aŭ la efika paĝo (ankaŭ inkluzivas uskladon).',
'logempty' => 'Neniaj artikoloj en la protokolo.',
'log-title-wildcard' => 'Serĉi titolojn komencantajn kun ĉi tiu teksto',
+'showhideselectedlogentries' => 'Montri/kaŝi elektitajn protokolerojn',
# Special:AllPages
'allpages' => 'Ĉiuj paĝoj',
'allpages-hide-redirects' => 'Kaŝi alidirektilojn',
# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Vi vidas version de la paĝo el kaŝmemoro, kiu eble estis aktuala ekde $1.',
+'cachedspecial-viewing-cached-ts' => 'Vi vidas version de la paĝo el kaŝmemoro, kiu eble ne estas la plej aktuala.',
'cachedspecial-refresh-now' => 'Vidas plej nova.',
# Special:Categories
'rollback' => 'Restarigi antaŭan redakton',
'rollback_short' => 'Malfari',
'rollbacklink' => 'malfari',
+'rollbacklinkcount' => 'nuligi $1 {{PLURAL:$1|redakton|redaktojn}}',
+'rollbacklinkcount-morethan' => 'nuligi pli ol $1 {{PLURAL:$1|redakton|redaktojn}}',
'rollbackfailed' => 'Malfaro malsukcesis',
'cantrollback' => 'Ne povas restarigi antaŭan redakton; la redaktinto lasta estas la sola aŭtoro de la paĝo.',
'alreadyrolled' => 'Ne povas restarigi la lastan redakton de [[:$1]] de la [[User:$2|$2]] ([[User talk:$2|diskuto]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
'import-invalid-interwiki' => 'Ne povas importi de la specifita vikio.',
'import-error-edit' => 'Paĝo "$1" ne estas importita ĉar vi ne rajtas radakti ĝin.',
'import-error-create' => 'Paĝo "$1" ne estas importita ĉar vi ne rajtas krei ĝin.',
+'import-error-interwiki' => 'Paĝo "$1" ne estis importita pro sia nomo estas deklarita por ekstera ligado (intervikia).',
'import-error-special' => 'Paĝo "$1" ne estas importata, ĉar ĝi apartenas al speciala nomspaco, kiu ne permesas paĝojn.',
'import-error-invalid' => 'Paĝo "$1" ne estas importata, ĉar ĝia nomo estas malvalida.',
'javascripttest' => 'Ĝavoskripta testado',
'javascripttest-disabled' => 'Ĉi tiu funkcio estas malŝaltita en ĉi tiu vikio.',
'javascripttest-title' => 'Irigante $1 testoj',
+'javascripttest-pagetext-noframework' => 'Ĉi tiu paĝo estas konservita por funkciigi testojn de JavaScript.',
+'javascripttest-pagetext-unknownframework' => 'Nekonta test-framo "$1".',
+'javascripttest-pagetext-frameworks' => 'Bonvolu elekti unu el la jenaj test-framoj: $1',
'javascripttest-pagetext-skins' => 'Elektu kun kio etoso irigi la testojn:',
'javascripttest-qunit-intro' => 'Vidu [$1 testa dokumentaro] en mediawiki.org.',
'javascripttest-qunit-heading' => 'Testaro QUnit por JavaScript de MediaWiki',
'spambot_username' => 'Trudmesaĝa forigo de MediaWiki',
'spam_reverting' => 'Restarigo de lasta versio ne entenante ligilojn al $1',
'spam_blanking' => 'Forviŝo de ĉiuj versioj entenantaj ligilojn al $1',
+'spam_deleting' => 'Ĉiuj versioj enhavis ligilojn al $1 - forigante',
# Info page
'pageinfo-title' => 'Informoj por "$1"',
'version-software' => 'Instalita programaro',
'version-software-product' => 'Produkto',
'version-software-version' => 'Versio',
+'version-entrypoints' => 'Eniropunktaj URL-oj',
+'version-entrypoints-header-entrypoint' => 'Eniropunkto',
+'version-entrypoints-header-url' => 'Retadreso',
# Special:FilePath
'filepath' => 'Vojo al dosiero',
* <span class="mw-specialpagecached">Memorkaŝitaj specialaj paĝoj (eble malaktualaj).</span>',
'specialpages-group-maintenance' => 'Raportoj pri prizorgado',
'specialpages-group-other' => 'Aliaj specialaj paĝoj',
-'specialpages-group-login' => 'Ensaluti / Krei novan konton',
+'specialpages-group-login' => 'Ensaluti / registriĝi',
'specialpages-group-changes' => 'Lastaj ŝanĝoj kaj protokoloj',
'specialpages-group-media' => 'Raportoj pri dosieroj kaj alŝutoj',
'specialpages-group-users' => 'Uzantoj kaj rajtoj',
'api-error-empty-file' => 'La dosiero kiun vi sendis estis malplena.',
'api-error-emptypage' => 'Kreo de novaj malplenaj paĝoj ne estas permesita.',
'api-error-fetchfileerror' => 'Interna eraro: io misfunkciis dum la dosiera prenado.',
+'api-error-fileexists-forbidden' => 'Dosiero nomata "$1" jam existas kaj ne estas surskribebla.',
+'api-error-fileexists-shared-forbidden' => 'Dosiero nomata "$1" jam existas en la kunhavata dosier-datumbazo kaj ne estas surskribebla.',
'api-error-file-too-large' => 'La dosiero kiun vi alŝutis estis tro granda.',
'api-error-filename-tooshort' => 'La dosiernomo estas tro mallonga.',
'api-error-filetype-banned' => 'Ĉi tiu tipo de dosiero estas malpermesita.',
'api-error-uploaddisabled' => 'Alŝutato estas malebligata en tiu ĉi vikio.',
'api-error-verification-error' => 'Tiu ĉi dosiero eble estas difektita, aŭ havas la malĝustan dosieran finaĵon.',
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|sekundo|sekundoj}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minuto|minutoj}}',
+'duration-hours' => '$1 {{PLURAL:$1|horo|horoj}}',
+'duration-days' => '$1 {{PLURAL:$1|tago|tagoj}}',
+'duration-weeks' => '$1 {{PLURAL:$1|semajno|$1 semajnoj}}',
+'duration-years' => '$1 {{PLURAL:$1|jaro|jaroj}}',
+'duration-decades' => '$1 {{PLURAL:$1|jardeko|jardekoj}}',
+'duration-centuries' => '$1 {{PLURAL:$1|jarcento|jarcentoj}}',
+'duration-millennia' => '$1 {{PLURAL:$1|jarmilo|jarmiloj}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 ne estas {{PLURAL:$4|permesita dosiero-tipo|permesitaj dosiero-tipoj}}. {{PLURAL:$3|Permesita dosiero-tipo|Permesitaj dosiero-tipoj}} estas $2.',
);
* @author Alhen
* @author Alpertron
* @author Alvaro qc
+ * @author Amire80
* @author Armando-Martin
* @author Ascánder
* @author Baiji
* @author Invadinado
* @author Jatrobat
* @author Jens Liebenau
+ * @author Jewbask
* @author Jurock
* @author Kaganer
+ * @author Larjona
* @author Lin linao
* @author Linterweb
* @author Locos epraix
'tog-editsectiononrightclick' => 'Habilitar la edición de secciones presionando el botón de la derecha en los títulos de secciones (requiere JavaScript)',
'tog-showtoc' => 'Mostrar el índice (para páginas con más de 3 encabezados)',
'tog-rememberpassword' => 'Recordar mi nombre de usuario y contraseña entre sesiones en este navegador (por un máximo de $1 {{PLURAL:$1|día|días}})',
-'tog-watchcreations' => 'Vigilar las páginas que yo cree',
-'tog-watchdefault' => 'Vigilar las páginas que yo modifique',
-'tog-watchmoves' => 'Vigilar las páginas que renombre',
-'tog-watchdeletion' => 'Vigilar las páginas que borre',
+'tog-watchcreations' => 'Añadir las páginas que cree y los archivos que cargue a mi lista de vigilancia',
+'tog-watchdefault' => 'Añadir la páginas y archivos que edite a mi lista de vigilancia',
+'tog-watchmoves' => 'Añadir las páginas y archivos que mueva a mi lista de vigilancia',
+'tog-watchdeletion' => 'Añadir la páginas y archivos que borre a mi lista de vigilancia',
'tog-minordefault' => 'Marcar todas las ediciones como menores de manera predeterminada',
'tog-previewontop' => 'Mostrar previsualización antes del cuadro de edición',
'tog-previewonfirst' => 'Mostrar previsualización en la primera edición',
'tog-nocache' => 'Desactivar la caché de páginas del navegador',
'tog-enotifwatchlistpages' => 'Enviarme un correo electrónico cuando se modifique una página o un archivo de mi lista de seguimiento',
'tog-enotifusertalkpages' => 'Enviarme un correo electrónico cuando se modifique mi página de discusión',
-'tog-enotifminoredits' => 'Notificarme también los cambios menores de páginas',
+'tog-enotifminoredits' => 'Notificarme también por correo electrónico los cambios menores de las páginas y archivos',
'tog-enotifrevealaddr' => 'Revelar mi dirección de correo electrónico en los correos de notificación',
'tog-shownumberswatching' => 'Mostrar el número de usuarios que la vigilan',
'tog-oldsig' => 'Firma actual:',
'youhavenewmessages' => 'Tienes $1 ($2).',
'newmessageslink' => 'mensajes nuevos',
'newmessagesdifflink' => 'última modificación',
+'newmessageslinkplural' => '{{PLURAL:$1|un nuevo mensaje|mensajes nuevos}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|última modificación|últimos cambios}}',
'youhavenewmessagesmulti' => 'Tienes nuevos mensajes en $1',
'editsection' => 'editar',
'editold' => 'editar',
'remembermypassword' => 'Mantenerme conectado en este navegador (hasta $1 {{PLURAL:$1|día|días}})',
'securelogin-stick-https' => 'Permanecer conectado a HTTPS después de iniciar sesión',
'yourdomainname' => 'Dominio',
+'password-change-forbidden' => 'No puedes cambiar las contraseñas de este wiki.',
'externaldberror' => 'Hubo un error de autenticación externa de la base de datos o bien no tienes autorización para actualizar tu cuenta externa.',
'login' => 'Iniciar sesión',
'nav-login-createaccount' => 'Iniciar sesión / crear cuenta',
'noarticletext-nopermission' => 'Actualmente no hay texto en esta página.
Puedes [[Special:Search/{{PAGENAME}}|buscar este título de página]] en otras páginas,
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados]</span>.',
+'missing-revision' => 'La revisión # $1 de la página denominada "{{PAGENAME}}" no existe.
+!¡ N!Esto es generalmente causado al seguir un enlace de historia obsoleto a una página que se ha borrado.!¡ N!Los detalles pueden encontrarse en el [{{fullurl: {{#Special:Log}} / delete|page = {{FULLPAGENAMEE}}}} registro de borrado].',
'userpage-userdoesnotexist' => 'La cuenta de usuario «<nowiki>$1</nowiki>» no está registrada. Por favor comprueba si quieres crear o editar esta página.',
'userpage-userdoesnotexist-view' => 'La cuenta de usuario «$1» no está registrada.',
'blocked-notice-logextract' => 'Este usuario está actualmente bloqueado.
'sectioneditnotsupported-text' => 'La edición de sección no es compatible con esta página.',
'permissionserrors' => 'Errores de permisos',
'permissionserrorstext' => 'No tienes permiso para hacer eso, por {{PLURAL:$1|el siguiente motivo|los siguientes motivos}}:',
-'permissionserrorstext-withaction' => 'No tienes permiso para $2, por los siguientes {{PLURAL:$1|motivo|motivos}}:',
+'permissionserrorstext-withaction' => 'No tienes permiso para $2, por {{PLURAL:$1|el siguiente motivo|los siguientes motivos}}:',
'recreate-moveddeleted-warn' => "'''Atención: estás volviendo a crear una página que ha sido borrada anteriormente.'''
Deberías considerar si es apropiado continuar editando esta página.
'expansion-depth-exceeded-warning' => 'Página que ha superado la profundidad de expansión',
'parser-unstrip-loop-warning' => 'Se ha detectado un bucle "unstrip"',
'parser-unstrip-recursion-limit' => 'Se ha superado el límite de recursión de "unstrip" ($1)',
+'converter-manual-rule-error' => 'Error detectado en la regla de conversión manual del lenguaje',
# "Undo" feature
'undo-success' => 'La edición puede deshacerse. Antes de deshacer la edición, comprueba la siguiente comparación para verificar que realmente es lo que quieres hacer, y entonces guarda los cambios para así deshacer la edición.',
'editundo' => 'deshacer',
'diff-multi' => '(No se {{PLURAL:$1|muestra una edición intermedia realizada|muestran $1 ediciones intermedias realizadas}} por {{PLURAL:$2|un usuario|$2 usuarios}})',
'diff-multi-manyusers' => '(No se {{PLURAL:$1|muestra una edición intermedia|muestran $1 ediciones intermedias}} de {{PLURAL:$2|un usuario|$2 usuarios}})',
+'difference-missing-revision' => '{{PLURAL:$2|Un revisión| $2 revisiones}} de esta diferencia ( $1 ) no {{PLURAL:$2| ha siado encontrada|han sido encontradas}}.
+!¡ N!Esto es generalmente causado por seguir un enlace de diffs obsoletas a una página que ha sido borrada.!¡ N!Los detalles pueden encontrarse en el [{{fullurl:{{#Special:log}} / delete|page = {{FULLPAGENAMEE}}}} registro de borrado].',
# Search results
'searchresults' => 'Resultados de la búsqueda',
'right-writeapi' => 'Hacer uso del API para escribir',
'right-delete' => 'Borrar páginas',
'right-bigdelete' => 'Borrar páginas con historiales grandes',
+'right-deletelogentry' => 'Borrar y recuperar entradas de registro específicas',
'right-deleterevision' => 'Borrar y restaurar revisiones específicas de páginas',
'right-deletedhistory' => 'Ver el historial de páginas borradas, sin el texto asociado',
'right-deletedtext' => 'Ver texto borrado y cambios entre revisiones borradas',
'lockmanager-fail-releaselock' => 'No se pudo liberar el bloqueo de "$1".',
'lockmanager-fail-db-bucket' => 'No se pudo contactar con las suficientes bases de datos del conjunto $1.',
'lockmanager-fail-db-release' => 'No se pudieron liberar los bloqueos registrados en la base de datos $1.',
+'lockmanager-fail-svr-acquire' => 'No se pudieron obtener bloqueos en el servidor $1.',
'lockmanager-fail-svr-release' => 'No se pudieron liberar los bloqueos registrados en el servidor $1.',
# ZipDirectoryReader
'disambiguations' => 'Páginas que enlazan con páginas de desambiguación',
'disambiguationspage' => 'Template:Desambiguación',
-'disambiguations-text' => "Las siguientes páginas enlazan con una '''página de desambiguación'''.
-En lugar de ello deberían enlazar con el tema apropiado.<br />
+'disambiguations-text' => "Las siguientes páginas contienen al menos un enlace a una '''página de desambiguación'''.
+En lugar de ello deberían enlazar a una página más apropiada.<br />
Una página es considerada página de desambiguación si utiliza la plantilla que está enlazada desde [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redirecciones dobles',
'rollback' => 'Revertir ediciones',
'rollback_short' => 'Revertir',
'rollbacklink' => 'revertir',
+'rollbacklinkcount' => 'revertir $1 {{PLURAL:$1|edición|ediciones}}',
+'rollbacklinkcount-morethan' => 'revertir más de $1 {{PLURAL:$1|edición|ediciones}}',
'rollbackfailed' => 'No se pudo revertir',
'cantrollback' => 'No se puede revertir la edición;
el último colaborador es el único autor de esta página.',
* <span class="mw-specialpagecached">Páginas especiales en caché (podrían ser obsoletas).</span>',
'specialpages-group-maintenance' => 'Reportes de mantenimiento',
'specialpages-group-other' => 'Otras páginas especiales',
-'specialpages-group-login' => 'Iniciar sesión / Registrarse',
+'specialpages-group-login' => 'Iniciar sesión / Crear cuenta',
'specialpages-group-changes' => 'Cambios recientes y registros',
'specialpages-group-media' => 'Páginas sobre archivos',
'specialpages-group-users' => 'Usuarios y permisos',
'duration-millennia' => '$1 {{PLURAL:$1|milenio|milenios}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'No se pudieron obtener bloqueos en el servidor $1.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|no es un tipo de archivo permitido|no son tipos de archivos permitidos}}. {{PLURAL:$3|El tipo de archivo permitido es|Los tipos de archivos permitidos son}} $2.',
);
'tog-editsectiononrightclick' => 'Peatükkide redigeerimine paremklõpsuga alampealkirjadel (JavaScript)',
'tog-showtoc' => 'Näita sisukorda (lehtedel, millel on rohkem kui 3 pealkirja)',
'tog-rememberpassword' => 'Parooli meeldejätmine tulevasteks seanssideks (kuni $1 {{PLURAL:$1|päevaks|päevaks}})',
-'tog-watchcreations' => 'Lisa minu loodud lehed jälgimisloendisse',
-'tog-watchdefault' => 'Jälgi uusi ja muudetud artikleid',
-'tog-watchmoves' => 'Lisa minu teisaldatud leheküljed jälgimisloendisse',
-'tog-watchdeletion' => 'Lisa minu kustutatud leheküljed jälgimisloendisse',
+'tog-watchcreations' => 'Lisa jälgimisloendisse minu alustatud leheküljed ja minu üles laaditud failid',
+'tog-watchdefault' => 'Lisa jälgimisloendisse minu muudetud leheküljed ja failid',
+'tog-watchmoves' => 'Lisa jälgimisloendisse minu teisaldatud leheküljed ja failid',
+'tog-watchdeletion' => 'Lisa jälgimisloendisse minu kustutatud leheküljed ja failid',
'tog-minordefault' => 'Märgi kõik parandused vaikimisi pisiparandusteks',
'tog-previewontop' => 'Näita eelvaadet toimetamiskasti ees',
'tog-previewonfirst' => 'Näita eelvaadet esimesel redigeerimisel',
'tog-nocache' => 'Keela võrgulehitsejal lehekülgede puhverdamine',
-'tog-enotifwatchlistpages' => 'Teata e-posti teel minu jälgitava lehekülje muutmisest',
+'tog-enotifwatchlistpages' => 'Teata e-posti teel minu jälgitava lehekülje või faili muutmisest',
'tog-enotifusertalkpages' => 'Teata e-posti teel minu arutelulehekülje muutmisest',
-'tog-enotifminoredits' => 'Teata e-posti teel ka pisiparandustest',
+'tog-enotifminoredits' => 'Teata e-posti teel mulle ka pisiparandustest lehekülgedel ja failides',
'tog-enotifrevealaddr' => 'Näita minu e-posti aadressi teavitus-e-kirjades',
'tog-shownumberswatching' => 'Näita jälgivate kasutajate hulka',
'tog-oldsig' => 'Praegune allkiri:',
'ok' => 'Sobib',
'pagetitle' => '$1 – {{SITENAME}}',
-'retrievedfrom' => 'Välja otsitud andmebaasist "$1"',
-'youhavenewmessages' => 'Teile on $1 ($2).',
+'retrievedfrom' => 'Pärit leheküljelt "$1"',
+'youhavenewmessages' => 'Sulle on $1 ($2).',
'newmessageslink' => 'uusi sõnumeid',
'newmessagesdifflink' => 'viimane muudatus',
+'youhavenewmessagesfromusers' => 'Sulle on $1 {{PLURAL:$3|ühelt|$3}} kasutajalt ($2).',
+'youhavenewmessagesmanyusers' => 'Sulle on $1 paljudelt kasutajatelt ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|uus sõnum|uusi sõnumeid}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|viimane muudatus|viimased muudatused}}',
'youhavenewmessagesmulti' => 'Sulle on uusi sõnumeid $1',
'editsection' => 'redigeeri',
'editsection-brackets' => '[$1]',
'page-rss-feed' => '"$1" RSS-toide',
'page-atom-feed' => '"$1" Atom-toide',
'red-link-title' => '$1 (pole veel kirjutatud)',
-'sort-descending' => 'Sordi kahanevas järjestuses',
-'sort-ascending' => 'Sordi kasvavas järjestuses',
+'sort-descending' => 'Järjesta kahanevalt',
+'sort-ascending' => 'Järjesta kasvavalt',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Artikkel',
'viewsourcetext' => 'Saad vaadata ja kopeerida lehekülje lähteteksti:',
'viewyourtext' => "Saad vaadata ja kopeerida sellel leheküljel tehtud '''enda muudatuste '''lähteteksti:",
'protectedinterface' => 'Sellel leheküljel on tarkvara kasutajaliidese tekst. Kuritahtliku muutmise vältimiseks on lehekülg lukustatud.',
-'editinginterface' => "'''Hoiatus:''' Te redigeerite tarkvara kasutajaliidese tekstiga lehekülge. Muudatused siin mõjutavad kõikide kasutajate kasutajaliidest. Tõlkijad, palun kaaluge MediaWiki tõlkimisprojekti – [//translatewiki.net/wiki/Main_Page?setlang=et translatewiki.net] kasutamist.",
+'editinginterface' => "'''Hoiatus:''' Redigeerid tarkvara kasutajaliidese tekstiga lehekülge. Muudatused siin mõjutavad kõikide kasutajate kasutajaliidest. Tõlkijad, palun kaaluge MediaWiki lokaliseerimisprojekti [//translatewiki.net/wiki/Main_Page?setlang=et translatewiki.net] kasutamist.",
'sqlhidden' => '(SQL päring peidetud)',
'cascadeprotected' => 'See lehekülg on muutmise eest kaitstud, sest see on osa {{PLURAL:$1|järgmisest leheküljest|järgmistest lehekülgedest}}, mis on kaskaadkaitse all:
$2',
'remembermypassword' => 'Jäta parool meelde (kuni $1 {{PLURAL:$1|päevaks|päevaks}})',
'securelogin-stick-https' => 'Jätka pärast sisselogimist HTTPS-ühenduse kasutamist',
'yourdomainname' => 'Sinu domeen:',
+'password-change-forbidden' => 'Selles vikis ei saa paroole muuta.',
'externaldberror' => 'Esines autentimistõrge või sul pole õigust konto andmeid muuta.',
'login' => 'Logi sisse',
'nav-login-createaccount' => 'Logi sisse või registreeru kasutajaks',
'noarticletext-nopermission' => 'Sellel leheküljel ei ole teksti.
Sa võid [[Special:Search/{{PAGENAME}}|otsida lehekülje nime]] teistelt lehekülgedelt
või <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} otsida lehekülje nime logidest]</span>.',
+'missing-revision' => 'Lehekülje "{{PAGENAME}}" redaktsiooni $1 pole.
+
+Harilikult tähendab see seda, et sind siia juhatanud link on vananenud ja siin asunud lehekülg on kustutatud.
+Üksikasjad leiad [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kustutamislogist].',
'userpage-userdoesnotexist' => 'Kasutajakontot "<nowiki>$1</nowiki>" pole olemas.
Palun mõtle järele, kas soovid seda lehte luua või muuta.',
'userpage-userdoesnotexist-view' => 'Kasutajakonto "$1" pole registreeritud.',
'storedversion' => 'Salvestatud redaktsioon',
'nonunicodebrowser' => "'''HOIATUS: Sinu brauser ei toeta unikoodi.'''
Probleemist möödahiilimiseks, selleks et saaksid lehekülgi turvaliselt redigeerida, näidatakse mitte-ASCII sümboleid toimetuskastis kuueteistkümnendsüsteemi koodidena.",
-'editingold' => "'''ETTEVAATUST! Te redigeerite praegu selle lehekülje vana redaktsiooni.
-Kui Te selle salvestate, siis lähevad kõik vahepealsed muudatused kaduma.'''",
+'editingold' => "'''Ettevaatust: Redigeerid praegu selle lehekülje vana redaktsiooni.'''
+Kui selle salvestad, lähevad kõik vahepealsed muudatused kaduma.",
'yourdiff' => 'Erinevused',
-'copyrightwarning' => "Pidage silmas, et kogu teie kaastöö võrgukohale {{SITENAME}} loetakse avaldatuks litsentsi $2 all (vaata ka $1). Kui te ei soovi, et teie kirjutatut halastamatult redigeeritakse ja oma äranägemise järgi kasutatakse, siis ärge seda siia salvestage.<br />
-Te kinnitate ka, et kirjutasite selle ise või võtsite selle kopeerimiskitsenduseta allikast.<br />
-'''ÄRGE SAATKE AUTORIÕIGUSEGA KAITSTUD MATERJALI ILMA LOATA!'''",
+'copyrightwarning' => "Pea silmas, et kogu kaastöö võrgukohale {{SITENAME}} loetakse avaldatuks litsentsi $2 all (üksikasjad leheküljel $1). Kui sa ei soovi, et sinu kirjutatut halastamatult redigeeritakse ja oma äranägemise järgi kasutatakse, siis ära seda siia salvesta.<br />
+Kinnitad ka, et kirjutasid selle ise või võtsid selle allikast, mille materjale ei kaitsta autoriõigusega või muust sarnasest vabast allikast.<br />
+'''Ära salvesta autoriõigusega kaitstud materjali ilma loata!'''",
'copyrightwarning2' => "Pea silmas, et teised kaastöölised võivad kogu {{GRAMMAR:inessive|{{SITENAME}}}} tehtud kaastööd muuta või eemaldada. Kui sa ei soovi, et su kirjutatut halastamatult redigeeritakse, siis ära seda siia salvesta.<br />
Sa kinnitad ka, et kirjutasid selle ise või võtsid selle kopeerimiskitsenduseta allikast (vaata ka $1).
'''Ära saada autoriõigusega kaitstud materjali loata!'''",
'editundo' => 'eemalda',
'diff-multi' => '({{PLURAL:$1|Ühte|$1}} vahepealset {{PLURAL:$2|ühe|$2}} kasutaja redaktsiooni ei näidata.)',
'diff-multi-manyusers' => '({{PLURAL:$1|Ühte|$1}} vahepealset rohkem kui {{PLURAL:$2|ühe|$2}} kasutaja redaktsiooni ei näidata.)',
+'difference-missing-revision' => 'Selle erinevuste vaate {{PLURAL:$2|üht|$2}} redaktsiooni ($1) ei leitud.
+
+Harilikult tähendab see seda, et sind siia juhatanud link on vananenud ja siin asunud lehekülg on kustutatud.
+Üksikasjad leiad [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kustutamislogist].',
# Search results
'searchresults' => 'Otsingu tulemused',
'right-writeapi' => 'Kasutada kirjutamise rakendusliidest',
'right-delete' => 'Lehekülgi kustutada',
'right-bigdelete' => 'Pikkade ajalugudega lehekülgi kustutada',
+'right-deletelogentry' => 'Kustutada ja taastada logisissekandeid',
'right-deleterevision' => 'Kustutada ja taastada lehekülgede teatud redaktsioone',
'right-deletedhistory' => 'Vaadata kustutatud ajalookirjeid ilma seotud tekstita',
'right-deletedtext' => 'Vaadata kustutatud teksti ja võrrelda kustutatud redaktsioone',
'tmp-write-error' => 'Viga ajutise faili kirjutamisel.',
'large-file' => 'On soovitatav, et üleslaaditavad failid ei oleks suuremad kui $1. Selle faili suurus on $2.',
'largefileserver' => 'Antud fail on suurem lubatud failisuurusest.',
-'emptyfile' => 'Fail, mille Te üles laadisite, paistab olevat tühi.
+'emptyfile' => 'Fail, mille üles laadisid, paistab olevat tühi.
See võib olla tingitud vigasest failinimest.
-Palun kaalutlege, kas Te tõesti soovite seda faili üles laadida.',
+Palun veendu, et soovid tõesti seda faili üles laadida.',
'windows-nonascii-filename' => 'Sellel vikil puudub erimärkidega failinimede tugi.',
'fileexists' => "Sellise nimega fail on juba olemas. Palun vaata lehekülge '''<tt>[[:$1]]</tt>''', kui sa pole kindel, kas soovid seda muuta.
[[$1|thumb]]",
'disambiguations' => 'Leheküljed, mis lingivad täpsustuslehekülgedele',
'disambiguationspage' => 'Template:Täpsustuslehekülg',
-'disambiguations-text' => "Loetletud leheküljed viitavad '''täpsustusleheküljele'''.
-Selle asemel peaks nad olema lingitud sobivasse artiklisse.
+'disambiguations-text' => "Järgmised leheküljed sisaldavad vähemalt üht linki '''täpsustusleheküljele'''.
+Võimalik, et sellised lingid peaks viitama sobivamatele lehekülgedele.
Lehekülg loetakse täpsustusleheküljeks, kui see kasutab malli, millele viitab sõnum [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Kahekordsed ümbersuunamised',
'rollback' => 'Tühista muudatused',
'rollback_short' => 'Tühista',
'rollbacklink' => 'tühista',
+'rollbacklinkcount' => 'tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}',
+'rollbacklinkcount-morethan' => 'tühista üle {{PLURAL:$1|ühe muudatuse|10 muudatuse}}',
'rollbackfailed' => 'Muudatuste tühistamine ebaõnnestus',
'cantrollback' => 'Ei saa muudatusi eemaldada, sest viimane kaastööline on artikli ainus autor.',
'alreadyrolled' => 'Muudatust, mille tegi lehele [[:$1]] kasutaja [[User:$2|$2]] ([[User talk:$2|arutelu]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), ei saa tühistada, sest keegi teine on seda lehte vahepeal muutnud.
'tooltip-ca-nstab-main' => 'Näita artiklit',
'tooltip-ca-nstab-user' => 'Näita kasutaja lehte',
'tooltip-ca-nstab-media' => 'Näita pildi lehte',
-'tooltip-ca-nstab-special' => 'See on erilehekülg, te ei saa seda redigeerida',
+'tooltip-ca-nstab-special' => 'See on erilehekülg, sa ei saa seda lehekülge ennast redigeerida.',
'tooltip-ca-nstab-project' => 'Näita projekti lehte',
'tooltip-ca-nstab-image' => 'Näita pildi lehte',
'tooltip-ca-nstab-mediawiki' => 'Näita süsteemi sõnumit',
'siteuser' => '{{GRAMMAR:genitive|{{SITENAME}}}} kasutaja $1',
'anonuser' => '{{GRAMMAR:genitive|{{SITENAME}}}} anonüümne kasutaja $1',
'lastmodifiedatby' => 'Viimati muutis lehekülge $3 $2 kell $1.',
-'othercontribs' => 'Põhineb kasutajate $1 tööl.',
+'othercontribs' => 'Põhineb järgmiste kasutajate kaastööl: $1.',
'others' => 'teiste',
-'siteusers' => 'võrgukoha {{SITENAME}} {{PLURAL:$2|kasutaja|kasutajate}} $1',
+'siteusers' => '{{GRAMMAR:genitive|{{SITENAME}}}} {{PLURAL:$2|kasutaja|kasutajad}} $1',
'anonusers' => '{{GRAMMAR:genitive|{{SITENAME}}}} {{PLURAL:$2|anonüümne kasutaja|anonüümsed kasutajad}} $1',
'creditspage' => 'Lehekülje toimetajate loend',
'nocredits' => 'Selle lehekülje toimetajate loend ei ole kättesaadav.',
'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|arutelu]])',
# Core parser functions
-'unknown_extension_tag' => 'Tundmatu lisa märgend "$1".',
+'unknown_extension_tag' => 'Tundmatu lisa silt "$1".',
'duplicate-defaultsort' => '\'\'\'Hoiatus:\'\'\' Järjestamisvõti "$2" tühistab eespool oleva järjestamisvõtme "$1".',
# Special:Version
* <span class="mw-specialpagecached">Uuendamata sisuga erileheküljed (ei pruugi enam kasutuses olla)</span>',
'specialpages-group-maintenance' => 'Hooldusaruanded',
'specialpages-group-other' => 'Teised erileheküljed',
-'specialpages-group-login' => 'Sisselogimine ja registreerumine',
+'specialpages-group-login' => 'Sisselogimine ja konto loomine',
'specialpages-group-changes' => 'Viimased muudatused ja logid',
'specialpages-group-media' => 'Failidega seonduv',
'specialpages-group-users' => 'Kasutajad ja õigused',
'duration-centuries' => '$1 {{PLURAL:$1|sajandi}}',
'duration-millennia' => '$1 {{PLURAL:$1|aastatuhande}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 pole lubatud {{PLURAL:$4|failitüüp|failitüübid}}. Lubatud {{PLURAL:$3|failitüüp|failitüübid}} on $2.',
);
'tog-showtoolbar' => 'نوار ابزار جعبهٔ ویرایش نمایش یابد (نیازمند جاوااسکریپت)',
'tog-editondblclick' => 'ویرایش صفحهها با دوکلیک (نیازمند جاوااسکریپت)',
'tog-editsection' => 'ویرایش بخشها از طریق پیوندهای [ویرایش] فعال باشد',
-'tog-editsectiononrightclick' => 'ویرایش بخشها با کلیک راست روی عناوین قسمتها فعال باشد (نیازمند جاوااسکریپت)',
+'tog-editsectiononrightclick' => 'ویرایش بخشها با کلیک راست روی عنوانهای بخشها فعال باشد (نیازمند جاوااسکریپت)',
'tog-showtoc' => 'فهرست مندرجات نمایش یابد (برای صفحههای دارای بیش از ۳ عنوان)',
'tog-rememberpassword' => 'گذرواژهٔ من (حداکثر $1 روز) در این مرورگر به خاطر سپرده شود',
'tog-watchcreations' => 'صفحههایی که میسازم و پروندههایی را که بارگذاری میکنم به فهرست پیگیریهایم افزوده شود',
'tog-previewonfirst' => 'پیشنمایش هنگام اولین ویرایش نمایش یابد',
'tog-nocache' => 'حافظهٔ نهانی مرورگر از کار انداخته شود',
'tog-enotifwatchlistpages' => 'اگر صفحه یا پروندهای از فهرست پیگیریهایم ویرایش شد برای من نامهای فرستاده شود',
-'tog-enotifusertalkpages' => 'هنگامی که در صفحهٔ بحث کاربریام تغییری صورت میگیرد به من نامهای فرستاده شود',
+'tog-enotifusertalkpages' => 'هنگامی که در صفحهٔ بحث کاربریام تغییری صورت میگیرد به من رایانامه فرستاده شود',
'tog-enotifminoredits' => 'برای تغییرات جزئی در صفحهها و پروندهها هم به من نامهای فرستاده شود',
-'tog-enotifrevealaddr' => 'Ù\86شاÙ\86Û\8c راÛ\8cاÙ\86اÙ\85Ù\87Ù\94 Ù\85Ù\86 را در نامههای اطلاعرسانی نمایش یابد',
+'tog-enotifrevealaddr' => 'Ù\86شاÙ\86Û\8c راÛ\8cاÙ\86اÙ\85Ù\87Ù\94 Ù\85Ù\86 در راÛ\8cانامههای اطلاعرسانی نمایش یابد',
'tog-shownumberswatching' => 'شمار کاربران پیگیریکننده نمایش یابد',
'tog-oldsig' => 'امضای کنونی:',
'tog-fancysig' => 'امضا به صورت ویکیمتن در نظر گرفته شود (بدون درج خودکار پیوند)',
'dec' => 'دسامبر',
# Categories related messages
-'pagecategories' => '{{PLURAL:$1|ردههای صفحه|ردههای صفحه}}',
+'pagecategories' => '{{PLURAL:$1|رده|ردهها}}',
'category_header' => 'صفحههای ردهٔ «$1»',
'subcategories' => 'زیرردهها',
'category-media-header' => 'پروندههای ردهٔ «$1»',
'hidden-categories' => '{{PLURAL:$1|ردهٔ پنهان|ردههای پنهان}}',
'hidden-category-category' => 'ردههای پنهان',
'category-subcat-count' => '{{PLURAL:$2|این رده تنها حاوی زیرردهٔ زیر است.|{{PLURAL:$1|این زیررده|این $1 زیررده}} در این رده قرار {{PLURAL:$1|دارد|دارند}}؛ این رده در کل حاوی $2 زیررده است.}}',
-'category-subcat-count-limited' => 'این رده شامل {{PLURAL:$1|یک زیررده|$1 زیررده}} زیر میباشد.',
+'category-subcat-count-limited' => 'این رده شامل {{PLURAL:$1|یک|$1}} زیرردهٔ زیر است.',
'category-article-count' => '{{PLURAL:$2|این رده فقط دارای صفحهٔ زیر است.|{{PLURAL:$1|این صفحه|این $1 صفحه}} در این رده قرار {{PLURAL:$1|دارد|دارند}}؛ این رده در کل حاوی $2 صفحه است.}}',
'category-article-count-limited' => '{{PLURAL:$1|صفحهٔ|$1 صفحهٔ}} زیر در ردهٔ فعلی قرار دارند.',
'category-file-count' => '{{PLURAL:$2|این رده تنها حاوی پروندهٔ زیر است.|{{PLURAL:$1|این پرونده|این $1 پرونده}} در این رده قرار {{PLURAL:$1|دارد|دارند}}؛ این رده در کل حاوی $2 پرونده است.}}',
'listingcontinuesabbrev' => '(ادامه)',
'index-category' => 'صفحههای نمایهشده',
'noindex-category' => 'صفحههای نمایهنشده',
-'broken-file-category' => 'صفحههای دارای پیوندهای پروندهٔ خراب',
+'broken-file-category' => 'صفحههای دارای پیوند خراب به پرونده',
'about' => 'درباره',
'article' => 'صفحهٔ محتوایی',
'edit' => 'ویرایش',
'create' => 'ایجاد',
'editthispage' => 'ویرایش این صفحه',
-'create-this-page' => 'این صفحه را ایجاد کنید',
+'create-this-page' => 'ایجاد این صفحه',
'delete' => 'حذف',
'deletethispage' => 'حذف این صفحه',
'undelete_short' => 'احیای {{PLURAL:$1|یک ویرایش|$1 ویرایش}}',
'disclaimerpage' => 'Project:تکذیبنامهٔ عمومی',
'edithelp' => 'راهنمای ویرایشکردن',
'edithelppage' => 'Help:چگونه صفحهها را ویرایش کنیم',
-'helppage' => 'Help:راهنما',
+'helppage' => 'Help:محتویات',
'mainpage' => 'صفحهٔ اصلی',
'mainpage-description' => 'صفحهٔ اصلی',
'policy-url' => 'Project:سیاستها',
'privacypage' => 'Project:سیاست حفظ اسرار',
'badaccess' => 'خطای دسترسی',
-'badaccess-group0' => 'شما اجازهٔ اجرای عملی که درخواست کردهاید را ندارید.',
+'badaccess-group0' => 'شما اجازهٔ اجرای عملی را که درخواست کردهاید ندارید.',
'badaccess-groups' => 'عملی که درخواست کردهاید منحصر به کاربران {{PLURAL:$2|این گروه|این گروهها}} است: $1.',
'versionrequired' => 'نسخهٔ $1 از نرمافزار مدیاویکی لازم است',
در غیر این صورت ممکن است اشکالی در نرمافزار پیدا کرده باشید.
لطفاً این مشکل را با ذکر نشانی اینترنتی به یکی از [[Special:ListUsers/sysop|مدیران]] گزارش دهید.',
-'missingarticle-rev' => '(نسخهٔ شماره: $1)',
+'missingarticle-rev' => '(شمارهٔ نسخه: $1)',
'missingarticle-diff' => '(تفاوت: $1، $2)',
'readonly_lag' => 'پایگاه داده به طور خودکار قفل شدهاست تا نسخههای پشتیبان با نسخهٔ اصلی هماهنگ شوند',
'internalerror' => 'خطای داخلی',
'filecopyerror' => 'نشد از پروندهٔ «$1» روی «$2» نسخهبرداری شود.',
'filerenameerror' => 'نشد پروندهٔ «$1» به «$2» تغییر نام یابد.',
'filedeleteerror' => 'نشد پروندهٔ «$1» حذف شود.',
-'directorycreateerror' => 'امکان ایجاد پوشه $1 وجود نداشت.',
+'directorycreateerror' => 'نشد مسیر $1 را ایجاد کرد.',
'filenotfound' => 'پروندهٔ «$1» یافت نشد.',
'fileexistserror' => 'امکان نوشتن روی پرونده $1 وجود ندارد: پرونده از قبل موجود است.',
'unexpected' => 'مقدار غیرمنتظره: «$1»=«$2».',
'remembermypassword' => 'گذرواژه را (تا حداکثر $1 {{PLURAL:$1|روز|روز}}) در این رایانه به خاطر بسپار',
'securelogin-stick-https' => 'پس از ورود به سامانه به HTTPS متصل بمان',
'yourdomainname' => 'دامنهٔ شما:',
+'password-change-forbidden' => 'شما نمیتوانید گذرواژهها را در این ویکی تغییر دهید.',
'externaldberror' => 'خطایی در ارتباط با پایگاه داده رخ دادهاست یا اینکه شما اجازهٔ بهروزرسانی حساب خارجی خود را ندارید.',
'login' => 'ورود به سامانه',
'nav-login-createaccount' => 'ورود به سامانه / ایجاد حساب کاربری',
'right-writeapi' => 'استفاده از API مربوط به نوشتن',
'right-delete' => 'حذف صفحهها',
'right-bigdelete' => 'حذف صفحههای دارای تاریخچهٔ بزرگ',
+'right-deletelogentry' => 'حذف و احیای مدخلهای خاصی از سیاهه',
'right-deleterevision' => 'حذف و احیای نسخههای خاصی از صفحه',
'right-deletedhistory' => 'مشاهدهٔ موارد حذفشده از تاریخچه، بدون دیدن متن آنها',
'right-deletedtext' => 'مشاهدهٔ متن حذفشده و تغییرات بین نسخههای حذفشده',
'rollback' => 'واگردانی ویرایشها',
'rollback_short' => 'واگردانی',
'rollbacklink' => 'واگردانی',
+'rollbacklinkcount' => 'واگردانی $1 ویرایش',
+'rollbacklinkcount-morethan' => 'واگردانی بیش از $1 ویرایش',
'rollbackfailed' => 'واگردانی نشد',
'cantrollback' => 'نمیتوان ویرایش را واگرداند؛
آخرین مشارکتکننده تنها مؤلف این مقاله است.',
'tooltip-n-mainpage-description' => 'مشاهدهٔ صفحهٔ اصلی',
'tooltip-n-portal' => 'پیرامون پروژه، آنچه میتوانید انجام دهید و اینکه چه چیز را کجا پیدا کنید',
'tooltip-n-currentevents' => 'یافتن اطلاعات پیشزمینه پیرامون رویدادهای کنونی',
-'tooltip-n-recentchanges' => 'فهرستی از تغییرات اخیر در ویکی',
+'tooltip-n-recentchanges' => 'فهرستی از تغییرات اخیر ویکی',
'tooltip-n-randompage' => 'آوردن یک صفحهٔ تصادفی',
'tooltip-n-help' => 'مکانی برای دریافتن',
'tooltip-t-whatlinkshere' => 'فهرست همهٔ صفحههایی که به این صفحه پیوند میدهند',
'duration-centuries' => '$1 قرن',
'duration-millennia' => '{{PLURAL:$1|هزار سال |$1 هزار سال}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '‎$1 {{PLURAL:$4|یک نوع پرونده غیرمجاز است|انواعی پرونده غیرمجاز هستند}}. {{PLURAL:$3|نوع پرونده مجاز|انواع پرونده مجاز}} از این قرار است: $2 .',
);
'pagecategories' => '{{PLURAL:$1|Luokka|Luokat}}',
'category_header' => "Luokan ''$1'' sisältämät sivut",
'subcategories' => 'Alaluokat',
-'category-media-header' => 'Luokan ”$1” sisältämät tiedostot',
+'category-media-header' => 'Tiedostot, jotka ovat luokassa $1',
'category-empty' => "''Tässä luokassa ei ole sivuja eikä tiedostoja.''",
'hidden-categories' => '{{PLURAL:$1|Piilotettu luokka|Piilotetut luokat}}',
'hidden-category-category' => 'Piilotetut luokat',
'cannotdelete' => 'Sivun tai tiedoston ”$1” poisto epäonnistui.
Joku muu on saattanut poistaa sen.',
'cannotdelete-title' => 'Sivua $1 ei voi poistaa',
+'delete-hook-aborted' => 'Laajennuskoohdi esti muokkauksen antamatta syytä.',
'badtitle' => 'Virheellinen otsikko',
'badtitletext' => 'Pyytämäsi sivuotsikko oli virheellinen, tyhjä tai väärin linkitetty kieltenvälinen tai wikienvälinen linkki.',
'perfcached' => 'Tiedot ovat välimuistista eivätkä välttämättä ole ajan tasalla. Välimuistissa on enintään {{PLURAL:$1|yksi tulos|$1 tulosta}}.',
'remembermypassword' => 'Muista minut (enintään $1 {{PLURAL:$1|päivä|päivää}})',
'securelogin-stick-https' => 'Jatka salatun yhteyden käyttämistä sisäänkirjautumisen jälkeen',
'yourdomainname' => 'Verkkonimi',
+'password-change-forbidden' => 'Et voi muuttaa salasanoja tässä wikissä.',
'externaldberror' => 'Tapahtui virhe ulkoisen autentikointitietokannan käytössä tai sinulla ei ole lupaa päivittää tunnustasi.',
'login' => 'Kirjaudu sisään',
'nav-login-createaccount' => 'Kirjaudu sisään tai luo tunnus',
'noarticletext-nopermission' => 'Tällä hetkellä tällä sivulla ei ole tekstiä.
Voit [[Special:Search/{{PAGENAME}}|etsiä sivun nimellä]] muilta sivuilta
tai <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} hakea aiheeseen liittyviä lokeja]</span>',
+'missing-revision' => 'Muutosta #$1 sivulla "{{PAGENAME}}" ei ole olemassa.
+
+Tämä yleensä johtuu vanhentuneesta historialinkistä sivulle, joka on poistettu.
+Lisätietoja löytyy [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} poistolokista].',
'userpage-userdoesnotexist' => 'Käyttäjätunnusta <nowiki>$1</nowiki> ei ole rekisteröity. Varmista haluatko muokata tätä sivua.',
'userpage-userdoesnotexist-view' => 'Käyttäjätunnusta ”$1” ei ole rekisteröity.',
'blocked-notice-logextract' => 'Tämä käyttäjä on tällä hetkellä estetty.
'editundo' => 'kumoa',
'diff-multi' => '(Näytettyjen versioiden välissä on {{PLURAL:$1|yksi muokkaus|$1 versiota, jotka ovat {{PLURAL:$2|yhden käyttäjän tekemiä|$2 eri käyttäjän tekemiä}}}}.)',
'diff-multi-manyusers' => '(Versioiden välissä on {{PLURAL:$1|yksi muu muokkaus|$1 muuta muokkausta, jotka on tehnyt {{PLURAL:$2|yksi käyttäjä|yli $2 eri käyttäjää}}}}.)',
+'difference-missing-revision' => '{{PLURAL:$2|Yhtä versiota|$2 versiota}} tästä diffistä ($1) {{PLURAL:$2|ei|ei}} löytynyt.
+
+Tämä johtuu yleensä seuraavasta vanhentuneeesta diffilinkistä sivulle, joka on poistettu.
+Lisätietoja löytyy [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} poistolokista].',
# Search results
'searchresults' => 'Hakutulokset',
'right-writeapi' => 'Käyttää kirjoitus-APIa',
'right-delete' => 'Poistaa sivuja',
'right-bigdelete' => 'Poistaa sivuja, joilla on pitkä historia',
+'right-deletelogentry' => 'Poista ja palauta tiettyjä lokimerkintöjä',
'right-deleterevision' => 'Poistaa ja palauttaa sivujen versioita',
'right-deletedhistory' => 'Tarkastella poistettuja versiotietoja ilman niihin liittyvää sisältöä',
'right-deletedtext' => 'Tarkastella poistettujen sivujen tekstiä ja muutoksia poistettujen versioiden välillä',
'duration-centuries' => '$1 {{PLURAL:$1|vuosisata|vuosisataa}}',
'duration-millennia' => '$1 {{PLURAL:$1|vuosituhat|vuosituhatta}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ei ole sallittu tiedostomuoto|eivät ole sallittuja tiedostomuotoja}}. {{PLURAL:$3|Sallittu tiedostomuoto on|Sallittuja tiedostomuotoja ovat}} $2.',
);
'tog-hidepatrolled' => 'Goym eftirkannaðar rættingar í seinastu broytingum',
'tog-newpageshidepatrolled' => 'Goym eftirkannaðar síður frá listanum yvir nýggjar síður',
'tog-extendwatchlist' => 'Víðka eftirlitslistan fyri at vísa allar broytingar, ikki bara tær seinastu',
-'tog-usenewrc' => 'Nýt betraðar seinastu broytingar (krevur JavaScript)',
+'tog-usenewrc' => 'Bólkað broytingar eftir síðu í seinastu rættingar og eftirlitslita (krevur JavaScript)',
'tog-numberheadings' => 'Sjálvtalmerking av yvirskrift',
'tog-showtoolbar' => 'Vís amboðslinju í rætting',
'tog-editondblclick' => 'Rætta síðu við at tvíklikkja (JavaScript)',
'tog-editsectiononrightclick' => 'Rætta greinpart við at høgraklikkja á yvirskrift av greinparti (JavaScript)',
'tog-showtoc' => 'Vís innihaldsyvurlit (Til greinir við meira enn trimun greinpartum)',
'tog-rememberpassword' => 'Minst til loyniorð á hesum kaga (í mesta lagi $1 {{PLURAL:$1|dag|dagar}})',
-'tog-watchcreations' => 'Legg síður, sum eg stovni, í mítt eftirlit',
-'tog-watchdefault' => 'Vaka yvur nýggjum og broyttum greinum',
-'tog-watchmoves' => 'Legg síður afturat, sum eg havi valt afturat mínum eftirkanningarlista.',
-'tog-watchdeletion' => 'Legg síður sum eg sletti afturat mínum vaktarlista',
+'tog-watchcreations' => 'Legg síður, sum eg stovni og fílur sum eg leggi út, afturat mínum eftirlitslista',
+'tog-watchdefault' => 'Legg síður sum eg rætti afturat mínum eftirlitslista',
+'tog-watchmoves' => 'Legg síður og fílur, sum eg flyti, afturat mínum eftirlitslista',
+'tog-watchdeletion' => 'Legg síður og fílur, sum eg striki, afturat mínum eftirlitslista',
'tog-minordefault' => 'Merk sum standard allar broytingar sum smærri',
'tog-previewontop' => 'Vís forhondsvísning áðren rættingarkassan',
'tog-previewonfirst' => 'Sýn forskoðan við fyrstu broyting',
'tog-nocache' => 'Deaktivera síðu "caching" í brovsaranum',
-'tog-enotifwatchlistpages' => 'Send mær teldupost, tá ein síða á mínum eftirlitslista er broytt',
+'tog-enotifwatchlistpages' => 'Send mær teldupost, tá ein síða ella fíla á mínum eftirlitslista er broytt',
'tog-enotifusertalkpages' => 'Send mær teldubræv, tá mín brúarasíða er broytt',
-'tog-enotifminoredits' => 'Send mær eisini teldupost viðvíkjandi smærri broytingum á síðunum',
+'tog-enotifminoredits' => 'Send mær eisini ein teldupost viðvíkjandi smærri broytingum á síðum og fílum',
'tog-enotifrevealaddr' => 'Avdúkað mína teldupost adressu í fráboðanar teldupostum',
'tog-shownumberswatching' => 'Vís tal av brúkarum sum fylgja við',
'tog-oldsig' => 'Verandi undirskrift:',
'youhavenewmessages' => 'Tú hevur $1 ($2).',
'newmessageslink' => 'nýggj boð',
'newmessagesdifflink' => 'seinasta broyting',
+'youhavenewmessagesfromusers' => 'Tú hevur $1 frá {{PLURAL:$3|øðrum brúkara|$3 brúkarum}} ($2).',
+'youhavenewmessagesmanyusers' => 'Tú hevur $1 frá fleiri brúkarum ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|eini nýggj boð|nýggj boð}}',
+'newmessagesdifflinkplural' => 'seinasta {{PLURAL:$1|broyting|broytingar}}',
'youhavenewmessagesmulti' => 'Tú hevur nýggj boð á $1',
'editsection' => 'rætta',
'editold' => 'rætta',
'cannotdelete-title' => 'Kann ikki strika síðu "$1"',
'badtitle' => 'Ógyldugt heiti',
'badtitletext' => 'Umbidna síðan er ógyldugt, tómt ella skeivt tilslóðað heiti millum mál ella wikur.',
-'perfcached' => 'Fylgjandi upplýsingar eru "cached" og eru møguliga ikki dagførdir. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts' => 'Fylgjandi dáta er goymt, og var seinast goymt $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
+'perfcached' => 'Fylgjandi upplýsingar eru "fangaðir" (cached) og eru møguliga ikki dagførdir. Í mesta lagi {{PLURAL:$1|eitt úrslit er|$1 úrslit eru}} tøk í cache.',
+'perfcachedts' => 'Fylgjandi dáta er "fangað" (cached), og var seinast dagført $1. Í mesta lagi {{PLURAL:$4|eitt úrslit er|$4 úrslit eru}} tøk í cache.',
'querypage-no-updates' => 'Tað ber í løtuni ikki til at dagføra hesa síðuna.
Dáta higani verður í løtuni ikki endurnýggjað.',
'wrong_wfQuery_params' => '↓ Skeiv parametir til wfQuery()<br />
'invalidemailaddress' => 'T-post bústaðurin kann ikki verða góðtikin, tí hann sær út til at hava ógyldugt format.
Vinarliga skriva t-post bústað í røttum formati ella lat handa teigin vera tóman.',
'cannotchangeemail' => 'T-post adressur, sum eru knýttar at brúkarakontum, kunnu ikki broytast á hesi wiki.',
+'emaildisabled' => 'Henda heimasíðan kann ikki senda teldupostar.',
'accountcreated' => 'Konto upprættað',
'accountcreatedtext' => 'Brúkarakontan hjá $1 er nú upprættað.',
'createaccount-title' => 'Upprætta brúkarakonto á {{SITENAME}}',
Tú kanst ikki brúka 'send t-post til henda brúkara' funktiónina, uttan so at ein galdandi t-post adressa er givin í tínum [[Special:Preferences|konto innstillingum]] og um tú ikki ert blivin sperraður frá at brúka hana.
Tín verandi IP adressa er $3, og sperrings ID er #$5.
Vinarliga tak allir hesir upplýsingar við í einum hvørjum fyrispurningi ið tí hevur.",
+'autoblockedtext' => 'Tín IP bústaður er blivin sjálvvirkandi sperraður, tí hann varð brúktur av einum øðrum brúkara, sum er blivin sperraður av $1.
+Viðkomandi gav hesa orsøk:
+:\'\'$2\'\'
+
+* Sperring byrjað: $8
+* Sperringin útgongur: $6
+* Intended blockee: $7
+
+Tú kanst seta teg í samband við $1 ella ein av hinum [[{{MediaWiki:Grouppage-sysop}}|administratorunum]] fyri at kjakast um sperringina.
+
+Legg til merkis, at tú kanst ikki brúka "send ein t-post til henda brúkara" funktiónina, uttan so at tú hevur ein gyldugan t-post bústað skrásettan í tínum
+[[Special:Preferences|brúkara ynskjum]] og at tú ikki ert blivin sperrað/ur frá at brúka hesa.
+
+IP adressan sum tú brúkar í løtuni er $3, og brúkara ID er #$5.
+Vinarliga tak allar hesar upplýsigarnar við í einum og hvørjum fyrispurningi, ið tú kanst hava.',
'blockednoreason' => 'Ongin orsøk er givin',
'whitelistedittext' => 'Tú mást $1 fyri at rætta hesa síðu.',
'confirmedittext' => 'Tú mást vátta tína teldupost adressu áðrenn tú rættar síður.
Tilevnaðar .css og .js síður brúka heiti sum byrja við lítlum bókstavi, t.d. {{ns:user}}:Foo/vector.css í mun til {{ns:user}}:Foo/Vector.css.",
'updated' => '(Dagført)',
'note' => "'''Viðmerking:'''",
-'previewnote' => "'''Minst til at hetta bara er ein forskoðan, sum enn ikki er goymd!'''",
+'previewnote' => "'''Minst til at hetta bara er ein forskoðan.'''
+Tínar broytingar eru ikki goymdar enn!",
+'continue-editing' => 'Halt fram við at rætta',
'previewconflict' => 'Henda forskoðanin vísir tekstin í erva soleiðis sum hann sær út, um tú velur at goyma.',
'session_fail_preview' => "'''Orsakað! Vit kundu ikki fullføra tínar broytingar, tí tínar sessións dáta eru horvin.'''
Vinarliga royn aftur.
Um tað enn ikki virkar, royn so at [[Special:UserLogout|rita út]] og rita so inn aftur.",
'edit_form_incomplete' => "'''Nakrir partar av rættingarskjalinum náddu ikki til servaran; eftirkanna tvær ferðir at tínar rættingar eru til staðar og royn so aftur.'''",
'editing' => 'Tú rættar $1',
+'creating' => 'Upprætta $1',
'editingsection' => 'Tú rættar $1 (partur)',
'editingcomment' => 'Tú rættar $1 (nýtt brot)',
'editconflict' => 'Rættingar konflikt: $1',
Um tú ikki ynskir at tín skriving verður broytt miskunnarleyst, so skal tú ikki skriva nakað her.<br />
Tú lovar okkum eisini, at tú sjálv/ur hevur skrivað hetta, ella at tú hevur avritað tað frá keldu sum er almenn ogn (public domain) ella frá líkandi fríum keldum (sí $1 fyri nærri upplýsingar).
'''Tú mást ikki senda tilfar inn, sum er vart av upphavsrætti, uttan so at tú hevur fingið loyvi til tað!'''",
-'longpageerror' => "'''Feilur: Teksturin sum tú hevur sent inn er $1 kilobytes (kB) langur, sum er størri enn mest loyvda sum er $2 kilobytes.'''
+'longpageerror' => "'''Feilur: Teksturin sum tú hevur sent inn er {{PLURAL:$1|eitt kilobyte|$1 kilobytes}} langur, sum er longri enn mest loyvda, sum er {{PLURAL:$2|eitt kilobyte|$2 kilobytes}}.'''
Teksturin kann tí ikki verða goymdur.",
'protectedpagewarning' => "'''Ávaring: Henda síðan er friðað, so at einans brúkarar við umboðsstjóra heimildum kunnu broyta hana.'''
Tann seinasta logg inn er goymt niðanfyri fyri ávísing:",
'deletedhist' => 'Strikingar søga',
'revdelete-hide-current' => 'Tað er hendur ein feilur tá luturin skuldi fjalast, luturin er dagfestur $2, kl. $1: Hetta er nýggjast versjónin.
Hon kann ikki fjalast.',
+'revdelete-show-no-access' => 'Feilur tá hesin lutur dagfestur $1 klokkan $2 skuldi vísast:Hesin lutur er blivin markeraður sum "avmarkaður".
+Tú hevur ikki atgongd til hann.',
+'revdelete-otherreason' => 'Onnur orsøk',
'revdelete-reasonotherlist' => 'Onnur orsøk',
'revdelete-edit-reasonlist' => 'Rætta strikingar orsøkir',
+'revdelete-offender' => 'Høvundurin av hesi endurskoðan:',
# History merging
'mergehistory-from' => 'Keldusíða:',
'mergelogpagetext' => 'Niðanfyri er ein listi við teimum nýggjastu samanflættingunum av einari síðu søgu til eina aðra.',
# Diffs
-'history-title' => 'Versjónssøgan hjá "$1"',
+'history-title' => 'Eftirlitssøgan hjá "$1"',
'difference-multipage' => '(Munur millum síður)',
'lineno' => 'Linja $1:',
'compareselectedversions' => 'Bera saman valdar útgávur',
'prefs-skin' => 'Hamur',
'skin-preview' => 'Forskoðan',
'prefs-datetime' => 'Dato og tíð',
+'prefs-user-pages' => 'Brúkarasíður',
'prefs-personal' => 'Brúkaradáta',
'prefs-rc' => 'Nýkomnar broytingar og stubbaskoðan',
'prefs-watchlist' => 'Eftirlit',
'prefs-watchlist-days-max' => 'Í mesta lagi $1 {{PLURAL:$1|dagur|dagar}}',
'prefs-watchlist-edits' => 'Tal av rættingum, sum skula vísast í víðkaðum eftirliti:',
'prefs-watchlist-edits-max' => 'Í mesta lagi: 1000',
+'prefs-watchlist-token' => 'Lykil til eftirlitslistan:',
'prefs-misc' => 'Ymiskar innstillingar',
'prefs-resetpass' => 'Broyt loyniorð',
'prefs-changeemail' => 'Broyt t-post adressu',
'yourrealname' => 'Títt navn*:',
'yourlanguage' => 'Mál til brúkaraflatu:',
'yournick' => 'Nýggj undirskrift:',
+'badsiglength' => 'Tín undirskrift er ov long.
+Hon má ikki hava meira enn $1 {{PLURAL:$1|tekn|tekn}}',
'yourgender' => 'Kyn:',
+'gender-unknown' => 'Ikki upplýst',
'gender-male' => 'Maður',
'gender-female' => 'Kvinna',
+'prefs-help-gender' => 'Tú avgerð sjálv/ur: Tað verður brúkt til at fáa kynsrættan tekst á nøkrum málum. Henda kunning verður almenn.',
'email' => 'T-post',
'prefs-help-realname' => 'Veruligt navn er valfrítt.
Um tú velur at skriva tað her, so verður tað nýtt til at geva tær æruna fyri títt arbeiði.',
Tín t-post adressa verður ikki avdúkað, tá aðrir brúkarir seta seg í samband við teg.',
'prefs-help-email-required' => 'T-post adressa er kravd.',
'prefs-info' => 'Grundleggjandi kunning',
+'prefs-i18n' => 'Altjóðagerð',
'prefs-signature' => 'Undirskrift',
+'prefs-timeoffset' => 'Tíðarmunur',
'prefs-advancedediting' => 'Víðkaðir møguleikar',
'prefs-advancedrc' => 'Víðkaðir møguleikar',
'prefs-advancedrendering' => 'Víðkaðir møguleikar',
'userrights-editusergroup' => 'Rætta brúkarabólkar',
'saveusergroups' => 'Goym brúkaraflokk',
'userrights-groupsmember' => 'Limur í:',
+'userrights-groupsmember-auto' => 'Óbeinleiðis limur í:',
+'userrights-groups-help' => 'Tú kanst broyta bólkalimaskap hjá hesum limi:
+* Ein krossaður kassi merkir, at hesin brúkari er limur í tí bólkinum.
+* Ein kassi sum ikki er krossaður (tjekk merktur) merkir, at brúkarin ikki er limur í tí bólkinum.
+* Ein * merkir, at tú kanst ikki taka bólkin burtur, tá tú fyrst hevur sett hann inn og mótsatt.',
'userrights-reason' => 'Orsøk:',
'userrights-no-interwiki' => 'Tú hevur ikki loyvi til at rætta brúkara rættindi á øðrum wikium.',
+'userrights-nodatabase' => 'Dátugrunnurin $1 er ikki til ella er hann ikki lokalur.',
'userrights-notallowed' => 'Tín konto hevur ikki loyvi til at seta ella taka burtur brúkara rættindi.',
'userrights-changeable-col' => 'Bólkar sum tú kanst broyta',
'userrights-unchangeable-col' => 'Bólkar, ið tú ikki kanst broyta',
'right-upload_by_url' => 'Legg fílur upp frá einum URL',
'right-delete' => 'Strika síður',
'right-bigdelete' => 'Strika síður við nógvum versjónum',
+'right-browsearchive' => 'Leita í strikaðum síðum',
'right-block' => 'Nokta øðrum brúkarum at rætta (blokka)',
'right-hideuser' => 'Sperra eitt brúkaranavn og goyma tað burtur fyri almenninginum',
+'right-unblockself' => 'Taka burtur sperring av sær sjálvum',
+'right-protect' => 'Broyt verjustøður og rætta vardar síður',
+'right-editprotected' => 'Rætta vardar síður (uttan niðurarvaða verju)',
'right-import' => 'Innflyt síður frá øðrum wikium',
'right-patrol' => 'Marka broytingar hjá øðrum sum eftirkannaðar',
'right-unwatchedpages' => 'Sí lista við síðum sum ikki eru eftiransaðar',
'right-userrights' => 'Rætta øll brúkaraloyvir',
'right-userrights-interwiki' => 'Broyt brúkara rættindi hjá brúkarum á øðrum wikium',
'right-sendemail' => 'Send t-post til aðrir brúkarar',
+'right-passwordreset' => 'Sí teldupostar til nullstilling av loyniorði',
# User rights log
+'rightslog' => 'Rættindaloggur',
+'rightslogtext' => 'Hetta er ein loggur sum vísir broytingar í brúkararættindum.',
+'rightslogentry' => 'broyttar bólka limaskap fyri $1 frá $2 til $3',
+'rightslogentry-autopromote' => 'varð sjálvvirkandi fluttur upp frá $2 til $3',
'rightsnone' => '(ongin)',
# Associated actions - in the sentence "You do not have permission to X"
'action-minoredit' => 'marka hesa rætting sum lítla',
'action-move' => 'flyt hesa síðu',
'action-move-subpages' => 'flyt hesa síðu og undirsíður hennara',
+'action-move-rootuserpages' => 'flyt høvuðs brúkarasíður',
'action-movefile' => 'flyt hesa fílu',
'action-upload' => 'send hesa fílu upp',
'action-delete' => 'Strika hesa síðu',
'newsectionsummary' => '/* $1 */ nýtt innlegg',
'rc-enhanced-expand' => 'Vís smálutir (krevur JavaScript)',
'rc-enhanced-hide' => 'Goym smálutir',
+'rc-old-title' => 'upprunaliga stovnað sum "$1"',
# Recent changes linked
'recentchangeslinked' => 'Viðkomandi broytingar',
# Upload
'upload' => 'Legg fílu upp',
'uploadbtn' => 'Legg fílu upp',
+'reuploaddesc' => 'Angra uppløðu og far aftur til upload formin',
+'upload-tryagain' => 'Goym broytta fílu frágreiðing',
'uploadnologin' => 'Ikki ritað inn',
'uploadnologintext' => 'Tú mást hava [[Special:UserLogin|ritað inn]]
fyri at leggja fílur upp.',
'large-file' => 'Tað verður viðmælt, at fílur ikki eru størri enn $1;
henda fílin er $2.',
'largefileserver' => 'Henda fílan er størri enn servarin er innstillaður til at loyva.',
+'file-deleted-duplicate' => 'Ein fíla, sum er líka sum henda ([[:$1]]) er fyrr blivin strikað.
+Tú eigur at kanna eftir strikingarsøguna hjá hesi fílu, áðrenn tú heldur áframm við at leggja hana út enn einaferð.',
+'uploadwarning' => 'Ávaring',
'savefile' => 'Goym fílu',
'uploadedimage' => 'sent "[[$1]]" upp',
'sourcefilename' => 'Kelda fílunavn:',
'upload-unknown-size' => 'Ókend stødd',
# File backend
+'backend-fail-notexists' => 'Fílan $1 er ikki til.',
'backend-fail-store' => 'Kundi ikki goyma fílu $1 á $2.',
'backend-fail-copy' => 'Kundi ikki avrita fílu $1 til $2.',
'backend-fail-move' => 'Kundi ikki flyta fílu $1 til $2.',
'backend-fail-writetemp' => 'Kundi ikki skriva til fyribils fílu.',
'backend-fail-closetemp' => 'Kundi ikki aftur fyribils fílu.',
'backend-fail-read' => 'Kundi ikki lesa fílu $1.',
-'backend-fail-create' => 'Kundi ikki skapa fílu $1.',
+'backend-fail-create' => 'Kundi ikki skriva fílu $1.',
# Lock manager
'lockmanager-notlocked' => 'Kundi ikki lata upp "$1"; hon er ikki stongd.',
'lockmanager-fail-closelock' => 'Kundi ikki lata aftur lás fílu fyri "$1".',
'lockmanager-fail-deletelock' => 'Kundi ikki sletta lás fílu fyri "$1".',
+'lockmanager-fail-acquirelock' => 'Kundi ikki fáa lás til "$1".',
+'lockmanager-fail-openlock' => 'Kundi ikki læsa upp fíluna til: "$1".',
+'lockmanager-fail-releaselock' => 'Kundi ikki læsa upp læsingina fyri: "$1".',
# img_auth script messages
+'img-auth-accessdenied' => 'Atgongd noktað',
+'img-auth-nologinnWL' => 'Tú ert ikki ritað/ur inn, og "$1" er ikki á hvítalista.',
+'img-auth-nofile' => 'Fílan "$1" er ikki til',
+'img-auth-isdir' => 'Tú roynir at fáa atgongd til mappuna "$1".
+Bert fílu atgongd er loyvd.',
+'img-auth-streaming' => 'Sendir "$1".',
'img-auth-noread' => 'Brúkarin hevur ikki rættindi til at lesa "$1".',
'img-auth-bad-query-string' => "URL'urin hevur ein ikki galdandi fyrispurning strong.",
# HTTP errors
'http-invalid-url' => 'Ógildug URL (internetadressa): $1',
+'http-invalid-scheme' => 'URLar av slagnum "$1" verða ikki stuðlaðir.',
+'http-request-error' => 'HTTP fyrispurningurin riggaði ikki av ókendum orsøkum.',
+'http-read-error' => 'HTTP lesifeilur.',
+'http-timed-out' => 'HTTP fyrispurningurin tók ov langa tíð.',
+'http-curl-error' => 'Feilur meðan vit heintaðu URL: $1',
+'http-host-unreachable' => 'Internetadressan er ikki atkomulig.',
+'http-bad-status' => 'Tað hendi ein feilur undir viðgerðini av HTTP fyrispurnininum: $1 $2',
+
+# Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
+'upload-curl-error6' => 'URLurin er ikki atkomuligur',
+'upload-curl-error6-text' => "URL'urin sum tú skrivaði er ikki atkomuligur.
+Vinarliga dupult-eftirkannað at URL er rættur og at heimasíðan koyrir.",
+'upload-curl-error28' => 'Tað gekk ov long tíð við uppload',
+'upload-curl-error28-text' => 'Heimasíðan tók ov langa tíð at svara.
+Vinarliga kanna eftir um síðan koyrir (er online), bíða eina lítla løtu og royn so aftur.
+Tú kanst eisini royna aftur, tá tað ikki eru so nógv í gongd her í senn.',
'license' => 'Lisensur:',
'license-header' => 'Lisensur',
'nolicense' => 'Onki valt',
'license-nopreview' => '(Fyrr ikki tøkt)',
+'upload_source_url' => '(ein galdandi, alment atkomuligan URL)',
+'upload_source_file' => '(ein fíla á tínari teldu)',
# Special:ListFiles
+'listfiles_search_for' => 'Leita eftir miðla navni:',
+'imgfile' => 'fíla',
'listfiles' => 'Myndalisti',
'listfiles_thumb' => 'Lítli mynd',
'listfiles_date' => 'Dagur',
'filehist-missing' => 'Fíla væntar',
'imagelinks' => 'Nýtsla av fílu',
'linkstoimage' => 'Fylgjandi {{PLURAL:$1|síða slóðar|$1 síður slóða}} til hesa fílu:',
+'linkstoimage-more' => 'Meira enn $1 {{PLURAL:$1|síða slóðar|síður slóða}} til hesa fílu.
+Hesin listin vísir {{PLURAL:$1|fyrstu síðu slóð|firstu $1 síðu slóðir}} bert til hesa fílu.
+Ein [[Special:WhatLinksHere/$2|fullur listi]] er tøkur.',
'nolinkstoimage' => 'Ongar síður slóða til hesa myndina.',
'morelinkstoimage' => 'Sí [[Special:WhatLinksHere/$1|fleiri leinkjur]] til hesa fílu.',
+'linkstoimage-redirect' => '$1 (fílu víðaristilling) $2',
'sharedupload-desc-here' => 'Henda fíla er frá $1 og kann verða brúka í øðrum verkætlanum.
Frágreiðingin á [$2 fílu frágreiðingar síðu] er víst her niðanfyri.',
+'shared-repo-from' => 'frá $1',
# File deletion
'filedelete' => 'Strika $1',
'deadendpages' => 'Gøtubotnssíður',
'protectedpages' => 'Friðaðar síður',
'listusers' => 'Brúkaralisti',
+'listusers-editsonly' => 'Vís bara brúkarar sum hava gjørt rættingar',
+'listusers-creationsort' => 'Bólkað eftir stovningardegnum',
+'usereditcount' => '$1 {{PLURAL:$1|rætting|rættingar}}',
'usercreated' => '{{GENDER:$3|Upprættað}} hin $1 kl. $2',
'newpages' => 'Nýggjar síður',
'newpages-username' => 'Brúkaranavn:',
[[Special:UnusedCategories|Ikki brúktir bólkar]] eru ikki vístar her.
Sí eisini [[Special:WantedCategories|ynsktir bólkar]].',
+# Special:DeletedContributions
+'deletedcontributions' => 'Slettaði brúkaraíkøst',
+'deletedcontributions-title' => 'Slettaði brúkaraíkøst',
+'sp-deletedcontributions-contribs' => 'íkøst',
+
# Special:LinkSearch
'linksearch-ns' => 'Navnarúm:',
'linksearch-ok' => 'Leita',
'listusers-submit' => 'Sýna',
'listusers-noresult' => 'Ongin brúkari var funnin.',
+# Special:ActiveUsers
+'activeusers-hidebots' => 'Fjal bottar',
+'activeusers-hidesysops' => 'Fjal umboðsstjórar (administratorar)',
+'activeusers-noresult' => 'Ongir brúkarar funnir.',
+
# Special:Log/newusers
'newuserlogpage' => 'Brúkara logg',
+'newuserlogpagetext' => 'Hetta er ein listi yvir seinast stovnaðu brúkarar.',
# Special:ListGroupRights
+'listgrouprights' => 'Brúkara bólka rættindi',
+'listgrouprights-summary' => 'Henda síða vísir ein lista av brúkarabólkum, sum eru útgreinaðir á hesi wiki og rættindini hjá teimum einstøku bólkunum.
+Møguliga er [[{{MediaWiki:Listgrouprights-helppage}}|meira kunning]] um einstøk rættindi.',
+'listgrouprights-key' => '* <span class="listgrouprights-granted">Givin rættindi</span>
+* <span class="listgrouprights-revoked">Frátikin rættindi</span>',
+'listgrouprights-group' => 'Bólkur',
+'listgrouprights-rights' => 'Rættindi',
+'listgrouprights-helppage' => 'Help:Bólka rættindi',
'listgrouprights-members' => '(limalisti)',
+'listgrouprights-removegroup' => 'Tak burtur {{PLURAL:$2|bólk|bólkar}}: $1',
+'listgrouprights-addgroup-all' => 'Legg til allir bólkar',
+'listgrouprights-removegroup-all' => 'Tak burtur allir bólkar',
+'listgrouprights-addgroup-self' => 'Legg {{PLURAL:$2|bólk|bólkar}} til tína egnu konto: $1',
+'listgrouprights-removegroup-self' => 'Tak burtur {{PLURAL:$2|bólk|bólkar}} frá egnari konto: $1',
+'listgrouprights-addgroup-self-all' => 'Legg allir bólkar til egna konto',
+'listgrouprights-removegroup-self-all' => 'Tak burtur allir bólkar frá egnari konto',
# E-mail user
+'mailnologin' => 'Ongin móttakara bústaður',
'mailnologintext' => 'Tú mást hava [[Special:UserLogin|ritað inn]]
og hava virkandi teldupostadressu í [[Special:Preferences|innstillingum]] tínum
fyri at senda teldupost til aðrar brúkarar.',
'emailuser' => 'Send t-post til brúkara',
'emailpage' => 'Send t-post til brúkara',
+'emailpagetext' => 'Tú kanst brúka skjalið niðanfyri til at senda ein teldupost til henda brúkaran.
+Teldupost adressan sum tú skrivaði í [[Special:Preferences|tíni brúkara ynskir]] kemur síðan fram sum "Frá" adressan í teldupostinum, soleiðis at móttakarin kann svara beinleiðis til tín.',
'defemailsubject' => '{{SITENAME}} t-postur frá brúkara $1',
'usermaildisabled' => 'Brúkara t-postur er óvirkin',
'usermaildisabledtext' => 'Tú kanst ikki senda teldupost til aðrir brúkarar á hesi wiki',
'noemailtitle' => 'Ongin t-post adressa',
'noemailtext' => 'Hesin brúkarin hevur ikki upplýst eina gylduga t-post-adressu.',
'nowikiemailtitle' => 'Ongin t-postur er loyvdur',
+'emailusername' => 'Brúkaranavn:',
'emailfrom' => 'Frá:',
'emailto' => 'Til:',
'emailsubject' => 'Evni:',
'confirm' => 'Vátta',
'excontent' => "innihald var: '$1'",
'excontentauthor' => "innihaldið var: '$1' (og einasti rithøvundur var '[[Special:Contributions/$2|$2]]')",
+'exbeforeblank' => 'innihaldið áðrenn síðan varð tømd var: "$1"',
'exblank' => 'síðan var tóm',
+'delete-confirm' => 'Strikað "$1"',
+'delete-legend' => 'Strikað',
'historywarning' => "'''Ávaring:''' Síðan, ið tú ert í gongd við at strika, hevur eina søgu við umleið $1 {{PLURAL:$1|broyting|broytingum}}:",
'confirmdeletetext' => 'Tú ert í gongd við endaliga at strika ein a síðu
ella mynd saman við allari søgu úr dátugrunninum.
'deletedtext' => '"$1" er nú strikað.
Sí $2 fyri fulla skráseting av strikingum.',
'dellogpage' => 'Striku logg',
+'dellogpagetext' => 'Niðanfyri síggjast tær nýggjastu strikingarnar.',
'deletionlog' => 'striku logg',
+'reverted' => 'Aftur til eina eldri verjsón',
'deletecomment' => 'Orsøk:',
+'deleteotherreason' => 'Onnur orsøk:',
+'deletereasonotherlist' => 'Onnur orsøk',
+'deletereason-dropdown' => '*Vanligar orsøkir til striking
+** Umbøn frá høvunda
+** Brot á upphavsrættin
+** Herverk (Vandalisma)',
+'delete-edit-reasonlist' => 'Rætta orsøkir til striking',
+'delete-toobig' => 'Henda síðan hevur eina langa rættingar søgu, meira enn $1 {{PLURAL:$1|versjón|versjónir}}.
+Striking av slíkum síðum er avmarkað fyri at forða fyri at onkur av óvart kemur til at forstýra {{SITENAME}}.',
# Rollback
'rollback' => 'Rulla broytingar aftur',
'rollback_short' => 'Rulla aftur',
'rollbacklink' => 'afturrulling',
'rollbackfailed' => 'Afturrulling miseydnað',
+'revertpage' => 'Tók burtur rættingar hjá [[Special:Contributions/$2|$2]] ([[User talk:$2|kjak]]) til seinastu versjón hjá [[User:$1|$1]]',
+'revertpage-nouser' => 'Tók burtur rættingar hjá (brúkaranavn tikið vekk) til seinastu versjón hjá [[User:$1|$1]]',
+'rollback-success' => 'Tók burtur rættingar hjá $1;
+broytti tað aftur til seinastu versjón hjá $2.',
# Protect
'protectlogpage' => 'Friðingarbók',
* @file
*
* @author Agzennay
+ * @author Amqui
* @author Arkanosis
+ * @author Boniface
+ * @author Brunoperel
* @author Cedric31
* @author ChrisPtDe
* @author Coyau
+ * @author Cquoi
* @author Crochet.david
* @author Csisc
* @author Damouns
* @author Dodoïste
* @author Elfix
* @author Enzoreg
+ * @author Erkethan
* @author Esbardu
* @author Fryed-peach
* @author Giro720
* @author Sherbrooke
* @author Skalman
* @author The Evil IP address
+ * @author Tititou36
* @author TouzaxA
* @author Tpt
* @author Urhixidur
'youhavenewmessages' => 'Vous avez $1 ($2).',
'newmessageslink' => 'de nouveaux messages',
'newmessagesdifflink' => 'dernière modification',
+'youhavenewmessagesfromusers' => "Vous avez $1 d'{{PLURAL:$3| un autre utilisateur|$3 autres utilisateurs}} ( $2 ).",
+'youhavenewmessagesmanyusers' => 'Vous avez $1 de nombreux utilisateurs ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un message|nouveaux messages}}',
+'newmessagesdifflinkplural' => 'dernières {{PLURAL:$1|modification|modifications}}',
'youhavenewmessagesmulti' => 'Vous avez de nouveaux messages sur $1.',
'editsection' => 'modifier',
'editold' => 'modifier',
'remembermypassword' => 'Me reconnecter automatiquement aux prochaines visites avec ce navigateur (au maximum $1 {{PLURAL:$1|jour|jours}})',
'securelogin-stick-https' => 'Rester connecté en HTTPS après la connexion',
'yourdomainname' => 'Votre domaine :',
+'password-change-forbidden' => 'Vous ne pouvez pas modifier les mots de passe sur ce wiki.',
'externaldberror' => 'Une erreur s’est produite avec la base de données d’authentification externe, ou bien vous n’êtes pas autorisé{{GENDER:||e|(e)}} à mettre à jour votre compte externe.',
'login' => 'Connexion',
'nav-login-createaccount' => 'Créer un compte ou se connecter',
'link_tip' => 'Lien interne',
'extlink_sample' => 'http://www.example.com titre du lien',
'extlink_tip' => 'Lien externe (n’oubliez pas le préfixe http://)',
-'headline_sample' => 'Texte de sous-titre',
+'headline_sample' => 'Texte du titre',
'headline_tip' => 'Sous-titre niveau 2',
'nowiki_sample' => 'Entrez le texte non formaté ici',
'nowiki_tip' => 'Ignorer la syntaxe wiki',
'noarticletext-nopermission' => 'Il n’y a pour l’instant aucun texte sur cette page.
Vous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,
ou <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>.',
+'missing-revision' => "La révision n° $1 de la page intitulée « {{PAGENAME}} » n'existe pas.
+
+Cela survient en général en suivant un lien historique obsolète vers une page qui a été supprimée.
+Vous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
'userpage-userdoesnotexist' => 'Le compte utilisateur « <nowiki>$1</nowiki> » n’est pas enregistré. Veuillez vérifier que vous voulez créer cette page.',
'userpage-userdoesnotexist-view' => 'Le compte utilisateur « $1 » n’est pas enregistré.',
'blocked-notice-logextract' => 'Cet utilisateur est actuellement bloqué.
'expansion-depth-exceeded-warning' => "Page dépassant la profondeur d'expansion",
'parser-unstrip-loop-warning' => 'Boucle non démontable détectée',
'parser-unstrip-recursion-limit' => 'Limite de récursion non démontable dépassée ($1)',
+'converter-manual-rule-error' => 'Erreur détectée dans la règle manuelle de conversion de langue',
# "Undo" feature
'undo-success' => 'Cette modification va être défaite. Veuillez vérifier les modifications ci-dessous, puis publier si c’est bien ce que vous voulez faire.',
'editundo' => 'défaire',
'diff-multi' => '({{PLURAL:$1|Une révision intermédiaire|$1 révisions intermédiaires}} par {{PLURAL:$2|un utilisateur|$2 utilisateurs}} {{PLURAL:$1|est masquée|sont masquées}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Une révision intermédiaire|$1 révisions intermédiaires}} par plus {{PLURAL:$2|d’un utilisateur|de $2 utilisateurs}} {{PLURAL:$1|est masquée|sont masquées}})',
+'difference-missing-revision' => "{{PLURAL:$2|Une révision|$2 révisions}} de cette différence ($1) {{PLURAL:$2|n'a pas été trouvée|n'ont pas été trouvées}}.
+
+Cela survient en général en suivant un lien de différence obsolète vers une page qui a été supprimée.
+Vous pouvez trouver des détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
# Search results
'searchresults' => 'Résultats de la recherche',
# Preferences page
'preferences' => 'Préférences',
-'mypreferences' => 'Préférences',
+'mypreferences' => 'Mes préférences',
'prefs-edits' => 'Nombre de modifications :',
'prefsnologin' => 'Non connecté',
'prefsnologintext' => 'Vous devez être <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} connecté]</span> pour modifier vos préférences d’utilisateur.',
'right-writeapi' => 'Utiliser l’API de modification du wiki',
'right-delete' => 'Supprimer des pages',
'right-bigdelete' => 'Supprimer des pages ayant un gros historique',
+'right-deletelogentry' => 'Supprimer et restaurer une entrée particulière du journal',
'right-deleterevision' => 'Supprimer ou restaurer une version particulière d’une page',
'right-deletedhistory' => 'Voir les entrées des historiques supprimées, mais sans leur texte',
'right-deletedtext' => 'Voir le texte supprimé et les différences entre les versions supprimées',
'filehist-revert' => 'rétablir',
'filehist-current' => 'actuel',
'filehist-datetime' => 'Date et heure',
-'filehist-thumb' => 'Miniature',
+'filehist-thumb' => 'Vignette',
'filehist-thumbtext' => 'Vignette pour la version du $1',
'filehist-nothumb' => 'Pas de miniature',
'filehist-user' => 'Utilisateur',
'sharedupload' => 'Ce fichier provient de : $1. Il peut être utilisé par d’autres projets.',
'sharedupload-desc-there' => 'Ce fichier provient de : $1. Il peut être utilisé par d’autres projets.
Veuillez consulter [$2 sa page de description] pour plus d’informations.',
-'sharedupload-desc-here' => 'Ce fichier provient de : $1. Il peut être utilisé par d’autres projets.
+'sharedupload-desc-here' => 'Ce fichier provient de $1. Il peut être utilisé par d’autres projets.
Sa description sur sa [$2 page de description] est affichée ci-dessous.',
'sharedupload-desc-edit' => 'Ce fichier provient de : $1. Il peut être utilisé par d’autres projets.
Vous voulez peut-être modifier la description sur sa [$2 page de description].',
'disambiguations' => 'Pages ayant des liens vers des pages d’homonymie',
'disambiguationspage' => 'Template:Homonymie',
-'disambiguations-text' => "Les pages suivantes comportent un lien vers une '''page d’homonymie'''.
-Ces liens ambigus devraient plutôt pointer vers le bon article.<br />
-Une page est considérée comme une page d’homonymie si elle inclut (directement ou récursivement) un des modèles listés sur [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Les pages suivantes comportent au moins un lien vers une '''page d’homonymie'''.
+Elles devraient plutôt pointer vers le bon article.<br />
+Une page est considérée comme une page d’homonymie si elle utilise un modèle lié à [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Doubles redirections',
'doubleredirectstext' => 'Voici une liste des pages qui redirigent vers des pages qui sont elles-mêmes des pages de redirection.
'rollback' => 'Révoquer les modifications',
'rollback_short' => 'Révoquer',
'rollbacklink' => 'révoquer',
+'rollbacklinkcount' => 'révoquer $1 {{PLURAL:$1|modification|modifications}}',
+'rollbacklinkcount-morethan' => 'révoquer plus de $1 {{PLURAL:$1|modification|modifications}}',
'rollbackfailed' => 'La révocation a échoué',
'cantrollback' => 'Impossible de révoquer la modification ;
le dernier contributeur est le seul auteur de cette page.',
** Tentative d’intimidation ou harcèlement
** Abus d’utilisation de comptes multiples
** Nom d’utilisateur inacceptable, injurieux ou diffamant',
-'ipb-hardblock' => 'Empêche les modifications des utilisateurs enregistrés utilisant cette adresse IP',
+'ipb-hardblock' => 'Empêcher les utilisateurs connectés de modifier en utilisant cette adresse IP',
'ipbcreateaccount' => 'Empêcher la création de compte',
'ipbemailban' => 'Empêcher l’utilisateur d’envoyer des courriels',
'ipbenableautoblock' => 'Bloquer automatiquement la dernière adresse IP utilisée par l’utilisateur et toutes ses IPs ultérieures qu’il pourrait essayer',
'ipbotherreason' => 'Motif différent ou supplémentaire :',
'ipbhidename' => 'Masquer le nom d’utilisateur des modifications et des listes',
'ipbwatchuser' => 'Suivre les pages utilisateur et de discussion de cet utilisateur',
-'ipb-disableusertalk' => 'Empêche cet utilisateur de modifier sa propre page de discussion pendant la durée de son blocage',
+'ipb-disableusertalk' => 'Empêcher cet utilisateur de modifier sa propre page de discussion pendant la durée de son blocage',
'ipb-change-block' => 'Bloquer à nouveau cet utilisateur avec ces paramètres',
'ipb-confirm' => 'Confirmer le blocage',
'badipaddress' => 'Adresse IP incorrecte',
'simple.js' => '/* Tout JavaScript ici sera chargé avec les pages accédées par les utilisateurs de l’habillage Simple uniquement */',
'modern.js' => '/* Tout JavaScript ici sera chargé avec les pages accédées par les utilisateurs de l’habillage Moderne uniquement */',
'vector.js' => '/* Tout code JavaScript placé ici sera chargé pour les utilisateurs de l’habillage Vector */',
+'group-autoconfirmed.js' => '/* Le JavaScript inclus ici n’affectera que les utilisateurs auto-confirmés */',
+'group-bot.js' => '/* Le JavaScript inclus ici n’affectera que les robots */',
'group-sysop.js' => '/* Le JavaScript inclus ici n’affectera que les administrateurs */',
+'group-bureaucrat.js' => '/* Le JavaScript inclus ici n’affectera que les bureaucrates */',
# Metadata
'notacceptable' => 'Ce serveur wiki ne peut pas fournir les données dans un format que votre client soit capable de lire.',
'duration-centuries' => '$1 {{PLURAL:$1|siècle|siècles}}',
'duration-millennia' => '$1 {{PLURAL:$1|millénaire|millénaires}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|n’est pas un type de fichier autorisé|ne sont pas des types de fichiers autorisés}}. {{PLURAL:$3|Le type de fichier autorisé est |Les types de fichiers autorisés sont}} $2.',
);
'tog-watchmoves' => 'Apondre les pâges et los fichiérs que renomo a ma lista de survelyence',
'tog-watchdeletion' => 'Apondre les pâges et los fichiérs que suprimo a ma lista de survelyence',
'tog-minordefault' => 'Marcar per dèfôt tôs los changements coment petiôts',
-'tog-previewontop' => 'Fâre vêre la prèvisualisacion en-dessus de la zona de changement',
+'tog-previewontop' => 'Fâre vêre la prèvisualisacion d’amont la zona de changement',
'tog-previewonfirst' => 'Fâre vêre la prèvisualisacion pendent lo premiér changement',
'tog-nocache' => 'Dèsactivar lo cacho de les pâges per lo navigator',
'tog-enotifwatchlistpages' => 'Mè mandar un mèssâjo quand na pâge ou ben un fichiér de ma lista de survelyence est changiê(ye)',
'tog-uselivepreview' => 'Empleyér l’apèrçu rapido (at fôta de JavaScript) (èxpèrimentâl)',
'tog-forceeditsummary' => 'M’avèrtir quand j’é pas spècifiâ de rèsumâ de changement',
'tog-watchlisthideown' => 'Cachiér mos prôpros changements dedens la lista de survelyence',
-'tog-watchlisthidebots' => 'Cachiér los changements fêts per des bots dedens la lista de survelyence',
+'tog-watchlisthidebots' => 'Cachiér los changements fêts per des robots dedens la lista de survelyence',
'tog-watchlisthideminor' => 'Cachiér los petiôts changements dedens la lista de survelyence',
'tog-watchlisthideliu' => 'Cachiér los changements fêts per des utilisators branchiês dedens la lista de survelyence',
'tog-watchlisthideanons' => 'Cachiér los changements fêts per des utilisators anonimos dedens la lista de survelyence',
'category_header' => 'Pâges dedens la catègorie « $1 »',
'subcategories' => 'Sot-catègories',
'category-media-header' => 'Fichiérs mèdia dedens la catègorie « $1 »',
-'category-empty' => "''Ora, cela catègorie contint gins de pâge ou de fichiér mèdia.''",
+'category-empty' => "''Ora cela catègorie contint gins de pâge ou de fichiér mèdia.''",
'hidden-categories' => '{{PLURAL:$1|Catègorie cachiêye|Catègories cachiêyes}}',
'hidden-category-category' => 'Catègories cachiêyes',
'category-subcat-count' => 'Cela catègorie-que at {{PLURAL:$2|ren que ceta sot-catègorie.|{{PLURAL:$1|ceta sot-catègorie|cetes $1 sot-catègories}}, sur na soma de $2.}}',
'cancel' => 'Anular',
'moredotdotdot' => 'Més...',
'mypage' => 'Ma pâge',
-'mytalk' => 'Pâge de discussion',
-'anontalk' => 'Discussion avouéc ceta adrèce IP',
+'mytalk' => 'Ma pâge de discussion',
+'anontalk' => 'Discussion avouéc cet’adrèce IP',
'navigation' => 'Navigacion',
'and' => ' et',
'qbpageinfo' => 'Contèxto',
'qbmyoptions' => 'Mes pâges',
'qbspecialpages' => 'Pâges spèciâles',
-'faq' => 'FDQ',
-'faqpage' => 'Project:FDQ',
+'faq' => 'Quèstions sovent posâyes',
+'faqpage' => 'Project:Quèstions sovent posâyes',
# Vector skin
'vector-action-addsection' => 'Apondre un sujèt',
'errorpagetitle' => 'Fôta',
'returnto' => 'Tornar a la pâge $1.',
-'tagline' => 'De {{SITENAME}}.',
+'tagline' => 'De {{SITENAME}}',
'help' => 'Éde',
'search' => 'Rechèrche',
-'searchbutton' => 'Chèrchiér',
+'searchbutton' => 'Rechèrchiér',
'go' => 'Alar trovar',
'searcharticle' => 'Liére',
'history' => 'Historico de la pâge',
'unprotect' => 'Changiér la protèccion',
'unprotectthispage' => 'Changiér la protèccion de ceta pâge',
'newpage' => 'Pâge novèla',
-'talkpage' => 'Pâge de discussion',
+'talkpage' => 'Discussion sur ceta pâge',
'talkpagelinktext' => 'discutar',
'specialpage' => 'Pâge spèciâla',
'personaltools' => 'Outils a sè',
'talk' => 'Discussion',
'views' => 'Visualisacions',
'toolbox' => 'Bouèta d’outils',
-'userpage' => 'Pâge utilisator',
+'userpage' => 'Vêde la pâge utilisator',
'projectpage' => 'Vêde la pâge du projèt',
'imagepage' => 'Vêde la pâge du fichiér',
'mediawikipage' => 'Vêde la pâge du mèssâjo',
'templatepage' => 'Vêde la pâge du modèlo',
'viewhelppage' => 'Vêde la pâge d’éde',
'categorypage' => 'Vêde la pâge de catègorie',
-'viewtalkpage' => 'Pâge de discussion',
+'viewtalkpage' => 'Vêde la pâge de discussion',
'otherlanguages' => 'Ôtres lengoues',
'redirectedfrom' => '(Redirigiê dês $1)',
'redirectpagesub' => 'Pâge de redirèccion',
'lastmodifiedat' => 'Dèrriér changement de ceta pâge lo $1 a $2.',
-'viewcount' => 'Ceta pâge at étâye vua {{PLURAL:$1|yon côp|$1 côps}}.',
+'viewcount' => 'Ceta pâge est étâye vua {{PLURAL:$1|yon côp|$1 côps}}.',
'protectedpage' => 'Pâge protègiêye',
'jumpto' => 'Alar a :',
'jumptonavigation' => 'navigacion',
'jumptosearch' => 'rechèrche',
'view-pool-error' => 'Dèconsolâ, los sèrviors sont lapidâs d’ôvra cetos temps.
-Trop d’utilisators chèrchont a vêre ceta pâge.
+Trop d’utilisators tâchont de vêre ceta pâge.
Volyéd atendre un moment devant que tornar tâchiér d’arrevar a ceta pâge.
$1',
-'pool-timeout' => 'Dèpassement du dèlê pendent l’atenta du vèrrolyâjo',
-'pool-queuefull' => 'La fela d’ôvra est plêna',
-'pool-errorunknown' => 'Èrror encognua',
+'pool-timeout' => 'Dèlê dèpassâ pendent l’atenta du vèrroly',
+'pool-queuefull' => 'La renche d’ôvra est plêna',
+'pool-errorunknown' => 'Fôta encognua',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
-'aboutsite' => 'A propôs de {{SITENAME}}',
+'aboutsite' => 'Sur {{SITENAME}}',
'aboutpage' => 'Project:A propôs',
-'copyright' => 'Lo contegnu est disponiblo desot $1.',
-'copyrightpage' => '{{ns:project}}:Drêt d’ôtor',
+'copyright' => 'Lo contegnu est disponiblo desot licence $1.',
+'copyrightpage' => '{{ns:project}}:Drêts d’ôtor',
'currentevents' => 'Novèles',
'currentevents-url' => 'Project:Novèles',
'disclaimers' => 'Avèrtissements',
'disclaimerpage' => 'Project:Avèrtissements g·ènèrals',
'edithelp' => 'Éde',
-'edithelppage' => 'Help:Coment changiér una pâge',
+'edithelppage' => 'Help:Coment changiér na pâge',
'helppage' => 'Help:Somèro',
'mainpage' => 'Reçua',
'mainpage-description' => 'Reçua',
'policy-url' => 'Project:Règlles de dedens',
'portal' => 'Comunôtât',
-'portal-url' => 'Project:Reçua',
+'portal-url' => 'Project:Reçua de la comunôtât',
'privacy' => 'Politica de confidencialitât',
'privacypage' => 'Project:Politica de confidencialitât',
-'badaccess' => 'Èrror de pèrmission',
-'badaccess-group0' => 'Vos avéd pas los drêts sufisents por rèalisar l’accion que vos demandâd.',
-'badaccess-groups' => 'L’accion que vos tâchiéd de rèalisar est accèssibla ren qu’ux usanciérs a {{PLURAL:$2|la tropa|les tropes}} : $1.',
+'badaccess' => 'Fôta de pèrmission',
+'badaccess-group0' => 'Vos avéd pas los drêts sufisents por rèalisar l’accion demandâye.',
+'badaccess-groups' => 'L’accion que vos tâchiéd de rèalisar est accèssibla ren qu’ux utilisators de {{PLURAL:$2|la tropa|les tropes}} : $1.',
'versionrequired' => 'Vèrsion $1 de MediaWiki nècèssèra',
-'versionrequiredtext' => 'La vèrsion $1 de MediaWiki est nècèssèra por utilisar ceta pâge.
+'versionrequiredtext' => 'La vèrsion $1 de MediaWiki est nècèssèra por empleyér ceta pâge.
Vêde la [[Special:Version|pâge de les vèrsions]].',
'ok' => 'D’acôrd',
-'retrievedfrom' => 'Rècupèrâ de « $1 »',
+'retrievedfrom' => 'Rècupèrâye de « $1 »',
'youhavenewmessages' => 'Vos avéd de $1 ($2).',
'newmessageslink' => 'mèssâjos novéls',
'newmessagesdifflink' => 'dèrriér changement',
-'youhavenewmessagesmulti' => 'Vos avéd de mèssâjos novéls dessus $1.',
+'youhavenewmessagesfromusers' => 'Vos avéd $1 {{PLURAL:$3|d’un ôtro utilisator|de $3 ôtros utilisators}} ($2).',
+'youhavenewmessagesmanyusers' => 'Vos avéd $1 d’un mouél d’utilisators ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un mèssâjo novél|de mèssâjos novéls}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|dèrriér changement|dèrriérs changements}}',
+'youhavenewmessagesmulti' => 'Vos avéd de mèssâjos novéls sur $1',
'editsection' => 'changiér',
'editold' => 'changiér',
'viewsourceold' => 'vêre lo tèxto sôrsa',
'toc' => 'Somèro',
'showtoc' => 'fâre vêre',
'hidetoc' => 'cachiér',
-'collapsible-collapse' => 'Recllôre',
-'collapsible-expand' => 'Dèvelopar',
+'collapsible-collapse' => 'repleyér',
+'collapsible-expand' => 'dèpleyér',
'thisisdeleted' => 'Voléd-vos fâre vêre ou ben refâre $1 ?',
'viewdeleted' => 'Fâre vêre $1 ?',
'restorelink' => '{{PLURAL:$1|yon changement suprimâ|$1 changements suprimâs}}',
'page-rss-feed' => 'Flux RSS de « $1 »',
'page-atom-feed' => 'Flux Atom de « $1 »',
'red-link-title' => '$1 (pâge pas ègzistenta)',
-'sort-descending' => 'Triyér per ôrdre dècrèssent',
-'sort-ascending' => 'Triyér per ôrdre crèssent',
+'sort-descending' => 'Betar per ôrdre dèscendent',
+'sort-ascending' => 'Betar per ôrdre montent',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Pâge',
-'nstab-user' => 'Pâge usanciér',
-'nstab-media' => 'Pâge de mèdia',
+'nstab-user' => 'Pâge utilisator',
+'nstab-media' => 'Fichiér mèdia',
'nstab-special' => 'Pâge spèciâla',
'nstab-project' => 'Pâge du projèt',
'nstab-image' => 'Fichiér',
# Main script and global functions
'nosuchaction' => 'Accion encognua',
-'nosuchactiontext' => 'L’accion spècefiâ dens l’URL est envalida.
+'nosuchactiontext' => 'L’accion spècifiâye dens l’URL est envalida.
Vos éd pôt-étre mâl-buchiê l’URL ou ben siuvu un lim fôx.
-Pôt asse-ben étre quèstion d’una cofierie dens la programeria utilisâ per {{SITENAME}}.',
+Pôt asse-ben étre quèstion d’una cofierie dedens la programeria empleyêye per {{SITENAME}}.',
'nosuchspecialpage' => 'Pâge spèciâla pas ègzistenta',
-'nospecialpagetext' => '<strong>Vos éd demandâ una pâge spèciâla qu’ègziste pas.</strong>
+'nospecialpagetext' => '<strong>Vos éd demandâ na pâge spèciâla qu’ègziste pas.</strong>
-Una lista de les pâges spèciâles valides sè trove dessus [[Special:SpecialPages|{{int:specialpages}}]].',
+Na lista de les pâges spèciâles valides sè trove dessus [[Special:SpecialPages|{{int:specialpages}}]].',
# General errors
-'error' => 'Èrror',
-'databaseerror' => 'Èrror de la bâsa de balyês',
-'dberrortext' => 'Una èrror de sintaxa de la requéta dens la bâsa de balyês est arrevâ.
-Cen pôt endicar una cofierie dens la programeria.
-La dèrriére requéta trètâ per la bâsa de balyês ére :
+'error' => 'Fôta',
+'databaseerror' => 'Fôta de la bâsa de donâs',
+'dberrortext' => 'Na fôta de sintaxa de la requéta dens la bâsa de donâs est arrevâye.
+Cen pôt endicar na cofierie dedens la programeria.
+La dèrriére requéta trètâye per la bâsa de donâs ére :
<blockquote><tt>$1</tt></blockquote>
dês la fonccion « <tt>$2</tt> ».
-La bâsa de balyês at retornâ l’èrror « <tt>$3 : $4</tt> ».',
-'dberrortextcl' => 'Una èrror de sintaxa de la requéta dens la bâsa de balyês est arrevâ.
-La dèrriére requéta trètâ per la bâsa de balyês ére :
+La bâsa de donâs at retornâ la fôta « <tt>$3 : $4</tt> ».',
+'dberrortextcl' => 'Na fôta de sintaxa de la requéta dens la bâsa de donâs est arrevâye.
+La dèrriére requéta trètâye per la bâsa de donâs ére :
« $1 »
dês la fonccion « $2 ».
-La bâsa de balyês at retornâ l’èrror « $3 : $4 ».',
+La bâsa de donâs at retornâ la fôta « $3 : $4 ».',
'laggedslavemode' => "'''Atencion :''' cela pâge pôt pas contegnir tôs los dèrriérs changements fêts.",
-'readonly' => 'Bâsa de balyês vèrrolyê',
-'enterlockreason' => 'Balyéd la rêson du vèrrolyâjo et pués una èstimacion de son temps',
-'readonlytext' => 'Ora, la bâsa de balyês est vèrrolyê por d’entrâs novèles et d’ôtros changements, probâblament por pèrmetre la mantegnence de la bâsa de balyês, aprés què tot tornerat a la normala.
+'readonly' => 'Bâsa de donâs vèrrolyêye',
+'enterlockreason' => 'Balyéd la rêson du vèrroly et pués n’èstimacion de la sina durâ',
+'readonlytext' => 'Ora la bâsa de donâs est vèrrolyêye por les entrâs novèles et los ôtros changements, probâblament por pèrmetre la sina mantegnence, dês cen tot tornerat en ôrdre.
-L’administrator que l’at vèrrolyê at balyê ceta èxplicacion : $1',
-'missing-article' => 'La bâsa de balyês at pas trovâ lo tèxto d’una pâge qu’el arêt diu trovar, avouéc lo titro « $1 » $2.
+L’administrator que l’at vèrrolyê at balyê cet’èxplicacion : $1',
+'missing-article' => 'La bâsa de donâs at pas trovâ lo tèxto d’una pâge qu’el arêt diu trovar, avouéc lo titro « $1 » $2.
-En g·ènèral, cen arreve en siuvent un lim de vers un dif dèpassâ ou ben de vers l’historico d’una pâge suprimâ.
+En g·ènèral, cen arreve en siuvent un lim de vers un dif dèpassâ ou ben de vers l’historico d’una pâge suprimâye.
-S’o est pas lo câs, pôt étre quèstion d’una cofierie dens la programeria.
+S’o est pas lo câs, pôt étre quèstion d’una cofierie dedens la programeria.
La volyéd signalar a un [[Special:ListUsers/sysop|administrator]] sen oubliar de lui endicar l’URL du lim.',
-'missingarticle-rev' => '(numerô de la vèrsion : $1)',
-'missingarticle-diff' => '(Dif : $1, $2)',
-'readonly_lag' => 'La bâsa de balyês at étâ vèrrolyê ôtomaticament pendent que los sèrvors secondèros ratrapont lor retârd sur lo sèrvor principâl.',
-'internalerror' => 'Èrror de dedens',
-'internalerror_info' => 'Èrror de dedens : $1',
+'missingarticle-rev' => '(numerô de vèrsion : $1)',
+'missingarticle-diff' => '(dif : $1, $2)',
+'readonly_lag' => 'La bâsa de donâs est étâye vèrrolyêye ôtomaticament pendent que los sèrviors secondèros ratrapont lor retârd sur lo sèrvior principâl.',
+'internalerror' => 'Fôta de dedens',
+'internalerror_info' => 'Fôta de dedens : $1',
'fileappenderrorread' => 'Empossiblo de liére « $1 » pendent l’aponsa.',
'fileappenderror' => 'Empossiblo d’apondre « $1 » a « $2 ».',
'filecopyerror' => 'Empossiblo de copiyér lo fichiér « $1 » vers « $2 ».',
'filedeleteerror' => 'Empossiblo de suprimar lo fichiér « $1 ».',
'directorycreateerror' => 'Empossiblo de fâre lo dossiér « $1 ».',
'filenotfound' => 'Empossiblo de trovar lo fichiér « $1 ».',
-'fileexistserror' => 'Empossiblo d’ècrire dens lo dossiér « $1 » : lo fichiér ègziste.',
+'fileexistserror' => 'Empossiblo d’ècrire lo fichiér « $1 » : lo fichiér ègziste.',
'unexpected' => 'Valor emprèvua : « $1 » = « $2 ».',
-'formerror' => 'Èrror : empossiblo de sometre lo formulèro',
-'badarticleerror' => 'Cela accion pôt pas étre fêta sur ceta pâge.',
+'formerror' => 'Fôta : empossiblo de sometre lo formulèro.',
+'badarticleerror' => 'Cel’accion pôt pas étre fêta sur ceta pâge.',
'cannotdelete' => 'Empossiblo de suprimar la pâge ou ben lo fichiér « $1 ».
-La suprèssion at pôt-étre ja étâ fêta per quârqu’un d’ôtro.',
+Pôt-étre la suprèssion est ja étâye fêta per quârqu’un d’ôtro.',
'cannotdelete-title' => 'Empossiblo de suprimar la pâge « $1 »',
'badtitle' => 'Crouyo titro',
-'badtitletext' => 'Lo titro de pâge demandâ est fôx, vouedo, ou ben o est un titro entèrlengoua ou entèrvouiqui mâl-liyê.
-Contint sûrement yon ou ben un mouél de caractèros que pôvont pas étre utilisâs dens los titros.',
-'perfcached' => 'Cetes balyês sont en cache et pôvont pas étre a jorn. {{PLURAL:$1|Yon rèsultat|$1 rèsultats}} u més {{PLURAL:$1|est disponiblo|sont disponiblos}} dedens lo cache.',
-'perfcachedts' => 'Cetes balyês sont en cache et ont étâ betâs a jorn por lo dèrriér côp a $1. {{PLURAL:$4|Yon rèsultat|$4 rèsultats}} u més {{PLURAL:$1|est disponiblo|sont disponiblos}} dedens lo cache.',
-'querypage-no-updates' => 'Ora, les mises a jorn por ceta pâge sont dèsactivâs.
-Les balyês ce-desot sont pas betâs a jorn.',
-'wrong_wfQuery_params' => 'Paramètres fôx dessus wfQuery()<br />
+'badtitletext' => 'Lo titro de la pâge demandâye est envalido, vouedo ou ben o est un titro entèrlengoua ou entèrvouiqui mâl-liyê.
+Contint sûrament yon ou ben un mouél de caractèros que pôvont pas étre empleyês dedens los titros.',
+'perfcached' => 'Cetes donâs sont en cacho et pôvont pas étre a jorn. Por lo més {{PLURAL:$1|yon rèsultat est disponiblo|$1 rèsultats sont disponiblos}} dedens lo cacho.',
+'perfcachedts' => 'Cetes donâs sont en cacho et sont étâyes betâyes a jorn por lo dèrriér côp a $1. Por lo més {{PLURAL:$1|yon rèsultat est disponiblo|$1 rèsultats sont disponiblos}} dedens lo cacho.',
+'querypage-no-updates' => 'Ora les mises a jorn por ceta pâge sont dèsactivâyes.
+Les donâs ce-desot sont pas betâyes a jorn.',
+'wrong_wfQuery_params' => 'Paramètros fôx dessus wfQuery()<br />
Fonccion : $1<br />
Requéta : $2',
'viewsource' => 'Vêre lo tèxto sôrsa',
-'viewsource-title' => 'Vêre la sôrsa de $1',
-'actionthrottled' => 'Accion limitâ',
-'actionthrottledtext' => 'Por combatre lo spame, la frèquence d’ègzécucion de cela accion est limitâ dens un moment prod côrt, et vos éd dèpassâ ceta limita.
-Volyéd tornar èprovar dens doux-três menutes.',
-'protectedpagetext' => 'Ceta pâge at étâ protègiê por empachiér son changement.',
-'viewsourcetext' => 'Vos pouede vêre et copiyér lo tèxto sôrsa de la pâge :',
-'viewyourtext' => "Vos pouede vêre et copiyér lo contegnu de '''voutros changements''' a ceta pâge :",
-'protectedinterface' => 'Ceta pâge fât de tèxto d’entèrface por la programeria et est protègiê por èvitar los abus.',
-'editinginterface' => "'''Atencion :''' vos éte aprés changiér una pâge utilisâ por fâre lo tèxto d’entèrface por la programeria.
-Los changements sè cognetront, d’aprés lo contèxto, sur totes ou ben quârques pâges visibles per los ôtros usanciérs.
-Por les traduccions, nos vos envitens a utilisar lo seto [//translatewiki.net/wiki/Main_Page?setlang=frp translatewiki.net], lo projèt de localisacion de MediaWiki.",
-'sqlhidden' => '(Requéta SQL cachiê)',
-'cascadeprotected' => 'Ora, cela pâge est protègiê perce qu’el est encllua dens {{PLURAL:$1|ceta pâge|cetes pâges}}, {{PLURAL:$1|qu’at étâ protègiê|qu’ont étâ protègiês}} avouéc lo chouèx « protèccion en cascâda » activâ :
+'viewsource-title' => 'Vêre lo tèxto sôrsa de $1',
+'actionthrottled' => 'Accion limitâye',
+'actionthrottledtext' => 'Por combatre lo spame, l’usâjo de cel’accion est limitâ a doux-três côps dens un moment prod côrt. S’acomplét que vos éd dèpassâ ceta limita.
+Volyéd tornar èprovar dens un tôrn.',
+'protectedpagetext' => 'Ceta pâge est étâye protègiêye por empachiér son changement.',
+'viewsourcetext' => 'Vos pouede vêre et pués copiyér lo tèxto sôrsa de ceta pâge :',
+'viewyourtext' => "Vos pouede vêre et pués copiyér lo tèxto sôrsa de '''voutros changements''' a ceta pâge :",
+'protectedinterface' => 'Ceta pâge balye de tèxto d’entèrface por la programeria et est vêr protègiêye por èvitar los abus.',
+'editinginterface' => "'''Atencion :''' vos éte aprés changiér na pâge empleyêye por fâre lo tèxto d’entèrface de la programeria.
+Los changements sè cognetront sur totes ou ben doux-três pâges visibles per los ôtros utilisators.
+Por les traduccions, nos vos envitens a empleyér [//translatewiki.net/wiki/Main_Page?setlang=frp translatewiki.net], lo projèt de localisacion de MediaWiki.",
+'sqlhidden' => '(Requéta SQL cachiêye)',
+'cascadeprotected' => 'Cela pâge-que est protègiêye perce qu’el est encllua dedens {{PLURAL:$1|ceta pâge, qu’est étâye protègiêye|cetes pâges, que sont étâyes protègiêyes}} avouéc lo chouèx « protèccion en cascâda » activâ :
$2',
-'namespaceprotected' => "Vos avéd pas la pèrmission de changiér les pâges de l’èspâço de noms '''« $1 »'''.",
-'customcssprotected' => 'Vos avéd pas la pèrmission de changiér cela pâge CSS, perce que contint les prèferences a un ôtro usanciér.',
-'customjsprotected' => 'Vos avéd pas la pèrmission de changiér cela pâge JavaScript, perce que contint les prèferences a un ôtro usanciér.',
-'ns-specialprotected' => 'Les pâges dens l’èspâço de noms « {{ns:special}} » pôvont pas étre changiês.',
-'titleprotected' => "Cél titro at étâ protègiê a la crèacion per [[User:$1|$1]].
-La rêson balyê est « ''$2'' ».",
+'namespaceprotected' => "Vos avéd pas la pèrmission de changiér les pâges de l’èspâço de noms « '''$1''' ».",
+'customcssprotected' => 'Vos avéd pas la pèrmission de changiér cela pâge CSS, perce que contint la configuracion a sè d’un ôtro utilisator.',
+'customjsprotected' => 'Vos avéd pas la pèrmission de changiér cela pâge JavaScript, perce que contint la configuracion a sè d’un ôtro utilisator.',
+'ns-specialprotected' => 'Les pâges spèciâles pôvont pas étre changiêyes.',
+'titleprotected' => "Cél titro est étâ protègiê a la crèacion per [[User:$1|$1]].
+La rêson balyêye est « ''$2'' ».",
'exception-nologin' => 'Pas branchiê',
# Virus scanner
'virus-unknownscanner' => 'antivirus encognu :',
# Login and logout pages
-'logouttext' => "'''Ora, vos éte dèbranchiê.'''
+'logouttext' => "'''Ora vos éte dèbranchiê{{GENDER:||ye|(ye)}}.'''
-Vos pouede continuar a utilisar {{SITENAME}} de façon anonima, ou ben [[Special:UserLogin|vos tornar branchiér]] desot lo mémo nom ou ben un ôtro.
-Notâd que quârques pâges pôvont étre adés montrâs coment se vos érâd tojorn branchiê, tant que vos èfaciéd lo cache de voutron navigator.",
+Vos pouede continuar a empleyér {{SITENAME}} de façon anonima ou ben [[Special:UserLogin|vos tornar branchiér]] desot lo mémo nom ou un ôtro.
+Notâd qu’y at des pâges que pôvont étre oncor fêtes vêre coment se vos érâd adés branchiê{{GENDER:||ye|(ye)}}, tant que vos èfaciéd lo cacho de voutron navigator.",
'welcomecreation' => '== Benvegnua, $1 ! ==
-Voutron compto usanciér at étâ fêt.
+Voutron compto utilisator est étâ fêt.
Oubliâd pas de pèrsonalisar voutres [[Special:Preferences|prèferences dessus {{SITENAME}}]].',
-'yourname' => 'Nom d’usanciér :',
+'yourname' => 'Nom d’utilisator :',
'yourpassword' => 'Contresegno :',
'yourpasswordagain' => 'Confirmâd lo contresegno :',
-'remembermypassword' => 'Sè rapelar de mon branchement sur ceti navigator (por $1 jorn{{PLURAL:$1||s}} u més)',
+'remembermypassword' => 'Sè rapelar de mon contresegno sur cél navigator (por lo més $1 jorn{{PLURAL:$1||s}})',
'securelogin-stick-https' => 'Réstar branchiê en HTTPS aprés lo branchement',
'yourdomainname' => 'Voutron domêno :',
-'externaldberror' => 'Ou ben una èrror est arrevâ avouéc la bâsa de balyês d’ôtenticacion de defôr, ou ben vos éte pas ôtorisâ a betar a jorn voutron compto de defôr.',
+'password-change-forbidden' => 'Vos pouede pas changiér los contresegnos sur ceti vouiqui.',
+'externaldberror' => 'Ou ben na fôta est arrevâye avouéc la bâsa de donâs d’ôtentificacion de defôr ou ben vos éte pas ôtorisâ{{GENDER:||ye|(ye)}} a betar a jorn voutron compto de defôr.',
'login' => 'Branchement',
-'nav-login-createaccount' => 'Fâre un compto ou sè branchiér',
+'nav-login-createaccount' => 'Sè branchiér / fâre un compto',
'loginprompt' => "Vos dête activar los tèmouens (''cookies'') por vos branchiér a {{SITENAME}}.",
-'userlogin' => 'Fâre un compto ou sè branchiér',
+'userlogin' => 'Sè branchiér / fâre un compto',
'userloginnocreate' => 'Sè branchiér',
'logout' => 'Sè dèbranchiér',
'userlogout' => 'Dèbranchement',
'notloggedin' => 'Pas branchiê',
-'nologin' => "Vos avéd pas un compto ? '''$1'''.",
+'nologin' => "Vos avéd p’oncor un compto utilisator ? '''$1.'''",
'nologinlink' => 'Féte un compto',
'createaccount' => 'Fâre un compto',
-'gotaccount' => "Vos avéd ja un compto ? '''$1'''.",
+'gotaccount' => "Vos avéd ja un compto utilisator ? '''$1.'''",
'gotaccountlink' => 'Branchiéd-vos',
'userlogin-resetlink' => 'Vos éd oubliâ voutros dètalys de branchement ?',
'createaccountmail' => 'per mèssageria èlèctronica',
'createaccountreason' => 'Rêson :',
'badretype' => 'Los contresegnos que vos éd buchiês sont pas pariérs.',
-'userexists' => 'Lo nom d’usanciér buchiê est ja utilisâ.
+'userexists' => 'Lo nom d’utilisator buchiê est ja empleyê.
Nen volyéd chouèsir un ôtro.',
-'loginerror' => 'Èrror de branchement',
-'createaccounterror' => 'Empossiblo de fâre lo compto : $1',
-'nocookiesnew' => "Lo compto usanciér at étâ fêt, mas vos éte pas branchiê.
-{{SITENAME}} utilise des tèmouens (''cookies'') por lo branchement mas vos los éd dèsactivâs.
+'loginerror' => 'Fôta de branchement',
+'createaccounterror' => 'Empossiblo de fâre lo compto utilisator : $1',
+'nocookiesnew' => "Lo compto utilisator est étâ fêt, mas vos éte pas branchiê{{GENDER:||ye|(ye)}}.
+{{SITENAME}} empleye des tèmouens (''cookies'') por lo branchement mas vos los éd dèsactivâs.
Los volyéd activar et pués vos tornar branchiér avouéc lo mémo nom et lo mémo contresegno.",
-'nocookieslogin' => "{{SITENAME}} utilise des tèmouens (''cookies'') por lo branchement mas vos los éd dèsactivâs.
-Los volyéd activar et pués vos tornar branchiér.",
-'nocookiesfornew' => "Lo compto usanciér at pas étâ fêt, perce que nos ens pas possu confirmar sa sôrsa.
+'nocookieslogin' => "{{SITENAME}} empleye des tèmouens (''cookies'') por lo branchement mas vos los éd dèsactivâs.
+Los volyéd activar et pués tornar èprovar.",
+'nocookiesfornew' => "Lo compto utilisator est pas étâ fêt, perce que nos ens pas possu confirmar la sina sôrsa.
Controlâd que vos éd activâ los tèmouens (''cookies''), rechargiéd la pâge et pués tornâd èprovar.",
-'noname' => 'Vos éd pas buchiê un nom d’usanciér valido.',
-'loginsuccesstitle' => 'Branchement reussi.',
-'loginsuccess' => "'''Ora, vos éte branchiê a {{SITENAME}} coment « $1 ».'''",
-'nosuchuser' => 'L’usanciér « $1 » ègziste pas.
-Los noms d’usanciér sont sensiblos a la câssa.
-Controlâd l’ortografia, ou ben [[Special:UserLogin/signup|féte un compto novél]].',
-'nosuchusershort' => 'Y at gins de contributor avouéc lo nom « $1 ».
+'noname' => 'Vos éd pas buchiê un nom d’utilisator valido.',
+'loginsuccesstitle' => 'Branchement reussi',
+'loginsuccess' => "'''Ora vos éte branchiê{{GENDER:||ye|(ye)}} a {{SITENAME}} coment « $1 ».'''",
+'nosuchuser' => 'L’utilisator « $1 » ègziste pas.
+Los noms d’utilisator sont sensiblos a la câssa.
+Controlâd l’ortografia ou ben [[Special:UserLogin/signup|féte un compto novél]].',
+'nosuchusershort' => 'Y at gins d’utilisator avouéc lo nom « $1 ».
Volyéd controlar l’ortografia.',
-'nouserspecified' => 'Vos dête buchiér un nom d’usanciér.',
-'login-userblocked' => 'Ceti usanciér est blocâ. Branchement pas ôtorisâ.',
+'nouserspecified' => 'Vos dête buchiér un nom d’utilisator.',
+'login-userblocked' => 'Cet’utilisator est blocâ. Branchement pas ôtorisâ.',
'wrongpassword' => 'Lo contresegno est fôx.
Volyéd tornar èprovar.',
'wrongpasswordempty' => 'Vos éd pas buchiê de contresegno.
Volyéd tornar èprovar.',
'passwordtooshort' => 'Voutron contresegno dêt contegnir u muens $1 caractèro{{PLURAL:$1||s}}.',
-'password-name-match' => 'Voutron contresegno dêt étre difèrent de voutron nom d’usanciér.',
-'password-login-forbidden' => 'L’usâjo de cél nom d’usanciér et de cél contresegno at étâ dèfendu.',
+'password-name-match' => 'Voutron contresegno dêt étre difèrent de voutron nom d’utilisator.',
+'password-login-forbidden' => 'L’usâjo de cél nom d’utilisator et de cél contresegno est étâ dèfendu.',
'mailmypassword' => 'Recêvre un contresegno novél per mèssageria èlèctronica',
'passwordremindertitle' => 'Contresegno temporèro novél por {{SITENAME}}',
'passwordremindertext' => 'Quârqu’un (probâblament vos, avouéc l’adrèce IP $1) at demandâ un contresegno
-novél por {{SITENAME}} ($4). Un contresegno temporèro at étâ fêt por
-l’usanciér « $2 » et est « $3 ». S’o ére voutra entencion, vos vos devréd
+novél por {{SITENAME}} ($4). Un contresegno temporèro est étâ fêt por
+l’utilisator « $2 » et est « $3 ». S’o ére voutra entencion, vos vos devréd
branchiér et pués chouèsir un contresegno novél.
Voutron contresegno temporèro èxpirerat dens $5 jorn{{PLURAL:$5||s}}.
-Se cela demanda vint pas de vos, ou ben se vos vos rapelâd ora
+Se cela demanda vint pas de vos ou ben se vos vos rapelâd ora
de voutron contresegno et que vos souhètâd pas més nen changiér, vos
-pouede ignorar ceti mèssâjo et continuar a utilisar voutron viely contresegno.',
-'noemail' => 'Niona adrèce èlèctronica at étâ encartâ por l’usanciér « $1 ».',
-'noemailcreate' => 'Vos dête balyér una adrèce èlèctronica valida',
-'passwordsent' => 'Un contresegno novél at étâ mandâ a l’adrèce èlèctronica a l’usanciér « $1 ».
+pouede ignorar ceti mèssâjo et continuar a empleyér voutron viely contresegno.',
+'noemail' => 'Niona adrèce èlèctronica est étâye enregistrâye por l’utilisator « $1 ».',
+'noemailcreate' => 'Vos dête balyér n’adrèce èlèctronica valida',
+'passwordsent' => 'Un contresegno novél est étâ mandâ a l’adrèce èlèctronica de l’utilisator « $1 ».
Vos volyéd tornar branchiér aprés l’avêr reçu.',
-'blocked-mailpassword' => 'Voutra adrèce IP est blocâ en ècritura, la fonccion de rapèl du contresegno est vêr dèsactivâ por èvitar los abus.',
-'eauthentsent' => 'Un mèssâjo de confirmacion at étâ mandâ a l’adrèce endicâ.
-Devant qu’un ôtro mèssâjo seye mandâ a ceti compto, vos devréd siuvre les enstruccions du mèssâjo et confirmar que lo compto est franc lo voutro.',
-'throttled-mailpassword' => 'Un mèssâjo de rapèl de voutron contresegno at ja étâ mandâ pendent {{PLURAL:$1|l’hora passâ|les $1 hores passâs}}.
+'blocked-mailpassword' => 'Voutron adrèce IP est blocâye en ècritura, la fonccion de rapèl du contresegno est vêr dèsactivâye por èvitar los abus.',
+'eauthentsent' => 'Un mèssâjo de confirmacion est étâ mandâ a l’adrèce endicâye.
+Devant qu’un ôtro mèssâjo seye mandâ a ceti compto, vos devréd siuvre les enstruccions du mèssâjo et pués confirmar que lo compto est franc lo voutro.',
+'throttled-mailpassword' => 'Un mèssâjo de rapèl de voutron contresegno est ja étâ mandâ pendent {{PLURAL:$1|l’hora passâye|les $1 hores passâyes}}.
Por èvitar los abus, solament yon mèssâjo de rapèl serat mandâ per {{PLURAL:$1|hora|entèrvalo de $1 hores}}.',
-'mailerror' => 'Èrror pendent l’èxpèdicion du mèssâjo : $1',
-'acct_creation_throttle_hit' => 'Quârqu’un qu’utilise voutra adrèce IP at fêt {{PLURAL:$1|yon compto|$1 comptos}} pendent les 24 hores passâs, cen qu’est la limita ôtorisâ dens ceti temps.
-Du côp, la crèacion de compto at étâ dèsactivâ temporèrament por cela adrèce IP.',
-'emailauthenticated' => 'Voutra adrèce èlèctronica at étâ ôtenticâ lo $2 a $3.',
-'emailnotauthenticated' => 'Voutra adrèce èlèctronica est p’oncor ôtenticâ.
+'mailerror' => 'Fôta pendent l’èxpèdicion du mèssâjo : $1',
+'acct_creation_throttle_hit' => 'Quârqu’un qu’empleye voutron adrèce IP at fêt {{PLURAL:$1|yon compto|$1 comptos}} pendent les 24 hores passâyes, cen qu’est la limita ôtorisâye dens ceti temps.
+Du côp, la crèacion de compto est étâye dèsactivâye temporèrament por cel’adrèce IP.',
+'emailauthenticated' => 'Voutron adrèce èlèctronica est étâye ôtentifiâye lo $2 a $3.',
+'emailnotauthenticated' => 'Voutron adrèce èlèctronica est p’oncor ôtentifiâye.
Nion mèssâjo serat mandâ por châcuna de cetes fonccions.',
-'noemailprefs' => 'Endicâd una adrèce èlèctronica dens voutres prèferences por utilisar cetes fonccions.',
-'emailconfirmlink' => 'Confirmâd voutra adrèce èlèctronica',
-'invalidemailaddress' => 'Ceta adrèce èlèctronica pôt pas étre accèptâ perce que semble avêr un format fôx.
-Volyéd buchiér una adrèce bien formatâ ou ben lèssiér cél champ vouedo.',
-'cannotchangeemail' => 'Les adrèces èlèctroniques des comptos pôvont pas étre changiês sur ceti vouiqui.',
+'noemailprefs' => 'Endicâd n’adrèce èlèctronica dens voutres prèferences por empleyér cetes fonccions.',
+'emailconfirmlink' => 'Confirmâd voutron adrèce èlèctronica',
+'invalidemailaddress' => 'Cet’adrèce èlèctronica pôt pas étre accèptâye perce que semble avêr un format fôx.
+Volyéd buchiér n’adrèce bien formatâye ou ben lèssiér cél champ vouedo.',
+'cannotchangeemail' => 'Les adrèces èlèctroniques des comptos pôvont pas étre changiêyes sur ceti vouiqui.',
'emaildisabled' => 'Ceti seto pôt pas mandar des mèssâjos.',
-'accountcreated' => 'Compto fêt.',
-'accountcreatedtext' => 'Lo compto usanciér por $1 at étâ fêt.',
+'accountcreated' => 'Compto fêt',
+'accountcreatedtext' => 'Lo compto utilisator por $1 est étâ fêt.',
'createaccount-title' => 'Crèacion d’un compto por {{SITENAME}}',
-'createaccount-text' => 'Quârqu’un at fêt un compto por voutra adrèce èlèctronica dessus {{SITENAME}} ($4) avouéc lo titro « $2 » et lo contresegno « $3 ».
+'createaccount-text' => 'Quârqu’un at fêt un compto utilisator por voutron adrèce èlèctronica dessus {{SITENAME}} ($4) avouéc lo titro « $2 » et lo contresegno « $3 ».
Vos vos devriâd branchiér et pués changiér dês ora voutron contresegno.
-Ignorâd ceti mèssâjo se cél compto at étâ fêt per èrror.',
+Ignorâd ceti mèssâjo se cél compto utilisator est étâ fêt per fôta.',
'usernamehasherror' => 'Lo nom d’usanciér pôt pas contegnir des caractèros de chaplâjo',
'login-throttled' => 'Vos éd tentâ dèrriérement un trop grant nombro de branchements.
Volyéd atendre devant que tornar èprovar.',
'right-writeapi' => 'Utilisar l’API d’ècritura',
'right-delete' => 'Suprimar des pâges',
'right-bigdelete' => 'Suprimar des pâges qu’ont un grant historico',
+'right-deletelogentry' => 'Suprimar et refâre n’entrâ spècifica du jornal',
'right-deleterevision' => 'Suprimar ou refâre una vèrsion spècefica d’una pâge',
'right-deletedhistory' => 'Vêre les entrâs des historicos suprimâs mas sen lor tèxto',
'right-deletedtext' => 'Vêre lo tèxto suprimâ et les difèrences entre les vèrsions suprimâs',
'disambiguations' => 'Pâges qu’ont des lims de vers des pâges d’homonimia',
'disambiguationspage' => 'Template:Homonimia',
-'disambiguations-text' => "Cetes pâges ont un lim de vers una '''pâge d’homonimia'''.
-Devriant pletout pouentar vers una pâge que vat avouéc.<br />
-Una pâge est trètâ coment una pâge d’homonimia s’encllut (tot drêt ou ben rècursivament) yon des modèlos listâs dessus [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Cetes pâges ont u muens yon lim de vers na '''pâge d’homonimia'''.
+Devriant pletout pouentar vers na pâge que vat avouéc.<br />
+Na pâge est trètâye coment na pâge d’homonimia s’empleye un modèlo liyê a [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redirèccions dobles',
'doubleredirectstext' => 'Vê-que la lista de les pâges que redirijont vers des pâges que sont lor-mémes des pâges de redirèccion.
Vos pouede rètrendre la vua en chouèséssent un tipo de jornal, un nom d’usanciér (sensiblo a la câssa) ou ben una pâge afèctâ (sensibla a la câssa avouéc).',
'logempty' => 'Nion èlèment d’ense at étâ trovâ dens lo jornal.',
'log-title-wildcard' => 'Chèrchiér permié los titros que començont per ceti tèxto',
+'showhideselectedlogentries' => 'Fâre vêre / cachiér les entrâs de jornal chouèsies',
# Special:AllPages
'allpages' => 'Totes les pâges',
'rollback' => 'Rèvocar los changements',
'rollback_short' => 'Rèvocar',
'rollbacklink' => 'rèvocar',
+'rollbacklinkcount' => 'rèvocar $1 changement{{PLURAL:$1||s}}',
+'rollbacklinkcount-morethan' => 'rèvocar més de $1 changement{{PLURAL:$1||s}}',
'rollbackfailed' => 'La rèvocacion at pas reussia',
'cantrollback' => 'Empossiblo de rèvocar lo changement ;
lo dèrriér contributor est lo solèt ôtor de ceta pâge.',
# JavaScriptTest
'javascripttest' => 'Èprôva de JavaScript',
+'javascripttest-disabled' => 'Cela fonccion-que est pas étâye activâye sur ceti vouiqui.',
'javascripttest-title' => 'Èprôves de $1 en cors',
'javascripttest-qunit-intro' => 'Vêde la [$1 documentacion de les èprôves] dessus mediawiki.org.',
'javascripttest-qunit-heading' => 'Suita d’èprôva QUnit de JavaScript dessus MediaWiki',
'tooltip-p-logo' => 'Pâge principâla',
'tooltip-n-mainpage' => 'Visitar la pâge de reçua du seto',
'tooltip-n-mainpage-description' => 'Alar a la reçua',
-'tooltip-n-portal' => 'A propôs du projèt',
+'tooltip-n-portal' => 'Sur lo projèt, cen que vos pouede fâre, yô que trovar les chouses',
'tooltip-n-currentevents' => 'Trovar les enformacions de fond sur les dèrriéres novèles',
'tooltip-n-recentchanges' => 'Lista des dèrriérs changements sur lo vouiqui',
'tooltip-n-randompage' => 'Fâre vêre una pâge a l’hasârd',
* <span class="mw-specialpagecached">Pâges spèciâles solament en cache (porriant étre dèpassâs).</span>',
'specialpages-group-maintenance' => 'Rapôrts de mantegnence',
'specialpages-group-other' => 'Ôtres pâges spèciâles',
-'specialpages-group-login' => 'Branchement / encartâjo',
+'specialpages-group-login' => 'Sè branchiér / fâre un compto',
'specialpages-group-changes' => 'Dèrriérs changements et jornals',
'specialpages-group-media' => 'Rapôrts et tèlèchargements de fichiérs mèdia',
'specialpages-group-users' => 'Usanciérs et drêts apondus',
'duration-centuries' => '$1 sièclo{{PLURAL:$1||s}}',
'duration-millennia' => '$1 milènèro{{PLURAL:$1||s}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|est pas un tipo de fichiér ôtorisâ|sont pas des tipos de fichiérs ôtorisâs}}. {{PLURAL:$3|Lo tipo de fichiér ôtorisâ est|Los tipos de fichiérs ôtorisâs sont}} $2.',
);
Falbhaidh an ùine air {{PLURAL:$3|an fhacal-fhaire|an dà fhacal-faire|na faclan-faire|na faclan-faire|na faclan-faire|na faclan-faire}} sealach seo ann an {{PLURAL:$5|latha|$5 latha|$5 latha|$5 latha|$5 làithean|$5 latha}}.
Bu chòir dhut clàradh a-steach agus facal-faire ùr a thaghadh an-dràsta. Ma dh'iarr cuideigin eile seo no ma chuimhnich thu air an fhacal-fhaire agad 's mur eil thu airson atharrachadh tuilleadh, leig seachad an teachdaireachd seo 's lean ort leis an t-seann fhacal-fhaire.",
+'passwordreset-emailelement' => 'Ainm-cleachdaiche: $1
+Facal-faire sealach: $2',
+'passwordreset-emailsent' => "Chaidh cuimhneachan a chur air a' phost-d.",
+'passwordreset-emailsent-capture' => "Chaidh cuimhneachan a chur air a' phost-d agus chì thu sin gu h-ìosal.",
+'passwordreset-emailerror-capture' => "Chaidh cuimhneachan a chur air a' phost-d agus chì thu sin gu h-ìosal ach cha b' urrainn dhuinn a chur dhan chleachdaiche: $1",
+
+# Special:ChangeEmail
+'changeemail' => 'Atharraich am post-d',
+'changeemail-header' => "Atharraich cunntas a' phuist-d",
+'changeemail-text' => 'Lìon am foirm seo gus am post-d agad atharrachadh. Feumaidh tu am facal-faire agad a chur a-steach a-rithist gus a dhearbhadh.',
+'changeemail-no-info' => 'Feumaidh tu clàradh a-steach mus dèan thu inntrigeadh dìreach dhan duilleag seo.',
+'changeemail-oldemail' => 'An seòladh puist-d làithreach:',
+'changeemail-newemail' => 'An seòladh puist-d ùr:',
+'changeemail-none' => '(chan eil gin)',
+'changeemail-submit' => 'Atharraich am post-d',
+'changeemail-cancel' => 'Sguir dheth',
# Edit page toolbar
'bold_sample' => 'Teacs trom',
'savearticle' => 'Sàbhail an duilleag',
'preview' => 'Ro-shealladh',
'showpreview' => 'Seall an ro-shealladh',
+'showlivepreview' => 'Ro-shealladh beò',
'showdiff' => 'Seall na mùthaidhean',
'anoneditwarning' => "'''Rabhadh:''' Chan eil thu air logadh a-steach.
Thèid an seòladh IP agad a chlàrachadh ann an eachdraidh na duilleige seo.",
+'anonpreviewwarning' => "''Chan eil thu air clàradh a-steach. Ma nì thu sàbhaladh, thèid an seòladh IP agad a chlàradh ann an eachdraidh deasachadh na duilleige seo.''",
+'missingsummary' => "'''Cuimhnich:''' Cha dug thu seachad gearr-chunntas air na dh'atharraich thu.
+Ma bhriogas tu air \"{{int:savearticle}}\" a-rithist, thèid na dheasaich thu a shàbhaladh as aonais gearr-chunntais.",
+'missingcommenttext' => 'Cuir a-steach beachd gu h-ìosal.',
+'missingcommentheader' => "'''Cuimhnich:''' Cha dug thu seachad cuspair/ceann airson a' bheachd seo.
+Ma bhriogas tu air \"{{int:savearticle}}\" a-rithist, thèid na dheasaich thu a shàbhaladh as aonais.",
'summary-preview' => "Ro-shealladh a' ghearr-chunntais:",
+'subject-preview' => "Ro-shealladh air a' chuspair/air a' cheann:",
'blockedtitle' => 'Tha an cleachdair air a bhacadh',
+'blockedtext' => "''Chaidh an t-ainm-cleachdaiche no an seòladh IP agad a bhacadh.'''
+
+'S e \$1 a chur am bacadh seo ort.
+Thug iad an cèill gun do rinn iad sinn air sgàth an adhbhair seo: ''\$2''.
+
+* Toiseach a' bhacaidh: \$8
+* Deireadh a' bhacaidh: \$6
+* An neach air a bheil am bacadh: \$7
+
+'S urrainn dhut fios a chur gu \$1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.
+Chan urrainn dhut am feart \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.
+'S e \$3 an seòladh IP làithreach agus agus 's e #\$5 ID a' bhacaidh.
+Thoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
+'autoblockedtext' => "''Chaidh an seòladh IP agad a bhacadh gu fèin-obrachail a chionn 's gun deach a chleachdadh le cuideigin eile a chaidh a bhacadh le \$1.'''
+Thug iad an cèill gun do rinn iad sinn air sgàth an adhbhair seo:
+
+:''\$2''.
+
+* Toiseach a' bhacaidh: \$8
+* Deireadh a' bhacaidh: \$6
+* An neach air a bheil am bacadh: \$7
+
+'S urrainn dhut fios a chur gu \$1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.
+
+Dh'fhaoidte nach urrainn dhut am feart \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.
+
+'S e \$3 an seòladh IP làithreach agus agus 's e #\$5 ID a' bhacaidh.
+Thoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
+'blockednoreason' => 'cha deach adhbhar a shònrachadh',
'loginreqlink' => 'log a-steach',
'accmailtitle' => 'Facal-faire air a chur.',
'accmailtext' => "Chaidh facal-faire a chruthachadh air thuaiream airson [[User talk:$1|$1]] 's a chur gu $2.
'youhavenewmessages' => 'Ten $1 ($2).',
'newmessageslink' => 'mensaxes novas',
'newmessagesdifflink' => 'diferenzas coa revisión anterior',
+'youhavenewmessagesfromusers' => 'Ten $1 {{PLURAL:$3|doutro usuario|de $3 usuarios}} ($2).',
+'youhavenewmessagesmanyusers' => 'Ten $1 de moitos usuarios ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|unha mensaxe nova|$1 mensaxes novas}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|última modificación|últimas modificacións}}',
'youhavenewmessagesmulti' => 'Ten mensaxes novas en $1',
'editsection' => 'editar',
'editold' => 'editar',
'remembermypassword' => 'Lembrar o meu contrasinal neste ordenador (ata $1 {{PLURAL:$1|día|días}})',
'securelogin-stick-https' => 'Permanecer conectado mediante as HTTPS despois de acceder',
'yourdomainname' => 'O seu dominio:',
+'password-change-forbidden' => 'Non pode mudar os contrasinais neste wiki.',
'externaldberror' => 'Ou ben se produciu un erro da base de datos na autenticación externa ou ben non se lle permite actualizar a súa conta externa.',
'login' => 'Acceder ao sistema',
'nav-login-createaccount' => 'Rexistro',
'noarticletext-nopermission' => 'Actualmente non hai ningún texto nesta páxina.
Pode [[Special:Search/{{PAGENAME}}|procurar polo título desta páxina]] noutras páxinas
ou <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ollar os rexistros relacionados]</span>.',
+'missing-revision' => 'A revisión nº$1 da páxina chamada "{{PAGENAME}}" non existe.
+
+A miúdo, isto está provocado por seguir unha ligazón de historial obsoleta cara a unha páxina que foi borrada.
+O [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rexistro de borrados] contén máis detalles.',
'userpage-userdoesnotexist' => 'A conta do usuario "<nowiki>$1</nowiki>" non está rexistrada. Comprobe se desexa crear/editar esta páxina.',
'userpage-userdoesnotexist-view' => 'A conta de usuario "$1" non está rexistrada.',
'blocked-notice-logextract' => 'Este usuario está bloqueado.
'expansion-depth-exceeded-warning' => 'Páxina que supera a profundidade de expansión',
'parser-unstrip-loop-warning' => 'Detectouse un bucle inamovible',
'parser-unstrip-recursion-limit' => 'Excedeuse o límite de recursión inamovible ($1)',
+'converter-manual-rule-error' => 'Detectouse un erro na regra manual de conversión da lingua',
# "Undo" feature
'undo-success' => 'A edición pódese desfacer.
'editundo' => 'desfacer',
'diff-multi' => '(Non se {{PLURAL:$1|mostra unha revisión|mostran $1 revisións}} do historial {{PLURAL:$1|feita|feitas}} por {{PLURAL:$2|un usuario|$2 usuarios}}.)',
'diff-multi-manyusers' => '(Non se {{PLURAL:$1|mostra unha revisión|mostran $1 revisións}} do historial {{PLURAL:$1|feita|feitas}} por máis {{PLURAL:$2|dun usuario|de $2 usuarios}}.)',
+'difference-missing-revision' => 'Non se {{PLURAL:$2|atopou revisión ningunha|atoparon $2 revisións}} desta diferenza ($1).
+
+A miúdo, isto está provocado por seguir unha ligazón de diferenzas obsoleta cara a unha páxina que foi borrada.
+O [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rexistro de borrados] contén máis detalles.',
# Search results
'searchresults' => 'Resultados da procura',
'right-writeapi' => 'Usar a API para modificar o wiki',
'right-delete' => 'Borrar páxinas',
'right-bigdelete' => 'Borrar páxinas con historiais grandes',
+'right-deletelogentry' => 'Borrar e restaurar entradas de rexistro específicas',
'right-deleterevision' => 'Borrar e restaurar versións específicas de páxinas',
'right-deletedhistory' => 'Ver as entradas borradas do historial, sen o seu texto asociado',
'right-deletedtext' => 'Ver texto borrado e cambios entre revisións eliminadas',
'lockmanager-fail-releaselock' => 'Non se puido liberar o peche de "$1".',
'lockmanager-fail-db-bucket' => 'Non se puido contactar cos peches de bases de datos suficientes no cubo $1.',
'lockmanager-fail-db-release' => 'Non se puideron liberar os peches na base de datos $1.',
+'lockmanager-fail-svr-acquire' => 'Non se puideron obter os peches no servidor $1.',
'lockmanager-fail-svr-release' => 'Non se puideron liberar os peches no servidor $1.',
# ZipDirectoryReader
'disambiguations' => 'Páxinas que ligan con páxinas de homónimos',
'disambiguationspage' => 'Template:Homónimos',
-'disambiguations-text' => "As seguintes páxinas ligan cunha '''páxina de homónimos'''.
+'disambiguations-text' => "As seguintes páxinas conteñen, polo menos, unha ligazón cara a unha '''páxina de homónimos'''.
No canto de ligar cos homónimos deben apuntar cara á páxina apropiada.<br />
Unha páxina trátase como páxina de homónimos cando nela se usa un modelo que está ligado desde [[MediaWiki:Disambiguationspage]].",
'rollback' => 'Reverter as edicións',
'rollback_short' => 'Reverter',
'rollbacklink' => 'reverter',
+'rollbacklinkcount' => 'reverter $1 {{PLURAL:$1|edición|edicións}}',
+'rollbacklinkcount-morethan' => 'reverter máis de $1 {{PLURAL:$1|edición|edicións}}',
'rollbackfailed' => 'Houbo un fallo ao reverter as edicións',
'cantrollback' => 'Non se pode desfacer a edición; o último colaborador é o único autor desta páxina.',
'alreadyrolled' => 'Non se pode desfacer a edición en "[[:$1]]" feita por [[User:$2|$2]] ([[User talk:$2|conversa]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); alguén máis editou ou desfixo os cambios desta páxina.
'duration-millennia' => '$1 {{PLURAL:$1|milenio|milenios}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Non se puideron obter os peches no servidor $1.',
+'api-error-filetype-banned-type' => '$1 non {{PLURAL:$4|é un tipo de ficheiro permitido|son tipos de ficheiro permitidos}}. {{PLURAL:$3|O tipo de ficheiro permitido é|Os tipos de ficheiro permitidos son}} $2.',
);
*
* @author Als-Chlämens
* @author Als-Holder
+ * @author Geitost
* @author Hendergassler
* @author J. 'mach' wust
* @author Kaganer
'duration-centuries' => '$1 {{PLURAL:$1|Johrhundert|Johrhundert}}',
'duration-millennia' => '$1 {{PLURAL:$1|Jahrtöusert|Jahrtöusert}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|isch e Dateiformat, wu nit erlaubt isch|sin Dateitype, wu nit erlaubt sin}}. Erlaubt {{PLURAL:$3|isch s Dateiformat|sin d Dateiformat}} $2.',
);
'tog-editsectiononrightclick' => 'વિભાગના મથાળાં ને રાઇટ ક્લિક દ્વારા ફેરફાર કરવાની રીત અપનાવો. (જાવાસ્ક્રિપ્ટ જરૂરી)',
'tog-showtoc' => 'અનુક્રમણિકા દર્શાવો (૩થી વધુ પેટા-મથાળા વાળા લેખો માટે)',
'tog-rememberpassword' => 'આ કમ્પ્યૂટર પર મારી લોગ-ઇન વિગતો યાદ રાખો (મહત્તમ $1 {{PLURAL:$1|દિવસ|દિવસ}} માટે)',
-'tog-watchcreations' => 'મà«\87àª\82 લàª\96à«\87લા નવા લà«\87àª\96à«\8b મારી ધ્યાનસૂચિમાં ઉમેરો',
-'tog-watchdefault' => 'હું ફેરફાર કરૂં તે પાના મારી ધ્યાનસૂચિમાં ઉમેરો',
-'tog-watchmoves' => 'હà«\81àª\82 àª\9cà«\87નà«\81àª\82 નામ બદલà«\81àª\82 તà«\87 પાના મારી ધ્યાનસૂચિમાં ઉમેરો',
-'tog-watchdeletion' => 'હà«\81àª\82 હàª\9fાવà«\81àª\82 તà«\87 પાના મારી ધ્યાનસૂચિમાં ઉમેરો',
+'tog-watchcreations' => 'મà«\87àª\82 àª\89મà«\87રà«\87લા પાનાàª\82àª\93 àª\85નà«\87 àª\85પલà«\8bડ àª\95રà«\87લ ફાàª\87લà«\8dસ મારી ધ્યાનસૂચિમાં ઉમેરો',
+'tog-watchdefault' => 'હું ફેરફાર કરૂં તે પાનાં અને ફાઇલ્સ મારી ધ્યાનસૂચિમાં ઉમેરો',
+'tog-watchmoves' => 'હà«\81àª\82 àª\96સà«\87ડà«\81àª\82 તà«\87 પાનાàª\82 àª\85નà«\87 ફાàª\87લà«\8dસ મારી ધ્યાનસૂચિમાં ઉમેરો',
+'tog-watchdeletion' => 'હà«\81àª\82 દà«\82ર àª\95રà«\81àª\82 તà«\87 પાનાàª\82 àª\85નà«\87 ફાàª\87લà«\8dસ મારી ધ્યાનસૂચિમાં ઉમેરો',
'tog-minordefault' => 'બધા નવા ફેરફારો નાના તરીકે માર્ક કરો.',
'tog-previewontop' => 'એડીટ બોક્સ પહેલાં પ્રિવ્યુ બતાવો.',
'tog-previewonfirst' => 'પ્રથમ ફેરફાર વખતે પ્રિવ્યુ બતાવો.',
'tog-nocache' => 'બ્રાઉઝરનું પેજ કેશિંગ અક્રિય કરો',
-'tog-enotifwatchlistpages' => 'મારà«\80 ધà«\8dયાનસà«\82àª\9aિમાàª\82નાàª\82 પાનામાં ફેરફાર થાય ત્યારે મને ઇ-મેલ મોકલો',
+'tog-enotifwatchlistpages' => 'મારà«\80 ધà«\8dયાનસà«\82àª\9aિમાàª\82નà«\81àª\82 પાનà«\81 àª\85નà«\87 ફાàª\87લમાં ફેરફાર થાય ત્યારે મને ઇ-મેલ મોકલો',
'tog-enotifusertalkpages' => 'મારી ચર્ચાનાં પાનામાં ફેરફાર થાય ત્યારે મને ઇ-મેલ મોકલો',
-'tog-enotifminoredits' => 'પાનામાàª\82 નાનાàª\82 ફà«\87રફાર થાય તà«\8dયારà«\87 પણ મને ઇ-મેલ મોકલો',
+'tog-enotifminoredits' => 'પાનાàª\82 àª\85નà«\87ફાàª\87લà«\8dસમાàª\82 નાનાàª\82 ફà«\87રફાર થાય તà«\8b પણ મને ઇ-મેલ મોકલો',
'tog-enotifrevealaddr' => 'નોટીફીકેશનના ઇમેલમાં મારૂ ઇમેલ એડ્રેસ બતાવો',
'tog-shownumberswatching' => 'ધ્યાનમાં રાખતા સભ્યોની સંખ્યા બતાવો',
'tog-oldsig' => 'હાલના હસ્તાક્ષર',
'youhavenewmessages' => 'તમારા માટે $1 ($2).',
'newmessageslink' => 'નવીન સંદેશ',
'newmessagesdifflink' => 'છેલ્લો ફેરફાર',
+'newmessageslinkplural' => '{{PLURAL:$1|નવો સંદેશ|નવાં સંદેશાઓ}}',
+'newmessagesdifflinkplural' => 'છેલ્લા {{PLURAL:$1|ફેરફાર|ફેરફારો}}',
'youhavenewmessagesmulti' => '$1 ઉપર તમારા માટે નવો સંદેશ છે.',
'editsection' => 'ફેરફાર કરો',
'editsection-brackets' => '[$1]',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'લેખ',
-'nstab-user' => 'મારા વિષà«\87',
+'nstab-user' => 'સàªà«\8dય પાનà«\81àª\82',
'nstab-media' => 'મિડીયા પાનું',
'nstab-special' => 'ખાસ પાનું',
'nstab-project' => 'પરિયોજનાનું પાનું',
'ns-specialprotected' => 'ખાસ પાનાંમાં ફેરફાર ન થઇ શકે.',
'titleprotected' => 'આ મથાળું (વિષય) [[User:$1|$1]] બનાવવા માટે સુરક્ષિત કરવામાં આવ્યો છે.
આ માટેનું કારણ છે-- "\'\'$2\'\'".',
+'exception-nologin' => 'પ્રવેશ કરેલ નથી',
# Virus scanner
'virus-badscanner' => "ખરાબ રૂપરેખા: અજાણ્યું વાઇરસ સ્કેનર: ''$1''",
'remembermypassword' => 'આ કોમ્યૂટર પર મારી લૉગ ઇન વિગતો ધ્યાનમાં રાખો (વધુમાં વધુ $1 {{PLURAL:$1|દિવસ|દિવસ}} માટે)',
'securelogin-stick-https' => 'લોગ-ઈન કર્યા પછી HTTPS સાથે જોડાયેલા રહો.',
'yourdomainname' => 'તમારૂં ડોમેઇન:',
+'password-change-forbidden' => 'તમે આ વિકિ માટે પાસવર્ડ્સ બદલી શકતા નથી.',
'externaldberror' => 'પ્રમાણભૂતતાની ત્રુટી આવી અથવા તમારૂ બહારનુ ખાતું અપડેટ કરવાનો અધિકાર તમને નથી.',
'login' => 'પ્રવેશ કરો',
'nav-login-createaccount' => 'પ્રવેશ કરો / નવું ખાતું ખોલો',
'invalidemailaddress' => 'આ ઈ-મેલ અયોગ્ય માળખું ધરાવ્હે છે માટે સ્વીકારી શકાશે નહીં
કૃપયા યોગ્ય માળખામાં ઇ-મેલ લખો',
'cannotchangeemail' => 'એકાઉન્ટ ઈ મેલ એડ્રસ આ વીકી પર નહિ બદલી શકાય.',
+'emaildisabled' => 'આ સાઇટ ઇ-મેલ્સ મોકલી શકતી નથી.',
'accountcreated' => 'ખાતું ખોલવામાં આવ્યું છે',
'accountcreatedtext' => '$1 માટે સભ્ય ખાતુ બનાવ્યું.',
'createaccount-title' => '{{SITENAME}} માટે ખાતુ બનાવ્યું',
'note' => "'''નોંધ:'''",
'previewnote' => "'''આ ફક્ત પૂર્વાવલોકન છે;'''
તમારા ફેરફારો હજુ સાચવવામાં નથી આવ્યા!",
+'continue-editing' => 'વાંચવાનું ચાલુ રાખો',
'previewconflict' => 'જો તમે આ પાનું સાચવશો તો આ પ્રિવ્યુમાં દેખાય છે તેવું સચવાશે.',
'session_fail_preview' => "'''અફસોસ છે! સત્ર માહિતી ખોઇ દેવાને કારણે અમે તમારું કાર્ય સાચવી ન શક્યાં.'''
કૃપયા ફરી પ્રયત્ન કરો.
જ્યારે તમે વેબ આધારિત અજ્ઞાત પ્રોક્સી વાપરતા હોવ ત્યારે આવું બની શકે છે.",
'edit_form_incomplete' => "'''ફેરફાર પત્રનો અમુક ભાગ સર્વર સુધી ન પહોંચ્યો; ખાત્રી કરો કે તમે કરેલા ફેરફાર બરાબર છે અને ફરી પ્રયત્ન કરો.'''",
'editing' => '$1નો ફેરફાર કરી રહ્યા છે',
+'creating' => '$1 બનાવે છે',
'editingsection' => '$1 (પરિચ્છેદ)નો ફેરફાર કરી રહ્યા છો',
'editingcomment' => '$1 (પરિચ્છેદ)નો ફેરફાર કરી રહ્યા છો',
'editconflict' => 'ફેરફારમાં વિસંગતતા: $1',
'edit-no-change' => 'તમારા ફેરફારો અવગણાયા, કેમકે અક્ષરકાયામાં કોઈ ફેરફારાના હતો',
'edit-already-exists' => 'નવું પાનું બનાવી ન શકાયું
તે પહેલેથી હાજર છે.',
+'defaultmessagetext' => 'મૂળભૂત સંદેશ લખાણ',
# Parser/template warnings
'expensive-parserfunction-warning' => "'''ચેતવણી:''' આ પાનું ખૂબ ખર્ચાળ પદચ્છેદ સૂત્ર ધરાવે છે.
# Diffs
'history-title' => '"$1"ના ફેરફારોનો ઇતિહાસ',
+'difference-title' => '"$1" ની આવૃત્તિઓ વચ્ચેનો તફાવત',
+'difference-title-multipage' => '"$1" અને "$2" પાનાંઓ વચ્ચેનો તફાવત',
'difference-multipage' => '(પાનાઓ વચ્ચેનો ફેરફાર)',
'lineno' => 'લીટી $1:',
'compareselectedversions' => 'પસંદ કરેલા સરખાવો',
'prefs-beta' => 'બીટા લક્ષણો',
'prefs-datetime' => 'તારીખ અને સમય',
'prefs-labs' => 'પ્રયોગશાળા લક્ષણો',
+'prefs-user-pages' => 'સભ્ય પાનાંઓ',
'prefs-personal' => 'સભ્ય ઓળખ',
'prefs-rc' => 'તાજા ફેરફારો',
'prefs-watchlist' => 'ધ્યાનસૂચિ',
'searchresultshead' => 'શોધો',
'resultsperpage' => 'પ્રતિ પાના પર પરિણામો',
'stub-threshold' => '<a href="#" class="stub">stub link</a>નાફોર્મમેટિંગ માટે શરૂઆતિ પગથિયું (બાઈટ્સ):',
-'stub-threshold-disabled' => 'નિષà«\8dàª\95à«\8dરà«\80યાનà«\8dવà«\80ત',
+'stub-threshold-disabled' => 'નિષà«\8dàª\95à«\8dરિય àª\95રà«\87લ',
'recentchangesdays' => 'તાજા ફેરફારોમાં દેખાડવાના દિવસો',
'recentchangesdays-max' => 'મહત્તમ $1 {{PLURAL:$1|દિવસ|દિવસો}}',
'recentchangescount' => 'સમાન્ય પણે ફલકમાં બતાવવાના ફેરફારોની સંખ્યા',
# Associated actions - in the sentence "You do not have permission to X"
'action-read' => 'આ પાનું વાંચો.',
-'action-edit' => 'àª\86 પાનામાàª\82 ફà«\87રફાર àª\95રવાનà«\80',
+'action-edit' => 'àª\86 પાનામાàª\82 ફà«\87રફાર àª\95રà«\8b',
'action-createpage' => 'નવો લેખ શરૂ કરો',
'action-createtalk' => 'ચર્ચાનું પાનું બનાવો',
'action-createaccount' => ' ખાતું ખોલો',
'rcshowhidepatr' => ' $1 ચોકીયાત ફેરફારો',
'rcshowhidemine' => 'મારા ફેરફારો $1',
'rclinks' => 'છેલ્લાં $2 દિવસમાં થયેલા છેલ્લાં $1 ફેરફારો દર્શાવો<br />$3',
-'diff' => 'àªà«\87દ',
+'diff' => 'તફાવત',
'hist' => 'ઇતિહાસ',
'hide' => 'છુપાવો',
'show' => 'બતાવો',
'disambiguations' => 'સંદિગ્ધ શીર્ષકવાળાં પાનાં સાથે જોડાતાં પૃષ્ઠો',
'disambiguationspage' => 'Template:અસંદિગ્ધ',
-'disambiguations-text' => "નીચેના પાના '''સંદિગ્ધ વાક્યો વાળા પાના''' સાથે કડી દ્વારા જોડાયેલા છે.
-તà«\87ના àª\95રતા તà«\87નà«\87 યà«\8bàª\97à«\8dય તà«\87 વિષà«\8dય સાથે જોડાયેલા હોવા જોઇએ.<br />
-àª\86 પાનાનà«\87 સàª\82દિàª\97à«\8dધ વાàª\95à«\8dયà«\8b વાળા પાના તà«\8dયારà«\87 àª\95હà«\80 શàª\95ાય àª\9cà«\8dયારà«\87 તà«\87 [[MediaWiki:Disambiguationspage]] નામના ઢાàª\82àª\9aા સાથà«\87 àª\9cà«\8bડાયà«\87લા હોય.",
+'disambiguations-text' => "નà«\80àª\9aà«\87ના પાના '''સàª\82દિàª\97à«\8dધ વાàª\95à«\8dયà«\8b વાળા પાના''' સાથà«\87 àª\93àª\9bામાàª\82 àª\93àª\9bà«\80 àª\8fàª\95 àª\95ડà«\80 દà«\8dવારા àª\9cà«\8bડાયà«\87લા àª\9bà«\87.
+તà«\87àª\93 વધà«\81 યà«\8bàª\97à«\8dય પાનાàª\82 સાથે જોડાયેલા હોવા જોઇએ.<br />
+પાનાનà«\87 સàª\82દિàª\97à«\8dધ વાàª\95à«\8dયà«\8b વાળà«\81àª\82 પાનà«\81àª\82 તà«\8dયારà«\87 àª\95હà«\80 શàª\95ાય àª\9cà«\8dયારà«\87 તà«\87 [[MediaWiki:Disambiguationspage]] નામના માળàª\96ા સાથà«\87 àª\9cà«\8bડાયà«\87લà«\81àª\82 હોય.",
'doubleredirects' => 'બમણું દિશાનિર્દેશન',
'doubleredirectstext' => 'આ પાનું દિશા નિર્દેશિત પાના પર થયેલા દિશા નિર્દેશિત પાનાની યાદિ બતાવે છે.
'rollback' => 'ફેરફારો ઉલટાવો',
'rollback_short' => 'ઉલટાવો',
'rollbacklink' => 'પાછું વાળો',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|ફેરફાર|ફેરફારો}} કરતાં વધુ પાછાં લાવો',
+'rollbacklinkcount-morethan' => '$1 {{PLURAL:$1|ફેરફાર|ફેરફારો}} કરતાં ઓછું પાછું લાવો',
'rollbackfailed' => 'ઉલટાવવું નિષ્ફળ',
'cantrollback' => 'આ ફેરફારો ઉલટાવી નહી શકાય
છેલ્લો ફેરફાર આ પાના ના રચયિતા દ્વારા જ થયો હતો',
'sp-contributions-deleted' => 'સભ્યનું ભૂંસેલું યોગદાન',
'sp-contributions-uploads' => 'ખાસ યોગદાન / ચડાવેલ ફાઇલ',
'sp-contributions-logs' => 'લૉગ',
-'sp-contributions-talk' => 'યà«\8bàª\97દાનàª\95રà«\8dતાનà«\80 àª\9aરà«\8dàª\9aા',
+'sp-contributions-talk' => 'ચર્ચા',
'sp-contributions-userrights' => 'સભ્ય હક્ક પ્રબંધન',
'sp-contributions-blocked-notice' => 'આ સભ્ય પ્રતિબંધિત છે
તમારા સંદર્ભ માટે પ્રતિબંધિત સભ્યોની યાદિ આપી છે',
'exif-meteringmode-4' => 'બિંદુઓ',
'exif-meteringmode-5' => 'ભાત',
'exif-meteringmode-6' => 'આશિંક',
-'exif-meteringmode-255' => 'બà«\80àª\9cà«\81àª\82 àª\95àª\88',
+'exif-meteringmode-255' => 'àª\85નà«\8dય',
'exif-lightsource-0' => 'અજાણ્યો',
'exif-lightsource-1' => 'દિવસ પ્રકાશ',
'version-extensions' => 'પ્રસ્થાપિત વિસ્તારકો',
'version-specialpages' => 'ખાસ પાનાં',
'version-parserhooks' => 'પદચ્છેદ ખૂંટો',
-'version-variables' => 'સહàª\97à«\81ણàª\95à«\8b',
+'version-variables' => 'àª\9aલ',
'version-antispam' => 'સ્પેમ સંરક્ષણ',
'version-skins' => 'ફલક',
'version-other' => 'અન્ય',
'version-software-product' => 'ઉત્પાદ',
'version-software-version' => 'આવૃત્તિ',
'version-entrypoints-header-entrypoint' => 'પ્રવેશ સ્થળ',
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'ફાઈલ પથ',
'api-error-verification-error' => 'ફાઈલ યાતો ખરાબ થઈ ગઈ છે યાતો તેનું ઍક્સટેન્શન (વિસ્તરક) ખોટો છે.',
# Durations
-'duration-seconds' => '$1 {{PLURAL:$1|સેકંડ|સેકંડ}}',
+'duration-seconds' => '$1 {{PLURAL:$1|સેકંડ|સેકંડો}}',
'duration-minutes' => '$1 {{PLURAL:$1|મિનિટ|મિનિટો}}',
'duration-hours' => '$1 {{PLURAL:$1|કલાક|કલાકો}}',
'duration-days' => '$1 {{PLURAL:$1|દિવસ|દિવસો}}',
'duration-centuries' => '$1 {{PLURAL:$1|શતાબ્દી|શતાબ્દીઓ}}',
'duration-millennia' => '$1 {{PLURAL:$1|સહસ્ત્રાબ્દી|સહસ્ત્રાબ્દીઓ}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4| માન્ય ફાઇલ પ્રકાર નથી| માન્ય ફાઇલ પ્રકારો નથી }}. માન્ય {{PLURAL:$3|ફાઇલ પ્રકાર છે|ફાઇલ પ્રકારો છે}} $2.',
);
'tog-previewontop' => 'הצגת תצוגה מקדימה לפני תיבת העריכה (או: אחריה)',
'tog-previewonfirst' => 'הצגת תצוגה מקדימה בעריכה ראשונה',
'tog-nocache' => 'מניעת אחסון הדפים בזיכרון המטמון בדפדפן',
-'tog-enotifwatchlistpages' => 'ש×\9c×\99×\97ת ×\93×\95×\90"×\9c ×\90×\9c×\99×\9a ×\9b×\90שר × ×¢×©×\94 ש×\99× ×\95×\99 ×\91×\93×£ ×\90×\95 ×\91ק×\95×\91×¥ ×\91רש×\99×\9eת ×\94×\9eעק×\91 ש×\9c×\9a',
-'tog-enotifusertalkpages' => 'ש×\9c×\99×\97ת ×\93×\95×\90"×\9c ×\90×\9c×\99×\9a ×\9b×\90שר × ×¢×©×\94 ש×\99× ×\95×\99 ×\91×\93×£ ש×\99×\97ת ×\94×\9eשת×\9eש ש×\9c×\9a',
-'tog-enotifminoredits' => 'ש×\9c×\99×\97ת ×\93×\95×\90"×\9c ×\90×\9c×\99×\9a גם על עריכות משניות של דפים וקבצים',
+'tog-enotifwatchlistpages' => '×\9cש×\9c×\95×\97 ×\90×\9c×\99×\99 ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 ×\9b×\90שר × ×¢×©×\94 ש×\99× ×\95×\99 ×\91×\93×£ ×\90×\95 ×\91ק×\95×\91×¥ ×\91רש×\99×\9eת ×\94×\9eעק×\91 ש×\9c×\99',
+'tog-enotifusertalkpages' => '×\9cש×\9c×\95×\97 ×\90×\9c×\99×\99 ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 ×\9b×\90שר × ×¢×©×\94 ש×\99× ×\95×\99 ×\91×\93×£ ש×\99×\97ת ×\94×\9eשת×\9eש ש×\9c×\99',
+'tog-enotifminoredits' => '×\9cש×\9c×\95×\97 ×\90×\9c×\99×\99 ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 גם על עריכות משניות של דפים וקבצים',
'tog-enotifrevealaddr' => 'חשיפת כתובת הדוא"ל שלך בהודעות דוא"ל',
'tog-shownumberswatching' => 'הצגת מספר המשתמשים העוקבים אחרי הדף',
'tog-oldsig' => 'החתימה הקיימת:',
'tog-watchlisthideanons' => 'הסתרת עריכות של משתמשים אנונימיים ברשימת המעקב',
'tog-watchlisthidepatrolled' => 'הסתרת עריכות בדוקות ברשימת המעקב',
'tog-nolangconversion' => 'ביטול המרת גרסאות שפה',
-'tog-ccmeonemails' => 'ק×\91×\9cת ×\94עתק×\99×\9d ש×\9c ×\94×\95×\93×¢×\95ת ×\93×\95×\90"×\9c ×\94× ×©×\9c×\97×\95ת ×\9e×\9e× י למשתמשים אחרים',
+'tog-ccmeonemails' => '×\9cש×\9c×\95×\97 ×\90×\9c×\99×\99 ×\94עתק×\99×\9d ש×\9c ×\94×\95×\93×¢×\95ת ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 שש×\9c×\97תי למשתמשים אחרים',
'tog-diffonly' => 'ביטול הצגת תוכן הדף מתחת להשוואות הגרסאות',
'tog-showhiddencats' => 'הצגת קטגוריות מוסתרות',
'tog-noconvertlink' => 'ביטול המרת קישורים לכותרות',
'youhavenewmessages' => 'יש לך $1 ($2).',
'newmessageslink' => 'הודעות חדשות',
'newmessagesdifflink' => 'השוואה לגרסה הקודמת',
+'youhavenewmessagesfromusers' => 'יש לך $1 {{PLURAL:$3|ממשתמש אחר|מ־$3 משתמשים}} ($2).',
+'youhavenewmessagesmanyusers' => 'יש לך $1 ממשתמשים רבים ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|הודעה חדשה|הודעות חדשות}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|שינוי אחרון|שינויים אחרונים}}',
'youhavenewmessagesmulti' => 'יש לך הודעות חדשות ב־$1',
'editsection' => 'עריכה',
'editold' => 'עריכה',
'remembermypassword' => 'שמירת הכניסה שלי בדפדפן הזה (ל{{PLURAL:$1|יום אחד|־$1 ימים|יומיים}} לכל היותר)',
'securelogin-stick-https' => 'המשך שימוש ב־HTTPS אחרי הכניסה',
'yourdomainname' => 'המתחם שלך:',
+'password-change-forbidden' => 'אינכם יכולים לשנות סיסמאות באתר זה.',
'externaldberror' => 'הייתה שגיאה בבסיס הנתונים של ההזדהות, או שאינכם רשאים לעדכן את חשבונכם החיצוני.',
'login' => 'כניסה לחשבון',
'nav-login-createaccount' => 'כניסה לחשבון / הרשמה',
'noarticletext-nopermission' => 'אין כרגע טקסט בדף זה.
באפשרותכם [[Special:Search/{{PAGENAME}}|לחפש את כותרת הדף]] בדפים אחרים,
או <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} לחפש ביומנים הרלוונטיים].</span>',
+'missing-revision' => 'גרסה #$1 של הדף "{{PAGENAME}}" אינה קיימת.
+
+זה נגרם בדרך כלל על־ידי לחיצה על קישור ישן לגרסה קודמת של דף שנמחק.
+אפשר למצוא פרטים ב[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} יומן המחיקות].',
'userpage-userdoesnotexist' => 'חשבון המשתמש "$1" אינו רשום.
אנא בדקו אם ברצונכם ליצור/לערוך דף זה.',
'userpage-userdoesnotexist-view' => 'חשבון המשתמש "$1" אינו רשום.',
'expansion-depth-exceeded-warning' => 'עומק ההרחבה בדף גדול מדי',
'parser-unstrip-loop-warning' => 'נמצאה לולאה בפריסה',
'parser-unstrip-recursion-limit' => 'עומק הרקורסיה של הפריסה עבר את המגבלה ($1)',
+'converter-manual-rule-error' => 'התגלתה שגיאה בכלל המרת שפה ידני',
# "Undo" feature
'undo-success' => 'ניתן לבטל את העריכה. אנא בִדקו את השוואת הגרסאות למטה כדי לוודא שזה מה שאתם רוצים לעשות, ואז שמרו את השינויים למטה כדי לבצע את ביטול העריכה.',
'editundo' => 'ביטול',
'diff-multi' => '({{PLURAL:$1|גרסת ביניים אחת|$1 גרסאות ביניים}} של {{PLURAL:$2|משתמש אחד|$2 משתמשים}} {{PLURAL:$1|אינה מוצגת|אינן מוצגות}})',
'diff-multi-manyusers' => '({{PLURAL:$1|גרסת ביניים אחת|$1 גרסאות ביניים}} של יותר {{PLURAL:$2|ממשתמש אחד|מ־$2 משתמשים}} {{PLURAL:$1|אינה מוצגת|אינן מוצגות}})',
+'difference-missing-revision' => '{{PLURAL:$2|גרסה אחת|$2 גרסאות}} של ההבדל הזה בין שתי גרסאות ($1) {{PLURAL:$2|לא נמצאה|לא נמצאו}}.
+
+זה נגרם בדרך כלל על־ידי לחיצה על קישור ישן להבדל בין גרסאות של דף שנמחק.
+אפשר למצוא פרטים ב[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} יומן המחיקות].',
# Search results
'searchresults' => 'תוצאות החיפוש',
'right-writeapi' => 'שימוש ב־API לשינוי דפים',
'right-delete' => 'מחיקת דפים',
'right-bigdelete' => 'מחיקת דפים עם היסטוריית דף ארוכה',
-'right-deleterevision' => 'מחיקת גרסאות מסוימות של דפים',
+'right-deletelogentry' => 'מחיקת ושחזור פעולות מסוימות ביומן',
+'right-deleterevision' => 'מחיקת ושחזור גרסאות מסוימות של דפים',
'right-deletedhistory' => 'צפייה בגרסאות מחוקות ללא הטקסט השייך להן',
'right-deletedtext' => 'צפייה בטקסט מחוק ובהבדלים בין גרסאות מחוקות',
'right-browsearchive' => 'חיפוש דפים מחוקים',
'lockmanager-fail-releaselock' => 'לא הייתה אפשרות לשחרר את הנעילה עבור "$1".',
'lockmanager-fail-db-bucket' => 'לא הייתה אפשרות לקבל מספיק מסדי נתונים של נעילות בדלי $1.',
'lockmanager-fail-db-release' => 'לא הייתה אפשרות לשחרר נעילות על מסד הנתונים $1.',
+'lockmanager-fail-svr-acquire' => 'לא הייתה אפשרות לבצע נעילות על השרת $1.',
'lockmanager-fail-svr-release' => 'לא הייתה אפשרות לשחרר נעילות על השרת $1.',
# ZipDirectoryReader
'statistics-pages' => 'דפים',
'statistics-pages-desc' => 'כל הדפים באתר הוויקי, כולל דפי שיחה, הפניות, וכדומה',
'statistics-files' => 'קבצים שהועלו',
-'statistics-edits' => '×\94ער×\99×\9b×\95ת ×\9e×\90×\96 ת×\97×\99×\9cת ×\94פע×\95×\9c×\94 ש×\9c {{SITENAME}}',
+'statistics-edits' => 'ער×\99×\9b×\95ת ש×\9c ×\93פ×\99×\9d ×\9e×\90×\96 ×\94×ª×§× ×ª {{SITENAME}}',
'statistics-edits-average' => 'מספר העריכות הממוצע לדף',
'statistics-views-total' => 'מספר הצפיות הכולל',
'statistics-views-total-desc' => 'צפיות בדפים שאינם קיימים ובדפים מיוחדים אינן כלולות',
'disambiguations' => 'דפים שמקשרים לדפי פירושונים',
'disambiguationspage' => 'Template:פירושונים',
-'disambiguations-text' => "×\94×\93פ×\99×\9d ×\94×\91×\90×\99×\9d ×\9eקשר×\99×\9d ×\9c'''×\93פ×\99 פירושונים'''.
-×¢×\9c×\99×\94×\9d ×\9cקשר ×\9c×\93×£ ×\94× ×\95ש×\90 ×\94ר×\9c×\95×\95× ×\98×\99 ×\91×\9eק×\95×\9d ×\96×\90ת.<br />
-×\94×\93×£ × ×\97ש×\91 ×\9c×\93×£ פ×\99ר×\95ש×\95× ×\99×\9d ×\90×\9d ×\94×\95×\90 ×\9eשת×\9eש ×\91ת×\91× ×\99ת ×\94×\9eק×\95שרת ×\9e×\94×\93×£ [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "×\91×\93פ×\99×\9d ×\94×\91×\90×\99×\9d ×\99ש ×\9cפ×\97×\95ת ק×\99ש×\95ר ×\90×\97×\93 ×\9c'''×\93×£ פירושונים'''.
+×\99×\99ת×\9b×\9f שע×\9c×\99×\94×\9d ×\9cקשר ×\91×\9eק×\95×\9d ×\96×\90ת ×\9c×\93×£ ×\9eת×\90×\99×\9d ×\99×\95תר.<br />
+דף נחשב לדף פירושונים אם הוא משתמש בתבנית המקושרת מהדף [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'הפניות כפולות',
'doubleredirectstext' => 'בדף הזה מופיעה רשימת דפי הפניה שמפנים לדפי הפניה אחרים.
'emailsubject' => 'נושא:',
'emailmessage' => 'הודעה:',
'emailsend' => 'שליחה',
-'emailccme' => '× ×\90 ×\9cש×\9c×\95×\97 ×\9c×\99 ×\91×\93×\95×\90ר ×\94×\90×\9cק×\98ר×\95× ×\99 ×\94עתק ש×\9c ×\94×\95×\93עת×\99.',
+'emailccme' => 'נא לשלוח לי בדואר אלקטרוני העתק של הודעתי.',
'emailccsubject' => 'העתק של הודעתך למשתמש $1: $2',
'emailsent' => 'הדואר נשלח',
'emailsenttext' => 'הודעת הדואר האלקטרוני שלך נשלחה.',
'rollback' => 'שחזור עריכות',
'rollback_short' => 'שחזור',
'rollbacklink' => 'שחזור',
+'rollbacklinkcount' => 'שחזור {{PLURAL:$1|עריכה אחת|$1 עריכות}}',
+'rollbacklinkcount-morethan' => 'שחזור יותר מ{{PLURAL:$1|עריכה אחת|־$1 עריכות}}',
'rollbackfailed' => 'השחזור נכשל',
'cantrollback' => 'לא ניתן לשחזר את העריכה;
התורם האחרון הוא היחיד שכתב בדף זה.',
-'alreadyrolled' => '×\9c×\90 × ×\99ת×\9f ×\9cש×\97×\96ר ×\90ת ער×\99×\9bת ×\94×\93×£ [[:$1]] ×¢×\9c ×\99×\93×\99 [[User:$2|$2]] ([[User talk:$2|ש×\99×\97×\94]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); ×\9e×\99ש×\94×\95 ×\90×\97ר ×\9b×\91ר ער×\9a ×\90×\95 ש×\97×\96ר ×\93×£ ×\96×\94.
+'alreadyrolled' => '×\9c×\90 × ×\99ת×\9f ×\9cש×\97×\96ר ×\90ת ×\94ער×\99×\9b×\94 ש×\9c [[User:$2|$2]] ([[User talk:$2|ש×\99×\97×\94]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ×\91×\93×£ [[:$1]]; ×\9e×\99ש×\94×\95 ×\90×\97ר ×\9b×\91ר ער×\9a ×\90×\95 ש×\97×\96ר ×\90ת ×\94×\93×£.
העריכה האחרונה הייתה של [[User:$3|$3]] ([[User talk:$3|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
'editcomment' => "תקציר העריכה היה: \"'''\$1'''\".",
* <span class="mw-specialpagerestricted">דפים מיוחדים מוגבלים.</span>',
'specialpages-group-maintenance' => 'דיווחי תחזוקה',
'specialpages-group-other' => 'דפים מיוחדים אחרים',
-'specialpages-group-login' => 'כניסה / הרשמה לחשבון',
+'specialpages-group-login' => 'כניסה לחשבון / הרשמה',
'specialpages-group-changes' => 'שינויים אחרונים ויומנים',
'specialpages-group-media' => 'קובצי מדיה והעלאות',
'specialpages-group-users' => 'משתמשים והרשאות',
'duration-millennia' => '{{PLURAL:$1|אלף שנה|$1 אלפי שנים|אלפיים שנה}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'לא הייתה אפשרות לבצע נעילות על השרת $1.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|הוא סוג קובץ אסור להעלאה|הם סוגי קבצים אסורים להעלאה}}. {{PLURAL:$3|סוג הקובץ המותר הוא|סוגי הקבצים המותרים הם}} $2.',
);
'tog-hidepatrolled' => 'हाल में हुए बदलावों में जाँचे हुए बदलाव छिपाएँ',
'tog-newpageshidepatrolled' => 'नए पृष्ठों की सूची में से जाँचे हुए पृष्ठों को छिपाएँ',
'tog-extendwatchlist' => 'केवल हालिया ही नहीं, बल्कि सभी परिवर्तनों को दिखाने के लिए ध्यानसूची को विस्तारित करें',
-'tog-usenewrc' => 'परिषà¥\8dà¤\95à¥\83त हालिया परिवरà¥\8dतनà¥\8bà¤\82 à¤\95ा à¤\89पयà¥\8bà¤\97 à¤\95रà¥\87à¤\82 (à¤\9cावासà¥\8dà¤\95à¥\8dरिपà¥\8dà¤\9f à¤\95à¥\80 à¤\86वशà¥\8dयà¤\95ता हà¥\88)',
+'tog-usenewrc' => 'हाल मà¥\87à¤\82 हà¥\81à¤\8f परिवरà¥\8dतनà¥\8bà¤\82 मà¥\87à¤\82 à¤\94र धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 परिवरà¥\8dतनà¥\8bà¤\82 à¤\95à¥\8b पà¥\83षà¥\8dठà¤\85नà¥\81सार समà¥\82हà¥\8bà¤\82 मà¥\87à¤\82 बाà¤\81à¤\9fà¥\87à¤\82 (à¤\9cावासà¥\8dà¤\95à¥\8dरिपà¥\8dà¤\9f à¤\86वशà¥\8dयà¤\95)',
'tog-numberheadings' => 'शीर्षक स्वयं-क्रमांकित करें',
'tog-showtoolbar' => 'सम्पादन औज़ारपट्टी दिखाएँ (जावास्क्रिप्ट की आवश्यकता है)',
'tog-editondblclick' => 'दुगुने क्लिक पर पृष्ठ संपादित करें (जावास्क्रिप्ट की आवश्यकता है)',
'tog-editsectiononrightclick' => 'अनुभाग शीर्षक पर दायाँ क्लिक करने पर अनुभाग सम्पादित करें (जावास्क्रिप्ट की आवश्यकता है)',
'tog-showtoc' => 'अनुक्रम दर्शायें (जिन पृष्ठों पर तीन से अधिक अनुभाग हों)',
'tog-rememberpassword' => 'इस ब्राउज़र पर मेरा कूटशब्द (अधिकतम $1 {{PLURAL:$1|दिन|दिनों}} तक) याद रखें',
-'tog-watchcreations' => 'मेरे द्वारा निर्मित पृष्ठों को मेरी ध्यानसूची में जोड़ें',
-'tog-watchdefault' => 'मेरे द्वारा सम्पादित पृष्ठों को मेरी ध्यानसूची में जोड़ें',
-'tog-watchmoves' => 'मेरे द्वारा स्थानांतरित पृष्ठों को मेरी ध्यानसूची में जोड़ें',
-'tog-watchdeletion' => 'मेरे द्वारा हटाए गए पृष्ठों को मेरी ध्यानसूची में जोड़ें',
+'tog-watchcreations' => 'मà¥\87रà¥\87 दà¥\8dवारा निरà¥\8dमित पà¥\83षà¥\8dठà¥\8bà¤\82 à¤\94र मà¥\87रà¥\80 à¤\85पलà¥\8bड à¤\95à¥\80 फ़ाà¤\87लà¥\8bà¤\82 à¤\95à¥\8b मà¥\87रà¥\80 धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 à¤\9cà¥\8bड़à¥\87à¤\82',
+'tog-watchdefault' => 'मà¥\87रà¥\87 दà¥\8dवारा समà¥\8dपादित पà¥\83षà¥\8dठà¥\8bà¤\82 à¤\94र फ़ाà¤\87लà¥\8bà¤\82 à¤\95à¥\8b मà¥\87रà¥\80 धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 à¤\9cà¥\8bड़à¥\87à¤\82',
+'tog-watchmoves' => 'मà¥\87रà¥\87 दà¥\8dवारा सà¥\8dथानाà¤\82तरित पà¥\83षà¥\8dठà¥\8bà¤\82 à¤\8fवà¤\82 फ़ाà¤\87लà¥\8bà¤\82 à¤\95à¥\8b मà¥\87रà¥\80 धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 à¤\9cà¥\8bड़à¥\87à¤\82',
+'tog-watchdeletion' => 'मà¥\87रà¥\87 दà¥\8dवारा हà¤\9fाà¤\8f à¤\97à¤\8f पà¥\83षà¥\8dठà¥\8bà¤\82 à¤\8fवà¤\82 फ़ाà¤\87लà¥\8bà¤\82 à¤\95à¥\8b मà¥\87रà¥\80 धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 à¤\9cà¥\8bड़à¥\87à¤\82',
'tog-minordefault' => 'मेरे सभी सम्पादन छोटे बदलाव हैं',
'tog-previewontop' => 'सम्पादन बक्से के ऊपर झलक दिखाएँ',
'tog-previewonfirst' => 'प्रथम सम्पादन के बाद झलक दिखाएँ',
'tog-nocache' => 'ब्राउज़र पृष्ठ कैशिंग अक्षम करें',
-'tog-enotifwatchlistpages' => 'मेरी ध्यानसूची में दर्ज किसी भी पृष्ठ में परिवर्तन होने पर मुझे ई-मेल करें',
+'tog-enotifwatchlistpages' => 'मà¥\87रà¥\80 धà¥\8dयानसà¥\82à¤\9aà¥\80 मà¥\87à¤\82 दरà¥\8dà¤\9c à¤\95िसà¥\80 à¤à¥\80 पà¥\83षà¥\8dठà¤\85थवा फ़ाà¤\87ल मà¥\87à¤\82 परिवरà¥\8dतन हà¥\8bनà¥\87 पर मà¥\81à¤\9dà¥\87 à¤\88-मà¥\87ल à¤\95रà¥\87à¤\82',
'tog-enotifusertalkpages' => 'मेरा वार्ता पृष्ठ परिवर्तित होने पर मुझे ई-मेल करें',
'tog-enotifminoredits' => 'छोटे परिवर्तनों के लिए भी मुझे ई-मेल भेजें',
'tog-enotifrevealaddr' => 'अधिसूचना ई-मेल में मेरा ई-मेल पता दर्शाएँ',
'passwordreset-text' => 'अपने खाते के विवरण का एक ई-मेल अनुस्मारक प्राप्त करने के लिए इस फ़ॉर्म को पूरा करें।',
'passwordreset-legend' => 'कूटशब्द रीसेट करें',
'passwordreset-disabled' => 'कूटशब्द रीसेट करना इस विकी पर अक्षम है।',
-'passwordreset-pretext' => '{{PLURAL:$1||नà¥\80à¤\9aà¥\87 दिà¤\8f à¤\97à¤\8f डà¥\87à¤\9fा à¤\95à¥\87 à¤\9fà¥\81à¤\95ड़à¥\8bà¤\82 में से एक लिखें}}',
+'passwordreset-pretext' => '{{PLURAL:$1||नà¥\80à¤\9aà¥\87 पà¥\82à¤\9bà¥\87 à¤\97à¤\8f डà¥\87à¤\9fा में से एक लिखें}}',
'passwordreset-username' => 'सदस्यनाम:',
'passwordreset-domain' => 'डोमेन:',
'passwordreset-capture' => 'परिणामस्वरूप बना ई-मेल देखें?',
'preferences' => 'मेरी वरीयताएँ',
'mypreferences' => 'मेरी वरीयताएँ',
'prefs-edits' => 'संपादन संख्या:',
-'prefsnologin' => 'लॉग इन नहीं किया हैं',
+'prefsnologin' => 'लॉग इन नहीं किया है',
'prefsnologintext' => 'वरीयताएँ बदलने के लिए आपको <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} सत्रारंभ]</span> करना होगा।',
'changepassword' => 'कूटशब्द बदलें',
'prefs-skin' => 'त्वचा',
'uploadbtn' => 'फ़ाइल अपलोड करें',
'reuploaddesc' => 'अपलोड रद्द करें और पुनः अपलोड फ़ॉर्म पर जाएँ',
'upload-tryagain' => 'संशोधित फ़ाइल विवरण भेजें',
-'uploadnologin' => 'à¤\86प लà¥\89à¤\97à¥\8dड à¤\87न नहà¥\80à¤\82 हà¥\88à¤\82।',
+'uploadnologin' => 'लà¥\89à¤\97 à¤\87न नहà¥\80à¤\82 à¤\95िया हà¥\88',
'uploadnologintext' => 'फ़ाइलें अपलोड करने के लिये [[Special:UserLogin|लॉग इन]] करना आवश्यक है।',
'upload_directory_missing' => 'अपलोड डाइरेक्टरी ($1) मौजूद नहीं है, और वेबसर्वर इसका निर्माण नहीं कर पाया।',
'upload_directory_read_only' => 'अपलोड डाइरेक्टरी ($1) में वेबसर्वर लिख नहीं पा रहा है।',
'backend-fail-writetemp' => 'अस्थायी फ़ाइल पर लिखना संभव नहीं हुआ।',
'backend-fail-closetemp' => 'अस्थाई फ़ाइल बंद नहीं हो पाई।',
'backend-fail-read' => 'फ़ाइल $1 पढ़ी नहीं जा सकी।',
-'backend-fail-create' => 'फ़ाà¤\87ल $1 बनाà¤\88 नहीं जा सकी।',
-'backend-fail-maxsize' => 'à¥\9eाà¤\87ल $1 बनाà¤\88 नहà¥\80à¤\82 à¤\9cा सà¤\95à¥\80 à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि यह {{PLURAL:$2|$2 बाà¤\88à¤\9f}} सà¥\87 बà¥\9cी है।',
+'backend-fail-create' => 'फ़ाà¤\87ल $1 लिà¤\96à¥\80 नहीं जा सकी।',
+'backend-fail-maxsize' => 'फ़ाà¤\87ल $1 लिà¤\96à¥\80 नहà¥\80à¤\82 à¤\9cा सà¤\95à¥\80 à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि यह {{PLURAL:$2|$2 बाà¤\88à¤\9f}} सà¥\87 बड़ी है।',
'backend-fail-readonly' => 'भंडारण बैकेंड "$1" इस समय केवल पढ़ा जा सकता है (रीड-ओन्ली है)। दिया गया कारण था: "$2"',
'backend-fail-synced' => 'फ़ाइल "$1" आतंरिक भंडारण बैकेंड में असंगत स्थिति में है।',
'backend-fail-connect' => '"$1" भंडारण बैकेंड से सम्पर्क स्थापित नहीं किया जा सका।',
'undeletedfiles' => '{{PLURAL:$1|1 फ़ाईल|$1 फ़ाईलें}} पुनर्स्थापित',
'cannotundelete' => 'पुनर्स्थापित नहीं कर सकें;
किसी और ने पहले ही पुनर्स्थापित कर दिया हों।',
-'undeletedpage' => "'''$1 को पुनर्स्थापित कर दिया गया हैं'''
+'undeletedpage' => "'''$1 को पुनर्स्थापित कर दिया गया है'''
-हाल में हटायें गये तथा पुनर्स्थापित किये हुए पन्नोंकी ज़ानकारी के लिये [[Special:Log/delete|हटानेकी सूची]] देखें।",
+हाल में हटाये गये तथा पुनर्स्थापित किये गए पन्नों की जानकारी के लिये [[Special:Log/delete|हटाने की लॉग]] देखें।",
'undelete-header' => 'हाल में हटाये गये पृष्ठ देखने के लियें [[Special:Log/delete|हटाने की सूची]] देखें।',
'undelete-search-title' => 'हटाये गये पृष्ठ खोज़ें',
'undelete-search-box' => 'हटायें गयें पृष्ठ खोजें',
इन मामलोंमे आपको स्वयं यह पृष्ठ जोडने पड़ सकते है ।",
'movearticle' => 'पृष्ठ का नाम बदलें',
'moveuserpage-warning' => 'चाल उपयोगकर्ता चेतावनी पृष्ठ',
-'movenologin' => 'लॉग इन नहीं किया',
+'movenologin' => 'लॉग इन नहीं किया है',
'movenologintext' => 'लेख स्थानान्तरित करने के लिये आपका [[Special:UserLogin|लॉग इन]] किया होना आवश्यक हैं।',
'movenotallowed' => 'आपको पृष्ठ स्थानांतरित करने की अनुमति नहीं है।',
'movenotallowedfile' => 'आपको फ़ाइलें स्थानांतरित करने की अनुमति नहीं है।',
'siteusers' => '{{SITENAME}} {{PLURAL:$2|सदस्य|सदस्य}} $1',
'anonusers' => '{{SITENAME}} अनाम {{PLURAL:$2|सदस्य|सदस्य}} $1',
'creditspage' => 'पान श्रेय नामावली',
-'nocredits' => 'à¤\87स पà¥\83षà¥\8dठà¤\95à¥\87 लियà¥\87 à¤\95à¥\8dरà¥\87डिà¤\9f à¤\9c़ानà¤\95ारà¥\80 नहà¥\80à¤\82 हà¥\88à¤\82।',
+'nocredits' => 'à¤\87स पà¥\83षà¥\8dठà¤\95à¥\87 लियà¥\87 à¤\95à¥\8dरà¥\87डिà¤\9f à¤\9cानà¤\95ारà¥\80 नहà¥\80à¤\82 हà¥\88।',
# Spam protection
'spamprotectiontitle' => 'स्पॅम सुरक्षा फिल्टर',
'markaspatrolleddiff' => 'देख लिया ऐसा मार्क करें',
'markaspatrolledtext' => 'इस पृष्ठ को देख लिया ऐसा मार्क करें',
'markedaspatrolled' => 'देख लिया ऐसा मार्क करें',
-'markedaspatrolledtext' => 'चुने हुए अवतरण पर देखा गया का मार्क किया।',
+'markedaspatrolledtext' => '[[:$1]] का चयनित अवतरण जाँचा हुआ चिन्हित किया गया।',
'rcpatroldisabled' => 'हाल में हुए बदलावों पर नजर रखना बंद कर दिया हैं',
'rcpatroldisabledtext' => 'हाल में हुए बदलावोंपर नजर रखने की सुविधा बंद कर दी गईं हैं।',
'markedaspatrollederror' => 'देख लिया ऐसा मार्क नहीं कर पायें',
'api-error-empty-file' => 'प्रस्तुत फ़ाइल खाली था।',
'api-error-emptypage' => 'नए खाली पृष्ठ बनाने की अनुमति नहीं है।',
'api-error-fetchfileerror' => 'आंतरिक त्रुटि: जब फ़ाइल लाया जा रहा तो कुछ गलत हो गया था।',
+'api-error-fileexists-forbidden' => '"$1" नाम की फ़ाइल पहले से मौजूद है और अधिलेखित नहीं की जा सकती।',
+'api-error-fileexists-shared-forbidden' => '"$1" नाम की फ़ाइल पहले से साझे फ़ाइल भण्डार में मौजूद है, और अधिलेखित नहीं की जा सकती।',
'api-error-file-too-large' => 'प्रस्तुत फ़ाइल बहुत बड़ी थी।',
'api-error-filename-tooshort' => 'फ़ाइल का नाम बहुत छोटा है।',
'api-error-filetype-banned' => 'इस प्रकार की फ़ाइल पर प्रतिबंध लगा दिया है।',
'duration-centuries' => '$1 {{PLURAL:$1|शताब्दी}}',
'duration-millennia' => '$1 {{PLURAL:$1|सहस्राब्दी}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 फ़ाइल {{PLURAL:$4|प्रकार|प्रकारों}} की अनुमति नहीं है। फ़ाइल प्रकार {{PLURAL:$3|जिसकी|जिनकी}} अनुमति है: $2।',
);
* @author Abdul Kadir
* @author AndySingh
* @author Bihari
+ * @author Brijlal
* @author Girmitya
* @author Kaganer
* @author Malafaya
'lockmanager-notlocked' => '"$1" ke nai khole sakaa hae; ii lock nai hae.',
'lockmanager-fail-closelock' => '"$1" ke khatir lock file ke nai band kare sakaa hae.',
'lockmanager-fail-deletelock' => '"$1" ke khatir lock file ke nai mitae sakaa hae.',
+'lockmanager-fail-openlock' => '"$1" ke khatir lock file ke nai khola jaae sake hae',
# Special:UploadStash
'uploadstash' => 'Gupt file ke upload karo',
'allpagesbadtitle' => 'Dewa gias panna ke title kharaab rahaa nai to inter-language nai to inter-wiki ke prefix hai.
Is me ek nai to jaada akchhar hai jiske title me nai kaam me lawa jaae sake hai.',
'allpages-bad-ns' => '{{SITENAME}} me namespace "$1" nai hai.',
+'allpages-hide-redirects' => 'Redirects lukao',
# Special:Categories
'categories' => 'Vibhag',
Nawaa mitawa gais aur badlao ke ulta karaa gais panna ke dekhe ke khatir [[Special:Log/delete|deletion log]] ke dekho.",
'undelete-header' => 'Nawaa mitawa gais panna ke dekhe ke khatir [[Special:Log/delete|the deletion log]] ke dekho.',
+'undelete-search-title' => 'Mitawa gais panna ke khojo',
'undelete-search-box' => 'Mitawa gais panna ke khojo',
'undelete-search-prefix' => 'Uu panna ke dekhao jon ki isse suruu hoe hai:',
'undelete-search-submit' => 'Khojo',
'ipusubmit' => 'Ii rukawat ke hatao',
'unblocked' => '[[User:$1|$1]] ke rukawat ke khalaas kar dewa gais hai',
'unblocked-id' => 'Roko $1 ke khalaas kar dewa gais hai',
+'blocklist' => 'Roka gais sadasya',
'ipblocklist' => 'Roka gais sadasya',
'ipblocklist-legend' => 'Ek roka gais sadasya ke khojo',
'blocklist-userblocks' => 'Roka gais account ke lukao',
'unblocklink' => 'rukawat khatam karo',
'change-blocklink' => 'rukawat ke badlo',
'contribslink' => 'yogdaan',
+'emaillink' => 'E-mail bhejo',
'autoblocker' => 'Apne se rokaa gais hai kaahe ki aap ke IP address ke abhi haali "[[User:$1|$1]]" use karis hai.
$1 ke roke ke kaaran hai: "$2"',
'blocklogpage' => 'Suchi ke roko',
Khayal rakhna ki agar jo nawaa title ke naam ke ek panna hai tab panna move '''nai''' hae saki jab tak ki panna khali nahi hai yah to redirect hai yah to koi pahile ke edit itihaas nahi hai.
Iske matlab ii hai ki aap ek panna ke naam badal ke wahi naam rakh de sakta hai jon naam pahile rahaa aur agar aap mistake karaa tab abhi ke panna ke overwrite nahi kare saktaa.
+'''CHETAWANI'''
+Ii ek lokpriye panna ke galti se badal de sake hai;
+meharbaani kar ke aap aapan karya ke natiija ke baare me socho aage kuch kare se pahile.",
+'movepagetext-noredirectfixer' => "Niche ke form kaam me laae se panna ke naam badal jaai aur iske itihass nawaa naam ke niche hoe jaai.
+Puraana title nawaa title pe redirect hoe jaai.
+Ii jaruri hae ki aap [[Special:DoubleRedirects|double]] nai to [[Special:BrokenRedirects|broken redirects]] ke check karo.
+Aap ke jimewaari hai ki dekho ki links right jagah point kare hai.
+
+Khayal rakhna ki agar jo nawaa title ke naam ke ek panna hai tab panna move '''nai''' hae saki jab tak ki panna khali nahi hai yah to redirect hai yah to koi pahile ke edit itihaas nahi hai.
+Iske matlab ii hai ki aap ek panna ke naam badal ke wahi naam rakh de sakta hai jon naam pahile rahaa aur agar aap mistake karaa tab abhi ke panna ke overwrite nahi kare saktaa.
+
'''CHETAWANI'''
Ii ek lokpriye panna ke galti se badal de sake hai;
meharbaani kar ke aap aapan karya ke natiija ke baare me socho aage kuch kare se pahile.",
'spam_blanking' => 'Sab badlao jisme $1 se jorr hai, ke mitawa jaawe hai',
# Info page
+'pageinfo-header-watchlist' => 'Dhyan suchi',
+'pageinfo-header-views' => 'Ketna angle se dekha jaae hae',
'pageinfo-subjectpage' => 'Panna',
+'pageinfo-edits' => 'Etna badlao rahaa',
+'pageinfo-viewsperedit' => 'Har ek badlao ke ketna dafe dekha gais hae',
# Patrolling
'markaspatrolleddiff' => 'Mark karo ke pahraa dewa jaawe hai',
* @author Kguirnela
* @author Oxyzen
* @author Tagimata
+ * @author Taylortheturtle
*/
$messages = array(
'tooltip-rollback' => '"Panumbalik" ginabalik ang (mga) na-islan sa sini nga pahina sa pinaka ulihi nga kontributor sa isa lang ka klik',
'tooltip-undo' => '"Indi pag-obrahon" ginabalik ang gin-islan kag gabukas sa isaln form sa may prebyu mode.
Gapasugot sa pagdugang sang rason sa kabilugan.',
+'tooltip-summary' => 'Maghatag sing diutay nga eksplikasyon',
# Browsing diffs
'previousdiff' => '← Mas daan nga na-islan',
'tog-hidepatrolled' => 'Sakrij pregledane izmjene u nedavnim promjenama',
'tog-newpageshidepatrolled' => 'Sakrij pregledane stranice iz popisa novih stranica',
'tog-extendwatchlist' => 'Proširi popis praćenih stranica tako da prikaže sve promjene, ne samo najnovije',
-'tog-usenewrc' => 'Koristi poboljšan izgled nedavnih promjena (zahtjeva JavaScripte)',
+'tog-usenewrc' => 'Rabi poboljšan izgled nedavnih promjena (zahtijeva JavaScript)',
'tog-numberheadings' => 'Automatski označi naslove brojevima',
'tog-showtoolbar' => 'Prikaži traku s alatima za uređivanje',
'tog-editondblclick' => 'Dvoklik otvara uređivanje stranice (JavaScript)',
'tog-showtoc' => 'U člancima s više od tri odlomka prikaži tablicu sadržaja.',
'tog-rememberpassword' => 'Zapamti moju lozinku u ovom pregledniku (najduže $1 {{PLURAL:$1|dan|dana|dana}})',
'tog-watchcreations' => 'Dodaj članke koje kreiram na moj popis praćenja',
-'tog-watchdefault' => 'Dodaj sve nove i izmijenjene stranice u popis praćenja',
-'tog-watchmoves' => 'Dodaj sve stranice koje premjestim na popis praćenja',
-'tog-watchdeletion' => 'Dodaj sve stranice koje izbrišem na popis praćenja',
+'tog-watchdefault' => 'Dodaj svaku stranicu koju uredim na moj popis praćenja',
+'tog-watchmoves' => 'Dodaj stranice i datoteke koje premjestim na moj popis praćenja',
+'tog-watchdeletion' => 'Dodaj stranice i datoteke koje izbrišem na popis praćenja',
'tog-minordefault' => 'Normalno označavaj sve moje izmjene kao manje',
'tog-previewontop' => 'Prikaži kako će stranica izgledati iznad okvira za uređivanje',
'tog-previewonfirst' => 'Prikaži kako će stranica izgledati čim otvorim uređivanje',
'tog-nocache' => 'Isključi međuspremnik (cache) stranica u pregledniku',
'tog-enotifwatchlistpages' => 'Pošalji mi e-mail kod izmjene stranice u popisu praćenja',
'tog-enotifusertalkpages' => 'Pošalji mi e-mail kod izmjene moje stranice za razgovor',
-'tog-enotifminoredits' => 'Pošalji mi e-mail i kod manjih izmjena',
+'tog-enotifminoredits' => 'Pošalji mi e-mail i kod manjih izmjena stranice',
'tog-enotifrevealaddr' => 'Prikaži moju e-mail adresu u obavijestima o izmjeni',
'tog-shownumberswatching' => 'Prikaži broj suradnika koji prate stranicu (u nedavnim izmjenama, popisu praćenja i samim člancima)',
'tog-oldsig' => 'Pregled postojećeg potpisa:',
'userinvalidcssjstitle' => "'''Upozorenje:''' Nema sučelja pod imenom \"\$1\". Ne zaboravite da imena stranica s .css and .js kodom počinju malim slovom, npr. {{ns:user}}:Mate/vector.css, a ne {{ns:user}}:Mate/Vector.css.",
'updated' => '(Ažurirano)',
'note' => "'''Napomena:'''",
-'previewnote' => "'''Ne zaboravite da je ovo samo pregled kako će stranica izgledati i da stranica još nije snimljena!'''",
+'previewnote' => "'''Ne zaboravite da je ovo samo pregled kako će stranica izgledati. Vaše uređivanje još nije snimljeno!'''",
'continue-editing' => 'Nastavi uređivati',
'previewconflict' => 'Ovaj pregled odražava stanje u gornjem polju za unos koje će biti sačuvano
ako pritisnete "Sačuvaj stranicu".',
# Suppression log
'suppressionlog' => 'Evidencije sakrivanja',
'suppressionlogtext' => 'Slijedi popis brisanja i blokiranja koji uključuje sadržaj skriven za administratore.
-Vidi [[Special:BlockList|Popis blokiranih IP adresa]] za popis trenutačno aktivnih blokiranih adresa.',
+Vidi [[Special:BlockList|Popis blokiranja]] za popis trenutačno aktivnih blokiranih adresa.',
# History merging
'mergehistory' => 'Spoji povijesti starih izmjena stranice',
'ipb-confirm' => 'Potvrdi blokiranje',
'badipaddress' => 'Nevaljana IP adresa.',
'blockipsuccesssub' => 'Uspješno blokirano',
-'blockipsuccesstext' => 'Suradnik [[Special:Contributions/$1|$1]] je blokiran.<br />
-Pogledaj [[Special:BlockList|popis blokiranih IP adresa]] za pregled.',
+'blockipsuccesstext' => '{{GENDER:$1|Suradnik|Suradnica}} [[Special:Contributions/$1|$1]] je {{GENDER:$1|blokiran|blokirana}}.<br />
+Pogledajte [[Special:BlockList|popis blokiranja]] za pregled blokiranih suradnika.',
'ipb-blockingself' => 'Blokirat ćete se! Jeste li sigurni da to želite?',
'ipb-confirmhideuser' => 'Upravo ćete blokirati suradnika koji ima mogućnost "sakrij suradnika" omogućenu. To će sakriti suradničko ime na svim popisima i evidencijama. Jeste li sigurni da želite to učiniti?',
'ipb-edit-dropdown' => 'Uredi razloge blokiranja',
Zapisnik skrivanja je prikazan ispod kao napomena:',
'blocklogentry' => 'Blokiran je "[[$1]]" na rok $2 $3',
'reblock-logentry' => 'promijenjene postavke blokiranja za [[$1]] na rok od $2 $3',
-'blocklogtext' => 'Ovo je evidencija blokiranja i deblokiranja. Na popisu nema automatski blokiranih IP adresa. Za popis trenutačnih zabrana i blokiranja vidi [[Special:BlockList|popis IP blokiranja]].',
+'blocklogtext' => 'Ovo je evidencija blokiranja i deblokiranja.
+Na popisu nema automatski blokiranih IP adresa.
+Za popis trenutačnih zabrana i blokiranja vidi [[Special:BlockList|popis blokiranja]].',
'unblocklogentry' => 'Deblokiran "$1"',
'block-log-flags-anononly' => 'samo za neprijavljene suradnike',
'block-log-flags-nocreate' => 'otvaranje novih suradničkih imena nije moguće',
'api-error-uploaddisabled' => 'Postavljanje datoteka je onemogućeno na ovom wikiprojektu.',
'api-error-verification-error' => 'Ova datoteka je možda oštećena ili ima pogrešan nastavak.',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|je nedopušteni tip datoteke|su nedopušteni tipovi datoteke}}. Dopušteni {{PLURAL:$3|tip datoteke je|tipovi datoteke su}} $2.',
);
'youhavenewmessages' => 'Maš $1 ($2).',
'newmessageslink' => 'nowe powěsće',
'newmessagesdifflink' => 'poslednja změna',
+'youhavenewmessagesfromusers' => 'Maš $1 wot {{PLURAL:$3|druheho wužiwarja|$3 wužiwarjow|$3 wužiwarjow|$3 wužiwarjow}} ($2).',
+'youhavenewmessagesmanyusers' => 'Maš $1 wot wjele wužiwarjow ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|nowa powěsć|nowej powěsći|nowe powěsće|nowe powěsće}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|poslednja změna|poslednjej změnje|poslednje změny|poslednje změny}}',
'youhavenewmessagesmulti' => 'Maš nowe powěsće: $1',
'editsection' => 'wobdźěłać',
'editold' => 'wobdźěłać',
'remembermypassword' => 'Na tutym ličaku přizjewjeny wostać (za maksimalnje $1 {{PLURAL:$1|dźeń|dnjej|dny|dnjow}})',
'securelogin-stick-https' => 'Po přizjewjenju z HTTPS zwjazany wostać',
'yourdomainname' => 'Twoja domejna:',
+'password-change-forbidden' => 'Njemóžeš hesła na tutym wikiju změnić.',
'externaldberror' => 'Běše pak eksterny zmylk awtentifikacije datoweje banki, pak njesměš swoje eksterne konto aktualizować.',
'login' => 'Přizjewić',
'nav-login-createaccount' => 'Konto wutworić abo so přizjewić',
'noarticletext' => 'Tuchwilu tuta strona žadyn tekst njewobsahuje. Móžeš [[Special:Search/{{PAGENAME}}|tutón titul strony na druhich stronach pytać]], <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} wotpowědne protokole pytać] abo [{{fullurl:{{FULLPAGENAME}}|action=edit}} tutu stronu wobdźěłać]</span>.',
'noarticletext-nopermission' => 'Tuchwilu žadyn tekst na tutej stronje njeje.
Móžeš [[Special:Search/{{PAGENAME}}|tutón titul strony]] na druhich stronach pytać abo <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pytaj wotpowědne protokole]</span>.',
+'missing-revision' => 'Wersija #$1 strony z mjenom "{{PAGENAME}}" njeeksistuje.
+
+Přičina je zwjetša zestarjeny wotkaz w stawiznach k stronje, kotraž je so zhašała.
+Podrobnosće móžeš w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolu wušmórnjenjow] namakać.',
'userpage-userdoesnotexist' => 'Wužiwarske konto „$1“ njeje zregistrowane. Prošu pruwuj, hač chceš tutu stronu woprawdźe wutworić/wobdźěłać.',
'userpage-userdoesnotexist-view' => 'Wužiwarske konto "$1" njeje zregistrowane.',
'blocked-notice-logextract' => 'Tutón wužiwar je tuchwilu zablokowany. Najnowši protokolowy zapisk so deleka jako referenca podawa:',
'expansion-depth-exceeded-warning' => 'Strona je ekspansisku hłubokosć překročił',
'parser-unstrip-loop-warning' => 'Njeskónčna sekla namakana',
'parser-unstrip-recursion-limit' => 'Rekursiska hranica překročena ($1)',
+'converter-manual-rule-error' => 'Zmylk w manuelnym prawidle rěčneho konwertowanja namakany',
# "Undo" feature
'undo-success' => 'Wersija je so wuspěšnje wotstroniła. Prošu přepruwuj deleka w přirunanskim napohledźe, hač twoja změna bu přewzata a klikń potom na „Składować”, zo by změnu składował.',
'editundo' => 'cofnyć',
'diff-multi' => '({{PLURAL:$1|Jedna mjezywersija|$1 mjezywersiji|$1 mjezywersije|$1 mjezywersijow}} wot {{PLURAL:$2|jednoho wužiwarja|$2 wužiwarjow|$2 wužiwarjow|$2 wužiwarjow}} {{PLURAL:$1|njepokazana|njepokazanej|njepokazane|njepokazane}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Jedna mjezywersija|$1 mjezywersiji|$1 mjezywersije|$1 mjezywersijow}} wot wjace hač {{PLURAL:$2|jednoho wužiwarja|$2 wužiwarjow|$2 wužiwarjow|$2 wužiwarjow}} {{PLURAL:$1|njepokazana|njepokazanej|njepokazane|njepokazane}})',
+'difference-missing-revision' => '{{PLURAL:$2|Jedna wersija|$2 wersiji|$2 wersije|$2 wersijow}} tutoho rozdźěla ($1) {{PLURAL:$2|njeje so namakała|njejstej so namakałoj|njejsu namakali|njeje so namakało}}.
+
+Přičina je zwjetša zestarjeny diferencny wotkaz k stronje, kotraž je so zhašała.
+Podrobnosće móžeš w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolu wušmórnjenjow] namakać.',
# Search results
'searchresults' => 'Pytanske wuslědki',
'right-writeapi' => 'writeAPI wužiwać',
'right-delete' => 'Strony zhašeć',
'right-bigdelete' => 'Strony z dołhimi stawiznami zničić',
+'right-deletelogentry' => 'Jednotliwe protokolowe zapiski zhašeć a wobnowić',
'right-deleterevision' => 'Jednotliwe wersije wušmórnyć a wobnowić',
'right-deletedhistory' => 'Wušmórnjene zapiski stawiznow bjez přisłušneho teksta wobhladać',
'right-deletedtext' => 'Wušmórnjeny tekst a změny mjez wušmórnjenymi wersijemi sej wobhladać',
'lockmanager-fail-releaselock' => 'Zawěra za "$1" njeda so dopušćić.',
'lockmanager-fail-db-bucket' => 'W zběrniku $1 njeda so dosć zawrjenskich datowych bankow kontaktować',
'lockmanager-fail-db-release' => 'Zawěry na datowu banku $1 njedadźa so dopušćić',
+'lockmanager-fail-svr-acquire' => 'Zawěry na serwer $1 njehodźa so wotwołać.',
'lockmanager-fail-svr-release' => 'Zawěry na serwer $1 njedadźa so dopušćić',
# ZipDirectoryReader
'disambiguations' => 'Strony, kotrež na strony wjacezmyslnosće wotkazuja',
'disambiguationspage' => 'Template:Wjacezmyslnosć',
-'disambiguations-text' => "Slědowace strony na '''rozjasnjenje wjacezmyslnosće''' wotkazuja. Měli město toho na poprawnu stronu wotkazać.<br />Strona so jako rozjasnjenje wjacezmyslnosće zarjaduje, jeli předłohu wužiwa, na kotruž so wot [[MediaWiki:Disambiguationspage]] wotkazuje.",
+'disambiguations-text' => "Slědowace strony wobsahuja znajmjeńša jedyn wotkaz k stronje '''rozjasnjenja wjacezmyslnosće'''. Měli město toho na poprawnu stronu wotkazać.<br />Maja stronu za stronu rozjasnjenja wjacezmyslnosće, jeli předłohu wužiwa, na kotruž so wot [[MediaWiki:Disambiguationspage]] wotkazuje.",
'doubleredirects' => 'Dwójne daleposrědkowanja',
'doubleredirectstext' => 'Tuta strona nalistuje strony, kotrež k druhim daleposrědkowanskim stronam dale posrědkuja.
'rollback' => 'Změny cofnyć',
'rollback_short' => 'Cofnyć',
'rollbacklink' => 'Cofnyć',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|změnu|změnje|změny|změnow}} cofnyć',
+'rollbacklinkcount-morethan' => 'přez $1 {{PLURAL:$1|změnu|změnje|změny|změnow}} cofnyć',
'rollbackfailed' => 'Cofnjenje njeporadźiło',
'cantrollback' => 'Njemóžno změnu cofnyć; strona nima druhich awtorow.',
'alreadyrolled' => 'Njemóžno poslednu změnu [[:$1]] přez wužiwarja [[User:$2|$2]] ([[User talk:$2|Diskusija]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) cofnyć; něchtó druhi je stronu wobdźěłał abo změnu hižo cofnył.
* <span class="mw-specialpagecached">Specialne strony z pufrowaka (móža zestarjene być).</span>',
'specialpages-group-maintenance' => 'Hladanske lisćiny',
'specialpages-group-other' => 'Druhe specialne strony',
-'specialpages-group-login' => 'Přizjewjenje',
+'specialpages-group-login' => 'Přizjewić/Konto załožić',
'specialpages-group-changes' => 'Poslednje změny a protokole',
'specialpages-group-media' => 'Medije',
'specialpages-group-users' => 'Wužiwarjo a prawa',
'duration-millennia' => '$1 {{PLURAL:$1|lěttysac|lěttysacaj|lěttysacy|lěttysacow}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Zawěry na serwer $1 njehodźa so wotwołać.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|dowoleny datajowy typ njeje|dowolenej datajowej typaj njejstej|dowolene datajowe typy njejsu|dowolene datajowe typy njejsu}}. {{PLURAL:$3|Dowoleny datajowy typ je|Dowolenej datajowej typaj stej|Dowolene datajowe typy su|Dowolene datajowe typy su}} $2.',
);
'hidden-category-category' => 'Rejtett kategóriák',
'category-subcat-count' => "''{{PLURAL:$2|Ennek a kategóriának csak egyetlen alkategóriája van.|Ez a kategória az alábbi {{PLURAL:$1|alkategóriával|$1 alkategóriával}} rendelkezik (összesen $2 alkategóriája van).}}''",
'category-subcat-count-limited' => 'Ebben a kategóriában {{PLURAL:$1|egy|$1}} alkategória található.',
-'category-article-count' => '{{PLURAL:$2|Csak a következő lap található ebben a kategóriában:|Az összesen $2 lapból a következő $1-t listázza ez a kategóriaoldal, a többi a további oldalakon található.}}',
+'category-article-count' => '{{PLURAL:$2|A kategóriában csak a következő lap található.|A következő $1 lap található a kategóriában, összesen $2 lapból.}}',
'category-article-count-limited' => 'Ebben a kategóriában a következő {{PLURAL:$1|lap|$1 lap}} található:',
'category-file-count' => '{{PLURAL:$2|Csak a következő fájl található ebben a kategóriában.|Az összesen $2 fájlból a következő $1-t listázza ez a kategórialap, a többi a további oldalakon található.}}',
'category-file-count-limited' => 'Ebben a kategóriában {{PLURAL:$1|egy|$1}} fájl található.',
'youhavenewmessages' => 'Új üzenet vár $1! (Az üzenetet $2.)',
'newmessageslink' => 'a vitalapodon',
'newmessagesdifflink' => 'külön is megtekintheted',
+'youhavenewmessagesmanyusers' => '$1ed van több szerkesztőtől ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|egy|$1}} új üzenet',
+'newmessagesdifflinkplural' => 'utolsó {{PLURAL:$1|egy|$1}} változtatás',
'youhavenewmessagesmulti' => 'Új üzenetet vár a(z) $1 wikin',
'editsection' => 'szerkesztés',
'editold' => 'szerkesztés',
'ns-specialprotected' => 'A speciális lapok nem szerkeszthetőek.',
'titleprotected' => "Ilyen címmel nem lehet szócikket készíteni, [[User:$1|$1]] letiltotta.
A blokkolás oka: „''$2''”.",
+'filereadonlyerror' => 'A(z) "$1" fájl nem módosítható, mert a(z) "$2" fájltároló csak olvasható módban üzemel.
+
+A lezárást végrehajtó rendszergazda az alábbi indoklást adta meg: "$3".',
+'invalidtitle-knownnamespace' => 'Érvénytelen cím "$2" névtérrel és "$3" szöveggel',
+'invalidtitle-unknownnamespace' => 'Érvénytelen cím az ismeretlen $1 névtérszámmal és "$2" szöveggel',
+'exception-nologin' => 'Nem vagy bejelentkezve.',
+'exception-nologin-text' => 'Ezen lap vagy művelet használatához be kell jelenetkezned erre a wikire.',
# Virus scanner
'virus-badscanner' => "Hibás beállítás: ismeretlen víruskereső: ''$1''",
'remembermypassword' => 'Emlékezzen rám ezen a számítógépen (legfeljebb $1 napig)',
'securelogin-stick-https' => 'Kapcsolódás HTTPS-en keresztül bejelentkezés után is',
'yourdomainname' => 'A domainneved:',
+'password-change-forbidden' => 'Nem módosíthatod a jelszót ezen a wikin.',
'externaldberror' => 'Hiba történt a külső adatbázis hitelesítése közben, vagy nem vagy jogosult a külső fiókod frissítésére.',
'login' => 'Bejelentkezés',
'nav-login-createaccount' => 'Bejelentkezés / fiók létrehozása',
vagy [{{fullurl:{{FULLPAGENAME}}|action=edit}} szerkesztheted a lapot].</span>',
'noarticletext-nopermission' => 'Ez a lap jelenleg nem tartalmaz szöveget.
[[Special:Search/{{PAGENAME}}|Rákereshetsz a lap címére]] más lapok tartalmában, vagy <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} megtekintheted a kapcsolódó naplófájlokat]</span>.',
+'missing-revision' => 'A(z) "{{PAGENAME}}" nevű oldal #$1 változata nem létezik.
+
+Ezt általában egy elavult, törölt oldalra mutató laptörténeti hivatkozás használata okozza. Részletek a [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} törlési naplóban] találhatóak.',
'userpage-userdoesnotexist' => 'Nincs „<nowiki>$1</nowiki>” nevű regisztrált felhasználónk.
Nézd meg, hogy valóban ezt a lapot szeretnéd-e létrehozni vagy szerkeszteni.',
'userpage-userdoesnotexist-view' => 'Nincs regisztrálva „$1” szerkesztői azonosító.',
'parser-template-loop-warning' => 'Végtelen ciklus a következő sablonban: [[$1]]',
'parser-template-recursion-depth-warning' => 'A sablon rekurzív beillesztésének mélysége átlépte a határérékét ($1)',
'language-converter-depth-warning' => 'A nyelvátalakító rekurzióinak száma túllépve ($1)',
+'expansion-depth-exceeded-category' => 'Lapok, melyeken a sablonok kibontása meghaladja a megengedett szintet',
# "Undo" feature
'undo-success' => 'A szerkesztés visszavonható. Kérlek ellenőrizd alább a változásokat, hogy valóban ezt szeretnéd-e tenni, majd kattints a lap mentése gombra a visszavonás véglegesítéséhez.',
A napló típusának, a szerkesztő nevének (kis- és nagybetűérzékeny), vagy az érintett lap kiválasztásával (ez is kis- és nagybetűérzékeny) szűkítheted a találatok listáját.',
'logempty' => 'Nincs illeszkedő naplóbejegyzés.',
'log-title-wildcard' => 'Így kezdődő címek keresése',
+'showhideselectedlogentries' => 'Kijelölt napló bejegyzések megjelenítése/elrejtése',
# Special:AllPages
'allpages' => 'Az összes lap listája',
'api-error-empty-file' => 'Az általad elküldött fájl üres volt.',
'api-error-emptypage' => 'Új, üres lap létrehozása nem engedélyezett.',
'api-error-fetchfileerror' => 'Belső hiba: valami baj történt a fájl beolvasása közben.',
+'api-error-fileexists-forbidden' => 'Már létezik „$1” nevű fájl, és nem lehet felülírni.',
+'api-error-fileexists-shared-forbidden' => 'Már létezik „$1” nevű fájl a megosztott fájlok között, és nem lehet felülírni.',
'api-error-file-too-large' => 'Az általad elküldött fájl túl nagy.',
'api-error-filename-tooshort' => 'A fájlnév túl rövid.',
'api-error-filetype-banned' => 'Tiltott fájltípus.',
'duration-centuries' => '{{PLURAL:$1|egy|$1}} évszázad',
'duration-millennia' => '{{PLURAL:$1|egy|$1}} évezred',
+# Unknown messages
+'api-error-filetype-banned-type' => '!A következő {{PLURAL:$4|fájltípus nem engedélyezett|fájltípusok nem engedélyezettek}}: $1. Engedélyezett {{PLURAL:$3|típus|típusok}}: $2.',
);
'tog-enotifminoredits' => 'էլ-փոստով տեղեկացնել էջերի նաև չնչին խմբագրումների մասին',
'tog-enotifrevealaddr' => 'Ցույց տալ իմ էլ-փոստի հասցեն ծանուցման նամակներում',
'tog-shownumberswatching' => 'Ցույց տալ էջ հսկող մասնակիցների թիվը',
-'tog-oldsig' => 'Առկա ստորագրության նախադիտում.',
+'tog-oldsig' => 'Ներկայիս ստորագրությունն է․',
'tog-fancysig' => 'Ստորագրությունը վիքիտեքստի տեսքով (առանց ավտոմատ հղման)',
'tog-externaleditor' => 'Օգտագործել արտաքին խմբագրիչ ըստ լռության (պահանջում է հատուկ նախընտրություններ ձեր համակարգչում)',
'tog-externaldiff' => 'Օգտագործել տարբերակների համեմատման արտաքին ծրագիր ըստ լռության (պահանջում է հատուկ նախընտրություններ ձեր համակարգչում)',
'tog-showjumplinks' => 'Միացնել «անցնել դեպի» օգնական հղումները',
'tog-uselivepreview' => 'Օգտագործել ուղիղ նախադիտում (JavaScript) (Փորձնական)',
-'tog-forceeditsummary' => 'Նախազգուշացնել փոփոխությունների ամփոփումը դատարկ թողնելու մասին',
+'tog-forceeditsummary' => 'Նախազգուշացնել խմբագրման ամփոփումը դատարկ թողնելու դեպքում',
'tog-watchlisthideown' => 'Թաքցնել իմ խմբագրումները հսկացանկից',
'tog-watchlisthidebots' => 'Թաքցնել բոտերի խմբագրումները հսկացանկից',
'tog-watchlisthideminor' => 'Թաքցնել չնչին խմբագրումները հսկացանկից',
Խնդրում ենք կրկին ներկայանալ համակարգին այն ստանալուց հետո։',
'blocked-mailpassword' => 'Ձեր IP հասցեից խմբագրումները արգելափակված են, և հետևաբար արգելված է նաև գաղտնաբառի վերականգնումը՝ հետագա չարաշահումների կանխման նպատակով։',
-'eauthentsent' => 'Առաջարկված էլ-հասցեին ուղարկվել է վավերացման նամակ։
-Õ\84Õ«Õ¶Õ¹Ö\87 Õ¸Ö\80Ö\87Õ§ Õ¡ÕµÕ¬ Õ¸Ö\82Õ²Õ¥Ö\80Õ»Õ¶Õ¥Ö\80 Õ¯Õ¸Ö\82Õ²Õ¡Ö\80Õ¯Õ¾Õ¥Õ¶ Õ¡ÕµÕ¤ Õ°Õ¡Õ½Ö\81Õ¥Õ«Õ¶, Õ±Õ¥Õ¦ Õ¡Õ¶Õ°Ö\80Õ¡ÕªÕ¥Õ·Õ¿ Õ§ Õ°Õ¥Õ¿Ö\87Õ¥Õ¬ Õ¶Õ¡Õ´Õ¡Õ¯Õ¸Ö\82Õ´ Õ¶Õ¯Õ¡Ö\80Õ¡Õ£Ö\80Õ¾Õ¡Õ® Õ£Õ¸Ö\80Õ®Õ¸Õ²Õ¸Ö\82Õ©ÕµÕ¸Ö\82Õ¶Õ¶Õ¥Ö\80Õ«Õ¶Õ\9d Õ°Õ¡Õ·Õ¾Õ« Õ±Õ¥Õ¦ ÕºÕ¡Õ¿Õ¯Õ¡Õ¶Õ¥Õ¬Õ¸Ö\82 Ö\83Õ¡Õ½Õ¿Õ¨ Õ¾Õ¡Õ¾Õ¥Ö\80Õ¡Ö\81Õ¶Õ¥Õ¬Õ¸Ö\82 Õ°Õ¡Õ´Õ¡Ö\80։',
+'eauthentsent' => 'Նոր էլ-հասցեին ուղարկվել է վավերացման նամակ։
+Õ\80Õ¥Õ¿Ö\87Õ¥Õ\9bÖ\84 Õ¶Õ¡Õ´Õ¡Õ¯Õ« Ö\81Õ¸Ö\82Ö\81Õ¸Ö\82Õ´Õ¶Õ¥Ö\80Õ«Õ¶Õ\9d Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬Õ¸Ö\82 Õ°Õ¡Õ´Õ¡Ö\80, Õ¸Ö\80 Õ¡ÕµÕ¤ Õ°Õ¡Õ½Ö\81Õ¥Õ¶ Õ±Õ¥Õ¦ Õ§ ÕºÕ¡Õ¿Õ¯Õ¡Õ¶Õ¸Ö\82Õ´Ö\89 Õ\84Õ«Õ¶Õ¹ Õ¡ÕµÕ¤ Õ¶Õ¸Ö\80 Õ°Õ¡Õ½Ö\81Õ¥Õ«Õ¶ Õ¡ÕµÕ¬ Õ¶Õ¡Õ´Õ¡Õ¯Õ¶Õ¥Ö\80 Õ¹Õ¥Õ¶ Õ¯Õ¡Ö\80Õ¸Õ² Õ¸Ö\82Õ²Õ¡Ö\80Õ¯Õ¾Õ¥Õ¬։',
'throttled-mailpassword' => 'Գաղտնաբառի հիշեցման ուղերձ արդեն ուղարկվել է վերջին {{PLURAL:$1|ժամվա|$1 ժամվա}} ընթացքում։ Չարաշահման կանխարգելման նպատակով թույլատրվում է միայն մեկ գաղտնաբառի հիշեցում ամեն {{PLURAL:$1|ժամվա|$1 ժամվա}} ընթացքում։',
'mailerror' => 'Փոստի ուղարկման սխալ. $1',
'acct_creation_throttle_hit' => 'Վերջին օրվա ընթացքում ձեր IP-հասցեից ստեղծվել է {{PLURAL:$1|1 մասնակցի հաշվիվ|$1 մասնակցի հաշվիվ}}, ինչը այս ժամանակաշրջանում առավելագույն թույլատրելի քանակն է։
'upload_directory_read_only' => 'Վեբ-սերվերը չունի գրելու իրավունք բեռնումների թղթապանակում ($1)։',
'uploaderror' => 'Բեռնման սխալ',
'uploadtext' => "Նիշք բեռնելու համար օգտագործեք ստորև բերված ձևը։
-Նախկինում բեռնված նիշքերը դիտելու կամ որոնելու համար այցելեք [[Սպասարկող:Պատկերներիցանկը|բեռնված նիշքերի ցանկը]]։ Բեռնումները գրանցվում են [[Սպասարկող:Տեղեկամատյան/upload|բեռնման տեղեկամատյանում]], ջնջումները՝ [[Սպասարկող:Տեղեկամատյան/delete|ջնջման տեղեկամատյանում]]։
+Նախկինում բեռնված նիշքերը դիտելու կամ որոնելու համար այցելեք [[Սպասարկող:Պատկերներիցանկը|բեռնված նիշքերի ցանկ]]։ Բեռնումները գրանցվում են [[Սպասարկող:Տեղեկամատյան/upload|բեռնման տեղեկամատյանում]], ջնջումները՝ [[Սպասարկող:Տեղեկամատյան/delete|ջնջման տեղեկամատյանում]]։
Այս նիշքը որևէ էջում ընդգրկելու համար օգտագործեք հետևյալ հղման ձևերը.
* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Նիշք.jpg]]</nowiki>''' - ամբողջական չափի պատկեր տեղադրելու համար,
'sp-contributions-deleted' => 'Մասնակցի ջնջված ներդրում',
'sp-contributions-uploads' => 'Բեռնումներ',
'sp-contributions-logs' => 'տեղեկամատյաններ',
-'sp-contributions-talk' => 'Քննարկում',
-'sp-contributions-userrights' => 'Õ\84ասնակիցների իրավունքների կառավարում',
+'sp-contributions-talk' => 'քննարկում',
+'sp-contributions-userrights' => 'Õ´ասնակիցների իրավունքների կառավարում',
'sp-contributions-search' => 'Որոնել ներդրումները',
'sp-contributions-username' => 'IP-հասե կամ մասնակցի անուն.',
'sp-contributions-toponly' => 'Ցույց տալ միայն այն խմբագրումները, որոնք վերջին փոփոխություն են',
'watchlisttools-edit' => 'Դիտել և խմբագրել հսկացանկը',
'watchlisttools-raw' => 'Խմբագրել հում հսկացանկը',
+# Signatures
+'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|քննարկում]])',
+
# Special:Version
'version' => 'ՄեդիաՎիքի տարբերակը',
'youhavenewmessages' => 'Tu ha $1 ($2).',
'newmessageslink' => 'nove messages',
'newmessagesdifflink' => 'ultime modification',
+'youhavenewmessagesfromusers' => 'Tu ha $1 de {{PLURAL:$3|un altere usator|$3 usatores}} ($2).',
+'youhavenewmessagesmanyusers' => 'Tu ha $1 de multe usatores ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un nove message|$1 nove messages}}',
+'newmessagesdifflinkplural' => 'ultime {{PLURAL:$1|modification|modificationes}}',
'youhavenewmessagesmulti' => 'Tu ha nove messages in $1',
'editsection' => 'modificar',
'editold' => 'modificar',
'remembermypassword' => 'Memorar mi contrasigno in iste navigator (pro un maximo de $1 {{PLURAL:$1|die|dies}})',
'securelogin-stick-https' => 'Remaner connectite via HTTPS post apertura de session',
'yourdomainname' => 'Tu dominio:',
+'password-change-forbidden' => 'Non es possibile cambiar le contrasigno in iste wiki.',
'externaldberror' => 'O il occurreva un error in le base de datos de authentication, o tu non ha le autorisation de actualisar tu conto externe.',
'login' => 'Aperir session',
'nav-login-createaccount' => 'Aperir session / crear conto',
'noarticletext-nopermission' => 'Al momento il non ha texto in iste pagina.
Tu pote [[Special:Search/{{PAGENAME}}|cercar le titulo de iste pagina]] in altere paginas,
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cercar in le registros pertinente].</span>',
+'missing-revision' => 'Le version №$1 del pagina nominate "{{PAGENAME}}" non existe.
+
+Isto es generalmente causate per sequer un ligamine de historia obsolete a un pagina que ha essite delite.
+Detalios se trova in le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de deletiones].',
'userpage-userdoesnotexist' => 'Le conto de usator "<nowiki>$1</nowiki>" non es registrate. Per favor verifica que tu vole crear/modificar iste pagina.',
'userpage-userdoesnotexist-view' => 'Le conto de usator "$1" non es registrate.',
'blocked-notice-logextract' => 'Iste usator es actualmente blocate.
'expansion-depth-exceeded-warning' => 'Le profunditate de expansion in iste pagina excede le limite',
'parser-unstrip-loop-warning' => 'Bucla de "unstrip" detegite',
'parser-unstrip-recursion-limit' => 'Limite de recursion de "unstrip" excedite ($1)',
+'converter-manual-rule-error' => 'Error detegite in le regula manual de conversion de lingua',
# "Undo" feature
'undo-success' => 'Le modification pote esser disfacite.
'editundo' => 'disfacer',
'diff-multi' => '({{PLURAL:$1|Un version intermedie|$1 versiones intermedie}} facite per {{PLURAL:$2|un usator|$2 usatores}} non es monstrate)',
'diff-multi-manyusers' => '({{PLURAL:$1|Un version intermedie|$1 versiones intermedie}} facite per plus de $2 {{PLURAL:$2|usator|usatores}} non es monstrate)',
+'difference-missing-revision' => '{{PLURAL:$2|Un version|$2 versiones}} de iste differentia ($1) non ha essite trovate.
+
+Isto es generalmente causate per sequer un ligamine de diff obsolete a un pagina que ha essite delite.
+Detalios se trova in le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de deletiones].',
# Search results
'searchresults' => 'Resultatos del recerca',
'right-writeapi' => 'Uso del API pro modificar le wiki',
'right-delete' => 'Deler paginas',
'right-bigdelete' => 'Deler paginas con historias longe',
+'right-deletelogentry' => 'Deler e restaurar specific entratas del registro',
'right-deleterevision' => 'Deler e restaurar versiones specific de paginas',
'right-deletedhistory' => 'Vider entratas de historia delite, sin lor texto associate',
'right-deletedtext' => 'Vider texto delite e differentias inter versiones delite',
'lockmanager-fail-releaselock' => 'Non poteva liberar le file de serratura pro "$1".',
'lockmanager-fail-db-bucket' => 'Non poteva contactar sufficiente bases de datos de serratura in situla $1.',
'lockmanager-fail-db-release' => 'Non poteva liberar le serraturas sur le base de datos $1.',
+'lockmanager-fail-svr-acquire' => 'Non poteva acquirer le serraturas sur le servitor $1.',
'lockmanager-fail-svr-release' => 'Non poteva liberar le serraturas sur le servitor $1.',
# ZipDirectoryReader
'disambiguations' => 'Paginas con ligamines a paginas de disambiguation',
'disambiguationspage' => 'Template:Disambiguation',
-'disambiguations-text' => "Le sequente paginas ha ligamines a un '''pagina de disambiguation'''.
-Istes deberea esser reimplaciate con ligamines al topicos appropriate.<br />
-Un pagina se tracta como pagina de disambiguation si illo usa un patrono al qual [[MediaWiki:Disambiguationspage]] ha un ligamine.",
+'disambiguations-text' => "Le sequente paginas contine al minus un ligamine a un '''pagina de disambiguation'''.
+Istes debe forsan ligar directemente al articulo sur le thema in question.<br />
+Un pagina se tracta como pagina de disambiguation si illo usa un patrono que es ligate ab [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redirectiones duple',
'doubleredirectstext' => 'Iste pagina lista paginas de redirection verso altere paginas de redirection.
'rollback' => 'Revocar modificationes',
'rollback_short' => 'Revocar',
'rollbacklink' => 'revocar',
+'rollbacklinkcount' => 'revocar $1 {{PLURAL:$1|modification|modificationes}}',
+'rollbacklinkcount-morethan' => 'revocar plus de $1 {{PLURAL:$1|modification|modificationes}}',
'rollbackfailed' => 'Revocation fallite',
'cantrollback' => 'Impossibile revocar le modification;
le ultime contributor es le sol autor de iste pagina.',
'talkexists' => "'''Le pagina mesme ha essite renominate con successo, ma su pagina de discussion non poteva esser renominate proque il ja existe un con le nove titulo.
Per favor fusiona los manualmente.'''",
'movedto' => 'renominate a',
-'movetalk' => 'Renominar etiam su pagina de discussion',
+'movetalk' => 'Renominar le pagina de discussion associate',
'move-subpages' => 'Renominar le subpaginas (usque a $1)',
'move-talk-subpages' => 'Renominar le subpaginas del pagina de discussion (usque a $1)',
'movepage-page-exists' => 'Le pagina $1 existe ja e non pote esser automaticamente superscribite.',
'import-upload' => 'Incargar datos XML',
'import-token-mismatch' => 'Perdita del datos del session. Per favor reprova.',
'import-invalid-interwiki' => 'Non pote importar ab le wiki specificate.',
-'import-error-edit' => 'Le pagina "$1" non es importate proque tu non ha le permission de modificar lo.',
-'import-error-create' => 'Le pagina "$1" non es importate proque tu non ha le permission de crear lo.',
+'import-error-edit' => 'Le pagina "$1" non es importate perque tu non ha le permission de modificar lo.',
+'import-error-create' => 'Le pagina "$1" non es importate perque tu non ha le permission de crear lo.',
'import-error-interwiki' => 'Le pagina "$1" non es importate perque su nomine es reservate pro ligation externe (interwiki).',
'import-error-special' => 'Le pagina "$1" non es importate perque illo pertine a un spatio de nomines special que non permitte paginas.',
'import-error-invalid' => 'Le pagina "$1" non es importate perque su nomine es invalide.',
'duration-millennia' => '$1 {{PLURAL:$1|millennio|millennios}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Non poteva acquirer le serraturas sur le servitor $1.',
+'api-error-filetype-banned-type' => '$1 non es {{PLURAL:$4|un typo|typos}} de file permittite. Le {{PLURAL:$3|typo|typos}} de file permittite es $2.',
);
Administrator yang terkunci menawarkan penjelasan ini: " $3 ".',
'invalidtitle-knownnamespace' => 'Judul yang tidak sah dengan ruangnama "$2" dan teks "$3"',
'invalidtitle-unknownnamespace' => 'Judul yang tidak sah dengan nomor ruang nama tidak diketahui $1 dan teks "$2"',
+'exception-nologin' => 'Belum masuk log',
+'exception-nologin-text' => 'Halaman atau tindakan ini mengharuskan Anda masuk log di wiki ini.',
# Virus scanner
'virus-badscanner' => "Kesalahan konfigurasi: pemindai virus tidak dikenal: ''$1''",
'remembermypassword' => 'Ingat kata sandi saya di komputer ini (selama $1 {{PLURAL:$1|hari|hari}})',
'securelogin-stick-https' => 'Tetap terhubung ke HTTPS setelah masuk',
'yourdomainname' => 'Domain Anda:',
+'password-change-forbidden' => 'Anda tidak dapat mengubah kata sandi pada wiki ini.',
'externaldberror' => 'Telah terjadi kesalahan otentikasi basis data eksternal atau Anda tidak diizinkan melakukan kemaskini terhadap akun eksternal Anda.',
'login' => 'Masuk log',
'nav-login-createaccount' => 'Masuk log / buat akun',
Anda dapat [[Special:Search/{{PAGENAME}}|melakukan pencarian untuk judul halaman ini]] di halaman-halaman lain, <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log terkait], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} menyunting halaman ini]</span>.',
'noarticletext-nopermission' => 'Saat ini tidak ada teks di halaman ini.
Anda dapat [[Special:Search/{{PAGENAME}}|melakukan pencarian untuk judul halaman ini]] di halaman-halaman lain, <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log terkait], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} menyunting halaman ini]</span>.',
+'missing-revision' => 'Revisi #$1 halaman berjudul "{{PAGENAME}}" tidak eksks.
+
+Hal ini biasanya disebabkan oleh tautan versi terdahulu menuju halaman yang sudah dihapus.
+Rinciannya dapat ditemukan di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].',
'userpage-userdoesnotexist' => 'Akun pengguna "<nowiki>$1</nowiki>" tidak terdaftar.',
'userpage-userdoesnotexist-view' => 'Pengguna "$1" tidak terdaftar.',
'blocked-notice-logextract' => 'Pengguna ini sedang diblokir.
'rollback' => 'Kembalikan suntingan',
'rollback_short' => 'Kembalikan',
'rollbacklink' => 'kembalikan',
+'rollbacklinkcount' => 'kembalikan $1 {{PLURAL:$1|suntingan|suntingan}}',
+'rollbacklinkcount-morethan' => 'kembalikan lebih dari $1 {{PLURAL:$1|suntingan|suntingan}}',
'rollbackfailed' => 'Pengembalian gagal dilakukan',
'cantrollback' => 'Tidak dapat mengembalikan suntingan;
kontributor terakhir adalah satu-satunya penulis halaman ini.',
'duration-centuries' => '{{PLURAL:$1||}}$1 abad',
'duration-millennia' => '{{PLURAL:$1||}}$1 milenium',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|adalah ekstensi berkas yang tidak diizinkan|adalah ekstensi berkas yang tidak diizinkan}}. {{PLURAL:$3|Jenis berkas yang diperolehkan adalah|Jenis berkas yang diperolehkan adalah}} $2.',
);
'duration-centuries' => '$1 {{PLURAL:$1|siglo|sig-siglo}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenio|mil-milenio}}',
+# Unknown messages
+'api-error-filetype-banned-type' => 'Ti $1 {{PLURAL:$4|ket saan a mapalubusan a kita ti papeles|ket dagiti saan a mapalubusan a kita ti papeles}}. Ti mapalubusan{{PLURAL:$3|a kita ti papeles ket|kadagiti kita ti papeles ket}} $2.',
);
'tog-editsectiononrightclick' => 'Virkja hlutabreytingu með því að hægrismella á hlutafyrirsagnir (JavaScript)',
'tog-showtoc' => 'Sýna efnisyfirlit (fyrir síður með meira en 3 fyrirsagnir)',
'tog-rememberpassword' => 'Muna innskráninguna mína í þessum vafra (í allt að $1 {{PLURAL:$1|dag|daga}})',
-'tog-watchcreations' => 'Bæta síðum sem ég bý til á vaktlistann minn',
-'tog-watchdefault' => 'Bæta síðum sem ég breyti á vaktlistann minn',
-'tog-watchmoves' => 'Bæta síðum sem ég færi á vaktlistann minn',
-'tog-watchdeletion' => 'Bæta síðum sem ég eyði á vaktlistann minn',
+'tog-watchcreations' => 'Bæta síðum sem ég bý til og skrám sem ég hleð inn á vaktlistann minn',
+'tog-watchdefault' => 'Bæta síðum og skrám sem ég breyti á vaktlistann minn',
+'tog-watchmoves' => 'Bæta síðum og skrám sem ég færi á vaktlistann minn',
+'tog-watchdeletion' => 'Bæta síðum og skrám sem ég eyði á vaktlistann minn',
'tog-minordefault' => 'Merkja allar breytingar sem minniháttar sjálfgefið',
'tog-previewontop' => 'Sýna forskoðun á undan breytingarkassanum',
'tog-previewonfirst' => 'Sýna forskoðun með fyrstu breytingu',
'tog-nocache' => 'Slökkva á flýtiminni vafrans',
-'tog-enotifwatchlistpages' => 'Senda mér tölvupóst þegar síðu á vaktlistanum mínu er breytt',
+'tog-enotifwatchlistpages' => 'Senda mér tölvupóst þegar síðu eða skrá á vaktlistanum mínu er breytt',
'tog-enotifusertalkpages' => 'Senda mér tölvupóst þegar notandaspjallinu mínu er breytt',
'tog-enotifminoredits' => 'Senda mér einnig tölvupóst vegna minniháttar breytinga á síðum',
'tog-enotifrevealaddr' => 'Gefa upp netfang mitt í tilkynningarpóstum',
'filereadonlyerror' => 'Ekki var hægt að breyta skránni "$1" því skráin í skráarsafninu "$2" er engöngu hægt að lesa.
Möppudýrið sem læsti skránni gaf þessa ástæðu: "\'\'$3\'\'".',
+'invalidtitle-knownnamespace' => 'Ógildur titill í nafnrými "$2" og með textann "$3"',
+'invalidtitle-unknownnamespace' => 'Ógildur titill með óþekkt nafnrými númer $1 og texta "$2"',
+'exception-nologin' => 'Óinnskráð(ur)',
+'exception-nologin-text' => 'Þessi síða eða aðgerð krefst þess að þú sért skráður inn á þessum wiki.',
# Virus scanner
'virus-badscanner' => "Slæm stilling: óþekktur veiruskannari: ''$1''",
'edit-no-change' => 'Breyting þín var hunsuð, því engin breyting var á textanum.',
'edit-already-exists' => 'Gat ekki skapað nýja síðu.
Hún er nú þegar til.',
+'defaultmessagetext' => 'Sjálfgefinn skilaboða texti',
# Parser/template warnings
'expensive-parserfunction-warning' => "'''Viðvörun:''' Þessi síða inniheldur of mörg vinnslufrek aðgerðar þáttunar köll.
'prefs-beta' => 'Stillingar á prufustigi',
'prefs-datetime' => 'Tímasnið og tímabelti',
'prefs-labs' => 'Stillingar á tilraunastigi',
+'prefs-user-pages' => 'Notendasíður',
'prefs-personal' => 'Notandaupplýsingar',
'prefs-rc' => 'Nýlegar breytingar',
'prefs-watchlist' => 'Vaktlistinn',
# Groups
'group' => 'Hópur:',
'group-user' => 'Notendur',
-'group-autoconfirmed' => 'Sjálfkrafa staðfesting notenda',
+'group-autoconfirmed' => 'Sjálfkrafa staðfestir notendur',
'group-bot' => 'Vélmenni',
'group-sysop' => 'Stjórnendur',
'group-bureaucrat' => 'Möppudýr',
'group-suppress-member' => '{{GENDER:$1|Umsjón}}',
'grouppage-user' => '{{ns:project}}:Notendur',
-'grouppage-autoconfirmed' => '{{ns:project}}:Sjálfkrafa staðfesting notenda',
+'grouppage-autoconfirmed' => '{{ns:project}}:Sjálfkrafa staðfestir notendur',
'grouppage-bot' => '{{ns:project}}:Vélmenni',
'grouppage-sysop' => '{{ns:project}}:Stjórnendur',
'grouppage-bureaucrat' => '{{ns:project}}:Möppudýr',
'right-writeapi' => 'Nota API skrifun',
'right-delete' => 'Eyða síðum',
'right-bigdelete' => 'Eyða síðum með stórum breytingaskrám',
+'right-deletelogentry' => 'Eyða og endurvekja sérstakar aðgerða færslur',
'right-deleterevision' => 'Eyða og endurvekja sérstaka breytignar á síðum',
'right-deletedhistory' => 'Skoða eyddar færslur úr breytingarskrá, án efnis þeirra',
'right-deletedtext' => 'Sjá eyddan texta og breytingar á milli eyddra útgáfna',
'backend-fail-writetemp' => 'Gat ekki skrifað í tímabundna skrá.',
'backend-fail-closetemp' => 'Mistókst að loka tímabundinni skrá.',
'backend-fail-read' => 'Mistókst að lesa skrá $1.',
-'backend-fail-create' => 'Mistókst að búa til skrá $1.',
-'backend-fail-maxsize' => 'Mistókst að búa til skránna $1 því hún er stærri en {{PLURAL:$2|eitt bæti|$2 bæti}}.',
+'backend-fail-create' => 'Mistókst að skrifa skrá $1.',
+'backend-fail-maxsize' => 'Mistókst að skrifa skránna $1 því hún er stærri en {{PLURAL:$2|eitt bæti|$2 bæti}}.',
'backend-fail-readonly' => 'Gagnabankann "$1" er engöngu hægt að lesa í augnablikinu. Ástæðan sem var gefin er: "\'\'$2\'\'"',
'backend-fail-connect' => 'Mistókst að tengjast gagnabankanum "$1".',
'backend-fail-internal' => 'Óþekkt villa átti sér stað í gagnabankanum "$1".',
Þú getur takmarkað listann með því að velja tegund aðgerðaskráar, notandanafn, eða síðu.',
'logempty' => 'Engin slík aðgerð fannst.',
'log-title-wildcard' => 'Leita að titlum sem byrja á þessum texta',
+'showhideselectedlogentries' => 'Sýna/fela valdar aðgerða færslur',
# Special:AllPages
'allpages' => 'Allar síður',
'allpages-bad-ns' => '{{SITENAME}} hefur ekki nafnrými „$1“.',
'allpages-hide-redirects' => 'Fela tilvísanir',
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Þú ert að skoða útgáfu síðunnar úr skyndiminni, sem getur verið allt að $1 gömul.',
+'cachedspecial-refresh-now' => 'Skoða síðustu',
+
# Special:Categories
'categories' => 'Flokkar',
'categoriespagetext' => 'Eftirfarandi {{PLURAL:$1|flokkur inniheldur|flokkar innihalda}} síður eða skrár.
Sjá einnig [[Special:WantedCategories|eftirsótta flokka]].',
'categoriesfrom' => 'Sýna flokka frá:',
'special-categories-sort-count' => 'raða eftir fjölda',
-'special-categories-sort-abc' => 'raða eftir stafrófinu',
+'special-categories-sort-abc' => 'raða í stafrófsröð',
# Special:DeletedContributions
'deletedcontributions' => 'Eyddar breytingar notanda',
'rollback' => 'Taka aftur breytingar',
'rollback_short' => 'Taka aftur',
'rollbacklink' => 'taka aftur',
+'rollbacklinkcount' => 'taka aftur $1 {{PLURAL:$1|breytingu|breytingar}}',
+'rollbacklinkcount-morethan' => 'taka aftur fleiri en $1 {{PLURAL:$1|breytingu|breytingar}}',
'rollbackfailed' => 'Mistókst að taka aftur',
'cantrollback' => 'Ekki hægt að taka aftur breytingu, síðasti höfundur er eini höfundur þessarar síðu.',
'alreadyrolled' => 'Ekki var hægt að taka síðustu breytingu [[:$1]] eftir [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) til baka;
** Breytingarstríð
** Síða með margar heimsóknir',
'protect-edit-reasonlist' => 'Breyta verndarástæðum',
-'protect-expiry-options' => '1 tím:1 hour,1 dag:1 day,1 viku:1 week,2 vikur:2 weeks,1 mánuð:1 month,3 mánuði:3 months,6 mánuði:6 months,1 ár:1 year,aldrei:infinite',
+'protect-expiry-options' => '1 tími:1 hour,1 dag:1 day,1 viku:1 week,2 vikur:2 weeks,1 mánuð:1 month,3 mánuði:3 months,6 mánuði:6 months,1 ár:1 year,aldrei:infinite',
'restriction-type' => 'Réttindi:',
'restriction-level' => 'Takmarkaði við:',
'minimum-size' => 'Lágmarksstærð',
'whatlinkshere-links' => '← tenglar',
'whatlinkshere-hideredirs' => '$1 tilvísanir',
'whatlinkshere-hidetrans' => '$1 ítengingar',
-'whatlinkshere-hidelinks' => '$1 tenglar',
+'whatlinkshere-hidelinks' => '$1 tengla',
'whatlinkshere-hideimages' => '$1 myndatenglar',
'whatlinkshere-filters' => 'Síur',
'badipaddress' => 'Ógilt vistfang',
'blockipsuccesssub' => 'Bann tókst',
'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] hefur verið bannaður/bönnuð.<br />
-Sjá [[Special:BlockList|bannaðar notendur og vistföng]] fyrir yfirlit yfir núverandi bönn.',
+Sjá [[Special:BlockList|bannaðir notendur og vistföng]] fyrir yfirlit yfir núverandi bönn.',
'ipb-blockingself' => 'Þú ert í þann mund að banna sjálfan þig! Ertu viss um að þú viljir gera það?',
'ipb-confirmhideuser' => 'Þú ert í þann mund að banna notenda sem er falinn. Notendanafn hans mun ekki birtast í listum og aðgerðarskrám. Ertu viss um að þú viljir gera það?',
'ipb-edit-dropdown' => 'Breyta ástæðu fyrir banni',
# Namespace 8 related
'allmessages' => 'Meldingar',
'allmessagesname' => 'Titill',
-'allmessagesdefault' => 'Sjálfgefinn texti',
+'allmessagesdefault' => 'Sjálfgefinn skilaboða texti',
'allmessagescurrent' => 'Núverandi texti',
'allmessagestext' => 'Þetta er listi yfir kerfismeldingar í Melding-nafnrýminu.
Gjörðu svo vel og heimsæktu [//www.mediawiki.org/wiki/Localisation MediaWiki-staðfæringuna] og [//translatewiki.net translatewiki.net] ef þú vilt taka þátt í almennri MediaWiki-staðfæringu.',
'import-invalid-interwiki' => 'Get ekki flutt inn frá þessum wiki.',
'import-error-edit' => 'Síðan "$1" var ekki flutt inn því þú hefur ekki réttindi til að breyta henni.',
'import-error-create' => 'Síðan "$1" var ekki flutt inn því þú hefur ekki réttindi til að stofna hana.',
+'import-error-interwiki' => 'Síðan "$1" var ekki flutt inn því nafn hennar er frátekið fyrir ytri tengla (tungumálatengla).',
+'import-error-special' => 'Síðan "$1" var ekki flutt inn því hún tilheyrir ákveðnu nafnrými sem leyfir ekki síður.',
+'import-error-invalid' => 'Síðan "$1" var ekki flutt inn því nafn hennar er ógilt.',
# Import log
'importlogpage' => 'Innflutningsskrá',
'import-logentry-interwiki-detail' => '$1 {{PLURAL:$1|breyting|breytingar}} frá $2',
# JavaScriptTest
+'javascripttest' => 'JavaScript prófun',
+'javascripttest-disabled' => 'Þessi möguleiki hefur ekki verið virkjaður á þessum wiki.',
'javascripttest-pagetext-skins' => 'Veldu þema sem á að keyra prófanirnar á:',
# Tooltip help for the actions
'api-error-duplicate-archive-popup-title' => 'Eins {{PLURAL:$1|skrá|skrár}} sem {{PLURAL:$1|hefur|hafa}} þegar verið eytt.',
'api-error-duplicate-popup-title' => '{{PLURAL:$1|Afrituð skrá|Afritaðar skrár}}',
'api-error-empty-file' => 'Skráin sem þú valdir er tóm.',
+'api-error-emptypage' => 'Stofnun nýrra, tómra síðna er óheimil.',
'api-error-fetchfileerror' => 'Innri villa: Mistókst að sækja skránna.',
+'api-error-fileexists-forbidden' => 'Skrá með nafninu "$1" er þegar til og ekki er hægt að yfirskrifa hana.',
'api-error-file-too-large' => 'Skráin sem þú valdir er of stór.',
'api-error-filename-tooshort' => 'Skráarnafnið er of stutt',
'api-error-filetype-banned' => 'Þessi gerð skráar er bönnuð.',
'api-error-unknown-code' => 'Óþekkt villa: "$1"',
'api-error-unknown-error' => 'Innri villa: Eitthvað fór úrskeiðis þegar að skráinni þinni var hlaðið inn.',
'api-error-unknown-warning' => 'Óþekkt viðvörun: $1',
+'api-error-unknownerror' => 'Óþekkt villa: "$1".',
'api-error-uploaddisabled' => 'Ekki er leyft að hlaða inn á þessum wiki.',
'api-error-verification-error' => 'Þessi skrá gæti verið skemmd, eða með vitlausa skráarendingu.',
'duration-years' => '$1 {{PLURAL:$1|ár|ár}}',
'duration-decades' => '$1 {{PLURAL:$1|áratugur|áratugir}}',
'duration-centuries' => '$1 {{PLURAL:$1|öld|aldir}}',
+'duration-millennia' => '$1 {{PLURAL:$1|árþúsund}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|er ekki leifileg skráargerð|eru ekki leifilegar skráargerðir}}. {{PLURAL:$3|Leyfileg skráargerð er|Leyfilegar skráargerðir eru}} $2.',
);
'tog-minordefault' => 'Indica ogni modifica come minore (solo come predefinito)',
'tog-previewontop' => "Mostra l'anteprima sopra la casella di modifica e non sotto",
'tog-previewonfirst' => "Mostra l'anteprima per la prima modifica",
-'tog-nocache' => 'Disabilitare la cache delle pagine del browser',
+'tog-nocache' => 'Disabilita la cache delle pagine del browser',
'tog-enotifwatchlistpages' => 'Inviami una email quando viene modificata una pagina o un file presente tra gli osservati speciali',
'tog-enotifusertalkpages' => 'Segnalami via e-mail le modifiche alla mia pagina di discussione',
'tog-enotifminoredits' => 'Inviami una email anche per le modifiche minori di pagine e file',
'retrievedfrom' => 'Estratto da "$1"',
'youhavenewmessages' => 'Hai $1 ($2).',
'newmessageslink' => 'nuovi messaggi',
-'newmessagesdifflink' => 'differenza con la revisione precedente',
+'newmessagesdifflink' => 'ultima modifica',
+'youhavenewmessagesfromusers' => 'Hai $1 da {{PLURAL:$3|un altro utente|$3 utenti}} ($2).',
+'youhavenewmessagesmanyusers' => 'Hai $1 da molti utenti ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un nuovo messaggio|nuovi messaggi}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|ultima modifica|ultime modifiche}}',
'youhavenewmessagesmulti' => 'Hai nuovi messaggi su $1',
'editsection' => 'modifica',
'editold' => 'modifica',
'remembermypassword' => 'Ricorda la password su questo browser (per un massimo di $1 {{PLURAL:$1|giorno|giorni}})',
'securelogin-stick-https' => 'Resta connesso attraverso HTTPS dopo il login',
'yourdomainname' => 'Specificare il dominio',
+'password-change-forbidden' => 'Non è possibile modificare le password su questo wiki.',
'externaldberror' => 'Si è verificato un errore con il server di autenticazione esterno, oppure non si dispone delle autorizzazioni necessarie per aggiornare il proprio accesso esterno.',
'login' => 'Entra',
'nav-login-createaccount' => 'Entra / registrati',
'logout' => 'Esci',
'userlogout' => 'Esci',
'notloggedin' => 'Accesso non effettuato',
-'nologin' => "Non hai ancora un account? '''$1'''.",
+'nologin' => "Non hai ancora un accesso? '''$1'''.",
'nologinlink' => 'Registrati',
-'createaccount' => 'Crea un account',
-'gotaccount' => "Hai già un account? '''$1'''.",
+'createaccount' => 'Crea un accesso',
+'gotaccount' => "Hai già un accesso? '''$1'''.",
'gotaccountlink' => 'Entra',
'userlogin-resetlink' => 'Hai dimenticato i tuoi dati di accesso?',
'createaccountmail' => 'Tramite email',
'anontalkpagetext' => "----''Questa è la pagina di discussione di un utente anonimo, che non ha ancora creato un accesso o comunque non lo usa. Per identificarlo è quindi necessario usare il numero del suo indirizzo IP. Gli indirizzi IP possono però essere condivisi da più utenti. Se sei un utente anonimo e ritieni che i commenti presenti in questa pagina non si riferiscano a te, [[Special:UserLogin/signup|crea un nuovo accesso]] o [[Special:UserLogin|entra con quello che già hai]] per evitare di essere confuso con altri utenti anonimi in futuro.''",
'noarticletext' => 'In questo momento la pagina richiesta è vuota. È possibile [[Special:Search/{{PAGENAME}}|cercare questo titolo]] nelle altre pagine del sito, <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cercare nei registri correlati] oppure [{{fullurl:{{FULLPAGENAME}}|action=edit}} modificare la pagina ora]</span>.',
'noarticletext-nopermission' => 'In questo momento la pagina richiesta è vuota. È possibile [[Special:Search/{{PAGENAME}}|cercare questo titolo]] nelle altre pagine del sito o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cercare nei registri correlati]</span>.',
+'missing-revision' => 'La revisione #$1 della pagina "{{PAGENAME}}" non esiste.
+
+Questo si verifica solitamente seguendo un collegamento a una pagina cancellata, in una cronologia non aggiornata.
+I dettagli possono essere trovati nel [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro delle cancellazioni].',
'userpage-userdoesnotexist' => 'L\'account "<nowiki>$1</nowiki>" non corrisponde a un utente registrato. Verificare che si intenda davvero creare o modificare questa pagina.',
'userpage-userdoesnotexist-view' => 'L\'account utente "$1" non è registrato.',
'blocked-notice-logextract' => "Questo utente è attualmente bloccato.
'expansion-depth-exceeded-warning' => 'Questa pagina ha superato la profondità di espansione',
'parser-unstrip-loop-warning' => 'Rilevato ciclo di Unstrip',
'parser-unstrip-recursion-limit' => 'Superati i limiti di ricorsione di Unstrip ($1)',
+'converter-manual-rule-error' => 'Rilevato errore nella regola manuale di conversione della lingua',
# "Undo" feature
'undo-success' => 'Questa modifica può essere annullata. Verificare il confronto presentato di seguito per accertarsi che il contenuto corrisponda a quanto desiderato e quindi salvare le modifiche per completare la procedura di annullamento.',
'revdelete-offender' => 'Autore della versione:',
# Suppression log
-'suppressionlog' => 'Log delle soppressioni',
+'suppressionlog' => 'Soppressioni',
'suppressionlogtext' => "Di seguito sono elencate le cancellazioni e i blocchi con del contenuto nascosto agli amministratori.
Vedi l'[[Special:BlockList|elenco dei blocchi]] per l'elenco dei bandi e dei blocchi attivi al momento.",
'editundo' => 'annulla',
'diff-multi' => '({{PLURAL:$1|Una revisione intermedia|$1 revisioni intermedie}} di {{PLURAL:$2|un utente|$2 utenti}} non mostrate)',
'diff-multi-manyusers' => '({{PLURAL:$1|Una revisione intermedia|$1 revisioni intermedie}} di oltre $2 {{PLURAL:$2|utente|utenti}} non mostrate)',
+'difference-missing-revision' => '{{PLURAL:$2|Una versione|$2 versioni}} di questa differenza ($1) {{PLURAL:$2|non è stata trovata|non sono state trovate}}.
+
+Questo si verifica solitamente seguendo un collegamento obsoleto di un diff a una pagina cancellata.
+I dettagli possono essere trovati nel [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro delle cancellazioni].',
# Search results
'searchresults' => 'Risultati della ricerca',
'right-writeapi' => "Usa l'API in scrittura",
'right-delete' => 'Cancella pagine',
'right-bigdelete' => 'Cancella pagine con cronologie lunghe',
+'right-deletelogentry' => 'Cancella e ripristina voci di registro specifiche',
'right-deleterevision' => 'Nasconde revisioni specifiche delle pagine',
'right-deletedhistory' => 'Visualizza le revisioni della cronologia cancellate senza il testo associato',
'right-deletedtext' => 'Visualizza testo cancellato e modifiche fra revisioni cancellate',
'right-ipblock-exempt' => 'Ignora i blocchi degli IP, i blocchi automatici e i blocchi di range di IP',
'right-proxyunbannable' => 'Scavalca i blocchi sui proxy',
'right-unblockself' => 'Sblocca se stesso',
-'right-protect' => 'Cambia i livelli di protezione',
+'right-protect' => 'Cambia i livelli di protezione e modifica pagine protette',
'right-editprotected' => 'Modifica pagine protette',
'right-editinterface' => "Modifica l'interfaccia utente",
'right-editusercssjs' => 'Modifica i file CSS e JS di altri utenti',
'disambiguations' => 'Pagine che si collegano a pagine di disambiguazione',
'disambiguationspage' => 'Template:Disambigua',
-'disambiguations-text' => "Le pagine nella lista che segue contengono dei collegamenti a '''pagine di disambiguazione''' e non all'argomento cui dovrebbero fare riferimento.<br />Vengono considerate pagine di disambiguazione tutte quelle che contengono i template elencati in [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Le pagine nella lista che segue contengono almeno un collegamento a una '''pagina di disambiguazione'''.
+Esse potrebbero dover puntare a una pagina più appropriata.<br />
+Vengono considerate pagine di disambiguazione tutte quelle che contengono i template elencati in [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redirect doppi',
'doubleredirectstext' => 'In questa pagina sono elencate pagine che reindirizzano ad altre pagine di redirect.
'rollback' => 'Annulla le modifiche',
'rollback_short' => 'Rollback',
'rollbacklink' => 'rollback',
+'rollbacklinkcount' => 'rollback di {{PLURAL:$1|una modifica|$1 modifiche}}',
+'rollbacklinkcount-morethan' => 'rollback di più di {{PLURAL:$1|una modifica|$1 modifiche}}',
'rollbackfailed' => 'Rollback fallito',
'cantrollback' => "Impossibile annullare le modifiche; l'utente che le ha effettuate è l'unico ad aver contribuito alla pagina.",
'alreadyrolled' => 'Non è possibile annullare le modifiche apportate alla pagina [[:$1]] da parte di [[User:$2|$2]] ([[User talk:$2|discussione]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); un altro utente ha già modificato la pagina oppure ha effettuato il rollback.
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
'seconds-abbrev' => '$1 s',
-'minutes-abbrev' => '$1nbsp;min',
-'hours-abbrev' => '$1nbsp;h',
-'days-abbrev' => '$1nbsp;gg.',
+'minutes-abbrev' => '$1 min',
+'hours-abbrev' => '$1 h',
+'days-abbrev' => '$1 gg.',
'seconds' => '{{PLURAL:$1|un secondo|$1 secondi}}',
'minutes' => '{{PLURAL:$1|un minuto|$1 minuti}}',
'hours' => "{{PLURAL:$1|un'ora|$1 ore}}",
# 'all' in various places, this might be different for inflected languages
'watchlistall2' => 'tutte',
-'namespacesall' => 'Tutti',
+'namespacesall' => 'tutti',
'monthsall' => 'tutti',
'limitall' => 'tutti',
* <span class="mw-specialpagecached">Pagine speciali disponibili in versione cache (potrebbero essere obsolete).</span>',
'specialpages-group-maintenance' => 'Resoconti di manutenzione',
'specialpages-group-other' => 'Altre pagine speciali',
-'specialpages-group-login' => 'Login / registrazione',
+'specialpages-group-login' => 'Accesso / creazione utenze',
'specialpages-group-changes' => 'Ultime modifiche e registri',
'specialpages-group-media' => 'File multimediali - caricamento e resoconti',
'specialpages-group-users' => 'Utenti e diritti',
'duration-centuries' => '$1 {{PLURAL:$1|secolo|secoli}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennio|millenni}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|non è un tipo di file consentito|non sono tipi di file consentiti}}. {{PLURAL:$3|Il tipo di file consentito è|I tipi di file consentiti sono}} $2.',
);
* @ingroup Language
* @file
*
+ * @author 2nd-player
* @author Akaniji
* @author Alexsh
* @author Ant176
'tog-newpageshidepatrolled' => '新しいページの一覧に巡回済みのページを表示しない',
'tog-extendwatchlist' => 'ウォッチリストを拡張し、最新のものだけではなくすべての変更を表示',
'tog-usenewrc' => '最近の更新とウォッチリストで複数の変更をページごとにまとめる(JavaScriptが必要)',
-'tog-numberheadings' => 'è\87ªå\8b\95ç\9a\84ã\81«è¦\8bå\87ºã\81\97ã\81«ç\95ªå\8f·ã\82\92振る',
+'tog-numberheadings' => 'è¦\8bå\87ºã\81\97ã\81«ç\95ªå\8f·ã\82\92è\87ªå\8b\95ç\9a\84ã\81«振る',
'tog-showtoolbar' => '編集用のツールバーを表示(JavaScriptが必要)',
'tog-editondblclick' => 'ダブルクリックで編集(JavaScriptが必要)',
'tog-editsection' => '[編集]リンクから節を編集できるようにする',
'tog-showtoc' => '目次を表示(ページに見出しが4つ以上ある場合)',
'tog-rememberpassword' => 'このブラウザーにログイン情報を記憶(最大 $1 {{PLURAL:$1|日間}})',
'tog-watchcreations' => '自分が作成したページとアップロードしたファイルをウォッチリストに追加',
-'tog-watchdefault' => 'è\87ªå\88\86ã\81\8cç·¨é\9b\86ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\81¨ファイルをウォッチリストに追加',
-'tog-watchmoves' => 'è\87ªå\88\86ã\81\8c移å\8b\95ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\81¨ファイルをウォッチリストに追加',
-'tog-watchdeletion' => 'è\87ªå\88\86ã\81\8cå\89\8aé\99¤ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\81¨ファイルをウォッチリストに追加',
+'tog-watchdefault' => 'è\87ªå\88\86ã\81\8cç·¨é\9b\86ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\82\84ファイルをウォッチリストに追加',
+'tog-watchmoves' => 'è\87ªå\88\86ã\81\8c移å\8b\95ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\82\84ファイルをウォッチリストに追加',
+'tog-watchdeletion' => 'è\87ªå\88\86ã\81\8cå\89\8aé\99¤ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\82\84ファイルをウォッチリストに追加',
'tog-minordefault' => '細部の編集に既定でチェックを入れる',
'tog-previewontop' => 'プレビューを編集ボックスの前に配置',
'tog-previewonfirst' => '編集開始時にもプレビューを表示',
'tog-nocache' => 'ブラウザーによるページのキャッシュを無効にする',
-'tog-enotifwatchlistpages' => 'ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\81«ã\81\82ã\82\8bã\83\9aã\83¼ã\82¸ã\81\8bファイルが更新されたらメールを受け取る',
+'tog-enotifwatchlistpages' => 'ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\81«ã\81\82ã\82\8bã\83\9aã\83¼ã\82¸ã\82\84ファイルが更新されたらメールを受け取る',
'tog-enotifusertalkpages' => '自分のトークページが更新されたらメールを受け取る',
'tog-enotifminoredits' => 'ページやファイルへの細部の編集でもメールを受け取る',
'tog-enotifrevealaddr' => '通知メールで自分のメールアドレスを明示',
'cannotdelete' => 'ページまたはファイル「$1」を削除できませんでした。
他の人が既に削除した可能性があります。',
'cannotdelete-title' => '「$1」というページを削除できません',
+'delete-hook-aborted' => 'フックによって削除が中止されました。
+理由は不明です。',
'badtitle' => '正しくないページ名',
'badtitletext' => '要求されたページ名は、無効、空、正しくない言語間リンク/ウィキ間リンクのページ名、のいずれかです。
ページ名に使用できない文字が1つ以上含まれている可能性があります。',
'filereadonlyerror' => 'ファイルリポジトリ「$2」が読み取り専用の状態にあるため、ファイル「$1」を変更できません。
読み取り専用に設定した管理者からの説明:「$3」',
+'exception-nologin' => 'ログインしていません',
+'exception-nologin-text' => 'このページまたは操作には、このウィキへのログインが必要です。',
# Virus scanner
'virus-badscanner' => "環境設定が不適合です:不明なウイルス検知ソフトウェア:''$1''",
'remembermypassword' => 'このブラウザーにログイン情報を保存 (最長 $1 {{PLURAL:$1|日|日間}})',
'securelogin-stick-https' => 'ログイン後にHTTPS接続を維持',
'yourdomainname' => 'ドメイン:',
+'password-change-forbidden' => 'このウィキではパスワードを変更できません。',
'externaldberror' => '外部の認証データベースでエラーが発生したか、または外部アカウント情報の更新が許可されていません。',
'login' => 'ログイン',
'nav-login-createaccount' => 'ログインまたはアカウント作成',
'expansion-depth-exceeded-warning' => 'ページが展開の深さ制限を超えました',
'parser-unstrip-loop-warning' => 'Unstrip のループが検出されました',
'parser-unstrip-recursion-limit' => 'Unstrip の再帰($1)が上限を超えました',
+'converter-manual-rule-error' => '手動の言語変換規則でエラーを検出しました。',
# "Undo" feature
'undo-success' => 'この編集を取り消せます。
# Groups
'group' => 'グループ:',
-'group-user' => '利用者',
+'group-user' => '登録利用者',
'group-autoconfirmed' => '自動承認された利用者',
'group-bot' => 'ボット',
'group-sysop' => '管理者',
'group-suppress' => '秘匿者',
'group-all' => '(全員)',
-'group-user-member' => '{{GENDER:$1|利用者}}',
+'group-user-member' => '{{GENDER:$1|登録利用者}}',
'group-autoconfirmed-member' => '{{GENDER:$1|自動承認された利用者}}',
'group-bot-member' => '{{GENDER:$1|ボット}}',
'group-sysop-member' => '{{GENDER:$1|管理者}}',
'group-bureaucrat-member' => '{{GENDER:$1|ビューロクラット}}',
'group-suppress-member' => '{{GENDER:$1|秘匿者}}',
-'grouppage-user' => '{{ns:project}}:利用者',
+'grouppage-user' => '{{ns:project}}:登録利用者',
'grouppage-autoconfirmed' => '{{ns:project}}:自動承認された利用者',
'grouppage-bot' => '{{ns:project}}:ボット',
'grouppage-sysop' => '{{ns:project}}:管理者',
'right-writeapi' => '書き込みAPIの使用',
'right-delete' => 'ページの削除',
'right-bigdelete' => '大きな履歴があるページを削除',
+'right-deletelogentry' => '特定の記録項目の削除と復帰',
'right-deleterevision' => 'ページの特定の版の削除と復帰',
'right-deletedhistory' => '削除された履歴項目(関連する本文を除く)を閲覧',
'right-deletedtext' => '削除された本文と削除された版間の差分を閲覧',
'disambiguations' => '曖昧さ回避ページにリンクしているページ',
'disambiguationspage' => 'Template:曖昧回避',
-'disambiguations-text' => "以ä¸\8bã\81®ã\83\9aã\83¼ã\82¸ã\81¯'''æ\9b\96æ\98§ã\81\95å\9b\9eé\81¿ã\83\9aã\83¼ã\82¸'''ã\81¸ã\83ªã\83³ã\82¯ã\81\97ã\81¦ã\81\84ます。
-ã\81\93ã\82\8cã\82\89ã\81®ã\83\9aã\83¼ã\82¸ã\81¯ã\80\81ã\82\88ã\82\8aé\81©ã\81\97ã\81\9f主é¡\8cã\81®ã\83\9aã\83¼ã\82¸ã\81¸ã\83ªã\83³ã\82¯ã\81\95ã\82\8cã\82\8bã\81¹ã\81\8dã\81§す。<br />
-[[MediaWiki:Disambiguationspage]]ã\81\8bã\82\89ã\83ªã\83³ã\82¯ã\81\95ã\82\8cã\81\9fã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ã\82\92使ç\94¨ã\81\97ã\81¦ã\81\84ã\82\8bã\83\9aã\83¼ã\82¸ã\81¯、曖昧さ回避ページと見なされます。",
+'disambiguations-text' => "以ä¸\8bã\81®ã\83\9aã\83¼ã\82¸ã\81«ã\81¯ã\80\81'''æ\9b\96æ\98§ã\81\95å\9b\9eé\81¿ã\83\9aã\83¼ã\82¸'''ã\81¸ã\81®ã\83ªã\83³ã\82¯ã\81\8c1å\80\8b以ä¸\8aã\81\82ã\82\8aます。
+ã\81\9dã\81®ã\82\88ã\81\86ã\81ªã\83ªã\83³ã\82¯ã\81¯ã\80\81ã\82\88ã\82\8aé\81©å\88\87ã\81ªã\83\9aã\83¼ã\82¸ã\81¸ã\81®ã\83ªã\83³ã\82¯ã\81«å¤\89æ\9b´ã\81\99ã\82\8bå¿\85è¦\81ã\81\8cã\81\82ã\82\8aã\81¾す。<br />
+[[MediaWiki:Disambiguationspage]]ã\81\8bã\82\89ã\83ªã\83³ã\82¯ã\81\95ã\82\8cã\81\9fã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ã\82\92使ç\94¨ã\81\97ã\81¦ã\81\84ã\82\8bã\83\9aã\83¼ã\82¸ã\81\8c、曖昧さ回避ページと見なされます。",
'doubleredirects' => '二重転送',
'doubleredirectstext' => 'これは他のリダイレクトページへのリダイレクトの一覧です。
'protectedtitles' => '作成保護されているページ名',
'protectedtitlestext' => '以下のページは新規作成が禁止されています',
'protectedtitlesempty' => 'これらの引数で現在保護されているページはありません。',
-'listusers' => '利用者の一覧',
+'listusers' => '利用者一覧',
'listusers-editsonly' => '投稿記録のある利用者のみを表示',
'listusers-creationsort' => '作成日順に並べ替え',
'usereditcount' => '$1{{PLURAL:$1|回の編集}}',
'emailccsubject' => '$1に送信したメールの控え:$2',
'emailsent' => 'メールを送信しました',
'emailsenttext' => 'メールを送信しました。',
-'emailuserfooter' => 'このメールは {{SITENAME}} の「利用者にメール送信」機能が、「$1」から「$2」に送信したものです。',
+'emailuserfooter' => 'このメールは$1から$2へ、{{SITENAME}}の「利用者にメールを送信」機能でお送りしました。',
# User Messenger
'usermessage-summary' => 'システムメッセージを残す。',
'rollback' => '編集を巻き戻し',
'rollback_short' => '巻き戻し',
'rollbacklink' => '巻き戻し',
+'rollbacklinkcount' => '$1個の{{PLURAL:$1|編集|編集}}を巻き戻し',
+'rollbacklinkcount-morethan' => '$1個以上の{{PLURAL:$1|編集|編集}}を巻き戻し',
'rollbackfailed' => '巻き戻しに失敗しました',
'cantrollback' => '編集を差し戻せません。
最後の投稿者が、このページの唯一の作者です。',
このページの最後の編集は[[User:$3|$3]]([[User talk:$3|トーク]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])によるものです。',
'editcomment' => "編集内容の要約:「''$1''」",
'revertpage' => '[[Special:Contributions/$2|$2]]([[User talk:$2|トーク]])による編集を[[User:$1|$1]]による直前の版へ差し戻しました',
-'revertpage-nouser' => '(利用者名削除)による編集を[[User:$1|$1]]による最新版へ差し戻しました',
+'revertpage-nouser' => '(利用者名削除)による編集を[[User:$1|$1]]による直前の版へ差し戻しました',
'rollback-success' => '$1による編集を差し戻しました。
$2による直前の版へ変更されました。',
'protectedarticle' => '「[[$1]]」を保護しました',
'modifiedarticleprotection' => '「[[$1]]」の保護レベルを変更しました',
'unprotectedarticle' => '「[[$1]]」の保護を解除しました',
-'movedarticleprotection' => ' が保護の設定を「[[$2]]」から「[[$1]]」へ移動しました',
+'movedarticleprotection' => 'が保護の設定を「[[$2]]」から「[[$1]]」へ移動しました',
'protect-title' => '「$1」の保護レベルを変更',
'protect-title-notallowed' => '「$1」の保護レベルを表示',
'prot_1movedto2' => '[[$1]] を [[$2]] へ移動',
参考のため、ブロックの記録を以下に示します:',
'blocklog-showsuppresslog' => 'この利用者は以前にブロックされ、隠されたことがあります。
参考のため、秘匿記録を以下に示します:',
-'blocklogentry' => ' が [[$1]] を$2ブロックしました。ブロックの詳細:$3',
-'reblock-logentry' => ' が [[$1]] のブロック設定を$2に変更しました。ブロックの詳細:$3',
+'blocklogentry' => 'が [[$1]] を$2ブロックしました。ブロックの詳細:$3',
+'reblock-logentry' => 'が [[$1]] のブロック設定を$2に変更しました。ブロックの詳細:$3',
'blocklogtext' => 'このページは利用者のブロックと解除の記録です。
自動的にブロックされたIPアドレスは表示されていません。
現時点で有効なブロックは[[Special:BlockList|ブロックの一覧]]をご覧ください。',
* <span class="mw-specialpagerestricted">制限されている特別ページ</span>',
'specialpages-group-maintenance' => 'メンテナンス報告',
'specialpages-group-other' => 'その他の特別ページ',
-'specialpages-group-login' => 'ログイン/利用者登録',
+'specialpages-group-login' => 'ログインまたはアカウント作成',
'specialpages-group-changes' => '最近の更新と記録',
'specialpages-group-media' => 'メディア情報とアップロード',
'specialpages-group-users' => '利用者と権限',
'api-error-empty-file' => '送信されたファイルは空でした。',
'api-error-emptypage' => '内容がないページの新規作成は許可されていません。',
'api-error-fetchfileerror' => '内部エラー:ファイルの取得中に問題が発生しました。',
+'api-error-fileexists-forbidden' => '「$1」という名前のファイルは存在しており、上書きはできません。',
+'api-error-fileexists-shared-forbidden' => '「$1」という名前のファイルは共有ファイルリポジトリに存在しており、上書きはできません。',
'api-error-file-too-large' => '送信されたファイルは大きすぎます。',
'api-error-filename-tooshort' => 'ファイル名が短すぎます。',
'api-error-filetype-banned' => 'この形式のファイルは禁止されています。',
'duration-centuries' => '$1 {{PLURAL:$1|世紀}}',
'duration-millennia' => '$1{{PLURAL:$1|,000 年}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1{{PLURAL:$4|は許可されていないファイル形式です}}。許可されている{{PLURAL:$3|ファイル形式}}は$2です。',
);
* @author Helix84
* @author Kaganer
* @author Meursault2004
+ * @author NoiX180
* @author Pras
* @author Rex
* @author StefanusRA
'tog-hidepatrolled' => 'Delikna suntingan sing wis dipatroli ing kaca owah-owahan',
'tog-newpageshidepatrolled' => 'Delikna kaca sing wis dipatroli saka daftar kaca anyar',
'tog-extendwatchlist' => 'Kembangna daftar pangawasan kanggo nuduhaké kabèh pangowahan, ora mung sing paling anyar',
-'tog-usenewrc' => 'Gunakna tampilan pangowahan pungkasan sing wis dikembangake (mbutuhake JavaScript)',
+'tog-usenewrc' => 'Owah-owahané paguyuban miturut kaca nèng owah-owahan anyar lan daptar panto (mbutuhaké JavaScript)',
'tog-numberheadings' => 'Wènèhana nomer judul secara otomatis',
'tog-showtoolbar' => 'Tuduhna <em>toolbar</em> (batang piranti) panyuntingan',
'tog-editondblclick' => 'Sunting kaca nganggo klik ping loro (JavaScript)',
'tog-editsectiononrightclick' => 'Fungsèkna panyuntingan sub-bagian mawa klik-tengen ing judul bagian (JavaScript)',
'tog-showtoc' => 'Tuduhna daftar isi (kanggo kaca sing nduwé luwih saka 3 sub-bagian)',
'tog-rememberpassword' => 'Émut tembung sandi kula ing peramban punika (salebeting $1 {{PLURAL:$1|dinten|dinten}})',
-'tog-watchcreations' => 'Tambahna kaca sing tak-gawé ing daftar pangawasan',
-'tog-watchdefault' => 'Tambahna kaca sing tak-sunting ing daftar pangawasan',
-'tog-watchmoves' => 'Tambahkan kaca sing tak-pindhah ing daftar pangawasan',
-'tog-watchdeletion' => 'Tambahkan kaca sing tak-busak ing daftar pangawasan',
+'tog-watchcreations' => 'Tambahaké kaca sing tak gawé lan berkas sing tak unggah nèng daptar pangawasan',
+'tog-watchdefault' => 'Tambahaké kaca lan berkas sing tak sunting nèng daptar pangawasan',
+'tog-watchmoves' => 'Tambahaké kaca lan berkas sing tak pindhahaké nèng daptar pangawasan',
+'tog-watchdeletion' => 'Tambahaké kaca lan berkas sing tak busak nèng daptar pangawasan',
'tog-minordefault' => 'Tandhanana kabèh suntingan dadi suntingan cilik secara baku',
'tog-previewontop' => 'Tuduhna pratayang sadurungé kothak sunting lan ora sawisé',
'tog-previewonfirst' => 'Tuduhna pratayang ing suntingan kapisan',
'tog-nocache' => 'Nonaktifaken penyinggahan kaca peramban',
-'tog-enotifwatchlistpages' => 'Kirimana aku layang e-mail yèn ana sawijining kaca sing tak-awasi owah',
+'tog-enotifwatchlistpages' => 'Kirimi kula layang èlèktronik yèn ana kaca utawa berkas nèng daptar pangawasanku sing diowah',
'tog-enotifusertalkpages' => 'Kirimana aku layang e-mail yèn kaca dhiskusiku owah',
-'tog-enotifminoredits' => 'Kirimana aku layang e-mail uga yèn ana pangowahan cilik',
+'tog-enotifminoredits' => 'Kirimi kula layang èlèktronik uga yèn ana suntingan cilik saka kaca lan berkas',
'tog-enotifrevealaddr' => 'Kirimana aku layang e-mail ing layang notifikasi',
'tog-shownumberswatching' => 'Tuduhna cacahé pangawas',
'tog-oldsig' => 'Tapak asma sing ana:',
'youhavenewmessages' => 'Panjenengan kagungan $1 ($2).',
'newmessageslink' => 'warta énggal',
'newmessagesdifflink' => 'mirsani bédané saka révisi sadurungé',
+'youhavenewmessagesfromusers' => 'Sampéyan nduwé $1 saka {{PLURAL:$3|panganggo liya|$3 panganggo}} ($2).',
+'youhavenewmessagesmanyusers' => 'Sampéyang nduwé $1 saka akèh panganggo ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|layang anyar|layang anyar}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|owahan|owahan}} pungkasan',
'youhavenewmessagesmulti' => 'Panjenengan olèh pesen-pesen anyar $1',
'editsection' => 'sunting',
'editold' => 'sunting',
'page-rss-feed' => "\"\$1\" ''RSS Feed''",
'page-atom-feed' => "\"\$1\" ''Atom Feed''",
'red-link-title' => '$1 (kaca durung ana)',
+'sort-descending' => 'Urutaké medhun',
+'sort-ascending' => 'Urutaké munggah',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Artikel',
'badarticleerror' => 'Pratingkah iku ora bisa katindhakaké ing kaca iki.',
'cannotdelete' => 'Kaca utawa berkas "$1" ora bisa dibusak.
Manawa wis dibusak déning wong liya.',
+'cannotdelete-title' => 'Ora bisa mbusak kaca "$1"',
+'delete-hook-aborted' => "Pambusakan dibatalaké déning ''hook''.
+Ora ana alesané.",
'badtitle' => 'Judhulé ora sah',
'badtitletext' => 'Judhul kaca sing panjenengan ora bisa dituduhaké, kosong, utawa dadi judhul antar-basa utawa judhul antar-wiki. Iku bisa uga ana sawijining utawa luwih aksara sing ora bisa didadèkaké judhul.',
-'perfcached' => 'Data iki dijupuk saka <em>cache</em> lan mbokmenawa dudu data pungkasan. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts' => 'Data iki dijupuk saka <em>cache</em>, lan dianyaraké ing pungkasan ing $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
+'perfcached' => 'Data iki mung dijupuk saka papan singgahan lan mungkin ora kaanyaran. Maksimum {{PLURAL:$1|sak asil|$1 asil}} sumadhiya nèng papan singgahan.',
+'perfcachedts' => 'Data iki mung dijupuk saka papan singgahan lan mungkin dianyari pungkasan $1. Maksimum {{PLURAL:$4|sak asil|$4 asil}} sumadhiya nèng papan singgahan.',
'querypage-no-updates' => 'Update saka kaca iki lagi dipatèni. Data sing ana ing kéné saiki ora bisa bakal dibalèni unggah manèh.',
'wrong_wfQuery_params' => 'Parameter salah menyang wfQuery()<br />Fungsi: $1<br />Panyuwunan: $2',
'viewsource' => 'Tuduhna sumber',
+'viewsource-title' => 'Delok sumberé $1',
'actionthrottled' => 'Tindakan diwatesi',
'actionthrottledtext' => 'Minangka sawijining pepesthèn anti-spam, panjenengan diwatesi nglakoni tindhakan iki sing cacahé kakèhan ing wektu cendhak.
Mangga dicoba manèh ing sawetara menit.',
'protectedpagetext' => 'Kaca iki dikunci supaya ora disunting.',
'viewsourcetext' => 'Panjenengan bisa mirsani utawa nulad sumber kaca iki:',
+'viewyourtext' => "Sampéyan bisa ndelok lan nyalin sumber '''suntingan Sampéyan''' nèng kaca iki:",
'protectedinterface' => 'Kaca iki isiné tèks antarmuka sing dienggo software lan wis dikunci kanggo menghindari kasalahan.',
'editinginterface' => "'''Pènget:''' Panjenengan nyunting kaca sing dianggo nyedyakaké tèks antarmuka kanggo piranti alus.
Pangowahan kaca iki bakal awèh pangaruh marang tampilan antarmuka panganggo kanggoné panganggo liya.
'cascadeprotected' => 'Kaca iki wis direksa saka panyuntingan amerga disertakaké ing {{PLURAL:$1|kaca|kaca-kaca}} ngisor iki sing wis direksa mawa opsi "runtun" diaktifaké:
$2',
'namespaceprotected' => "Panjenengan ora kagungan idin kanggo nyunting kaca ing bilik nama '''$1'''.",
+'customcssprotected' => 'Sampéyan ora dililakaké nyunting kaca CSS iki amarga kaisi pangaturan pribadi saka panganggo liya.',
+'customjsprotected' => 'Sampéyan ora dililakaké nyunting kaca JavaScript iki amarga kaisi pangaturan pribadi saka panganggo liya.',
'ns-specialprotected' => 'Kaca ing bilik nama astaméwa utawa kusus, ora bisa disunting.',
'titleprotected' => "Irah-irahan iki direksa ora olèh digawé déning [[User:$1|$1]].
Alesané yaiku ''$2''.",
+'filereadonlyerror' => 'Ora bisa ngowah berkas "$1" amarga panyimpenan berkas "$2" ana ing mode mung-bisa-diwaca.
+
+Pangurus sing ngopèni kuwi ngawedharaké: "$3".',
+'invalidtitle-knownnamespace' => 'Irah-irahan ora sah mawa bilik jeneng "$2" lan tèks "$3"',
+'invalidtitle-unknownnamespace' => 'Judhul ora sah mawa angka $1 lan tèks "$2" bilik jeneng sing ora dingertèni',
+'exception-nologin' => 'Durung mlebu log',
+'exception-nologin-text' => 'Kaca utawa kelakon iki mbutuhaké mlebu log nèng wiki iki dhisik.',
# Virus scanner
'virus-badscanner' => "Kasalahan konfigurasi: pamindai virus ora dikenal: ''$1''",
'virus-unknownscanner' => 'Antivirus ora ditepungi:',
# Login and logout pages
-'logouttext' => "'''Panjenengan sampun medal (oncat) saking sistem.'''
+'logouttext' => "'''Sampéyan wis metu log'''
+
+Sampéyan bisa nganggo {{SITENAME}} sacara anonim, utawa bisa [[Special:UserLogin|mlebu log manèh]] kanthi jeneng panganggo sing padha utawa beda.
-Panjenengan saged migunakaken {{SITENAME}} kanthi anonim, utawi panjenengan saged [[Special:UserLogin|mlebet malih]] minangka pangangge ingkang sami utawi pangangge sanes.
-Supados dipunmangertosi bilih wonten kaca ingkang nedahaken manawi panjenengan taksih mlebet log ngantos panjenengan mbusak singgahan ing panjelajah web panjenengan.",
+Cathet yèn sapérangan kaca mungkin isih nampilaké tulisan yèn Sampéyan isih nèng njero log, kuwi bisa ilang yèn Sampéyan ngresiki ''cache'' pramban Sampéyan.",
'welcomecreation' => '== Sugeng rawuh, $1! ==
Akun panjenengan wis kacipta. Aja lali nata konfigurasi [[Special:Preferences|preferensi {{SITENAME}}]] panjenengan.',
'remembermypassword' => 'Émut tembung sandi kula (salebeting $1 {{PLURAL:$1|dinten|dinten}})',
'securelogin-stick-https' => 'Tetep kahubung dhumateng HTTPS sasampunipun mlebet log',
'yourdomainname' => 'Dhomain panjenengan',
+'password-change-forbidden' => 'Sampéyan ora bisa ngganti tembung sandhi nèng wiki iki.',
'externaldberror' => 'Ana kasalahan otèntikasi basis dhata èksternal utawa panjenengan ora pareng nglakoni pemutakhiran marang akun èksternal panjenengan.',
'login' => 'Mlebu log',
'nav-login-createaccount' => 'Log mlebu / nggawé rékening (akun)',
'createaccount' => 'Nggawé akun anyar',
'gotaccount' => "Wis kagungan akun? '''$1'''.",
'gotaccountlink' => 'Mlebu',
+'userlogin-resetlink' => 'Lali rincian mlebu log Sampéyan?',
'createaccountmail' => 'liwat layang e-mail',
'createaccountreason' => 'Alesan:',
'badretype' => 'Sandhi panjenengan ora gathuk',
-'userexists' => 'Asma panganggo sing panjenengan pilih wis kanggo.
-Mangga pilih asma liyané.',
+'userexists' => 'Jeneng panganggo sing dilebokaké lagi dianggo.
+Mangga pilih jeneng liya.',
'loginerror' => 'Kasalahan mlebu log',
'createaccounterror' => 'Ora bisa nyipta akun: $1',
'nocookiesnew' => "Rékening utawa akun panganggo panjenengan wis digawé, nanging panjenengan durung mlebu log. {{SITENAME}} nggunakaké ''cookies'' kanggo log panganggo. ''Cookies'' ing panjlajah wèb panjengengan dipatèni. Mangga diaktifaké lan mlebu log manèh mawa jeneng panganggo lan tembung sandhi panjenengan.",
'noemailprefs' => 'Panjenengan kudu milih alamat e-mail supaya bisa nganggo fitur iki.',
'emailconfirmlink' => 'Ndhedhes (konfirmasi) alamat e-mail panjenengan',
'invalidemailaddress' => 'Alamat e-mail iki ora bisa ditampa amarga formaté ora bener. Tulung lebokna alamat mawa format sing bener utawa kosongaké waé isèn kasebut.',
+'cannotchangeemail' => 'Alamat layang èlèktronik akun ora bisa diganti nèng wiki iki.',
+'emaildisabled' => 'Situs iki ora bisa ngirim layang èlèktronik.',
'accountcreated' => 'Akun wis kacipta.',
'accountcreatedtext' => 'Akun kanggo $1 wis kacipta.',
'createaccount-title' => 'Gawé rékening kanggo {{SITENAME}}',
'usernamehasherror' => 'Jeneng panganggo ora bisa ngandhut tandha pager',
'login-throttled' => 'Panjenengan wis kakèhan njajal mlebu log.
Tulung nunggu dhisik sadurungé njajal manèh.',
+'login-abort-generic' => 'Sampéyan ora suksès mlebu log - Dibatalaké',
'loginlanguagelabel' => 'Basa: $1',
'suspicious-userlogout' => 'Panjaluk panjenengan supaya metu ditolak amarga katoné panjlajah internt utawa proksi panyinggah.',
+# E-mail sending
+'php-mail-error-unknown' => 'Kasalahan ora dingertèni nèng piguna mail() PHP.',
+'user-mail-no-addy' => 'Njajal ngirim layang èlèktronik tanpa alamat layang èlèktronik.',
+
# Change password dialog
'resetpass' => 'Ganti tembung sandi',
'resetpass_announce' => 'Panjenengan wis mlebu log mawa kodhe sementara sing dikirim mawa e-mail. Menawa kersa nglanjutaké, panjenengan kudu milih tembung sandhi anyar ing kéné:',
'resetpass-temp-password' => 'Tembung sandi sauntara:',
# Special:PasswordReset
+'passwordreset' => 'Balèni setèl tembung sandhi',
+'passwordreset-text' => 'Ganepi pormulir iki kanggo nampa pangéling layang èlèktronik kanggo rincian akun Sampéyan.',
+'passwordreset-legend' => 'Balèni setèl tembung sandhi',
+'passwordreset-disabled' => 'Piranti kanggo mbalèni nyetèl tembung sandhi dipatèni nèng wiki iki.',
+'passwordreset-pretext' => '{{PLURAL:$1||Lebokaké siji bagéyan data ngisor iki}}',
'passwordreset-username' => 'Jeneng panganggo:',
+'passwordreset-domain' => 'Domain:',
+'passwordreset-capture' => 'Delok layang èlèktronik sing diasilaké?',
+'passwordreset-emailelement' => 'Jeneng panganggo: $1
+Tembung sandhi sawetara: $2',
+'passwordreset-emailsent' => 'Layang èlèktronik pangèling wis dikirim.',
+'passwordreset-emailsent-capture' => 'Layang èlèktronik pangèling wis dikirim kaya ngisor iki.',
+'passwordreset-emailerror-capture' => 'Layang èlèktronik pangèling ditampilaké nèng ngisor iki, nanging ora kasil dikirim: $1',
+
+# Special:ChangeEmail
+'changeemail' => 'Ganti alamat layang èlèktronik',
+'changeemail-header' => 'Ganti alamat layang èlèktronik akun',
+'changeemail-no-info' => 'Sampéyan kudu mlebu log kanggo ngaksès kaca iki langsung.',
+'changeemail-oldemail' => 'Alamat layang èlèktronik saiki:',
+'changeemail-newemail' => 'Alamat layang èlèktronik anyar:',
+'changeemail-none' => '(ora ana)',
+'changeemail-submit' => 'Ganti layang èlèktronik',
+'changeemail-cancel' => 'Batal',
# Edit page toolbar
'bold_sample' => 'Tèks iki bakal dicithak kandel',
'showlivepreview' => 'Pratayang langsung',
'showdiff' => 'Tuduhna pangowahan',
'anoneditwarning' => 'Panjenengan ora kadaftar mlebu. Alamat IP panjenengan bakal kacathet ing sajarah panyuntingan kaca iki.',
+'anonpreviewwarning' => "''Sampéyan durung mlebu log. Nyimpen bakal nyathet alamat IP Sampéyan nèng riwayat sunting kaca iki.''",
'missingsummary' => "'''Pènget:''' Panjenengan ora nglebokaké ringkesan panyuntingan. Menawa panjenengan mencèt tombol Simpen manèh, suntingan panjenengan bakal kasimpen tanpa ringkesan panyuntingan.",
'missingcommenttext' => 'Tulung lebokna komentar ing ngisor iki.',
-'missingcommentheader' => "'''Pènget:''' Panjenengan durung mènèhi subyèk utawa judhul kanggo komentar panjenengan. Menawa panjenengan mencèt Simpan, suntingan panjenengan bakal kasimpen tanpa komentar iku.",
+'missingcommentheader' => "'''Pangéling:''' Sampéyan durung nyadhiyakaké judhul/jejer kanggo tanggepan iki.
+Yèn Sampéyan klik \"{{int:savearticle}}\" manèh, suntingan Sampéyan bakal kasimpen tanpa kuwi.",
'summary-preview' => 'Pratayang ringkesan:',
'subject-preview' => 'Pratayang subyèk/judhul:',
'blockedtitle' => 'Panganggo diblokir',
'edit-no-change' => 'Suntingan panjenengan dilirwakaké amerga panjenengan ora nglakoni pangowahan apa-apa ing tèks.',
'edit-already-exists' => 'Ora bisa nggawé kaca anyar.
Amerga wis ana.',
+'defaultmessagetext' => 'Tèks layang gawan',
# Parser/template warnings
'expensive-parserfunction-warning' => "Pènget: Kaca iki ngandhut kakèhan panggunan fungsi ''parser'' sing larang.
'post-expand-template-argument-category' => 'Kaca-kaca kanthi argumèn cithakan sing dilirwakaké',
'parser-template-loop-warning' => "Ana ''loop'' cithakan: [[$1]]",
'parser-template-recursion-depth-warning' => "Wates ''recursion depth'' cithakan wis ngliwati ($1)",
+'language-converter-depth-warning' => 'Wates jeroné pangganti basa wis kapunjulen ($1)',
# "Undo" feature
'undo-success' => 'Suntingan iki bisa dibatalaké. Tulung priksa prabandhingan ing ngisor iki kanggo mesthèkaké yèn prakara iki pancèn sing bener panjenengan pèngin lakoni, banjur simpenen pangowahan iku kanggo ngrampungaké pambatalan suntingan.',
'page_last' => 'pungkasan',
'histlegend' => "Pilihen rong tombol radhio banjur pencèten tombol ''bandhingna'' kanggo mbandhingaké versi. Klik sawijining tanggal kanggo ndeleng versi kaca ing tanggal iku.<br />(skr) = prabédan karo vèrsi saiki, (akir) = prabédan karo vèrsi sadurungé, '''s''' = suntingan sithik, '''b''' = suntingan bot, → = suntingan bagian, ← = ringkesan otomatis",
'history-fieldset-title' => 'Njlajah sajarah vèrsi sadhurungé',
+'history-show-deleted' => 'Namung sing dibusak',
'histfirst' => 'Suwé dhéwé',
'histlast' => 'Anyar dhéwé',
'historysize' => '($1 {{PLURAL:$1|bita|bita}})',
'revdelete-unsuppress' => 'Busak watesan ing revisi sing dibalèkaké',
'revdelete-log' => 'Alesan:',
'revdelete-submit' => 'Trapna ing {{PLURAL:$1|révisi|révisi}} kapilih',
-'revdelete-success' => 'Aturan pandhelikan revisi bisa kasil ditrapaké.',
-'revdelete-failure' => "'''Mode tampilan révisi ora bisa disèt:'''
+'revdelete-success' => "'''Kawujudan repisi sukses dianyari.'''",
+'revdelete-failure' => "'''Panampakan rèvisi ora bisa dianyari:'''
$1",
'logdelete-success' => 'Aturan pandhelikan tindhakan bisa kasil ditrapaké.',
'logdelete-failure' => "'''Aturan pandhelikan ora bisa disèt:'''
$1",
'revdel-restore' => 'Ngowahi visiblitas (pangatonan)',
+'revdel-restore-deleted' => 'revisi kabusak',
+'revdel-restore-visible' => 'revisi kétok',
'pagehist' => 'Sajarah kaca',
'deletedhist' => 'Sajarah sing dibusak',
'revdelete-hide-current' => 'Gagal ndhelikaké révisi tanggal $2, $1: iki arupa révisi paling anyar.
'revdelete-no-change' => "'''Pènget:''' révisi tanggal $1, jam $2 wis nduwèni aturan pandhelikan kasebut.",
'revdelete-concurrent-change' => 'Gagal ngowahi révisi tanggal $1, jam $2: statusé mbokmanawa wis diowahi déning panganggo liya bebarengan karo panjenengan.
Mangga priksa cathetan log.',
-'revdelete-only-restricted' => 'Panjenengan ora bisa ndhelikaké révisi-révisi iki saka pangurus tanpa milih uga salah siji opsi pandhelikan liyané.',
+'revdelete-only-restricted' => 'Ora bisa ndhelikaké siji barang mawa tanggal $1 wanci $2: Sampéyan ora bisa ndhelikaké barang kuwi saka pangurus tanpa milih salah sawiji pilihan kanggo ndhelikaké sing liyané.',
+'revdelete-reason-dropdown' => '*Alesan mbusak sing umum
+** Planggaran hak cipta
+** Inpormasi pribadi sing ora patut
+** Inpormasi sing potènsial ngrusak martabat',
'revdelete-otherreason' => 'Alesan liya/tambahan:',
'revdelete-reasonotherlist' => 'Alesan liya',
'revdelete-edit-reasonlist' => 'Sunting alesan pambusakan',
# Suppression log
'suppressionlog' => "Log barang-barang sing didelikaké (''oversight'')",
-'suppressionlogtext' => "Ing ngisor iki kapacak daftar pambusakan lan pamblokiran pungkasan sing uga nyangkut isi sing didelikaké saka para opsis. Mangga mirsani [[Special:IPBlockList|daftar pamblokiran IP]] kanggo daftar pambuwangan (''ban'') lan pamblokiran sing saiki lagi operasional.",
+'suppressionlogtext' => 'Ngisor iki daptar apa-apa waé sing wis dibusak lan diblokir kalebu kontèn sing didhelikaké saka para pangurus.
+Delok [[Special:BlockList|daptar blokiran]] sing isiné daptar apa-apa waé sing lagi dilarang lan diblokir.',
# History merging
'mergehistory' => 'Gabung sejarah kaca',
'mergelogpagetext' => 'Ing ngisor iki kapacak daftar panggabungan sajarah kaca ing kaca liyané.',
# Diffs
-'history-title' => 'Sajarah revisi saka "$1"',
+'history-title' => 'Riwayat rèvisi saka "$1"',
'difference-title' => '$1: Bèntenipun revisi',
+'difference-title-multipage' => 'Prabédhan antara kaca "$1" lan "$2"',
+'difference-multipage' => '(Prabédhan antar kaca)',
'lineno' => 'Larikan $1:',
'compareselectedversions' => 'Bandhingna vèrsi kapilih',
'showhideselectedversions' => 'Tampilaké/dhelikaké révisi kapilih',
'editundo' => 'batalna',
-'diff-multi' => '({{PLURAL:$1|Sawiji|$1}} revisi antara sing ora dituduhaké.)',
+'diff-multi' => '({{PLURAL:$1Siji rèvisi sedhengan|$1 rèvisi sedhengan}} déning {{PLURAL:$2|sak panganggo|$2 panganggo}} ora dituduhaké)',
+'diff-multi-manyusers' => '({{PLURAL:$1Siji rèvisi sedhengan|$1 rèvisi sedhengan}} déning luwih saka $2 {{PLURAL:$2|panganggo|panganggo}} ora dituduhaké)',
+'difference-missing-revision' => '{{PLURAL:$2|Sak pambenahan|$2 pambenahan}} saka prabédan iki ($1) {{PLURAL:$2|ora ditemokaké|ora ditemokaké}}.
+
+Iki biasané kasebab pranala prabedan sing wis ora kanggo saka kaca isi wis dibusak.
+Rinciané bisa ditemokaké nèng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].',
# Search results
'searchresults' => 'Kasil panggolèkan',
'searchprofile-everything-tooltip' => 'Panggolèkan kabèh isi (klebu kaca-kaca wicara)',
'searchprofile-advanced-tooltip' => "Panggolèkan ing bilik jeneng biasa (''custom'')",
'search-result-size' => '$1 ({{PLURAL:$2|1 tembung|$2 tembung}})',
+'search-result-category-size' => '{{PLURAL:$1|1 anggota|$1 anggota}} ({{PLURAL:$2|1 subkatégori|$2 subkatégori}}, {{PLURAL:$3|1 berkas|$3 berkas}})',
'search-result-score' => 'Relevansi: $1%',
'search-redirect' => '(pangalihan $1)',
'search-section' => '(sèksi $1)',
'qbsettings-fixedright' => 'Tetep sisih tengen',
'qbsettings-floatingleft' => 'Ngambang sisih kiwa',
'qbsettings-floatingright' => 'Ngambang sisih tengen',
+'qbsettings-directionality' => 'Wis pesthi, gumantung saka wujud skrip basané Sampéyan',
# Preferences page
'preferences' => 'Preferensi (pilihan)',
'prefs-skin' => 'Kulit',
'skin-preview' => 'Pratilik',
'datedefault' => 'Ora ana préferènsi',
+'prefs-beta' => 'Piranti béta',
'prefs-datetime' => 'Tanggal lan wektu',
+'prefs-labs' => 'Piranti lab',
+'prefs-user-pages' => 'Kaca panganggo',
'prefs-personal' => 'Profil panganggo',
'prefs-rc' => 'Owah-owahan pungkasan',
'prefs-watchlist' => 'Dhaftar pangawasan',
'prefs-watchlist-days' => 'Cacahé dina sing dituduhaké ing dhaftar pangawasan:',
-'prefs-watchlist-days-max' => 'Maximum $1 {{PLURAL:$1|day|days}}',
+'prefs-watchlist-days-max' => 'Maksimum $1 {{PLURAL:$1|dina|dina}}',
'prefs-watchlist-edits' => 'Cacahé suntingan maksimum sing dituduhaké ing dhaftar pangawasan sing luwih jangkep:',
'prefs-watchlist-edits-max' => 'Gunggung maksimum: 1000',
'prefs-watchlist-token' => 'Token pantauan:',
'prefs-misc' => 'Liya-liya',
'prefs-resetpass' => 'Ganti tembung sandi',
+'prefs-changeemail' => 'Ganti alamat layang èlèktronik',
+'prefs-setemail' => 'Setèl alamat layang èlèktronik',
'prefs-email' => 'Opsi layang-e',
'prefs-rendering' => 'Tampilan',
'saveprefs' => 'Simpen',
'searchresultshead' => 'Panggolèkan',
'resultsperpage' => 'Cacahing klik saben kaca:',
'stub-threshold' => 'Ambang wates kanggo format <a href="#" class="stub">pranala rintisan</a>:',
+'stub-threshold-disabled' => 'Dipatèni',
'recentchangesdays' => 'Cacahé dina sing dituduhaké ing owah-owahan pungkasan:',
'recentchangesdays-max' => '(maksimum $1 {{PLURAL:$1|dina|dina}})',
'recentchangescount' => 'Cacahé suntingan sing ditampilaké:',
'savedprefs' => 'Préferènsi Panjenengan wis disimpen',
'timezonelegend' => 'Zona wektu:',
'localtime' => 'Wektu saenggon:',
-'timezoneuseserverdefault' => 'Gunakna standar server',
+'timezoneuseserverdefault' => 'Anggo gawan wiki ($1)',
'timezoneuseoffset' => 'Liya (jelasna prabédan)',
'timezoneoffset' => 'Prabédan¹:',
'servertime' => 'Wektu server:',
'prefs-files' => 'Berkas',
'prefs-custom-css' => 'CSS pribadi',
'prefs-custom-js' => 'JS pribadi',
+'prefs-common-css-js' => 'CSS/JS didumaké kanggo kabèh kulit:',
'prefs-reset-intro' => 'Panjenengan bisa migunakaké kaca iki kanggo mbalèkaké préferensi panjenengan marang setèlan baku situs.
Pembalikan ora bisa dibatalaké.',
'prefs-emailconfirm-label' => 'Konfirmasi layang-e:',
'prefs-registration' => 'Wektu régistrasi:',
'yourrealname' => 'Asma sajatiné :',
'yourlanguage' => 'Basa sing dianggo:',
-'yourvariant' => 'Varian basa',
+'yourvariant' => 'Werna basa isi:',
+'prefs-help-variant' => 'Varian utawa ortograpi sing Sampéyan pilih kanggo nampilaké kaca kontèn saka wiki iki.',
'yournick' => 'Asma sesinglon/samaran (kagem tapak asta):',
'prefs-help-signature' => 'Komentar ing kaca wicara kudu ditapak astani nganggo "<nowiki>~~~~</nowiki>" sing bakal dikonvèrsi dadi tapak asta panjenengan lan tanggal wektu.',
'badsig' => 'Tapak astanipun klèntu; cèk rambu HTML.',
Informasi iki bakal kabuka kanggo publik.',
'email' => 'Layang élèktronik (E-mail)',
'prefs-help-realname' => '* <strong>Asma asli</strong> (ora wajib): menawa panjenengan maringi, asma asli panjenengan bakal digunakaké kanggo mènèhi akrédhitasi kanggo kasil karya tulis panjenengan.',
-'prefs-help-email' => 'Layang-e (ora wajib), nanging sawanci-wanci panjenengan lali tembung sandi, bisa dikirimi liwat layang-e kasebut.
-Panjenengan uga bisa milih supaya wong liya ngubungi panjenengan liwat jeneng panganggo utawa kaca wicara panjenengan tanpa perlu nuduhaké idhèntitas panjenengan.',
+'prefs-help-email' => 'Alamat layang èlèktronik sipaté mung pilihan, nanging dibutuhaké kanggo nyetèl ulang tembung sandhi yèn Sampéyan lali.',
+'prefs-help-email-others' => 'Sampéyan uga bisa milih kanggo ngidinaké wong liya ngubungi Sampéyan liwat layang èlèktronik sing ana ing kaca panganggo utawa kaca guneman.
+Alamat layang èlèktronik Sampéyan ora dituduhaké nalika wong liya ngubungi Sampéyan.',
'prefs-help-email-required' => 'Alamat layang-e dibutuhaké.',
'prefs-info' => 'Informasi dhasar',
'prefs-i18n' => 'Internasionalisasi',
'prefs-displaywatchlist' => 'Opsi tampilan',
'prefs-diffs' => 'Prabédan',
+# User preference: e-mail validation using jQuery
+'email-address-validity-valid' => 'Alamat layang èlèktronik kayané sah',
+'email-address-validity-invalid' => 'Lebokaké alamat layang èlèktronik sing sah',
+
# User rights
'userrights' => 'Manajemen hak panganggo',
'userrights-lookup-user' => 'Ngatur kelompok panganggo',
'userrights-user-editname' => 'Lebokna jeneng panganggo:',
'editusergroup' => 'Sunting kelompok panganggo',
-'editinguser' => "Ngowahi hak-hak aksès panganggo saka panganggo '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]])",
+'editinguser' => "Ngganti hak panganggo '''[[User:$1|$1]]''' $2",
'userrights-editusergroup' => 'Sunting kelompok panganggo',
'saveusergroups' => 'Simpen kelompok panganggo',
'userrights-groupsmember' => 'Anggota saka:',
+'userrights-groupsmember-auto' => 'Anggota implisit saka:',
'userrights-groups-help' => 'Panjenengan bisa ngowahi grup-grup sing ana panganggoné iki.
* Kothak sing dicenthang tegesé panganggo iki ana sajroné grup iku.
* Kothak sing ora dicenthang tegesé panganggo iku ora ana ing grup iku.
'userrights-no-interwiki' => 'Panjenengan ora ana hak kanggo ngowahi hak panganggo ing wiki liyané.',
'userrights-nodatabase' => 'Basis data $1 ora ana utawa ora lokal.',
'userrights-nologin' => 'Panjenengan kudu [[Special:UserLogin|mlebu log]] mawa nganggo akun utawa rékening pangurus supaya bisa ngowahi hak panganggo.',
-'userrights-notallowed' => 'Panjenengan ora ndarbèni hak kanggo ngowahi hak panganggo.',
+'userrights-notallowed' => 'Akun Sampéyan ora nduwé idin kanggo nambah utawa nyuda hak-hak panganggo.',
'userrights-changeable-col' => 'Grup sing bisa panjenengan owahi',
'userrights-unchangeable-col' => 'Grup sing ora bisa diowahi panjenengan',
'group-suppress' => "Para pangawas (''oversight'')",
'group-all' => '(kabèh)',
-'group-user-member' => 'Panganggo',
-'group-autoconfirmed-member' => 'Panganggo sing otomatis didhedhes (dikonfirmasi)',
-'group-bot-member' => 'Bot',
-'group-sysop-member' => 'Pangurus',
-'group-bureaucrat-member' => 'Birokrat',
-'group-suppress-member' => "Pangawas (''oversight'')",
+'group-user-member' => '{{GENDER:$1|panganggo}}',
+'group-autoconfirmed-member' => '{{GENDER:$1|panganggo dipesthèni otomatis}}',
+'group-bot-member' => '{{GENDER:$1|bot}}',
+'group-sysop-member' => '{{GENDER:$1|pangurus}}',
+'group-bureaucrat-member' => '{{GENDER:$1|birokrat}}',
+'group-suppress-member' => '{{GENDER:$1|pangawasan}}',
'grouppage-user' => '{{ns:project}}:Para panganggo',
'grouppage-autoconfirmed' => '{{ns:project}}:Panganggo sing otomatis didhedhes (dikonfirmasi)',
'right-writeapi' => 'Migunakaké API panulisan',
'right-delete' => 'Busak kaca-kaca',
'right-bigdelete' => 'Busak kaca-kaca mawa sajarah panyuntingan sing gedhé',
+'right-deletelogentry' => 'Busak lan batalaké mbusak isi log spésipik',
'right-deleterevision' => 'Busak lan batal busak révisi tartamtu kaca-kaca',
'right-deletedhistory' => 'Ndeleng sajarah èntri-èntri kabusak, tanpa bisa ndeleng apa sing dibusak',
+'right-deletedtext' => 'Delok tèks kabusak lan panggantèn antara rèpisi kabusak',
'right-browsearchive' => 'Golèk kaca-kaca sing wis dibusak',
'right-undelete' => 'Batal busak sawijining kaca',
'right-suppressrevision' => 'Ndeleng lan mbalèkaké révisi-révisi sing didelikaké saka para opsis',
'right-hideuser' => 'Blokir jeneng panganggo, lan delikna saka umum',
'right-ipblock-exempt' => 'Bypass pamblokiran IP, pamblokiran otomatis lan pamblokiran rangkéan',
'right-proxyunbannable' => 'Bypass pamblokiran otomatis proxy-proxy',
+'right-unblockself' => 'Bukak blokirané dhéwéké',
'right-protect' => 'Ganti tingkatan pangreksan lan sunting kaca-kaca sing direksa',
'right-editprotected' => 'Sunting kaca-kaca sing direksa (tanpa pangreksan runtun)',
'right-editinterface' => 'Sunting interface (antarmuka) panganggo',
'right-siteadmin' => 'Kunci lan buka kunci basis data',
'right-override-export-depth' => "Èkspor kaca klebu kaca kagandhèng nganti tataran/''depth'' 5",
'right-sendemail' => 'Ngirim layang listrik (e-mail) menyang panganggo liya',
+'right-passwordreset' => 'Delok layang èlèktronik panyetèlulangan tembung sandhi',
# User rights log
'rightslog' => 'Log pangowahan hak aksès',
'rightslogtext' => 'Ing ngisor iki kapacak log pangowahan marang hak-hak panganggo.',
'rightslogentry' => 'ngganti kaanggotan kelompok kanggo $1 saka $2 dadi $3',
+'rightslogentry-autopromote' => 'otomatis ditawakaké saka $2 nèng $3',
'rightsnone' => '(ora ana)',
# Associated actions - in the sentence "You do not have permission to X"
'action-suppressionlog' => 'mirsani log pribadi iki',
'action-block' => 'blok panganggo iki saka panyuntingan',
'action-protect' => 'owahi tataran pangreksan kaca iki',
+'action-rollback' => 'gelis mbalèkaké suntingané panganggo pungkasan nèng sawijining saca',
'action-import' => 'impor kaca iki saka wiki liya',
'action-importupload' => 'impor kaca iki saka pamunggahan berkas',
'action-patrol' => 'nandhani suntingan panganggo liya minangka wis kapriksa',
'action-userrights' => 'ngowahi kabèh hak panganggo',
'action-userrights-interwiki' => 'ngowahi hak aksès saka panganggo ing wiki liya',
'action-siteadmin' => 'ngunci utawa mbukak kunci basis data',
+'action-sendemail' => 'kirim layang èlèktronik',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|pangowahan|owah-owahan}}',
'number_of_watching_users_pageview' => '[$1 {{PLURAL:$1|cacahé sing ngawasi|cacahé sing ngawasi}}]',
'rc_categories' => 'Watesana nganti kategori (dipisah karo "|")',
'rc_categories_any' => 'Apa waé',
+'rc-change-size-new' => '$1 {{PLURAL:$1|bita|bita}} sakwisé diowah',
'newsectionsummary' => '/* $1 */ bagéyan anyar',
'rc-enhanced-expand' => 'Tuduhaké princèn (merlokaké JavaScript)',
'rc-enhanced-hide' => 'Dhelikaké princèn',
+'rc-old-title' => 'wigatiné digawé minangka "$1"',
# Recent changes linked
'recentchangeslinked' => 'Pranala Pilihan',
'upload' => 'Unggah',
'uploadbtn' => 'Unggahna berkas',
'reuploaddesc' => 'Bali ing formulir pamotan',
+'upload-tryagain' => 'Kirim déskripsi berkas sing wis diowah',
'uploadnologin' => 'Durung mlebu log',
'uploadnologintext' => 'Panjenengan kudu [[Special:UserLogin|mlebu log]] supaya olèh ngunggahaké gambar utawa berkas liyané.',
'upload_directory_missing' => 'Direktori pamunggahan ($1) ora ditemokaké lan ora bisa digawé déning server wèb.',
'upload_directory_read_only' => 'Dirèktori pangunggahan ($1) ora bisa ditulis déning server wèb.',
'uploaderror' => 'Kaluputan pangunggahan berkas',
+'upload-recreate-warning' => "'''Pèngetan: Berkas mawa jeneng kuwi wis dibusak utawa disingkiraké.'''
+
+Log pambusakan lan panyingkiran saka kaca iki sumadhiya nèng kéné:",
'uploadtext' => "Anggé formulir ing ngandhap punika kanggé nginggahaké gambar.
Kanggé mirsani utawi madosi gambar ingkang sampun dipununggah sakdèrèngipun pigunakaken [[Special:FileList|dhaftar berkas sing wis diunggah]], gambar ingkang dipununggah ulang ugi kadhaftar ing [[Special:Log/upload|log pangunggahan]], pambusakan ing [[Special:Log/delete|Log pambusakan]].
'ignorewarnings' => 'Lirwakna pèngetan apa waé',
'minlength1' => 'Jeneng berkas paling ora minimal kudu awujud saaksara.',
'illegalfilename' => 'Jeneng berkas "$1" ngandhut aksara sing ora diparengaké ana sajroning irah-irahan kaca. Mangga owahana jeneng berkas iku lan cobanen diunggahaké manèh.',
+'filename-toolong' => 'Jeneng berkas ora olèh luwih dawa saka 240 bita.',
'badfilename' => 'Berkas wis diowahi dados "$1".',
+'filetype-mime-mismatch' => 'Èkstènsi berkas ".$1" ora cocok karo jinis MIME sing kadètèk saka berkas ($2).',
'filetype-badmime' => 'Berkas mawa tipe MIME "$1" ora pareng diunggahaké.',
'filetype-bad-ie-mime' => 'Ora bisa ngunggahaké berkas iki amarga Internet Explorer ndhétèksi minangka "$1", sing ora diidinaké lan minangka tipe berkas sing nduwèni potènsi mbebayani.',
'filetype-unwanted-type' => "'''\".\$1\"''' klebu jenis berkas sing ora diidinaké.
Luwih becik {{PLURAL:\$3|jinis berkas|Jinis-jinis berkas}} \$2.",
-'filetype-banned-type' => "'''\".\$1\"''' kalebu jenis berkas sing ora diidinaké.
-{{PLURAL:\$3|Jinis berkas sing|Jinis-jinis berkas sing}} diidinaké yaiku \$2.",
+'filetype-banned-type' => '\'\'\'".$1"\'\'\' {{PLURAL:$4|dudu jinis berkas sing dililakaké|dudu jinis berkas sing dililakaké}}.
+{{PLURAL:$3|Berkas|Berkas}} sing dililakaké $2.',
'filetype-missing' => 'Berkas ini ora duwé ekstènsi (contoné ".jpg").',
+'empty-file' => 'Berkas sing Sampéyan kirim kosong.',
+'file-too-large' => 'Berkas sing Sampéyan kirim kagedhèn.',
+'filename-tooshort' => 'Jeneng berkas kacendhèken.',
+'filetype-banned' => 'Jinis berkas iki dilarang.',
+'verification-error' => 'Berkas iki ora lulus pangesahan.',
+'hookaborted' => 'Pangowahan sing Sampéyan coba dibatalaké déning èkstènsi.',
+'illegal-filename' => 'Jeneng berkas ora dililakaké.',
+'overwrite' => 'Nibani berkas sing wis ana ora dililakaké.',
+'unknown-error' => 'Ana masalah sing ora dingertèni.',
+'tmp-create-error' => 'Ora bisa nggawé berkas sawetara.',
+'tmp-write-error' => 'Ora bisa nulis berkas sawetara.',
'large-file' => 'Ukuran berkas disaranaké supaya ora ngluwihi $1 bita; berkas iki ukurané $2 bita.',
'largefileserver' => 'Berkas iki luwih gedhé tinimbang sing bisa kaparengaké server.',
'emptyfile' => 'Berkas sing panjenengan unggahaké katoné kosong. Mbokmenawa iki amerga anané salah ketik ing jeneng berkas. Mangga dipastèkaké apa panjenengan pancèn kersa ngunggahaké berkas iki.',
+'windows-nonascii-filename' => 'Wiki iki ora nyengkuyung jeneng berkas mawa karakter kusus.',
'fileexists' => "Sawijining berkas mawa jeneng iku wis ana, mangga dipriksa '''<tt>[[:$1]]</tt>''' yèn panjenengan ora yakin sumedya ngowahiné.
[[$1|thumb]]",
'filepageexists' => "Kaca dèskripsi kanggo berkas iki wis digawé ing '''<tt>[[:$1]]</tt>''', nanging saiki iki ora ditemokaké berkas mawa jeneng iku. Ringkesan sing panjenengan lebokaké ora bakal metu ing kaca dèskripsi. Kanggo ngetokaké dèskripsi iki, panjenengan kudu nyunting sacara manual. [[$1|thumb]]",
'file-exists-duplicate' => 'Berkas iki duplikat utawa padha karo {{PLURAL:$1|berkas|berkas-berkas}} ing ngisor:',
'file-deleted-duplicate' => 'Sawijining berkas persis berkas iki ([[:$1]]) wis tau dibusak. Mangga panjenengan priksani sajarah pambusakan berkas kasebut sadurungé nerusaké ngunggahaké berkas kuwi manèh.',
'uploadwarning' => 'Pèngetan pangunggahan berkas',
+'uploadwarning-text' => 'Mangga owah katrangan berkas nèng ngisor lan coba manèh.',
'savefile' => 'Simpen berkas',
'uploadedimage' => 'gambar "[[$1]]" kaunggahaké',
'overwroteimage' => 'ngunggahaké vèrsi anyar saka "[[$1]]"',
'uploaddisabled' => 'Nuwun sèwu, fasilitas pangunggahan dipatèni.',
+'copyuploaddisabled' => 'Ngunggah mawa URL dipatèni.',
+'uploadfromurl-queued' => 'Unggahan Sampéyan wis mlebu antrian.',
'uploaddisabledtext' => 'Pangunggahan berkas ora diidinaké.',
'php-uploaddisabledtext' => 'Pangunggahan berkas dipatèni ing PHP.
Mangga priksa panyetèlan pangunggahan berkas.',
'uploadscripted' => 'Berkas iki ngandhut HTML utawa kode sing bisa diinterpretasi salah déning panjlajah wèb.',
'uploadvirus' => 'Berkas iki ngamot virus! Détil: $1',
+'uploadjava' => 'Berkas kuwi berkas ZIP sing kaisi berkas .class Java.
+Ngungga berkas Java ora dililakaké amarga bisa nyebabaké ngluwèhaké wates kamanan.',
'upload-source' => 'Berkas sumber',
'sourcefilename' => 'Jeneng berkas sumber',
'sourceurl' => 'URL sumber:',
Mangga priksanen $1 sadurungé ngunggahaké berkas iku manèh.',
'filename-bad-prefix' => "Jeneng berkas sing panjenengan unggahaké, diawali mawa '''\"\$1\"''', sing sawijining jeneng non-dèskriptif sing biasané diwènèhaké sacara otomatis déning kamera digital. Mangga milih jeneng liyané sing luwih dèskriptif kanggo berkas panjenengan.",
'upload-success-subj' => 'Kasil diamot',
+'upload-success-msg' => 'Unggahan Sampéyan saka [$2] sukses. Kuwi sumadhiya nèng kéné: [[:{{ns:file}}:$1]]',
+'upload-failure-subj' => 'Perkara pangunggahan',
'upload-failure-msg' => 'Ana prakara karo pangunggahan panjenengan seka [$2]:
$1',
'upload-warning-subj' => 'Pèngetan pangunggahan berkas',
'upload-too-many-redirects' => 'URL ngandhut kakèhan pengalihan',
'upload-unknown-size' => 'Ukuran ora diweruhi',
'upload-http-error' => 'Ana kasalahan HTTP: $1',
+'upload-copy-upload-invalid-domain' => 'Unggahan salinan ora sumadhiya nèng domain iki.',
+
+# File backend
+'backend-fail-stream' => 'Ora bisa milikaké berkas "$1".',
+'backend-fail-backup' => 'Ora bisa nyadangaké berkas "$1".',
+'backend-fail-notexists' => 'Berkas $1 ora ana.',
+'backend-fail-hashes' => 'Ora bisa ngéntukaké has berkas kanggo mbandingaké.',
+'backend-fail-notsame' => 'Berkas nonidèntik wis ana nèng "$1".',
+'backend-fail-invalidpath' => '"$1" dudu jurusan nyimpen sing sah.',
+'backend-fail-delete' => 'Ora bisa mbusak berkas "$1".',
+'backend-fail-alreadyexists' => 'Berkas "$1" wis ana.',
+'backend-fail-store' => 'Ora bisa nyèlèhaké berkas "$1" nèng "$2".',
+'backend-fail-copy' => 'Ora bisa nyalin berkas "$1" nèng "$2".',
+'backend-fail-move' => 'Ora bisa mindhahaké berkas "$1" nèng "$2".',
+'backend-fail-opentemp' => 'Ora bisa mbukak berkas sawetara.',
+'backend-fail-writetemp' => 'Ora bisa nulis berkas sawetara.',
+'backend-fail-closetemp' => 'Ora bisa nutup berkas sawetara.',
+'backend-fail-read' => 'Ora bisa maca berkas "$1".',
+'backend-fail-create' => 'Ora bisa nulis berkas "$1".',
+'backend-fail-maxsize' => 'Ora bisa nulis berkas "$1" amarga luwih gedhé saka {{PLURAL:$2|sak bita|$2 bita}}.',
+
+# Lock manager
+'lockmanager-notlocked' => 'Ora bisa mbukak gembok "$1"; kuwi ora kagembok.',
+'lockmanager-fail-closelock' => 'Ora bisa nutup berkas gembok kanggo "$1".',
+'lockmanager-fail-deletelock' => 'Ora bisa mbusak berkas gembok kanggo "$1".',
+'lockmanager-fail-acquirelock' => 'Ora bisa njaluk gembok kanggo "$1".',
+'lockmanager-fail-openlock' => 'Ora bisa mbukak berkas gembok kanggo "$1".',
+'lockmanager-fail-releaselock' => 'Ora bisa ngetokaké gembok kanggo "$1".',
+'lockmanager-fail-db-bucket' => 'Ora bisa ngubungi cukup basis data gembok nèng èmbèr $1.',
+
+# ZipDirectoryReader
+'zip-wrong-format' => 'Berkas sing diawèhaké dudu berkas ZIP.',
+
+# Special:UploadStash
+'uploadstash' => 'Unggah pandhelikan',
+'uploadstash-clear' => 'Busak berkas kadhelikaké',
+'uploadstash-nofiles' => 'Sampéyan ora nduwé berkas kadhelikaké.',
+'uploadstash-errclear' => 'Ngresiki berkas ora suksès.',
# img_auth script messages
'img-auth-accessdenied' => 'Aksès ditulak',
'sp-contributions-newbies-title' => 'Kontribusi panganggo anyar',
'sp-contributions-blocklog' => 'Log pemblokiran',
'sp-contributions-deleted' => 'kontribusi panganggo sing dibusak',
+'sp-contributions-uploads' => 'unggahan',
'sp-contributions-logs' => 'log',
'sp-contributions-talk' => 'wicara',
'sp-contributions-userrights' => 'pengaturan hak panganggo',
'sp-contributions-search' => 'Golèk kontribusi',
'sp-contributions-username' => 'Alamat IP utawa jeneng panganggo:',
+'sp-contributions-toponly' => 'Tuduhaké was suntingan saka benahan pungkasan',
'sp-contributions-submit' => 'Golèk',
# What links here
'whatlinkshere-filters' => 'Filter-filter',
# Block/unblock
+'autoblockid' => 'Blokir otomatis #$1',
+'block' => 'Blokir panganggo',
+'unblock' => 'Uculaké blokirané panganggo',
'blockip' => 'Blokir panganggo',
'blockip-title' => 'Blokir panganggo',
'blockip-legend' => 'Blokir panganggo',
'ipbhidename' => 'Delikna jeneng panganggo saka suntingan lan pratélan',
'ipbwatchuser' => 'Ngawasi kaca panganggo lan kaca-kaca dhiskusi panganggo iki',
'ipb-change-block' => 'Blokir manèh panganggo kanthi sèting iki',
+'ipb-confirm' => 'Pesthèkaké blokir',
'badipaddress' => 'Alamat IP klèntu',
'blockipsuccesssub' => 'Pemblokiran suksès',
'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] wis diblokir.<br />
-Pirsani [[Special:BlockList|Daftar blokir IP]] kanggo ninjo ulang pamblokiran.',
+Delok [[Special:BlockList|daptar blokir]] kanggo ninjo blokiran.',
+'ipb-blockingself' => 'Sampéyan arep mblokir Sampéyan dhéwé! Sampéyan yakin arep nglakoni kuwi?',
'ipb-edit-dropdown' => 'Sunting alesan pamblokiran',
'ipb-unblock-addr' => 'Ilangna blokir $1',
'ipb-unblock' => 'Ilangna blokir sawijining panganggo utawa alamat IP',
'unblockiptext' => 'Nggonen formulir ing ngisor iki kanggo mbalèkaké aksès nulis sawijining alamt IP utawa panganggo sing sadurungé diblokir.',
'ipusubmit' => 'Ilangna blokir iki',
'unblocked' => 'Blokir marang [[User:$1|$1]] wis dijabel',
+'unblocked-range' => '$1 ora diblokir manèh',
'unblocked-id' => 'Blokir $1 wis dijabel',
-'ipblocklist' => 'Daftar alamat-alamat IP lan para panganggo sing diblokir',
+'blocklist' => 'Panganggo diblokir',
+'ipblocklist' => 'Panganggo diblokir',
'ipblocklist-legend' => 'Golèk panganggo sing diblokir',
+'blocklist-userblocks' => 'Dhelikaké blokiran akun',
+'blocklist-tempblocks' => 'Dhelikaké blokiran sawetara',
+'blocklist-addressblocks' => 'Dhelikaké blokiran IP tunggal',
+'blocklist-rangeblocks' => 'Dhelikaké adohé blokiran',
+'blocklist-timestamp' => 'Cap wektu',
+'blocklist-target' => 'Patujon',
+'blocklist-expiry' => 'Kedaluwarsa',
+'blocklist-by' => 'Pangurus pamblokir',
+'blocklist-params' => 'Paramèter blokiran',
+'blocklist-reason' => 'Alesan',
'ipblocklist-submit' => 'Golèk',
'ipblocklist-localblock' => 'Blokade lokal',
+'ipblocklist-otherblocks' => '{{PLURAL:$1|Blokiran|Blokiran}} liya',
'infiniteblock' => 'salawasé',
'expiringblock' => 'kadaluwarsa ing $1, $2',
'anononlyblock' => 'namung anon',
'tooltip-diff' => 'Tuduhna owah-owahan panjenengan ing tèks iki.',
'tooltip-compareselectedversions' => 'Delengen prabédan antara rong vèrsi kaca iki sing dipilih.',
'tooltip-watch' => 'Tambahna kaca iki ing daftar pangawasan panjenengan',
+'tooltip-watchlistedit-normal-submit' => 'Singkiraké judhul',
+'tooltip-watchlistedit-raw-submit' => 'Anyari daptar pangawasan',
'tooltip-recreate' => 'Gawéa kaca iki manèh senadyan tau dibusak',
'tooltip-upload' => 'Miwiti pangunggahan',
'tooltip-rollback' => 'Mbalèkaké suntingan-suntingan ing kaca iki menyang kontributor pungkasan nganggo sak klik.',
'tooltip-undo' => 'Mbalèkaké révisi iki lan mbukak kothak panyuntingan jroning mode pratayang. Wènèhi kasempatan kanggo ngisi alesan ing kothak ringkesan.',
+'tooltip-preferences-save' => 'Simpen préperensi',
+'tooltip-summary' => 'Lebkaké ringkesan cedhèk',
# Metadata
'notacceptable' => 'Server wiki ora bisa nyedyakaké data sajroning format sing bisa diwaca déning klièn panjenengan.',
'spam_reverting' => 'Mbalèkaké menyang vèrsi pungkasan sing ora ana pranalané menyang $1',
'spam_blanking' => 'Kabèh révisi sing duwé pranala menyang $1, pangosongan',
+# Info page
+'pageinfo-title' => 'Inpormasi kanggo "$1"',
+'pageinfo-header-edits' => 'Suntingan',
+'pageinfo-header-watchlist' => 'Daptar pangawasan',
+'pageinfo-header-views' => 'Delokan',
+'pageinfo-subjectpage' => 'Kaca',
+'pageinfo-talkpage' => 'Kaca guneman',
+'pageinfo-watchers' => 'Cacahing pangawas',
+'pageinfo-edits' => 'Cacahing suntingan',
+'pageinfo-authors' => 'Cacahing beda-beda panganggit',
+'pageinfo-views' => 'Cacahing delokan',
+'pageinfo-viewsperedit' => 'Delokan per suntingan',
+
# Patrolling
'markaspatrolleddiff' => 'Tandhanana wis dipatroli',
'markaspatrolledtext' => 'Tandhanana artikel iki wis dipatroli',
'nextdiff' => 'Panyuntingan sing luwih anyar →',
# Media information
-'mediawarning' => "'''Pènget:''' Berkas iki mbokmenawa ngandhut kode sing bebayani, yèn dilakokaké sistém panjenengan bisa kena pangaruh ala.",
+'mediawarning' => "'''Pèngetan''': Jinis berkas iki mungkin isiné kodhé mbebayani.
+Yèn dilakokaké, sistem Sampéyan bisa kaserang.",
'imagemaxsize' => "Wates ukuran gambar:<br />''(kanggo kaca dhèskripsi berkas)''",
'thumbsize' => 'Ukuran gambar cilik (thumbnail):',
'widthheightpage' => '$1 × $2, $3 {{PLURAL:$3|kaca|kaca}}',
'file-info' => 'ukuran berkas: $1, tipe MIME: $2',
'file-info-size' => '$1 × $2 piksel, ukuran berkas: $3, tipe MIME: $4',
+'file-info-size-pages' => '$1 × $2 piksel, gedhéné berkas: $3, jinisé MIME: $4, $5 {{PLURAL:$5|kaca|kaca}}',
'file-nohires' => 'Ora ana résolusi sing luwih dhuwur.',
'svg-long-desc' => 'Berkas SVG, nominal $1 × $2 piksel, gedhené berkas: $3',
'show-big-image' => 'Résolusi kebak',
+'show-big-image-preview' => 'Gedhéné pratayang iki: $1',
+'show-big-image-other' => '{{PLURAL:$2|Résolusi|Résolusi}} liya: $1.',
+'show-big-image-size' => '$1 × $2 piksel',
'file-info-gif-looped' => 'mubeng',
'file-info-gif-frames' => '$1 {{PLURAL:$1|rangka|rangka}}',
+'file-info-png-looped' => 'mubeng',
+'file-info-png-repeat' => 'diputer {{PLURAL:$1|ping|ping}} $1',
+'file-info-png-frames' => '$1 {{PLURAL:$1|rangka|rangka}}',
# Special:NewFiles
'newimages' => 'Galeri berkas anyar',
'bydate' => 'miturut tanggal',
'sp-newimages-showfrom' => 'Tuduhna gambar anyar wiwit saka $2, $1',
+# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
+'seconds' => '{{PLURAL:$1|$1 detik|$1 detik}}',
+'minutes' => '{{PLURAL:$1|$1 menit|$1 menit}}',
+'hours' => '{{PLURAL:$1|$1 jam|$1 jam}}',
+'days' => '{{PLURAL:$1|$1 dina|$1 dina}}',
+'ago' => '$1 kapungkur',
+
# Bad image list
'bad_image_list' => "Formaté kaya mengkéné:
'exif-colorspace' => 'Papan werna',
'exif-componentsconfiguration' => 'Teges saben komponèn',
'exif-compressedbitsperpixel' => 'Modhe komprèsi gambar',
-'exif-pixelydimension' => 'Jembar gambar sing sah',
-'exif-pixelxdimension' => 'Dhuwur gambar sing sah',
+'exif-pixelydimension' => 'Jembaré gambar',
+'exif-pixelxdimension' => 'Dhuwuré gambar',
'exif-usercomment' => 'Komentar panganggo',
'exif-relatedsoundfile' => 'Berkas audio sing kagandhèng',
'exif-datetimeoriginal' => 'Tanggal lan wektu nggawé data',
'exif-gpsareainformation' => 'Jeneng wilayah GPS',
'exif-gpsdatestamp' => 'Tanggal GPS',
'exif-gpsdifferential' => 'Korèksi diférènsial GPS',
+'exif-jpegfilecomment' => 'Tanggepan berkas JPEG',
+'exif-keywords' => 'Tembung kunci',
+'exif-worldregiondest' => 'Wewengkon dunya katampilaké',
+'exif-countrydest' => 'Nagara katampilaké',
+'exif-countrycodedest' => 'Kodhe nagara katampilaké',
+'exif-provinceorstatedest' => 'Propinsi utawa nagara bagéyan katampilaké',
+'exif-citydest' => 'Kutha katampilaké',
+'exif-sublocationdest' => 'Dhaèrahé kutha katampilaké',
+'exif-objectname' => 'Judhul cendhèk',
+'exif-specialinstructions' => 'Prèntah kusus',
+'exif-headline' => 'Warta utama',
+'exif-credit' => 'Krédit/Panyadhiya',
+'exif-source' => 'Sumber',
+'exif-editstatus' => 'Status kapanyuntingan gambar',
+'exif-urgency' => 'Kawigatèn',
+'exif-fixtureidentifier' => 'Jeneng pikstur',
+'exif-locationdest' => 'Panggon digambaraké',
+'exif-locationdestcode' => 'Kodhe dhaérah kagambaraké',
+'exif-objectcycle' => 'Wektu katujon mèdia kuwi',
+'exif-contact' => 'Inpormasi kontak',
+'exif-writer' => 'Panulis',
+'exif-languagecode' => 'Basa',
+'exif-iimversion' => 'Vèrsi IIM',
+'exif-iimcategory' => 'Katègori',
+'exif-iimsupplementalcategory' => 'Katègori tambahan',
+'exif-datetimeexpires' => 'Aja dianggo sakbaré',
+'exif-datetimereleased' => 'Dimetukaké ing',
+'exif-rightscertificate' => 'Sertipikat pranata hak',
+'exif-copyrighted' => 'Status hak cipta',
+'exif-copyrightowner' => 'Sing ndarbèni hak cipta',
+'exif-usageterms' => 'Katemton panganggoan',
+'exif-webstatement' => 'Pranyatan hak cipta online',
+'exif-originaldocumentid' => 'ID unik dokumèn asli',
+'exif-licenseurl' => 'URL kanggo lisènsi hak cipta',
+'exif-morepermissionsurl' => 'Inpormasi lisènsi alternatip',
+'exif-pngfilecomment' => 'Tanggepan berkas PNG',
+'exif-disclaimer' => 'Pamaidonan',
+'exif-contentwarning' => 'Pèngetan kontèn',
+'exif-giffilecomment' => 'Tanggepan berkas GIF',
+'exif-intellectualgenre' => 'Jinis barang',
+'exif-subjectnewscode' => 'Aturan jejer',
+'exif-scenecode' => 'Aturan adegan IPTC',
+'exif-event' => 'Kadadéan digambaraké',
+'exif-organisationinimage' => 'Organisasi digambaraké',
+'exif-personinimage' => 'Uwong digambaraké',
+'exif-originalimageheight' => 'Dhuwuré gambar sakdurungé dikethok',
+'exif-originalimagewidth' => 'Jembaré gambar sakdurungé dikethok',
# EXIF attributes
'exif-compression-1' => 'Ora dikomprèsi',
+'exif-copyrighted-true' => 'Mawa hak cipta',
+'exif-copyrighted-false' => 'Domain umum',
+
'exif-unknowndate' => 'Tanggal ora dingertèni',
'exif-orientation-1' => 'Normal',
'exif-orientation-3' => 'Diputer 180°',
'exif-orientation-4' => 'Baliken sacara vèrtikal',
'exif-orientation-5' => 'Diputer 90° nglawan arah dom jam dan dibalik sacara vèrtikal',
-'exif-orientation-6' => 'Diputer 90° miturut arah dom jam',
+'exif-orientation-6' => 'Puter 90° lawan arah dom jam',
'exif-orientation-7' => 'Diputer 90° miturut arah dom jam lan diwalik sacara vèrtikal',
-'exif-orientation-8' => 'Diputer 90° miturut lawan arah dom jam',
+'exif-orientation-8' => 'Puter 90° saarah dom jam',
'exif-planarconfiguration-1' => "format ''chunky'' (kumothak)",
'exif-planarconfiguration-2' => 'format planar',
+'exif-colorspace-65535' => 'Ora dikalibrasi',
+
'exif-componentsconfiguration-0' => 'ora ana',
'exif-exposureprogram-0' => 'Ora didéfinisi',
'exif-sensingmethod-7' => 'Sènsor trilinéar',
'exif-sensingmethod-8' => 'Sènsor linéar werna urut-urutan',
+'exif-filesource-3' => 'Kaméra meneng digital',
+
'exif-scenetype-1' => 'Gambar foto langsung',
'exif-customrendered-0' => 'Prosès normal',
'exif-gpslongitude-e' => 'Bujur wétan',
'exif-gpslongitude-w' => 'Bujur kulon',
+# Pseudotags used for GPSAltitudeRef
+'exif-gpsaltitude-above-sealevel' => '$1 {{PLURAL:$1|mèter|mèter}} ndhuwur segara',
+'exif-gpsaltitude-below-sealevel' => '$1 {{PLURAL:$1|mèter|mèter}} ngisor segara',
+
'exif-gpsstatus-a' => 'Pangukuran lagi dilakoni',
'exif-gpsstatus-v' => 'Interoperabilitas pangukuran',
'exif-gpsspeed-m' => 'Mil per jam',
'exif-gpsspeed-n' => 'Knot',
+# Pseudotags used for GPSDestDistanceRef
+'exif-gpsdestdistance-k' => 'Kilomèter',
+'exif-gpsdestdistance-m' => 'Mil',
+'exif-gpsdestdistance-n' => 'Mil segara',
+
+'exif-gpsdop-excellent' => 'Apik banget ($1)',
+'exif-gpsdop-good' => 'Apik ($1)',
+'exif-gpsdop-moderate' => 'Sedhengan ($1)',
+'exif-gpsdop-fair' => 'Cukup ($1)',
+'exif-gpsdop-poor' => 'Èlèk ($1)',
+
+'exif-objectcycle-a' => 'Èsuk thok',
+'exif-objectcycle-p' => 'Mbengi thok',
+'exif-objectcycle-b' => 'Èsuk lan mbengi',
+
# Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
'exif-gpsdirection-t' => 'Arah sejati',
'exif-gpsdirection-m' => 'Arah magnètis',
+'exif-ycbcrpositioning-1' => 'Kapusat',
+
+'exif-dc-contributor' => 'Kontributor',
+'exif-dc-coverage' => 'Cakepan latar utawa wektu média',
+'exif-dc-date' => 'Tanggal',
+'exif-dc-publisher' => 'Panyithak',
+'exif-dc-relation' => 'Média kakait',
+'exif-dc-rights' => 'Hak',
+'exif-dc-source' => 'Mèdia sumber',
+'exif-dc-type' => 'Jinisé média',
+
+'exif-rating-rejected' => 'Ditolak',
+
+'exif-isospeedratings-overflow' => 'Luwih saka 65535',
+
+'exif-iimcategory-ace' => 'Seni, budhaya lan dolanan',
+'exif-iimcategory-clj' => 'Kriminal lan ukum',
+'exif-iimcategory-dis' => 'Musibah lan kacilakan',
+'exif-iimcategory-fin' => 'Èkonomi lan bisnis',
+'exif-iimcategory-edu' => 'Pandhidhikan',
+'exif-iimcategory-evn' => 'Lingkungan',
+'exif-iimcategory-hth' => 'Kasehatan',
+'exif-iimcategory-hum' => 'Kasenengan manungsa',
+'exif-iimcategory-lab' => 'Buruh',
+'exif-iimcategory-lif' => 'Gaya urip lan peprèian',
+'exif-iimcategory-pol' => 'Politik',
+'exif-iimcategory-rel' => 'Agama lan kapitayan',
+'exif-iimcategory-sci' => 'Èlmu lan tehnologi',
+'exif-iimcategory-soi' => 'Bab masarakat',
+'exif-iimcategory-spo' => 'Krida',
+'exif-iimcategory-war' => 'Perang, cengkah, rusuh',
+'exif-iimcategory-wea' => 'Mangsa',
+
+'exif-urgency-normal' => 'Sedhengan ($1)',
+'exif-urgency-low' => 'Cendhèk ($1)',
+'exif-urgency-high' => 'Dhuwur ($1)',
+'exif-urgency-other' => 'Prioritas sing ditetepaké panganggo ($1)',
+
# External editor support
'edit-externally' => 'Sunting berkas iki mawa aplikasi jaba',
'edit-externally-help' => '(Deleng [//www.mediawiki.org/wiki/Manual:External_editors instruksi pangaturan] kanggo informasi sabanjuré)',
'confirm-purge-top' => "Busak ''cache'' kaca iki?",
'confirm-purge-bottom' => 'Ngresiki kaca bakal sekaligus mbusak singgahan lan nampilaké vèrsi kaca pungkasan.',
+# action=watch/unwatch
+'confirm-watch-button' => 'Oké',
+'confirm-watch-top' => 'Tambahaké kaca iki nènh daptar pangawasan Sampéyan?',
+'confirm-unwatch-button' => 'Oké',
+'confirm-unwatch-top' => 'Singkiraké kaca iki saka daptar pangawasan Sampéyan?',
+
# Multipage image navigation
'imgmultipageprev' => '← kaca sadurungé',
'imgmultipagenext' => 'kaca sabanjuré →',
'table_pager_first' => 'Kaca kapisan',
'table_pager_last' => 'Kaca pungkasan',
'table_pager_limit' => 'Tuduhna $1 entri per kaca',
+'table_pager_limit_label' => 'Barang per kaca:',
'table_pager_limit_submit' => 'Golèk',
'table_pager_empty' => 'Ora ditemokaké',
'watchlisttools-edit' => 'Tuduhna lan sunting daftar pangawasan',
'watchlisttools-raw' => 'Sunting daftar pangawasan mentah',
+# Signatures
+'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|wicara]])',
+
# Core parser functions
'unknown_extension_tag' => 'Tag èkstènsi ora ditepungi "$1"',
'duplicate-defaultsort' => 'Pènget: Kunci pilih asal (\'\'Default sort key\'\') "$2" nggantèkaké kunci pilih asal sadurungé "$1".',
'version-specialpages' => 'Kaca astaméwa (kaca kusus)',
'version-parserhooks' => 'Canthèlan parser',
'version-variables' => 'Variabel',
+'version-antispam' => 'Pambendhung spam',
+'version-skins' => 'Kulit',
'version-other' => 'Liyané',
'version-mediahandlers' => 'Pananganan média',
'version-hooks' => 'Canthèlan-canthèlan',
'version-hook-subscribedby' => 'Dilanggani déning',
'version-version' => '(Vèrsi $1)',
'version-license' => 'Lisènsi',
+'version-poweredby-credits' => "Wiki iki disengkuyung déning '''[//www.mediawiki.org/ MediaWiki]''', hak cipta © 2001-$1 $2.",
+'version-poweredby-others' => '[{{SERVER}}{{SCRIPTPATH}}/KRÈDIT liyané]',
'version-software' => "''Software'' wis diinstalasi",
'version-software-product' => 'Prodhuk',
'version-software-version' => 'Vèrsi',
+'version-entrypoints' => 'URL tithik lebon',
+'version-entrypoints-header-entrypoint' => 'Tithik lebon',
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'Lokasi berkas',
'fileduplicatesearch-info' => '$1 × $2 piksel<br />Ukuran berkas: $3<br />Tipe MIME: $4',
'fileduplicatesearch-result-1' => 'Berkas "$1" ora duwé duplikat idèntik.',
'fileduplicatesearch-result-n' => 'Berkas "$1" ora ndarbèni {{PLURAL:$2|1 duplikat idèntik|$2 duplikat idèntik}}.',
+'fileduplicatesearch-noresults' => 'Ora ana berkas mawa jeneng "$1" ditemokaké.',
# Special:SpecialPages
'specialpages' => 'Kaca istiméwa',
'specialpages-note' => '----
-Katrangan tampilan:
-* Kaca astamèwa normal
-* <strong class="mw-specialpagerestricted">Kaca astamèwa winates</strong>',
+* Kaca astamiwa biasa.
+* <span class="mw-specialpagerestricted">Kaca astamiwa kawatesan.</span>',
'specialpages-group-maintenance' => 'Lapuran pangopènan',
'specialpages-group-other' => 'Kaca-kaca astaméwa liyané',
-'specialpages-group-login' => 'Mlebu log / ndaftar',
+'specialpages-group-login' => 'Mlebu log / nggawé akun',
'specialpages-group-changes' => 'Owah-owahan pungkasan lan log',
'specialpages-group-media' => 'Lapuran média lan pangunggahan',
'specialpages-group-users' => 'Panganggo lan hak-haké',
'compare-page2' => 'Kaca 2',
'compare-rev1' => 'Révisi 1',
'compare-rev2' => 'Révisi 2',
+'compare-submit' => 'Bandingaké',
+'compare-invalid-title' => 'Judhul sing Sampéyan awèhaké ora sah.',
+'compare-title-not-exists' => 'Judhul sing Sampéyan jaluk ora ana.',
+'compare-revision-not-exists' => 'Benahan sing Sampéyan jaluk ora ana.',
# Database error messages
'dberr-header' => 'Wiki iki duwé masalah',
'htmlform-float-invalid' => 'Sing panjenengan lebokaké dudu angka.',
'htmlform-int-toolow' => 'Aji sing panjenengan lebokaké keciliken ing sangisoré aji minimum $1',
'htmlform-int-toohigh' => 'Aji sing panjenengan lebokaké kegedhèn ngluwihi aji maksimum $1',
+'htmlform-required' => 'Nilé iki dibutuhaké',
'htmlform-submit' => 'Kirim',
'htmlform-reset' => 'Batalna pangowahan',
'htmlform-selectorother-other' => 'Liya',
+# SQLite database support
+'sqlite-has-fts' => '$1 mawa sengkuyungan golèkan tèks jangkep',
+'sqlite-no-fts' => '$1 tanpa sengkuyungan golèkan tèks jangkep',
+
# New logging system
+'logentry-delete-delete' => '$1 mbusak kaca $3',
+'logentry-delete-restore' => '$1 mbalèkaké kaca $3',
+'logentry-delete-event' => '$1 ngganti patampilan {{PLURAL:$5|sak kadadéan log|$5 kadadéan log}} nèng $3: $4',
+'logentry-delete-revision' => '$1 ngganti patampilan {{PLURAL:$5|sak pambenahan|$5 pambenahan}} nèng kaca $3: $4',
+'logentry-delete-event-legacy' => '$1 ngganti patampilan saka kadadéan log nèng $3',
+'logentry-delete-revision-legacy' => '$1 ngganti patampilan saka pambenahan nèng kaca $3',
+'logentry-suppress-delete' => '$1 neken kaca $3',
+'logentry-suppress-event' => '$1 ndhelik-ndhelik ngganti patampilan saka {{PLURAL:$5|sak kadadéan log|$5 kadadéan log}} nèng $3: $4',
+'logentry-suppress-revision' => '$1 ndhelik-ndhelik ngganti patampilan saka {{PLURAL:$5|sak pambenahan|$5 pambenahan}} nèng kaca $3: $4',
+'logentry-suppress-event-legacy' => '$1 ndhelik-ndhelik ngganti patampilan saka kadadéan log nèng $3',
+'logentry-suppress-revision-legacy' => '$1 ndhelik-ndhelik ngganti patampilan saka pambenahan nèng kaca $3',
+'revdelete-content-hid' => 'kontèn didhelikaké',
+'revdelete-summary-hid' => 'ringkesan suntingan didhelikaké',
+'revdelete-uname-hid' => 'jeneng panganggo didhelikaké',
+'revdelete-content-unhid' => 'kontèn dituduhaké',
+'revdelete-summary-unhid' => 'ringkesan suntingan dituduhaké',
+'revdelete-uname-unhid' => 'jeneng panganggo dituduhaké',
'revdelete-restricted' => 'rèstriksi ditrapaké marang para opsis',
'revdelete-unrestricted' => 'rèstriksi marang para opsis dijabel',
+'logentry-move-move' => '$1 mindhahaké kaca $3 nèng $4',
+'logentry-move-move-noredirect' => '$1 mindhahaké kaca $3 nèng $4 tanpa nginggalaké pangalihan',
+'logentry-move-move_redir' => '$1 mindhahaké kaca $3 nèng $4 ngliwati pangalihan',
+'logentry-move-move_redir-noredirect' => '$1 mindhahaké kaca $3 nèng $4 ngliwati pangalihan tanpa nginggalaké pangalihan',
+'logentry-patrol-patrol' => '$1 nandhai benahan $4 saka kaca $3 kaawasi',
+'logentry-patrol-patrol-auto' => '$1 otomatis nandhai benahan $4 saka kaca $3 kaawasai',
+'logentry-newusers-newusers' => '$1 nggawé akun panganggo',
+'logentry-newusers-create' => '$1 nggawé akun panganggo',
+'logentry-newusers-create2' => '$1 nggawé akun panganggo $3',
+'logentry-newusers-autocreate' => 'Akun $1 digawé otomatis',
'newuserlog-byemail' => 'tembung sandhi wis dikirim liwat e-mail',
+# Feedback
+'feedback-bugornote' => 'Yèn Sampéyan siap njelasaké masalah tèhnis kanthi rinci mangga [$1 laporaké bug].
+Utawa, Sampéyan bisa nganggo pormulir gampang ngisor. Tanggepan Sampéyan bakal ditambahaké nèng kaca "[$3 $2]", bebarengan karo jeneng panganggo Sampéyan lan pramban sing Sampéyan anggo.',
+'feedback-subject' => 'Jejer:',
+'feedback-message' => 'Layang:',
+'feedback-cancel' => 'Batal',
+'feedback-submit' => 'Kirim Lebon Saran',
+'feedback-adding' => 'Nambahaké lebon saran nèng kaca...',
+'feedback-error1' => 'Kasalahan: Asil ora dikenal saka API',
+'feedback-error2' => 'Kasalahan: Gagal nyunting',
+'feedback-error3' => 'Kasalahan: Ora ana tanggepan saka API',
+'feedback-thanks' => 'Nuwun! Lebon saran Sampéyan wis dipasang nèng kacané "[$2 $1]".',
+'feedback-close' => 'Rampung',
+'feedback-bugcheck' => 'Apik! Pesthèké kuwi dudu sawijining [$1 bug sing dingertèni].',
+'feedback-bugnew' => 'Aku wis mriksa. Kandakaké bug anyar',
+
+# API errors
+'api-error-badaccess-groups' => 'Sampéyan ora dililakaké ngunggah berkas nèng wiki iki.',
+'api-error-badtoken' => 'Kasalahan njero: Token èlèk.',
+'api-error-copyuploaddisabled' => 'Ngunggah saka URL dipatèni nèng sasana iki.',
+'api-error-duplicate' => 'Ana {{PLURAL:$1|[$2 berkas liya]|[$2 pirang-pirang berkas liya]}} sing wis ana nèng situsé saha isiné padha.',
+'api-error-duplicate-archive' => 'Ana {{PLURAL:$1|[$2 berkas liya]|[$2 pirang-pirang berkas liya]}} sing wis ana nèng situsé saha isiné padha, nanging {{PLURAL:$1|kuwi|kuwi kabèh}} wis dibusak.',
+'api-error-duplicate-archive-popup-title' => 'Gandhakaké {{PLURAL:$1berkas sing wis|berkas sing wis}} dibusak.',
+'api-error-duplicate-popup-title' => 'Gandhakaké {{PLURAL:$1berkas|berkas}}.',
+'api-error-empty-file' => 'Berkas sing Sampéyan kirim kosong.',
+'api-error-emptypage' => 'Nggawé kaca kosong anyar ora dilikaké.',
+'api-error-fetchfileerror' => 'Kasalahan njero: Ana sing salah nalika ngètukaké berkas iki.',
+'api-error-fileexists-forbidden' => 'Berkas mawa jeneng "$1" wis ana, lan ora bisa diganti.',
+'api-error-fileexists-shared-forbidden' => 'Berkas mawa jeneng "$1" wis ana nèng gudhang berkas bebarengan, lan ora bisa diganti.',
+'api-error-file-too-large' => 'Berkas sing Sampéyan kirim kagedhèn.',
+'api-error-filename-tooshort' => 'Jeneng berkas kacendhèken.',
+'api-error-filetype-banned' => 'Jinis berkas iki dilarang.',
+'api-error-filetype-missing' => 'Jeneng berkas ora nduwèni èkstènsi.',
+'api-error-hookaborted' => 'Pangowahan sing Sampéyan coba dibatalaké déning èkstènsi.',
+'api-error-http' => 'Kasalahan njero: Ora bisa ngubungi sasana.',
+'api-error-illegal-filename' => 'Jeneng berkas ora dililakaké.',
+'api-error-internal-error' => 'Kasalahan njero: Ana sing salah saka pamrosèsan unggahan Sampéyan nèng wiki.',
+'api-error-invalid-file-key' => 'Kasalahan njero: Berkas ora ditemokaké nèng panyimpenan sawetara.',
+'api-error-missingparam' => 'Kasalahan njero: Paramètèr panjalukan ilang.',
+'api-error-missingresult' => 'Kasalahan njero: Ora bisa mesthèkaké yèn nyaliné suksès.',
+'api-error-mustbeloggedin' => 'Sampéyan kudu mlebu log kanggo ngunggah berkas.',
+'api-error-mustbeposted' => 'Kasalahan njero: Panjalukan mbutuhaké HTTP POST.',
+'api-error-noimageinfo' => 'Ngunggah suksès. nanging sasana ora ngawèhi awak dhéwé katrangan bab berkas kuwi.',
+'api-error-nomodule' => 'Kasalahan njero: Ora ana modul ngunggah sing dipatrapaké.',
+'api-error-ok-but-empty' => 'Kasalahan njero: Ora ana tanggepan saka sasana.',
+'api-error-overwrite' => 'Nibani berkas sing wis ana ora dililakaké.',
+'api-error-stashfailed' => 'Kasalahan njero: Sasana gagal nyèlèhaké berkas sawetara.',
+'api-error-timeout' => 'Sasana ora nanggepi nèng wektu sing karepaké.',
+'api-error-unclassified' => 'Ana masalah sing ora dingertèni.',
+'api-error-unknown-code' => 'Kasalahan ora dingertèni: "$1".',
+'api-error-unknown-error' => 'Kasalahan njero: Ana sing salah nalika njajal ngunggah berkas Sampéyan.',
+'api-error-unknown-warning' => 'Pèngetan ora dingertèni: "$1".',
+'api-error-unknownerror' => 'Kasalahan ora dingertèni: "$1".',
+'api-error-uploaddisabled' => 'Piranti ngunggah dipatèni nèng wiki iki.',
+'api-error-verification-error' => 'Berkas iki mungkin rusak, utawa nduwéni èkstènsi salah.',
+
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|detik|detik}}',
+'duration-minutes' => '$1 {{PLURAL:$1|menit|menit}}',
+'duration-hours' => '$1 {{PLURAL:$1|jam|jam}}',
+'duration-days' => '$1 {{PLURAL:$1|dina|dina}}',
+'duration-weeks' => '$1 {{PLURAL:$1|minggu|minggu}}',
+'duration-years' => '$1 {{PLURAL:$1|taun|taun}}',
+'duration-decades' => '$1 {{PLURAL:$1|dékade|dékade}}',
+'duration-centuries' => '$1 {{PLURAL:$1|abad|abad}}',
+'duration-millennia' => '$1 {{PLURAL:$1|milénium|milénium}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|dudu jinis berkas sing dililakaké|dudu jinis berkas sing dililakaké}}. {{PLURAL:$3|Jinis berkas|Jinis berkas}} sing dililakaké $2.',
);
$messages = array(
# User preference toggles
-'tog-underline' => 'á\83®á\83\90á\83\96á\83\98 á\83\92á\83\90á\83£á\83¡á\83\95á\83\98 á\83\91á\83\9bá\83£á\83\9aá\83\94á\83\91á\83¡:',
+'tog-underline' => 'á\83\91á\83\9bá\83£á\83\9aá\83\94á\83\91á\83\98á\83¡ á\83®á\83\90á\83\96á\83\92á\83\90á\83¡á\83\9bá\83\90:',
'tog-justify' => 'გაასწორე პარაგრაფები',
'tog-hideminor' => 'დამალე მცირე რედაქტირება ბოლო ცვლილებებში',
'tog-hidepatrolled' => 'დამალეთ შესწორებული რედაქტირებები ბოლო ცვლილებებში',
'youhavenewmessages' => 'თქვენ გაქვთ $1 ($2).',
'newmessageslink' => 'ახალი შეტყობინებები',
'newmessagesdifflink' => 'განსხვავება წინა ვერსიასთან',
+'newmessageslinkplural' => '{{PLURAL:$1|ახალი შეტყობინება|ახალი შეტყობინება}}',
+'newmessagesdifflinkplural' => 'ბოლო {{PLURAL:$1|ცვლილება|ცვლილება}}',
'youhavenewmessagesmulti' => 'თქვენ გაქვთ ახალი შეტყობინება $1-ზე',
'editsection' => 'რედაქტირება',
'editsection-brackets' => '[$1]',
'remembermypassword' => 'დამიმახსოვრე ამ კომპიუტერზე (მაქსიმუმ $1 {{PLURAL:$1|დღე}})',
'securelogin-stick-https' => 'გააგრძელეთ კავშირი HTTPS-თან შესვლის შემდეგ',
'yourdomainname' => 'თქვენი დომენი',
+'password-change-forbidden' => 'თქვენ არ შეგიძლიათ ამ ვიკიში პაროლის შეცვლა.',
'externaldberror' => 'საგარეო მონაცემთა ბაზაში აუტენტიფიკაციის შეცდომაა, ან თქვენ არ გაქვთ საკმარისი უფლებები საგარეო ანგარიშში ცვლილებების შესატანად.',
'login' => 'შესვლა',
'nav-login-createaccount' => 'შესვლა / რეგისტრაცია',
'right-writeapi' => 'API-ს გამოყენება ჩაწერისთვის',
'right-delete' => 'გვერდების წაშლა',
'right-bigdelete' => 'გრძელი ისტორიის გვერდების წაშლა',
+'right-deletelogentry' => 'ჟურნალის კონკრეტული ჩანაწერების წაშლა და აღდგენა',
'right-deleterevision' => 'გვერდის კონკრეტული ვერსიების წაშლა და აღდგენა',
'right-deletedhistory' => 'წაშლილი გვერდების ხილავა წაშლილ ტექსთან ურთიერთობის გარეშე',
'right-deletedtext' => 'წაშლილი ტექსტის და განსხვავებების ხილვა.',
'listfiles_search_for' => 'ძიება სურათის სახელის მიხედვით:',
'imgfile' => 'ფაილი',
'listfiles' => 'სურათების სია',
-'listfiles_thumb' => 'მინიატურა',
+'listfiles_thumb' => 'á\83\9bá\83\98á\83\9cá\83\98á\83\90á\83¢á\83\98á\83£á\83 á\83\90',
'listfiles_date' => 'თარიღი',
'listfiles_name' => 'სახელი',
'listfiles_user' => 'მომხმარებელი',
'filehist-revert' => 'დააბრუნე',
'filehist-current' => 'მიმდინარე',
'filehist-datetime' => 'თარიღი/დრო',
-'filehist-thumb' => 'მინიატურა',
+'filehist-thumb' => 'á\83\9bá\83\98á\83\9cá\83\98á\83\90á\83¢á\83\98á\83£á\83 á\83\90',
'filehist-thumbtext' => 'მინიატურა $1 ვერსიისთვის',
'filehist-nothumb' => 'არ არის მინიატურა',
'filehist-user' => 'მომხმარებელი',
'disambiguations' => 'გვერდები, რომელთაც აქვთ ბმული მრავალმნიშვნელოვან გვერდებზე',
'disambiguationspage' => 'Template:მრავალმნიშვნელოვანი',
-'disambiguations-text' => "á\83¨á\83\94á\83\9bá\83\93á\83\94á\83\92á\83\98 á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91á\83\98 á\83\93á\83\90á\83\99á\83\90á\83\95á\83¨á\83\98á\83 á\83\94á\83\91á\83£á\83\9aá\83\94á\83\91á\83\98 á\83\90á\83 á\83\98á\83\90á\83\9c '''á\83\9bá\83 á\83\90á\83\95á\83\90á\83\9aá\83\9bá\83\9cá\83\98á\83¨á\83\95á\83\9cá\83\94á\83\9aá\83\9dá\83\91á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\97á\83\90á\83\9c'''.
-ამის ნაცვლად იგი უნდა შეესაბამოს კონკრეტულ სტატიას.<br />
-á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 á\83\98á\83\97á\83\95á\83\9aá\83\94á\83\91á\83\90 á\83\9bá\83 á\83\90á\83\95á\83\90á\83\9aá\83\9bá\83\9cá\83\98á\83¨á\83\95á\83\9cá\83\94á\83\9aá\83\9dá\83\95á\83\9cá\83\90á\83\93, á\83\97á\83£ á\83\9bá\83\90á\83¡á\83\96á\83\94 á\83\92á\83\90á\83\9bá\83\9dá\83§á\83\94á\83\9cá\83\94á\83\91á\83£á\83\9aá\83\98á\83\90 [[MediaWiki:Disambiguationspage|á\83\9bá\83\98á\83\97á\83¡á\83\90á\83\97á\83\98á\83\97á\83\94á\83\91á\83\94á\83\9aá\83\98 á\83\97á\83\90á\83 á\83\92á\83\98]].",
+'disambiguations-text' => "á\83¨á\83\94á\83\9bá\83\93á\83\94á\83\92á\83\98 á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91á\83\98 á\83¨á\83\94á\83\98á\83ªá\83\90á\83\95á\83\94á\83\9c á\83¡á\83£á\83\9a á\83\9bá\83ªá\83\98á\83 á\83\94 á\83\94á\83 á\83\97 á\83\91á\83\9bá\83£á\83\9aá\83¡ '''á\83\9bá\83 á\83\90á\83\95á\83\90á\83\9aá\83\9bá\83\9cá\83\98á\83¨á\83\95á\83\9cá\83\94á\83\9aá\83\9dá\83\91á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94'''.
+ამის ნაცვლად, სავარაუდოდ, ისისნი უნდა მიუთითებდნენ შესაბამის კონკრეტულ სტატიაზე.<br />
+á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 á\83\98á\83\97á\83\95á\83\9aá\83\94á\83\91á\83\90 á\83\9bá\83 á\83\90á\83\95á\83\90á\83\9bá\83\9cá\83\98á\83¨á\83\95á\83\9cá\83\94á\83\9aá\83\9dá\83\95á\83\9cá\83\90á\83\93, á\83\97á\83£ á\83\9bá\83\90á\83¡á\83¨á\83\98 á\83\92á\83\90á\83\9cá\83\97á\83\90á\83\95á\83¡á\83\94á\83\91á\83£á\83\9aá\83\98á\83\90 á\83\97á\83\90á\83 á\83\92á\83\98, á\83 á\83\9dá\83\9bá\83\9aá\83\98á\83¡ á\83¡á\83\90á\83®á\83\94á\83\9aá\83\98á\83ª á\83\9bá\83\98á\83\97á\83\98á\83\97á\83\94á\83\91á\83£á\83\9aá\83\98á\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'ორმაგი გადამისამართება',
'doubleredirectstext' => 'ამ გვერდზე ჩამოთვლილია გვერდები, რომლებიც გადამისამართებულია სხვა გადამისამართების გვერდებზე.
'rollback' => 'რცვლილებების გაუქმება',
'rollback_short' => 'სწრაფი გაუქმება',
'rollbacklink' => 'სწრაფი გაუქმება',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|ცვლილების|ცვლილების}} დაბრუნება',
+'rollbacklinkcount-morethan' => '$1-ზე მეტი {{PLURAL:$1|ცვლილების|ცვლილების}} დაბრუნება',
'rollbackfailed' => 'შეცდომა გაუქმებისას',
'cantrollback' => 'შეუძლებელია უწინდელი რედაქციის აღდგენა; ის, ვინც უკანასკნელი ცვლილებები შეიტანა, ამ სტატიის ერთადერთი ავტორია.',
'alreadyrolled' => 'შეუძლებელია ბოლო ცვლილების გაუქმება [[:$1]], გაკეებული [[User:$2|$2]] ([[User talk:$2|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
'version-api' => 'API',
'version-other' => 'სხვა',
'version-mediahandlers' => 'მედია დამუშავება',
-'version-hooks' => 'ჰუკებш',
+'version-hooks' => 'ჰუკები',
'version-extension-functions' => 'გაფართოებათა ფუნქციები',
'version-parser-extensiontags' => 'სინტაქსური ანალიზატორის თეგი',
'version-parser-function-hooks' => 'სინტაქსური ანალიზატორის ჰუკი',
'duration-centuries' => '$1 {{PLURAL:$1|საუკუნე|საუკუნე}}',
'duration-millennia' => '$1 {{PLURAL:$1|ათასწლეული|ათასწლეული}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|მიუღებელი ფაილის ტიპია|მიუღებელი ფაილის ტიპებია}}. მისაღებიი ფაილის {{PLURAL:$3|ტიპია|ტიპებია}} $2.',
);
* @author Agzennay
* @author Azwaw
* @author Mmistmurt
+ * @author MoubarikBelkasim
* @author Teak
* @author Urhixidur
*/
'revertmerge' => 'Fru',
# Diffs
-'history-title' => 'Amazray n allasen n "$1"',
+'history-title' => 'Tiẓṛi tiss sint umezruy n "$1"',
'lineno' => 'Ajerriḍ $1:',
'compareselectedversions' => 'Ẓer imgerraden ger tisiwal i textareḍ',
'editundo' => 'ssefsu',
'yourtext' => 'Metnê to',
'storedversion' => 'Metıno qeydkerde',
'yourdiff' => 'Ferqi',
-'copyrightwarning' => "Diqet ke, iştırakê ke benê be pela {{SITENAME}}i, pêro bınê $2 de rakerde vêrenê (serba daêna melumati qaytê $1 ke).
+'copyrightwarning' => "Diqet ke, iştıraqê ke benê be pela {{SITENAME}}i, pêro bınê $2 de rakerde vêrenê (serba daêna melumati qaytê $1 ke).
İştırakunê ho, eke nêwazena wa terefê binu ra bıvuriyê ya ki caunê binu ra vıla bê, o taw ita menuse.<br />
Zobina ki ebe ita nustene ra sond wena ke nê iştıraki terefê to ra nuşiyê, ya çımê do rakerdey ra ya ki çımê do serbest ra kopya biyê.
'''Gurêo ke ebe telifheqiye ra sevekiyo bê destur ita darde meke!'''",
'sp-contributions-talk' => 'hurênais',
'sp-contributions-search' => 'Ebe iştıraku cı feteliye',
'sp-contributions-username' => 'IP ya ki karber:',
-'sp-contributions-toponly' => 'Teyna revizyonanê tewr peniyan bimocne',
+'sp-contributions-toponly' => 'Tenya rewizyonanê tewr peyniyan bimocne',
'sp-contributions-submit' => 'Cı feteliye',
# What links here
'tog-hidepatrolled' => 'លាក់កំណែប្រែដែលបានល្បាត នៅក្នុងបំលាស់ប្ដូរថ្មីៗ',
'tog-newpageshidepatrolled' => 'លាក់ទំព័រដែលបានល្បាត ពីបញ្ជីទំព័រថ្មី',
'tog-extendwatchlist' => 'ពង្រីកបញ្ជីតាមដានដើម្បីបង្ហាញគ្រប់បំលាស់ប្ដូរ មិនមែនត្រឹមតែបំលាស់ប្ដូរថ្មីៗនោះទេ',
-'tog-usenewrc' => 'បង្ហាញបំលាស់ប្ដូរថ្មីៗតាមរបៀបទំនើប (តម្រូវអោយមាន JavaScript)',
+'tog-usenewrc' => ' បំលាស់ប្ដូរជាក្រុមតាមទំព័រ ក្នុងបំលាស់ប្តូរថ្មីៗនិងបញ្ជីតាមដាន (តម្រូវឲ្យមាន JavaScript)',
'tog-numberheadings' => 'បង្ហាញលេខចំណងជើងរងដោយស្វ័យប្រវត្តិ',
'tog-showtoolbar' => 'បង្ហាញរបារឧបករណ៍កែប្រែ (តម្រូវអោយមាន JavaScript)',
'tog-editondblclick' => 'កែប្រែទំព័រដោយចុចពីរដងជាប់គ្នា (តម្រូវអោយមាន JavaScript)',
'tog-editsectiononrightclick' => 'អនុញ្ញាតកែប្រែផ្នែកណាមួយ ដោយចុចស្តាំកណ្តុរលើចំណងជើងរបស់វា (តម្រូវអោយមាន JavaScript)',
'tog-showtoc' => 'បង្ហាញតារាងមាតិកា (ចំពោះទំព័រដែលមានចំណងជើងរងលើសពី៣)',
'tog-rememberpassword' => 'ចងចាំការកត់ឈ្មោះចូលរបស់ខ្ញុំលើកុំព្យូទ័រនេះ (សំរាប់រយៈពេលយ៉ាងយូរ$1 {{PLURAL:$1|ថ្ងៃ|ថ្ងៃ}})',
-'tog-watchcreations' => 'បន្ថែមទំព័រដែលខ្ញុំបង្កើតទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
-'tog-watchdefault' => 'បន្ថែមទំព័រដែលខ្ញុំកែប្រែទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
-'tog-watchmoves' => 'បន្ថែមទំព័រដែលខ្ញុំប្តូរទីតាំងទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
-'tog-watchdeletion' => 'បន្ថែមទំព័រដែលខ្ញុំលុបចោលទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
+'tog-watchcreations' => 'á\9e\94á\9e\93á\9f\92á\9e\90á\9f\82á\9e\98â\80\8bá\9e\91á\9f\86á\9e\96á\9f\90á\9e\9aâ\80\8bá\9e\91á\9e¶á\9f\86á\9e\84á\9e¡á\9e¶á\9e\99á\9e\8aá\9f\82á\9e\9bá\9e\81á\9f\92á\9e\89á\9e»á\9f\86á\9e\94á\9e\84á\9f\92á\9e\80á\9e¾á\9e\8fâ\80\8bá\9e\91á\9f\85â\80\8bá\9e\94á\9e\89á\9f\92á\9e\87á\9e¸á\9e\8fá\9e¶á\9e\98á\9e\8aá\9e¶á\9e\93â\80\8bá\9e\9aá\9e\94á\9e\9fá\9f\8bá\9e\81á\9f\92á\9e\89á\9e»á\9f\86',
+'tog-watchdefault' => 'បន្ថែមទំព័រទាំងឡាយដែលខ្ញុំកែប្រែទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
+'tog-watchmoves' => 'á\9e\94á\9e\93á\9f\92á\9e\90á\9f\82á\9e\98â\80\8bá\9e\91á\9f\86á\9e\96á\9f\90á\9e\9aâ\80\8bá\9e\91á\9e¶á\9f\86á\9e\84á\9e¡á\9e¶á\9e\99á\9e\8aá\9f\82á\9e\9bá\9e\81á\9f\92á\9e\89á\9e»á\9f\86á\9e\94á\9f\92á\9e\8fá\9e¼á\9e\9aá\9e\91á\9e¸á\9e\8fá\9e¶á\9f\86á\9e\84â\80\8bá\9e\91á\9f\85â\80\8bá\9e\94á\9e\89á\9f\92á\9e\87á\9e¸á\9e\8fá\9e¶á\9e\98á\9e\8aá\9e¶á\9e\93â\80\8bá\9e\9aá\9e\94á\9e\9fá\9f\8bá\9e\81á\9f\92á\9e\89á\9e»á\9f\86',
+'tog-watchdeletion' => 'បន្ថែមទំព័រទាំងឡាយដែលខ្ញុំលុបចោលទៅបញ្ជីតាមដានរបស់ខ្ញុំ',
'tog-minordefault' => "ចំណាំគ្រប់កំណែប្រែរបស់ខ្ញុំថាជា'កំណែប្រែតិចតួច'",
'tog-previewontop' => 'បង្ហាញការមើលមុនពីលើប្រអប់កែប្រែ',
'tog-previewonfirst' => 'បង្ហាញការមើលមុនចំពោះកំណែប្រែដំបូង',
'rev-deleted-unhide-diff' => "កំណែប្រែមួយនៃភាពខុសគ្នានេះត្រូវបាន'''លុប'''។
ប្រហែលជាមានព័ត៌មានលម្អិតនៅក្នុង[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} កំណត់ហេតុនៃការលុប]។
អ្នកនៅតែអាច[$1 មើលភាពខុសគ្នានេះ]ប្រសិនបើអ្នកចង់។",
+'rev-suppressed-unhide-diff' => "កំណែប្រែនៃទំព័រនេះត្រូវបាន'''ហាមឃាត់'''។
+ប្រហែលជាមានព័ត៌មានលម្អិតនៅក្នុង[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} កំណត់ហេតុនៃការហាមឃាត់]។
+អ្នកនៅតែអាច[$1 មើលកំណែនេះ]ប្រសិនបើអ្នកចង់។",
+'rev-deleted-diff-view' => "កំណែប្រែនៃទំព័រនេះត្រូវបាន'''លុប'''។
+អ្នកអាចមើលកំណែប្រែនេះបាន។ ប្រហែលជាមានព័ត៌មានលម្អិតនៅក្នុង[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} កំណត់ហេតុនៃការលុប]។",
+'rev-suppressed-diff-view' => "កំណែប្រែនៃទំព័រនេះត្រូវបាន'''ហាមឃាត់'''។
+អ្នកអាចមើលវាបាន។ ប្រហែលជាមានព័ត៌មានលម្អិតនៅក្នុង[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} កំណត់ហេតុនៃការហាមឃាត់]។",
'rev-delundel' => 'បង្ហាញ/លាក់',
'rev-showdeleted' => 'បង្ហាញ',
'revisiondelete' => 'លុបចេញ / លែងលុបចេញ កំណែនានា',
'showhideselectedversions' => 'បង្ហាញ/លាក់កំណែប្រែដែលបានជ្រើសយក',
'editundo' => 'មិនធ្វើវិញ',
'diff-multi' => '({{PLURAL:$1|កំណែប្រែកម្រិតបង្គួរមួយ|កំណែប្រែកម្រិតបង្គួរចំនួន $1}}មិនត្រូវបានបង្ហាញ)',
+'diff-multi-manyusers' => '({{PLURAL:$1|កំណែប្រែកម្រិតបង្គួរមួយ|កំណែប្រែកម្រិតបង្គួរចំនួន $1}}មិនត្រូវបានបង្ហាញ)',
# Search results
'searchresults' => 'លទ្ធផលស្វែងរក',
# User preference toggles
'tog-underline' => '링크에 밑줄 표시하기:',
'tog-justify' => '문단 정렬하기',
-'tog-hideminor' => 'ì\82¬ì\86\8cí\95\9c í\8e¸ì§\91ì\9d\84 ìµ\9cê·¼ ë°\94ë\80\9cì\97\90ì\84\9c 숨기기',
+'tog-hideminor' => 'ìµ\9cê·¼ ë°\94ë\80\9cì\97\90ì\84\9c ì\82¬ì\86\8cí\95\9c í\8e¸ì§\91ì\9d\84 숨기기',
'tog-hidepatrolled' => '최근 바뀜에서 검토한 편집을 숨기기',
'tog-newpageshidepatrolled' => '새 문서 목록에서 검토한 문서를 숨기기',
'tog-extendwatchlist' => '주시문서 목록에서 가장 최근의 편집만이 아닌 모든 편집을 보기',
'youhavenewmessages' => '다른 사용자가 $1란에 글을 남겼습니다. ($2)',
'newmessageslink' => '사용자 토론',
'newmessagesdifflink' => '바뀐 내용 비교',
+'youhavenewmessagesfromusers' => '{{PLURAL:$3|다른 사용자|$3 사용자}}가 $1란에 글을 남겼습니다. ($2)',
+'youhavenewmessagesmanyusers' => '여러 사용자가 $1란에 글을 남겼습니다. ($2)',
+'newmessageslinkplural' => '{{PLURAL:$1|새 메시지}}',
+'newmessagesdifflinkplural' => '최근 {{PLURAL:$1|바뀜}}',
'youhavenewmessagesmulti' => '다른 사용자가 $1란에 글을 남겼습니다.',
'editsection' => '편집',
'editold' => '편집',
'welcomecreation' => '== $1 님, 환영합니다! ==
계정이 만들어졌습니다.
[[Special:Preferences|{{SITENAME}} 사용자 환경 설정]]을 바꿀 수 있습니다.',
-'yourname' => '계정 이름:',
+'yourname' => '사용자 이름:',
'yourpassword' => '비밀번호:',
'yourpasswordagain' => '비밀번호 다시 입력:',
'remembermypassword' => '이 컴퓨터에서 로그인 상태를 저장하기 (최대 $1일)',
'securelogin-stick-https' => '로그인 후에도 HTTPS 연결 상태를 유지합니다',
'yourdomainname' => '도메인 이름:',
+'password-change-forbidden' => '이 위키에서 비밀번호를 바꿀 수 없습니다.',
'externaldberror' => '외부 인증 데이터베이스에 오류가 있거나 외부 계정을 새로 고칠 권한이 없습니다.',
'login' => '로그인',
'nav-login-createaccount' => '로그인 / 계정 만들기',
'createaccount' => '계정 만들기',
'gotaccount' => "계정이 이미 있다면, '''$1'''.",
'gotaccountlink' => '로그인하세요',
-'userlogin-resetlink' => '계정 이름이나 비밀번호를 잊으셨나요?',
+'userlogin-resetlink' => '사용자 이름이나 비밀번호를 잊으셨나요?',
'createaccountmail' => '이메일로 보내기',
'createaccountreason' => '이유:',
'badretype' => '입력한 비밀번호가 서로 다릅니다.',
'accountcreated' => '계정 만들어짐',
'accountcreatedtext' => '"$1" 사용자 계정이 만들어졌습니다.',
'createaccount-title' => '{{SITENAME}} 계정 만들기',
-'createaccount-text' => '누군가가 {{SITENAME}} ($4)에서 계정 이름 ‘$2’, 비밀번호 ‘$3’로 당신의 이메일 주소가 등록된 계정을 만들었습니다.
+'createaccount-text' => '누군가가 {{SITENAME}} ($4)에서 사용자 이름 "$2", 비밀번호 "$3"로 당신의 이메일 주소가 등록된 계정을 만들었습니다.
지금 로그인하여 비밀번호를 바꾸십시오.
실수로 계정을 잘못 만들었다면 이 메시지는 무시해도 됩니다.',
-'usernamehasherror' => '계정 이름에는 해시 문자가 들어갈 수 없습니다.',
+'usernamehasherror' => '사용자 이름에는 해시 문자가 들어갈 수 없습니다.',
'login-throttled' => '로그인에 연속으로 실패하였습니다.
잠시 후에 다시 시도해주세요.',
'login-abort-generic' => '로그인에 실패했습니다 - 중지됨',
'passwordreset-legend' => '비밀번호 재설정',
'passwordreset-disabled' => '이 위키에서는 비밀번호를 재설정할 수 없습니다.',
'passwordreset-pretext' => '{{PLURAL:$1||아래에 한 가지 정보를 입력하세요}}',
-'passwordreset-username' => '계정 이름:',
+'passwordreset-username' => '사용자 이름:',
'passwordreset-domain' => '도메인:',
'passwordreset-capture' => '발송 결과 이메일을 보시겠습니까?',
'passwordreset-capture-help' => '이 상자에 체크하면 이메일이 발송된 즉시 임시 비밀번호가 담긴 이메일을 볼 수 있습니다.',
'passwordreset-email' => '이메일 주소:',
-'passwordreset-emailtitle' => '{{SITENAME}} ê³\84ì \95 ì\83\81ì\84¸ 정보',
+'passwordreset-emailtitle' => '{{SITENAME}} ê³\84ì \95 ì\9e\90ì\84¸í\95\9c 정보',
'passwordreset-emailtext-ip' => 'IP 주소 $1을 사용하는 누군가(아마도 당신이), {{SITENAME}} ($4)의 비밀번호 찾기를 요청하였습니다.
이 이메일 주소와 연관된 계정의 목록입니다:
이 {{PLURAL:$3|임시 비밀번호}}는 $5일 후에 만료됩니다.
이 비밀번호로 로그인한 후 비밀번호를 바꾸십시오. 만약 당신이 아닌 다른 사람이 요청하였거나,
원래의 비밀번호를 기억해냈다면, 이 메시지를 무시하고 이전의 비밀번호를 계속 사용할 수 있습니다.',
-'passwordreset-emailelement' => '계정 이름: $1
+'passwordreset-emailelement' => '사용자 이름: $1
임시 비밀번호: $2',
'passwordreset-emailsent' => '비밀번호 찾기 이메일을 보냈습니다.',
'passwordreset-emailsent-capture' => '비밀번호 찾기 이메일이 발송되었으며, 아래에 표시되어 있습니다.',
'noarticletext-nopermission' => '이 문서가 존재하지 않습니다.
이 문서와 제목이 비슷한 문서가 있는지 [[Special:Search/{{PAGENAME}}|검색]]하거나,
이 문서에 관련된 <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 기록]을 확인할 수 있습니다.</span>',
+'missing-revision' => '"{{PAGENAME}}"이라는 문서의 #$1판이 존재하지 않습니다.
+
+이 문제는 주로 삭제된 문서를 가리키는 오래된 문서 역사 링크로 인해 발생합니다.
+자세한 내용은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 확인할 수 있습니다.',
'userpage-userdoesnotexist' => '"$1" 계정은 등록되어 있지 않습니다.
이 문서를 만들거나 편집하려면 계정이 존재 하는지 확인해주세요.',
'userpage-userdoesnotexist-view' => '"$1" 사용자 계정은 등록되지 않았습니다.',
일부 틀은 포함되지 않을 수 있습니다.",
'post-expand-template-inclusion-category' => '사용한 틀의 크기가 지나치게 큰 문서의 목록',
'post-expand-template-argument-warning' => "'''경고:''' 이 문서는 전개 후 크기가 너무 큰 틀 변수가 하나 이상 포함되어 있습니다.
-ì\9d´ ë³\80ì\88\98ë\93¤ì\9d\80 ì\83\9dë\9eµë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.",
+ì\9d´ ë³\80ì\88\98ë\8a\94 ì\83\9dë\9eµí\96\88ì\8aµë\8b\88ë\8b¤.",
'post-expand-template-argument-category' => '생략된 틀 변수를 포함한 문서',
'parser-template-loop-warning' => '재귀적인 틀이 발견되었습니다: [[$1]]',
'parser-template-recursion-depth-warning' => '틀 반복 횟수 제한을 초과함($1)',
'expansion-depth-exceeded-warning' => '페이지가 확장 깊이를 초과하였습니다',
'parser-unstrip-loop-warning' => '스트립하지 않는 반복이 감지되었습니다',
'parser-unstrip-recursion-limit' => '스트립하지 않는 재귀 한도가 초과됨 ($1)',
+'converter-manual-rule-error' => '언어 변환 규칙을 수동으로 지정하는 도중 오류',
# "Undo" feature
'undo-success' => '편집을 되돌릴 수 있습니다.
# Revision deletion
'rev-deleted-comment' => '(편집 요약 삭제됨)',
-'rev-deleted-user' => '(계정 이름 삭제됨)',
+'rev-deleted-user' => '(사용자 이름 삭제됨)',
'rev-deleted-event' => '(기록 동작 삭제됨)',
-'rev-deleted-user-contribs' => '[계정 이름 또는 IP 주소 삭제됨 - 기여 목록에서 숨겨짐]',
+'rev-deleted-user-contribs' => '[사용자 이름 또는 IP 주소 삭제됨 - 기여 목록에서 편집이 숨겨짐]',
'rev-deleted-text-permission' => "해당 편집이 문서 역사에서 '''삭제'''되었습니다.
자세한 사항은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 볼 수 있습니다.",
'rev-deleted-text-unhide' => "해당 편집이 문서 역사에서 '''삭제'''되었습니다.
'revdelete-hide-image' => '파일을 숨기기',
'revdelete-hide-name' => '기록 내용과 대상을 숨기기',
'revdelete-hide-comment' => '편집 요약을 숨기기',
-'revdelete-hide-user' => '편집자의 계정 이름/IP를 숨기기',
+'revdelete-hide-user' => '편집자의 사용자 이름/IP를 숨기기',
'revdelete-hide-restricted' => '관리자도 보지 못하게 숨기기',
'revdelete-radio-same' => '(바꾸지 않음)',
'revdelete-radio-set' => '예',
'editundo' => '편집 취소',
'diff-multi' => '({{PLURAL:$2|한 사용자의|사용자 $2명의}} 중간의 편집 $1개 숨겨짐)',
'diff-multi-manyusers' => '({{PLURAL:$2|한 사용자의|사용자 $2명 이상의}} 중간의 편집 $1개 숨겨짐)',
+'difference-missing-revision' => '문서 비교에서 {{PLURAL:$2|하나|$2개}}의 판($1)을 찾을 수 없습니다.
+
+이 문제는 주로 삭제된 문서를 가리키는 오래된 문서 비교 링크로 인해 발생합니다.
+자세한 내용은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 확인할 수 있습니다.',
# Search results
'searchresults' => '찾기 결과',
'prefs-files' => '파일',
'prefs-custom-css' => '사용자 CSS',
'prefs-custom-js' => '사용자 자바스크립트',
-'prefs-common-css-js' => '모든 스킨에 적용되는 공통 CSS/자바스크립트:',
+'prefs-common-css-js' => '모든 스킨에 대한 공통 CSS/JavaScript:',
'prefs-reset-intro' => '이 사이트의 기본값으로 환경 설정을 되돌릴 수 있습니다.
복구할 수 없습니다.',
'prefs-emailconfirm-label' => '이메일 인증:',
'prefs-textboxsize' => '편집창의 크기',
'youremail' => '이메일:',
-'username' => '계정 이름:',
+'username' => '사용자 이름:',
'uid' => '사용자 ID:',
'prefs-memberingroups' => '소속 {{PLURAL:$1|그룹}}:',
'prefs-registration' => '등록 일시:',
# User rights
'userrights' => '사용자 권한 관리',
'userrights-lookup-user' => '사용자 권한 관리',
-'userrights-user-editname' => '계정 이름:',
+'userrights-user-editname' => '사용자 이름 입력:',
'editusergroup' => '사용자 그룹 편집',
'editinguser' => "사용자 '''[[User:$1|$1]]''' $2의 권한 바꿈",
'userrights-editusergroup' => '사용자 그룹 편집',
'right-writeapi' => 'API 작성',
'right-delete' => '문서 삭제',
'right-bigdelete' => '문서 역사가 긴 문서를 삭제',
+'right-deletelogentry' => '특정 기록 항목을 삭제 및 복구',
'right-deleterevision' => '문서의 특정 판을 삭제 및 복구',
'right-deletedhistory' => '삭제된 문서의 내용을 제외한 역사를 보기',
'right-deletedtext' => '삭제된 문서의 내용과 편집상의 차이를 보기',
# Special:UploadStash
'uploadstash' => '파일 올리기 임시 저장',
-'uploadstash-summary' => 'ì\9d´ 문ì\84\9cë\8a\94 ì\9c\84í\82¤ì\97\90 ë\93±ë¡\9dë\90\98ì§\80ë\8a\94 ì\95\8aì\95\98ì§\80ë§\8c ì\98¬ë¦¬ë\8a\94 ê³¼ì \95 ì¤\91ì\97\90 ì\9e\88ë\8a\94 í\8c\8cì\9d¼ì\9d\84 ì\97´ë\9e\8cí\95 ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\9d´ í\8c\8cì\9d¼ë\93¤은 올린이 외에는 볼 수 없습니다.',
+'uploadstash-summary' => 'ì\9d´ 문ì\84\9cë\8a\94 ì\9c\84í\82¤ì\97\90 ë\93±ë¡\9dë\90\98ì§\80ë\8a\94 ì\95\8aì\95\98ì§\80ë§\8c ì\98¬ë¦¬ë\8a\94 ê³¼ì \95 ì¤\91ì\97\90 ì\9e\88ë\8a\94 í\8c\8cì\9d¼ì\9d\84 ì \91ê·¼í\95 ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\9d´ í\8c\8cì\9d¼은 올린이 외에는 볼 수 없습니다.',
'uploadstash-clear' => '임시 저장한 파일 제거하기',
'uploadstash-nofiles' => '임시 저장한 파일이 없습니다.',
'uploadstash-badtoken' => '이 동작을 수행하는 데 실패했습니다. 편집 토큰이 만료되었을 가능성이 있습니다. 다시 시도하세요.',
'disambiguations' => '동음이의 문서를 가리키는 문서 목록',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "ë\8b¤ì\9d\8cì\9d\98 문ì\84\9cë\93¤ì\9d\80 '''동음이의 문서'''를 가리키고 있습니다.
-ê·¸ ë§\81í\81¬ë¥¼ ë\8b¤ë¥¸ ì \81ì \88í\95\9c 문ì\84\9cë¡\9c ì\97°ê²°í\95´ 주ì\96´ì\95¼ í\95©니다.<br />
+'disambiguations-text' => "ë\8b¤ì\9d\8cì\9d\98 문ì\84\9cë\8a\94 ì \81ì\96´ë\8f\84 í\95\98ë\82\98 ì\9d´ì\83\81 '''동음이의 문서'''를 가리키고 있습니다.
+ê·¸ ë§\81í\81¬ë\8a\94 ë\8b¤ë¥¸ ì \81ì \88í\95\9c 문ì\84\9cë¡\9c ì\97°ê²°í\95 í\95\84ì\9a\94ê°\80 ì\9e\88ì\8aµ니다.<br />
[[MediaWiki:Disambiguationspage]]에서 링크된 틀을 사용하는 문서를 동음이의 문서로 간주합니다.",
'doubleredirects' => '이중 넘겨주기 목록',
'protectedpagestext' => '다음의 문서는 이동/편집이 불가능하도록 보호되어 있습니다.',
'protectedpagesempty' => '보호되어 있는 문서가 없습니다.',
'protectedtitles' => '만들기 보호된 표제어 목록',
-'protectedtitlestext' => 'ë\8b¤ì\9d\8c í\91\9cì \9cì\96´ë\93¤ì\9d\80 만들기가 금지되어 있습니다.',
+'protectedtitlestext' => 'ë\8b¤ì\9d\8c í\91\9cì \9cì\96´ë\8a\94 만들기가 금지되어 있습니다.',
'protectedtitlesempty' => '해당 조건에 맞는 만들기 금지 표제어가 없습니다.',
'listusers' => '사용자 목록',
'listusers-editsonly' => '기여가 있는 사용자만 보기',
'usereditcount' => '편집 $1회',
'usercreated' => '$1 $2에 계정 {{GENDER:$3|만들어짐}}',
'newpages' => '새 문서 목록',
-'newpages-username' => '이름:',
+'newpages-username' => 'ì\82¬ì\9a©ì\9e\90 ì\9d´ë¦\84:',
'ancientpages' => '오래된 문서 목록',
'move' => '이동',
'movethispage' => '문서 이동하기',
'unusedimagestext' => '다음은 어떤 문서도 사용하지 않는 파일의 목록입니다.
다른 사이트에서 URL 접근을 통해 파일을 사용할 수 있기 때문에, 아래 목록에 있는 파일도 실제로 사용 중일 가능성이 있다는 점을 주의해주세요.',
-'unusedcategoriestext' => '사용하지 않는 분류 문서들의 목록입니다.',
+'unusedcategoriestext' => '사용하지 않는 분류 문서의 목록입니다.',
'notargettitle' => '해당하는 문서 없음',
'notargettext' => '기능을 수행할 대상 문서나 사용자를 지정하지 않았습니다.',
'nopagetitle' => '해당 문서 없음',
'log' => '기록 목록',
'all-logs-page' => '모든 공개 기록',
'alllogstext' => '{{SITENAME}}에서의 기록이 모두 나와 있습니다.
-로그 종류, 계정 이름, 문서 이름을 선택해서 볼 수 있습니다. 검색시에는 대소문자를 구별합니다.',
+기록 종류, 사용자 이름 (대소문자 구분), 문서 이름을 선택해서 볼 수 있습니다. 검색시에는 대소문자를 구별합니다.',
'logempty' => '일치하는 항목이 없습니다.',
'log-title-wildcard' => '다음 글로 시작하는 제목 검색',
'showhideselectedlogentries' => '선택한 기록 항목 보이기/숨기기',
'categoriespagetext' => '{{PLURAL:$1}}문서나 자료를 담고 있는 분류 목록입니다.
[[Special:UnusedCategories|사용되지 않는 분류]]는 여기에 보이지 않습니다.
[[Special:WantedCategories|필요한 분류]]도 참고하세요.',
-'categoriesfrom' => 'ë\8b¤ì\9d\8cì\9c¼ë¡\9c ì\8b\9cì\9e\91í\95\98ë\8a\94 ë¶\84ë¥\98ë\93¤ì\9d\84 보여주기:',
+'categoriesfrom' => 'ë\8b¤ì\9d\8cì\9c¼ë¡\9c ì\8b\9cì\9e\91í\95\98ë\8a\94 ë¶\84ë¥\98를 보여주기:',
'special-categories-sort-count' => '항목 갯수 순으로 정렬',
'special-categories-sort-abc' => '알파벳순으로 정렬',
'linksearch' => '외부 링크 찾기',
'linksearch-pat' => '검색 패턴:',
'linksearch-ns' => '이름공간:',
-'linksearch-ok' => '검색',
+'linksearch-ok' => '찾기',
'linksearch-text' => '"*.wikipedia.org"와 같이 와일드카드를 사용할 수 있습니다.
적어도 "*.org"와 같이 최상위 도메인을 입력해야 합니다.<br />
지원하는 프로토콜 목록: <tt>$1</tt> (검색할 때 이것을 추가하지 마세요)',
'noemailtext' => '이 사용자는 올바른 이메일 주소를 입력하지 않았습니다.',
'nowikiemailtitle' => '이메일이 허용되지 않음',
'nowikiemailtext' => '이 사용자는 다른 사용자로부터의 이메일을 받지 않도록 설정하였습니다.',
-'emailnotarget' => '수신자로 없는 사용자를 지정하였거나 계정 이름이 잘못되었습니다.',
-'emailtarget' => '수신자 계정 이름 입력',
-'emailusername' => '계정 이름:',
+'emailnotarget' => '받는이로 없는 사용자를 지정하였거나 사용자 이름이 잘못되었습니다.',
+'emailtarget' => '수신자 사용자 이름 입력',
+'emailusername' => '사용자 이름:',
'emailusernamesubmit' => '확인',
'email-legend' => '{{SITENAME}}의 다른 사용자에게 이메일을 보내기',
'emailfrom' => '이메일 발신자:',
'watchnochange' => '주어진 기간 중에 바뀐 주시문서가 없습니다.',
'watchlist-details' => '토론을 제외하고 문서 $1개를 주시하고 있습니다.',
'wlheader-enotif' => '* 이메일 알림 기능이 활성화되었습니다.',
-'wlheader-showupdated' => "* ë§\88ì§\80ë§\89ì\9c¼ë¡\9c 방문í\95\9c ì\9d´í\9b\84ì\97\90 ë°\94ë\80\90 문ì\84\9cë\93¤ì\9d\80 '''굵은 글씨'''로 표시됩니다.",
+'wlheader-showupdated' => "* ë§\88ì§\80ë§\89ì\9c¼ë¡\9c 방문í\95\9c ì\9d´í\9b\84ì\97\90 ë°\94ë\80\90 문ì\84\9cë\8a\94 '''굵은 글씨'''로 표시됩니다.",
'watchmethod-recent' => '주시된 문서를 확인하고자 최근 편집을 확인',
'watchmethod-list' => '최근 편집을 확인하고자 주시된 문서 확인',
'watchlistcontains' => '문서 $1개를 주시하고 있습니다.',
'rollback' => '편집 되돌리기',
'rollback_short' => '되돌리기',
'rollbacklink' => '되돌리기',
+'rollbacklinkcount' => '되돌리기 편집 $1회',
+'rollbacklinkcount-morethan' => '되돌리기 편집 $1회 이상',
'rollbackfailed' => '되돌리기 실패',
'cantrollback' => '편집을 되돌릴 수 없습니다.
문서를 편집한 사용자가 한명뿐입니다.',
마지막으로 이 문서를 편집한 사용자는 [[User:$3|$3]] ([[User talk:$3|토론]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])입니다.',
'editcomment' => '편집 요약: "$1"',
'revertpage' => '[[Special:Contributions/$2|$2]]([[User talk:$2|토론]])의 편집을 [[User:$1|$1]]의 마지막 버전으로 되돌림',
-'revertpage-nouser' => '(계정 이름 삭제됨)의 편집을 [[User:$1|$1]]의 마지막 편집으로 되돌림',
+'revertpage-nouser' => '(사용자 이름 삭제됨)의 편집을 [[User:$1|$1]]의 마지막 편집으로 되돌림',
'rollback-success' => '$1의 편집을 $2의 마지막 버전으로 되돌렸습니다.',
# Edit tokens
'undeleterevdel' => '복구하려는 문서의 최신판이 삭제되어 있는 경우 문서를 복구시킬 수 없습니다.
이러한 경우, 삭제된 최신판 문서의 체크박스를 선택 해제하거나 숨김을 해제해야 합니다.',
'undeletehistorynoadmin' => '이 문서는 삭제되었습니다.
-삭제된 이유와 삭제되기 전에 이 문서를 편집한 사용자들이 아래에 나와 있습니다.
+삭제된 이유와 삭제되기 전에 이 문서를 편집한 사용자가 아래에 나와 있습니다.
삭제된 문서의 내용을 보려면 관리자 권한이 필요합니다.',
'undelete-revision' => '삭제된 $1 문서의 $4 $5 버전 (기여자 $3):',
'undeleterevision-missing' => '해당 판이 잘못되었거나 존재하지 않습니다.
'undeletedfiles' => '파일 $1개를 복구했습니다',
'cannotundelete' => '복구에 실패했습니다.
다른 사용자가 이미 복구했을 수도 있습니다.',
-'undeletedpage' => "'''$1 문서ê°\80 복구ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.'''
+'undeletedpage' => "'''$1 문서를 복구í\96\88ì\8aµë\8b\88ë\8b¤.'''
[[Special:Log/delete|삭제 기록]]에서 최근의 삭제와 복구 기록을 볼 수 있습니다.",
'undelete-header' => '최근에 삭제한 문서에 대한 기록은 [[Special:Log/delete|여기]]에서 볼 수 있습니다.',
'year' => '연도:',
'sp-contributions-newbies' => '새 사용자의 기여만 보기',
-'sp-contributions-newbies-sub' => '새 사용자들의 기여',
+'sp-contributions-newbies-sub' => '새 사용자의 기여',
'sp-contributions-newbies-title' => '새 사용자의 기여',
'sp-contributions-blocklog' => '차단 기록',
'sp-contributions-deleted' => '삭제된 기여 목록',
'sp-contributions-blocked-notice-anon' => '이 IP 주소는 현재 차단되어 있습니다.
차단 기록은 다음과 같습니다:',
'sp-contributions-search' => '기여 찾기',
-'sp-contributions-username' => 'IP 주소 혹은 계정 이름:',
+'sp-contributions-username' => 'IP 주소 또는 사용자 이름:',
'sp-contributions-toponly' => '최신판만 보기',
'sp-contributions-submit' => '찾기',
'sp-contributions-explain' => '',
'blockiptext' => '차단할 IP 주소나 사용자 이름을 아래에 적어 주세요.
차단은 문서 훼손을 막기 위해, [[{{MediaWiki:Policy-url}}|정책]]에 의해서만 이루어져야 합니다.
차단 이유를 같이 적어주세요(예: 특정 문서 훼손).',
-'ipadressorusername' => 'IP 주소 또는 계정 이름:',
+'ipadressorusername' => 'IP 주소 또는 사용자 이름:',
'ipbexpiry' => '기간:',
'ipbreason' => '이유:',
'ipbreasonotherlist' => '다른 이유',
** 장난 편집
** 협박성 행동
** 다중 계정 악용
-** 부적절한 계정 이름',
+** 부적절한 사용자 이름',
'ipb-hardblock' => '이 IP를 이용하는 로그인한 사용자가 편집하는 것을 막기',
'ipbcreateaccount' => '계정 만들기를 막기',
'ipbemailban' => '이메일을 보내지 못하도록 막기',
'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] 사용자가 차단되었습니다.<br />
차단된 사용자 목록은 [[Special:BlockList|여기]]에서 볼 수 있습니다.',
'ipb-blockingself' => '자기 자신을 차단하려고 합니다! 정말로 실행할까요?',
-'ipb-confirmhideuser' => '당신은 사용자를 차단하면서 "계정 숨기기" 설정을 선택했습니다. 이로써 모든 기록에서 이 사용자의 계정 이름을 숨기게 됩니다. 정말로 계정을 숨기시겠습니까?',
+'ipb-confirmhideuser' => '당신은 사용자를 차단하면서 "사용자 숨기기" 설정을 선택했습니다. 이로써 모든 기록에서 이 사용자의 사용자 이름을 숨기게 됩니다. 정말로 계정을 숨기시겠습니까?',
'ipb-edit-dropdown' => '차단 이유 목록 편집하기',
'ipb-unblock-addr' => '$1 차단 해제하기',
'ipb-unblock' => '사용자 또는 IP 주소 차단 해제하기',
해당 사용자의 차단 기록은 다음과 같습니다:',
'blocklog-showsuppresslog' => '이 사용자는 과거에 차단된 적이 있으며, 그 기록이 숨겨져 있습니다.
해당 사용자의 차단 기록은 다음과 같습니다:',
-'blocklogentry' => '[[$1]] 사용자를 $2 차단함 $3',
+'blocklogentry' => '사용자가 [[$1]] 사용자를 $2 차단함 $3',
'reblock-logentry' => '[[$1]] 사용자의 차단 기간을 $2(으)로 바꿈 $3',
'blocklogtext' => '이 목록은 사용자 차단/차단 해제 기록입니다.
자동으로 차단된 IP 주소는 여기에 나오지 않습니다.
'block-log-flags-noemail' => '이메일 막음',
'block-log-flags-nousertalk' => '자신의 토론 문서 편집 불가',
'block-log-flags-angry-autoblock' => '향상된 자동 차단 활성화됨',
-'block-log-flags-hiddenname' => '계정 이름 숨겨짐',
+'block-log-flags-hiddenname' => '사용자 이름 숨겨짐',
'range_block_disabled' => 'IP 범위 차단 기능이 비활성화되어 있습니다.',
'ipb_expiry_invalid' => '차단 기간이 잘못되었습니다.',
'ipb_expiry_temp' => '사용자 이름을 숨기는 차단은 반드시 무기한이어야 합니다.',
'ipb_already_blocked' => '"$1" 사용자는 이미 차단됨',
'ipb-needreblock' => '$1 사용자는 이미 차단되었습니다. 차단 설정을 바꾸시겠습니까?',
'ipb-otherblocks-header' => '다른 {{PLURAL:$1|차단}} 기록',
-'unblock-hideuser' => '이 계정 이름이 숨겨져 있기 때문에 이 사용자를 차단 해제할 수 없습니다.',
+'unblock-hideuser' => '이 사용자 이름이 숨겨져 있기 때문에 이 사용자를 차단 해제할 수 없습니다.',
'ipb_cant_unblock' => '오류: 차단 ID $1이(가) 존재하지 않습니다. 이미 차단 해제되었을 수 있습니다.',
'ipb_blocked_as_range' => '오류: IP 주소 $1은 직접 차단되지 않았기 때문에 차단 해제할 수 없습니다.
하지만 $2로 광역 차단되었기 때문에, 광역 차단 해제로 차단을 해제할 수 있습니다.',
'sorbs_create_account_reason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다.
계정을 만들 수 없습니다.',
'cant-block-while-blocked' => '당신이 차단되어 있는 동안에는 다른 사용자를 차단할 수 없습니다.',
-'cant-see-hidden-user' => '당신이 차단하려 하는 사용자는 이미 차단되었고 계정 숨김 처리되었습니다.
-당신이 계정 숨기기 권한을 갖고 있지 않기 때문에, 이 사용자의 차단 기록을 보거나 차단 설정을 바꿀 수 없습니다.',
+'cant-see-hidden-user' => '당신이 차단하려 하는 사용자는 이미 차단되었고 숨김 처리되었습니다.
+당신이 사용자 숨기기 권한을 갖고 있지 않기 때문에, 이 사용자의 차단 기록을 보거나 차단 설정을 바꿀 수 없습니다.',
'ipbblocked' => '당신은 차단되어 있기 때문에 다른 사용자를 차단하거나 차단을 해제할 수 없습니다.',
'ipbnounblockself' => '당신은 자기 스스로를 차단 해제할 수 없습니다.',
이 경우에는 문서를 직접 이동하거나 두 문서를 합쳐야 합니다.",
'movearticle' => '문서 이동하기',
-'moveuserpage-warning' => "'''경고:''' 당신은 사용자 문서를 옮기려 하고 있습니다. 사용자 문서만 이동되며 계정 이름이 바뀌지 '''않는다'''는 점을 명심해주시기 바랍니다.",
+'moveuserpage-warning' => "'''경고:''' 당신은 사용자 문서를 옮기려 하고 있습니다. 사용자 문서만 이동되며 사용자 이름이 바뀌지 '''않는다'''는 점을 명심해주시기 바랍니다.",
'movenologin' => '로그인하지 않음',
'movenologintext' => '문서를 이동하려면 [[Special:UserLogin|로그인]]해야 합니다.',
'movenotallowed' => '문서를 이동할 권한이 없습니다.',
* <span class="mw-specialpagerestricted">제한된 특수 문서.</span>',
'specialpages-group-maintenance' => '관리용 목록',
'specialpages-group-other' => '다른 특수 문서',
-'specialpages-group-login' => 'ë¡\9cê·¸ì\9d¸ / ê°\80ì\9e\85',
+'specialpages-group-login' => 'ë¡\9cê·¸ì\9d¸ / ê³\84ì \95 ë§\8cë\93¤ê¸°',
'specialpages-group-changes' => '최근 바뀜과 기록',
'specialpages-group-media' => '파일 관리',
'specialpages-group-users' => '사용자와 권한',
'logentry-suppress-revision-legacy' => '$1 사용자가 비공개적으로 $3 문서의 특정 판에 대한 표시 설정을 바꾸었습니다.',
'revdelete-content-hid' => '내용 숨겨짐',
'revdelete-summary-hid' => '편집 요약 숨겨짐',
-'revdelete-uname-hid' => '계정 이름 숨겨짐',
+'revdelete-uname-hid' => '사용자 이름 숨겨짐',
'revdelete-content-unhid' => '내용 숨김 해제됨',
'revdelete-summary-unhid' => '편집 요약 숨김 해제됨',
-'revdelete-uname-unhid' => '계정 이름 숨김 해제됨',
+'revdelete-uname-unhid' => '사용자 이름 숨김 해제됨',
'revdelete-restricted' => '관리자에게 제한을 적용함',
'revdelete-unrestricted' => '관리자에 대한 제한을 해제함',
'logentry-move-move' => '$1 사용자가 $3 문서를 $4 문서로 옮겼습니다.',
# Feedback
'feedback-bugornote' => '기술적 문제를 구체적으로 설명할 준비가 되었다면 [$1 버그를 신고]해 주세요.
-아니면 아래에 쉬운 양식을 쓸 수 있습니다. 당신의 의견은 계정 이름과 사용 중인 브라우저 정보와 함께 "[$3 $2]"에 남겨질 것입니다.',
+아니면 아래에 쉬운 양식을 쓸 수 있습니다. 당신의 의견은 사용자 이름과 사용 중인 브라우저 정보와 함께 "[$3 $2]"에 남겨질 것입니다.',
'feedback-subject' => '제목:',
'feedback-message' => '내용:',
'feedback-cancel' => '취소',
'api-error-fileexists-shared-forbidden' => '"$1" 이름으로 된 파일이 이미 공용 저장소에 존재하며 덮어쓸 수 없습니다.',
'api-error-file-too-large' => '당신이 올리려는 파일이 너무 큽니다.',
'api-error-filename-tooshort' => '파일 이름이 너무 짧습니다.',
-'api-error-filetype-banned' => 'ì\9d´ë\9f° í\98\95ì\8b\9dì\9d\98 í\8c\8cì\9d¼은 올릴 수 없습니다.',
+'api-error-filetype-banned' => 'ì\9d´ë\9f° í\8c\8cì\9d¼ í\98\95ì\8b\9d은 올릴 수 없습니다.',
'api-error-filetype-missing' => '파일 이름에 확장자가 없습니다.',
'api-error-hookaborted' => '당신이 시도한 수정이 확장 기능 훅에 의해 중단되었습니다.',
'api-error-http' => '내부 오류: 서버에 연결할 수 없습니다.',
'duration-centuries' => '$1{{PLURAL:$1|세기}}',
'duration-millennia' => '$1{{PLURAL:$1|천년}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|파일 형식은 올릴 수 없습니다}}. $2 {{PLURAL:$3|파일 형식만 사용할 수 있습니다}}.',
);
'duration-centuries' => '$1 {{PLURAL:$1|ёмюр}}',
'duration-millennia' => '$1 {{PLURAL:$1|мингджыллыкъ}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 — {{PLURAL:$4|джасакъланнган файл типди|джасакъланнган файл типледиле}}. Эркинлик берилген {{PLURAL:$3|файл тип|файл типле}}: $2.',
);
'tog-previewontop' => 'Zeich de Vör-Aansich üvver däm Feld för dä Tex enzejevve aan.',
'tog-previewonfirst' => 'Zeich de Vör-Aansich tirek för et eetste Mol beim Bearbeide aan',
'tog-nocache' => 'Dun et Sigge Zweschespeichere en Dingem Brauser avschalte',
-'tog-enotifwatchlistpages' => 'Scheck mer en e-mail, wann en Sigg us minge Oppaßlėß verändert woode es',
+'tog-enotifwatchlistpages' => 'Scheck mer en <i lang="en">e-mail</i>, wann en Sigg us minge Oppaßlėß verändert woode es',
'tog-enotifusertalkpages' => 'Scheck mer en E-Mail, wann ming Klaaf Sigg jeändert weed',
-'tog-enotifminoredits' => 'Scheck mer och en e-mail för de klein Mini-Änderonge',
+'tog-enotifminoredits' => 'Scheck mer och en <i lang="en">e-mail</i> för de klein Mini-Änderonge',
'tog-enotifrevealaddr' => 'Zeich dä Andere ming E-Mail Adress aan, en de Benohrichtijunge per E-Mail',
'tog-shownumberswatching' => 'Zeich de Aanzahl Metmaacher, die op die Sigg am oppasse sin',
'tog-oldsig' => 'Esu süht Ding „Ongerschreff“ us:',
'tog-watchlisthideanons' => 'Namelose Metmaacher ier Änderunge jedesmol <strong>nit</strong> en minger Oppassliss aanzeije',
'tog-watchlisthidepatrolled' => 'Dun de nohjeloorte Änderunge et eez ens <strong>nit</strong> en minger Oppassliss aanzeije',
'tog-nolangconversion' => 'Sprochevariante nit ömwandele',
-'tog-ccmeonemails' => 'Scheck mer en Kopie, wann ich en e-mail an ene andere Metmaacher scheck',
+'tog-ccmeonemails' => 'Scheck mer en Kopie, wann ich en <i lang="en">e-mail</i> an ene andere Metmaacher scheck',
'tog-diffonly' => 'Zeich beim Versione Verjliche nur de Ungerscheide aan (ävver pack nit noch de janze Sigg dodronger)',
'tog-showhiddencats' => 'Donn de verstoche Saachjroppe aanzeije',
'tog-noconvertlink' => 'Don de Tittele nit ümwandelle',
'print' => 'Drocke',
'view' => 'Beloore',
'edit' => 'Ändere',
-'create' => 'Aanläje',
+'create' => 'Aanlääje',
'editthispage' => 'De Sigg ändere',
'create-this-page' => 'Neu aanläje',
'delete' => 'Fottschmieße',
'thisisdeleted' => '$1 - aanluure oder widder zeröckholle?',
'viewdeleted' => '$1 aanzeije?',
'restorelink' => '{{PLURAL:$1|eijn fottjeschmesse Änderung|$1 fottjeschmesse Änderunge|keij fottjeschmesse Änderunge}}',
-'feedlinks' => 'Abonnomang-Kannal (Feed):',
-'feed-invalid' => 'Esu en Zoot Abonnomang-Kannal (Feed) jitt et nit.',
-'feed-unavailable' => 'Mer han kein esu en Abonnomangs-Kannäl (Feeds) aam Loufe.',
+'feedlinks' => 'Abonnomangs-Kannal (<i lang="en">Feed</i>):',
+'feed-invalid' => 'Esu en Zoot Abonnomang-Kannal (<i lang="en">Feed</i>) jitt et nit.',
+'feed-unavailable' => 'Mer han kein esu en Abonnomangs-Kannäl (<i lang="en">Feeds</i>) aam Loufe.',
'site-rss-feed' => 'RSS-Abonnomang-Kannal (Feed) för de „$1“',
'site-atom-feed' => 'Atom-Abonnomang-Kannal (Feed) för de „$1“',
-'page-rss-feed' => 'RSS-Abonnomang-Kannal (Feed) för de Sigg „$1“',
-'page-atom-feed' => 'Atom-Abonnomang-Kannal (Feed) för de Sigg „$1“',
+'page-rss-feed' => 'RSS-Abonnomang-Kannal (<i lang="en">Feed</i>) för de Sigg „$1“',
+'page-atom-feed' => 'Atom-Abonnomang-Kannal (<i lang="en">Feed</i>) för de Sigg „$1“',
'feed-atom' => 'Atom',
'feed-rss' => 'RSS',
'red-link-title' => '$1 — en Sigg, die et noch nit jitt',
Muss De repareere.',
'perfcached' => 'Di Daate heh noh kumme usem Zweschespeicher (cache) un künnte nit mieh janz de allerneuste sin.
{{PLURAL:$1|Bloß ein Antwoot es|Nit mieh wi $1 Antwoote sin|Kein Antwoot es}} doh faßjehallde un ze han.',
-'perfcachedts' => 'De Daate heenoh kumme usem Zweschespeicher (Cache) un woodte aam $2 öm $3 opjenumme. Se künnte nit janz de allerneuste sin.
+'perfcachedts' => 'De Daate heenoh kumme usem Zweschespeicher (<i lang="en">cache</i>) un woodte aam $2 öm $3 opjenumme. Se künnte nit janz de allerneuste sin.
{{PLURAL:$4|Bloß ein Antwoot es|Nit mieh wi $4 Antwoote sind|Kein Antwoot es}} doh ze han.',
'querypage-no-updates' => "'''Heh die Sigg weed nit mieh op ene neue Stand jebraat.'''",
'wrong_wfQuery_params' => 'Verkihrte Parameter för: <strong><code>wfQuery()</code></strong><br />
'logouttext' => "'''Jetz bes de usjelogg'''
Do künnts heh em Wiki wigger maache, als ene namelose Metmaacher. Do kanns De ävver och [[Special:UserLogin|widder enlogge]], als däselve oder och ene andere Metmaacher.
-Künnt sin, dat De de ein oder ander Sigg immer wigger aanjezeich kriss, wie wann de noch enjelogg wörs. Dun Dingem Brauser singe Cache fottschmieße oder leddich maache, öm us dä Nummer erus ze kumme!",
+Künnt sin, dat De de ein oder ander Sigg immer wigger aanjezeich kriss, wie wann de noch enjelogg wörs. Dun Dingem Brauser singe <i lang=\"en\">Cache</i> fottschmieße oder leddich maache, öm us dä Nummer erus ze kumme!",
'welcomecreation' => '== Dach, $1! ==
Dinge Zojang för heh es do.
Do bes jetz aanjemeldt.
Denk dran, Do künnts Der [[Special:Preferences|Ding Enstellunge heh för {{GRAMMAR:Akk|{{SITENAME}}}} zeräächmaache]].',
-'yourname' => 'Metmaacher Name:',
+'yourname' => 'Metmaacher_Naame:',
'yourpassword' => 'Paßwoot:',
'yourpasswordagain' => 'Noch ens dat Passwood',
'remembermypassword' => 'Op Duur aanmelde (hält {{PLURAL:$1|för eine Daach|bes op $1 Dääsch|bloß för hück}})',
'externaldberror' => 'Do wor ene Fähler en de externe Daatebank, oder Do darfs Ding extern Daate nit ändere. Dat Aanmelde jingk jedenfalls donevve.',
'login' => 'Enlogge',
'nav-login-createaccount' => 'Enlogge, Aanmälde',
-'loginprompt' => 'Öm heh enlogge ze künne, muss De de Cookies en Dingem Brauser enjeschalt han.',
+'loginprompt' => 'Öm heh enlogge ze künne, muss De de <i lang="en">Cookies</i> en Dingem Brauser enjeschalt han.',
'userlogin' => 'Enlogge odder Metmaacher wääde',
'userloginnocreate' => 'Enlogge',
'logout' => 'Ußlogge',
{{ucfirst:{{GRAMMAR:Nom|{{SITENAME}}}}}} bruch Cookies, öm ze merke, wä enjelogg es.
Wann De Cookies avjeschald häs en Dingem Brauser, dann kann dat nit laufe.
Sök Der ene Brauser, dä et kann, dun se enschalte, un dann log Dich noch ens neu en, met Dingem neue Metmaacher Name un Passwood.',
-'nocookieslogin' => '{{ucfirst:{{GRAMMAR:Nominativ|{{SITENAME}}}}}} bruch cookies för et Enlogge. Et süht esu us, als hätts De de cookies avjeschalt. Dun se aanschalte un dann versök et noch ens. Odder söök Der ene Brauser, dä et kann.',
+'nocookieslogin' => '{{ucfirst:{{GRAMMAR:Nominativ|{{SITENAME}}}}}} bruch <i lang="en">cookies</i> för et Enlogge. Et süht esu us, als hätts De de <i lang="en">cookies</i> avjeschalt. Dun se aanschalte un dann versök et noch ens. Odder söök Der ene Brauser, dä et kann.',
'nocookiesfornew' => 'Et wood keine Zohjang opjemaat, weil mer nit jeweß sin künne, woh de Daate her kohme.
-Dinge Brauser moß cookies enjeschalldt han.
+Dinge Brauser moß <i lang="en">cookies</i> enjeschalldt han.
Donn dat prööfe, donn heh di Sigg norr_ens neu laade, un dann versöhk et norr_ens.',
'noname' => 'Dat jeiht nit als ene Metmaacher Name. Jetz muss De et noch ens versöke.',
'loginsuccesstitle' => 'Dat Enlogge hät jeflupp.',
--
{{SITENAME}}: {{fullurl:{{Mediawiki:mainpage}}}}',
'noemail' => 'Dä Metmaacher „$1“ hät en dämm sing Enstellunge kein E-Mail Adress aanjejovve.',
-'noemailcreate' => 'Do moß en jöltijje Adräß för Ding e-mail aanjävve',
+'noemailcreate' => 'Do moß en jöltijje Adräß för Ding <i lang="en">e-mail</i> aanjävve',
'passwordsent' => 'E neu Passwood es aan de E-Mail Adress vun däm Metmaacher „$1“ ungerwähs. Meld dich domet aan, wann De et häs. Dat ahle Passwood bliev erhalde un kann och noch jebruch wääde, bes dat De Dich et eetste Mol met däm Neue enjelogg häs.',
'blocked-mailpassword' => 'Ding IP Adress es blockeet.',
-'eauthentsent' => 'En e-mail es jetz ungerwähs aan di Adress, die en de Enstellunge steiht. Ih dat e-mails üvver {{GRAMMAR:Genitiv iere male|{{SITENAME}}}} e-mail-Knopp verscheck wääde künne, muss de e-mail-Adress eets ens bestätich woode sin. Wat mer doför maache moß, steiht en dä e-mail dren, die jrad avjescheck woode es.',
+'eauthentsent' => 'En <i lang="en">e-mail</i> es jetz ungerwähs aan di Adress, die en de Enstellunge steiht. Ih dat <i lang="en">e-mails</i> üvver {{GRAMMAR:Genitiv iere male|{{SITENAME}}}} <i lang="en">e-mail</i>-Knopp verscheck wääde künne, muss de <i lang="en">e-mail</i>-Adress eets ens bestätich woode sin. Wat mer doför maache moß, steiht en dä <i lang="en">e-mail</i> dren, die jrad avjescheck woode es.',
'throttled-mailpassword' => 'En Erennerung för di Passwood es ungerwähs. Domet ene fiese Möpp keine Dress fabrizeet, passeet dat hüchstens eimol en {{PLURAL:$1|der Stund|$1 Stunde|nidd ens eine Stund}}.',
'mailerror' => 'Fähler beim E-Mail Verschecke: $1.',
'acct_creation_throttle_hit' => '<b>Schad.</b>
'emailauthenticated' => 'Ding E-Mail Adress wood aam <strong>$2</strong> öm <strong>$3</strong> Uhr bestätich.',
'emailnotauthenticated' => 'Ding E-Mail Adress es <strong>nit</strong> bestätich. Dröm kann kein E-Mail aan Dich jescheck wääde för:',
'noemailprefs' => 'Dun en E-Mail Adress endrage, domet dat et all fluppe kann.',
-'emailconfirmlink' => 'Dun Ding e-mail Adress bestätije looße',
-'invalidemailaddress' => 'Wat De do als en Adreß för Ding e-mail aanjejovve häs, süht noh Dress us. En e-mail Adreß en däm Format, dat jitt et nit. Muss De repareere - oder Do mähs dat Feld leddich un schrievs nix eren. Un dann versök et noch ens.',
-'cannotchangeemail' => 'Sing e-mail Addreß kam_mer ehe em Wiki nit ändere.',
-'emaildisabled' => 'Heh dat Wiki kann kein e-mails verschecke.',
+'emailconfirmlink' => 'Dun Ding <i lang="en">e-mail</i> Adress bestätije looße',
+'invalidemailaddress' => 'Wat De do als en Adreß för Ding <i lang="en">e-mail</i> aanjejovve häs, süht noh Dress us. En <i lang="en">e-mail</i> Adreß en däm Format, dat jitt et nit. Muss De repareere - oder Do mähs dat Feld leddich un schrievs nix eren. Un dann versök et noch ens.',
+'cannotchangeemail' => 'Sing <i lang="en">e-mail<i> Addreß kam_mer ehe em Wiki nit ändere.',
+'emaildisabled' => 'Heh dat Wiki kann kein <i lang="en">e-mails</i> verschecke.',
'accountcreated' => 'Aanjemeldt',
'accountcreatedtext' => 'De Aanmeldung för dä Metmaacher „<strong>$1</strong>“ es dorsch, De kanns jetz enlogge.',
'createaccount-title' => 'Enne neue Metmaacher aanmelde för {{GRAMMAR:Akkusativ|{{SITENAME}}}}',
'login-abort-generic' => 'Dat Enlogge hät nit jeflup.',
'loginlanguagelabel' => 'Sproch: $1',
'suspicious-userlogout' => "Do bes '''nit''' ußjelogg.
-Et süht us, wi wann ene kappodde Brauser udder proxyẞööver met Zwescheschpeischer noh däm Ußlogge jefrooch hät.",
+Et süht us, wi wann ene kappodde Brauser udder <i lang=\"en\">proxy</i>ẞööver met Zwescheschpeischer noh däm Ußlogge jefrooch hät.",
# E-mail sending
'php-mail-error-unknown' => 'Nit bekannte Fähler met dä Funxjohn <code lang="en">mail()</code> vum PHP',
-'user-mail-no-addy' => 'Do häs versöhg en e-mail der ohne en Adräß ze verschecke',
+'user-mail-no-addy' => 'Do häs versöhg en <i lang="en">e-mail</i> der ohne en Adräß ze verschecke',
# Change password dialog
'resetpass' => 'Passwood tuusche udder neu ußjävve',
# Special:PasswordReset
'passwordreset' => 'Et Paßwoot zeröck säze',
-'passwordreset-text' => 'Donn dat Fommulaa heh ußfölle, öm en e-mail ze krijje, woh jät övver der Zohjang heh obb et Wiki för Desch dren shteiht.',
+'passwordreset-text' => 'Donn dat Fommulaa heh ußfölle, öm en <i lang="en">e-mail</i> ze krijje, woh jät övver der Zohjang heh obb et Wiki för Desch dren shteiht.',
'passwordreset-legend' => 'Et Paßwoot zeröck säze',
'passwordreset-disabled' => 'Et Paßwoot zeröck ze säze es heh em Wiki afjeschalldt.',
'passwordreset-pretext' => '{{PLURAL:$1||Jiff ein vun dä Saache heh dronger enn|}}',
'passwordreset-username' => 'Metmaacher Name:',
'passwordreset-domain' => 'Domähn:',
-'passwordreset-capture' => 'Wells De di e-mail beloore?',
-'passwordreset-capture-help' => 'Wann De heh e Krüzje määß, kriß de di e-mail met däm neue Paßwoot aanjezeish, ußer dat dä Metmaacher se och zohjescheck kritt.',
-'passwordreset-email' => 'De Adräß för de e-mail:',
+'passwordreset-capture' => 'Wells De di <i lang="en">e-mail</i> beloore?',
+'passwordreset-capture-help' => 'Wann De heh e Krüzje määß, kriß de di <i lang="en">e-mail</i> met däm neue Paßwoot aanjezeish, ußer dat dä Metmaacher se och zohjescheck kritt.',
+'passwordreset-email' => 'De Adräß för de <i lang="en">e-mail</i>:',
'passwordreset-emailtitle' => 'Einzelheite för der Zohjang op {{GRAMMAR:Akkusativ|{{SITENAME}}}}',
'passwordreset-emailtext-ip' => 'Do künns et sällver jewääse sin, öhnswää hät vun dä Adräß $1 en Internet öm
en e-mail jefrooch, met Daate övver Dinge Zohjäng op {{GRAMMAR:Akkusativ|{{SITENAME}}}}
moß De jäz jaa nix donn, un kanns Ding Paßwoot wigger bruche.',
'passwordreset-emailelement' => 'Metmaacher Name: $1
Eijmohl-Paßwoot: $2',
-'passwordreset-emailsent' => 'En e-mail met Aanjaabe zom Zohjang heh es verscheck.',
-'passwordreset-emailsent-capture' => 'En e-mail es verscheck. Heh dronger kanns De se lässe.',
-'passwordreset-emailerror-capture' => 'En e-mail sullt verscheck wääde. Heh dronger kanns De se lässe. Dat Verschecke hät ävver nit jeflup, wääje: $1.',
+'passwordreset-emailsent' => 'En <i lang="en">e-mail</i> met Aanjaabe zom Zohjang heh es verscheck.',
+'passwordreset-emailsent-capture' => 'En <i lang="en">e-mail</i> es verscheck. Heh dronger kanns De se lässe.',
+'passwordreset-emailerror-capture' => 'En <i lang="en">e-mail</i> sullt verscheck wääde. Heh dronger kanns De se lässe. Dat Verschecke hät ävver nit jeflup, wääje: $1.',
# Special:ChangeEmail
-'changeemail' => 'Donn Ding Address för de e-mail ändere',
-'changeemail-header' => 'Donn en Adräß för de e-mail ändere',
-'changeemail-text' => 'Föll dat Fommulaa uß, öm Ding Adräß för de e-mail ze ändere.
+'changeemail' => 'Donn Ding Address för de <i lang="en">e-mail</i> ändere',
+'changeemail-header' => 'Donn en Adräß för de <i lang="en">e-mail</i> ändere',
+'changeemail-text' => 'Föll dat Fommulaa uß, öm Ding Adräß för de <i lang="en">e-mail</i> ze ändere.
Do moß Ding Paßwoot enjävve, öm Ding Änderong ze bschtäätejje.',
'changeemail-no-info' => 'Do mööts ald enjelogg sin, öm tiräk op di Sigg jonn ze dörve',
-'changeemail-oldemail' => 'Ding Address för de e-mail es jäz:',
-'changeemail-newemail' => 'Ding neue Address för de e-mail sull wääde:',
+'changeemail-oldemail' => 'Ding Address för de <i lang="en">e-mail</i> es jäz:',
+'changeemail-newemail' => 'Ding neue Address för de <i lang="en">e-mail</i> sull wääde:',
'changeemail-none' => '(kein)',
'changeemail-submit' => 'Lohß jonn!',
'changeemail-cancel' => 'Ophüre',
'showpreview' => 'Vör-Aansich zeije',
'showlivepreview' => 'Lebendije Vör-Aansich zeije',
'showdiff' => 'De Ungerscheide zeije',
-'anoneditwarning' => "'''Opjepaß:''' Weil De nit enjelogg bes, weed Ding IP-Adräß en dä Sigg ier Leß met de Versione faßjehallde wääde.",
+'anoneditwarning' => "'''Opjepaß:''' Weil De nit enjelogg bes, weed Ding <i lang=\"en\">IP</i>-Adräß en dä Sigg ier Leß met de Versione faßjehallde wääde.",
'anonpreviewwarning' => "''Weil De nit enjlogg bes, weed Ding <code lang=\"en\">IP</code>-Addräß zoamme met dä neue Version faßjehallde, wann de heh di Sigg avspeichere deihß.''",
'missingsummary' => '<strong>Opjepass:</strong> Do häs nix bei „{{int:summary}}“ enjejovve. Dun noch ens op „{{int:savearticle}}“ klicke, öm Ding Änderunge ohne de Zosammefassung ze Speichere. Ävver besser jiss De do jetz tirek ens jet en!',
'missingcommenttext' => 'Jevv en „Koot Zosammejefass, Quell“ aan!',
Luur op de [[{{MediaWiki:Helppage}}|Sigge met Hölp]] noh, wann De mieh dodrüvver wesse wells.
Wann De jar nit heh hen kumme wollts, dann jangk zeröck op die Sigg, wo De herjekumme bes, Dinge Brauser hät ene Knopp doför.',
'anontalkpagetext' => '----
-Dat heh es de Klaaf Sigg för ene namenlose Metmaacher. Dä hät sich noch keine Metmaacher Name jejovve un
+<i>Dat heh es de Klaaf Sigg för ene namenlose Metmaacher. Dä hät sich noch keine Metmaacher Name jejovve un
enjerich, ov deit keine bruche. Dröm bruche mer sing IP Adress öm It oder In en uns Lisste fasszehalde.
Su en IP Adress kann vun janz vill Metmaacher jebruch wääde, un eine Metmaacher kann janz flöck
zwesche de ungerscheidlichste IP Adresse wähßele, womöchlich ohne dat hä et merk. Wann Do jetz ene namenlose
Metmaacher bes, un fings, dat heh Saache an Dich jeschrevve wääde, wo Do jar nix met am Hot häs, dann bes Do
wahrscheinlich och nit jemeint. Denk villeich ens drüvver noh, datte Dich [[Special:UserLogin/signup|anmelde]] deis,
-domet De dann donoh nit mieh met esu en Ömständ ze dun häs, wie de andere namenlose Metmaacher heh. Wann de aanjemelldt bes un deis [[Special:UserLogin|enlogge]], dann kam_mer Desch och fun alle andere Metmaacher ongerschejde.',
+domet De dann donoh nit mieh met esu en Ömständ ze dun häs, wie de andere namenlose Metmaacher heh. Wann de aanjemelldt bes un deis [[Special:UserLogin|enlogge]], dann kam_mer Desch och fun alle andere Metmaacher ongerschejde.</i>',
'noarticletext' => '<span class="plainlinks">Em Momang es keine Tex op dä Sigg. Jangk en de Texte vun ander Sigge [[Special:Search/{{PAGENAME}}|noh däm Titel söke]], odder [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} donn en de Logböcher donoh loore], oder [{{FULLURL:{{FULLPAGENAME}}|action=edit}} fang die Sigg aan] ze schrieve, oder jangk zeröck wo de her koms. Do hät Dinge Brauser ene Knopp för.</span>',
'noarticletext-nopermission' => 'Op dä Sigg es em Momang nix drop.
Do kanns noh däm Tittel vun heh dä Sigg [[Special:Search/{{PAGENAME}}|em Tex op ander Sigge söhke]],
\'\'\'Opjepass:\'\'\'
Noh em Speichere künnd et sin, datt De Dingem Brauser singe Cache Speicher
övverlisste muss, ih datt De de Änderunge och ze sinn kreß.
-* Beim \'\'\'Mozilla\'\'\' un \'\'\'Firefox\'\'\' un \'\'\'Safari\'\'\', dröck dä Jrußschreffknopp eronger un kleck dann op Reload / Aktualisieren, udder dröck Ctrl+F5 / Strg+F5, udder dröck Ctrl+R / Strg+R, obb enem Macintosh dröck ⌘+R.
-* Beim \'\'\'Google Chrome\'\'\', dröck Ctrl+Jrußschreffknopp+R / Strg+Jrußschreffknopp+R, obb enem Macintosh dröck ⌘+Jrußschreffknopp+R.
-* Beim \'\'\'Internet Explorer\'\'\', dröck dä Jrußschreffknopp eronger un kleck dann op Refresh / Aktualisieren, udder dröck Ctrl+F5 / Strg+F5,
-* Beim \'\'\'Konqueror\'\'\' kleck op Reload / Aktualisieren, udder dröck op F5.
-* Beim \'\'\'Opera\'\'\' kanns De övver et Menue jonn un däm janze Zwescheschpeischer singe Enhald övver Tools → Preferences / Werkzeug → Einstellungen fottschmieße, neuerdings jeiht et och met Alt+F5.',
+* Beim \'\'\'<i lang="en">Mozilla</i>\'\'\' un \'\'\'<i lang="en">Firefox</i>\'\'\' un \'\'\'<i lang="en">Safari</i>\'\'\', dröck dä Jrußschreffknopp eronger un kleck dann op <i lang="en">Reload</i> / <i lang="de">Aktualisieren</i>, udder dröck <i lang="de">Ctrl+F5</i> / <i lang="de">Strg+F5</i>, udder dröck <i lang="en">Ctrl+R</i> / <i lang="de">Strg+R</i>, obb enem <i lang="en">Macintosh</i> dröck <i lang="de">⌘+R</i>.
+* Beim \'\'\'<i lang="en">Google Chrome</i>\'\'\', dröck <i lang="en">Ctrl+Jrußschreffknopp+R</i> / <i lang="de">Strg+Jrußschreffknopp+R</i>, obb enem <i lang="en">Macintosh</i> dröck <i lang="de">⌘+Jrußschreffknopp+R</i>.
+* Beim \'\'\'<i lang="en">Internet Explorer</i>\'\'\', dröck dä Jrußschreffknopp eronger un kleck dann op <i lang="en">Refresh</i> / <i lang="de">Aktualisieren</i>, udder dröck <i lang="de">Ctrl+F5</i> / <i lang="de">Strg+F5</i>,
+* Beim \'\'\'<i lang="en">Konqueror</i>\'\'\' kleck op <i lang="en">Reload</i> / <i lang="de">Aktualisieren</i>, udder dröck op <i>F5</i>.
+* Beim \'\'\'<i lang="en">Opera</i>\'\'\' kanns De övver et Menue jonn un däm janze Zwescheschpeischer singe Enhald övver <i lang="en">Tools → Preferences</i> / <i lang="de">Werkzeug → Einstellungen</i> fottschmieße, neuerdings jeiht et och met <i>Alt+F5</i>.',
'usercssyoucanpreview' => '<b>Tipp:</b> Dun met däm <b style="padding:2px; background-color:#ddd;
color:black">Vör-Aansich Zeije</b>-Knopp usprobeere, wat Ding neu
Metmaacher_CSS/Java_Skripp mäht, ih dat et avspeichere deis!',
''Dat Wiki heh hät rüh HTML zojelooße, dröm weed de Vör-Aansich nit jezeich. Domet solls De jeschötz wääde - hoffe mer - un Aanjreffe met Java_Skripp jäje Dinge Kompjuter künne Der nix aandun.''
'''Falls för Dich söns alles jod ussüht, versök et jrad noch ens. Wann dat widder nit flupp, dann versök et ens met [[Special:UserLogout|Uslogge]] un widder Enlogge.'''",
-'token_suffix_mismatch' => "'''Ding Änderung ham_mer nit övvernomme. Dinge Brauser hät Sazzeijche em verstoche Token för et Ändere versout. Dat paßeet och ens, wann enne Proxy nit fungkßjeneet. Et Affspeichere wör do jefährlesch, do künt dä Sigge_Enhaldt kapott bei jon.'''",
+'token_suffix_mismatch' => "'''Ding Änderung ham_mer nit övvernomme. Dinge Brauser hät Sazzeijche em verstoche <i lang=\"en\">Token</i> för et Ändere versout. Dat paßeet och ens, wann enne <i lang=\"en\">Proxy</i> nit fungkßjeneet. Et Affspeichere wör do jefährlesch, do künt dä Sigge_Enhaldt kapott bei jon.'''",
'edit_form_incomplete' => "'''Ene Aandeil vun dämm Fommulaa es nit reshtesh om ẞööver aanjekumme. Donn Ding Ennjaabe prööve, repareere, un versöhg et norrens.'''",
'editing' => 'De Sigg „$1“ ändere',
'creating' => 'De Sigg „$1“ aanlääje',
'currentrev' => 'Neuste Version',
'currentrev-asof' => 'De neuste Version fum $2 öm $3 Uhr',
'revisionasof' => 'De Version vum $2 öm $3 Uhr',
-'revision-info' => 'Dat heh es de övverhollte Version $3, vum $2 aam $4 öm $5 Uhr afjeshpeichert.',
+'revision-info' => 'Dat heh es de övverhollte Version $3, {{GENDER:$2|vum|vum|vum Metmaacher|vun dä|vum}} $2 aam $4 öm $5 Uhr afjeshpeichert.',
'previousrevision' => '← De Version dovör zeije',
'nextrevision' => 'De Version donoh zeije →',
'currentrevisionlink' => 'De neuste Version',
'rev-deleted-comment' => '(„Koot Zosammejefass, Quell“ usjeblendt)',
'rev-deleted-user' => '(Metmaacher Name usjeblendt)',
'rev-deleted-event' => '(Logboch-Enndraach fottjenomme)',
-'rev-deleted-user-contribs' => '[Däm Metmaacher singe Name udder sing IP-Addräß wood veschtoche, un heh di Änderung douch nit mieh en de Leß met de Beidrääsch op]',
+'rev-deleted-user-contribs' => '[Däm Metmaacher singe Name udder sing <i lang="en">IP</i>-Addräß wood veschtoche, un heh di Änderung douch nit mieh en de Leß met de Beidrääsch op]',
'rev-deleted-text-permission' => "Die Version fun dä Sigg es '''fottjeschmeße'''.
Wann Ehr en [{{FULLURL:{{#spezial:Log}}/delete|page={{FULLPAGENAMEE}}}} {{lcfirst:{{int:deletionlog}}}}] loore doht, künnt Ehr velleisch mieh do drövver lesse.",
'rev-deleted-text-unhide' => '{{int:rev-deleted-text-permission}} Als ene Wiki-Köbes kanns De [$1 se ävver doch bekike], wann De wells.',
'powersearch-legend' => 'Extra Söhke',
'powersearch-ns' => 'Söök en de Apachtemangs:',
'powersearch-redir' => 'Ömleidunge aanzeije',
-'powersearch-field' => 'Söök noh:',
+'powersearch-field' => 'Söhk noh',
'powersearch-togglelabel' => ' ',
'powersearch-toggleall' => 'Övverall Höhksche draan maache',
'powersearch-togglenone' => 'All Höhksche fott nämme',
'prefs-watchlist-token' => 'Oppassleß-Kennzeishe:',
'prefs-misc' => 'Söns',
'prefs-resetpass' => 'Dat Passwood ändere',
-'prefs-changeemail' => 'Donn en Adräß för de e-mail ändere',
-'prefs-setemail' => 'Donn en Adräß för de e-mail faßlääje',
-'prefs-email' => 'e-mail',
+'prefs-changeemail' => 'Donn en Adräß för de <i lang="en">e-mail</i> ändere',
+'prefs-setemail' => 'Donn en Adräß för de <i lang="en">e-mail</i> faßlääje',
+'prefs-email' => '<i lang="en">e-mail</i>',
'prefs-rendering' => 'Et Sigge-Aanzeije',
'saveprefs' => 'Faßhalde',
'resetprefs' => 'Zeröck setze',
'recentchangesdays-max' => '(Nit mieh wie {{PLURAL:$1|eine Daach|$1 Dääsh|keine Daach}})',
'recentchangescount' => 'Aanzahl Änderunge en de Leß, als Shtandad:',
'prefs-help-recentchangescount' => 'Dat ömfaß de „{{int:recentchanges}}“, de Versione uß de Fojangeheit, un de Logbööcher.',
-'prefs-help-watchlist-token' => 'Wann dat Feld met enem jeheime Schlößel ußjeföllt es, määt et Wiki ene RSS-Enspeisung en et Näz för Ding Oppaßleß op.
+'prefs-help-watchlist-token' => 'Wann dat Feld met enem jeheime Schlößel ußjeföllt es, määt et Wiki ene <i lang="en">RSS</i>-Enspeisung en et Näz för Ding Oppaßleß op.
Wä dä Schlößel weiß, kann ding Oppaßleß lesse. Donn alsu ene seschere un jeheime Wäät doför nämme.
Ene zohfällesch ußjewörfelte Schlößel, dää De nämme künnß, wöhr: <code>$1</code>',
'savedprefs' => 'Ding Enstellunge sin jetz jesechert.',
'defaultns' => 'Söns don en hee dä Appachtemengs söhke:',
'default' => 'Standaad',
'prefs-files' => 'Dateie',
-'prefs-custom-css' => 'Selfsjemaat Cascading Style Sheet',
+'prefs-custom-css' => 'Selfsjemaat <i lang="en">Cascading Style Sheet</i>',
'prefs-custom-js' => 'Selfsjemaat JavaSkripp',
'prefs-common-css-js' => 'Gemeinsam CSS un JavaSkrepp för all de Bovverfläshe:',
'prefs-reset-intro' => 'Op dä Sigg kanns De Ding Enstellunge op dämm Wiki singe Shandatt setze lohße. Ävver Opjepaß: Do jidd et keine „Retuur“-Knopp för!',
-'prefs-emailconfirm-label' => 'Beshtätejung övver e-mail:',
+'prefs-emailconfirm-label' => 'Beshtätejung övver <i lang="en">e-mail</i>:',
'prefs-textboxsize' => 'Wi jruuß sull dat Feld för et Afschnedde un Sigge ändere sin',
'youremail' => 'E-Mail *',
'username' => 'Metmaacher Name:',
'prefs-registration' => 'Aanjemeldt zick',
'prefs-registration-date-time' => 'dem $2 öm $3 Uhr',
'yourrealname' => 'Dinge richtije Name *',
-'yourlanguage' => 'Die Sproch, die et Wiki kalle soll:',
+'yourlanguage' => 'Di Schprooch, di et Wiki kalle soll:',
'yourvariant' => 'Der Dijaläk, de Schriefwies, de Zoot Shprooch för der Enhald.',
'prefs-help-variant' => 'Der Dijaläk udder de Schriefwies udder de Zoot Shprooch, di De för der Enhald.vun Sigge am leevsde häs.',
'yournick' => 'Ding „Ongerschreff“ *',
'gender-male' => 'Kääl odder Jung',
'gender-female' => 'Möhn, Weech odder Mädche',
'prefs-help-gender' => '* Moß mer nit aanjevve, un wann et aanjejovve eß, dann kallt et Wiki övver Desch als „dä Pitter“ udder „dat Tiina“, sönß uns „Metmaacher Pütz“. Dat kritt de janne Welt ze sinn, nit nur Do allein.',
-'email' => 'e-mail',
+'email' => '<i lang="en">e-mail</i>',
'prefs-help-realname' => '* Dinge richtije Name — kanns De fott looße — wann De en ävver nenne wells, dann weed dä jebruch, öm Ding Beidräch domet ze schmöcke.',
-'prefs-help-email' => 'Ding e-mail Adress - kanns De fottlooße, un se es för Andre nit ze sinn - mäht et ävver müjjelich, Der e neu Passwoot ze schecke, wann De et ens verjäße häß.',
-'prefs-help-email-others' => 'Do kannß och zohlohße, dat mer Der domet övver Ding Metmaacherklaafsigg en e-mail schecke kann. Esu künne ander Metmaacher met Der en Kontak kumme, ohne dat se Dinge Name oder Ding e-Mail Adress kenne mööte.',
-'prefs-help-email-required' => 'Do moß en e-mail-Addräß aanjevve.',
+'prefs-help-email' => 'Ding <i lang="en">e-mail</i> Adress - kanns De fottlooße, un se es för Andre nit ze sinn - mäht et ävver müjjelich, Der e neu Passwoot ze schecke, wann De et ens verjäße häß.',
+'prefs-help-email-others' => 'Do kannß och zohlohße, dat mer Der domet övver Ding Metmaacherklaafsigg en <i lang="en">e-mail</i> schecke kann. Esu künne ander Metmaacher met Der en Kontak kumme, ohne dat se Dinge Name oder Ding <i lang="en">e-Mail</i> Adress kenne mööte.',
+'prefs-help-email-required' => 'Do moß en <i lang="en>e-mail</i>-Addräß aanjevve.',
'prefs-info' => 'Jrundlare',
'prefs-i18n' => 'Shprooche-Enshtellunge',
'prefs-signature' => 'Ongerschreff',
'prefs-diffs' => 'Ongerscheide un Verjliische',
# User preference: e-mail validation using jQuery
-'email-address-validity-valid' => 'De Addräß fö de e-mail schingk en Odenung',
-'email-address-validity-invalid' => 'Jivv en jöltijje Addräß fö de e-mail en',
+'email-address-validity-valid' => 'De Addräß fö de <i lang="en">e-mail</i> schingk en Odenung',
+'email-address-validity-invalid' => 'Jivv en jöltijje Addräß fö de <i lang="en">e-mail</i> en',
# User rights
'userrights' => 'Metmaacher ehr Räächde verwalde',
'userrights-nodatabase' => 'De Datebank „<strong>$1</strong>“ is nit doh, oder se litt op enem andere ẞööver.',
'userrights-nologin' => 'Do moss als ene Wiki-Köbes [[Special:UserLogin|enjelog sin]], för dat De Metmaacher ier Rääschte ändere kanns.',
'userrights-notallowed' => 'Met Dingem Zohjang heh häs De nit dat Rääsch, Rääschde aan Metmaacher ze verdeile udder se fott ze nämme.',
-'userrights-changeable-col' => '{{PLURAL:$1:Di Jropp|Jruppe|kein Jropp doh}}, die De ändere kanns',
-'userrights-unchangeable-col' => '{{PLURAL:$1:Di Jropp|Jruppe|kein Jropp doh}}, die De nit ändere kanns',
+'userrights-changeable-col' => '{{PLURAL:$1:Di Jropp|Jroppe|kein Jropp doh}}, di De ändere kanns',
+'userrights-unchangeable-col' => '{{PLURAL:$1:Di Jropp|Jroppe|kein Jropp doh}}, di De nit ändere kanns',
'userrights-irreversible-marker' => '$1 *',
# Groups
'group-user-member' => '{{GENDER:$1|Metmaacher|Metmaacherin}}',
'group-autoconfirmed-member' => 'automattesch beshtääteshte {{GENDER:$1|Metmaacher|Metmaacherėn}}',
-'group-bot-member' => 'Bot',
-'group-sysop-member' => 'Wiki-Köbes',
+'group-bot-member' => '{{GENDER:$1|Bot}}',
+'group-sysop-member' => '{{GENDER:$1|Wiki-Köbes}}',
'group-bureaucrat-member' => '{{GENDER:$1|Bürrokrad|Bürrokraatėn}}',
-'group-suppress-member' => 'Kontrolletti',
+'group-suppress-member' => '{{GENDER:$1|Kontrolletti}}',
'grouppage-user' => '{{ns:project}}:Metmaacher',
'grouppage-autoconfirmed' => '{{ns:project}}:Bestätichte Metmaacher',
'right-edituserjs' => 'Anderlücks JS-Dateie ä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 ([http://www.mediawiki.org/wiki/Manual:%24wgRateLimits $wgRateLimits])',
+'right-noratelimit' => 'Kein Beschränkunge dorch Jrenze (<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-userrights-interwiki' => 'Metmaacher ier Rääschte in ander Wikis ändere',
'right-siteadmin' => 'De Datebank deeschmaache un opmaache för Änderunge',
'right-override-export-depth' => 'Beim Sigge Expoteere de Sigge metnämme, woh Lingks drop jon — beß fönef Schredde wigk',
-'right-sendemail' => 'e-mail aan ander Metmaacher schecke',
-'right-passwordreset' => 'De e-mails vum Paßwoot neu Säze aanloore',
+'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',
# User rights log
'rightslog' => 'Logboch för Änderunge aan Metmaacher-Räächde',
'action-userrights' => 'alle Metmaacher ier Rääschte ze ändere',
'action-userrights-interwiki' => 'dä Metmaacher fun ander Wikis ier Rääschte ze ändere',
'action-siteadmin' => 'de Datebank ze sperre udder widder freizejävve',
-'action-sendemail' => 'e-mails ze verschecke',
+'action-sendemail' => '<i lang="en">e-mails</i> ze verschecke',
# Recent changes
'nchanges' => '{{PLURAL:$1|Ein Änderung|$1 Änderunge|Kein Änderung}}',
'recentchanges' => 'Neuste Änderunge',
'recentchanges-legend' => 'Enstellunge',
'recentchanges-summary' => 'Op dä Sigg hee sin de neuste Änderunge am Wiki opjeliss.',
-'recentchanges-feed-description' => 'Op dämm Abonnomang-Kannal (Feed) kannze de {{int:recentchanges}} aam Wiki en Laif un en Färve metloore.',
+'recentchanges-feed-description' => 'Op dämm Abonnomang-Kannal (<i lang="en">Feed</i>) kannze de {{int:recentchanges}} aam Wiki en Laif un en Färve metloore.',
'recentchanges-label-newpage' => 'Heh di Sigg es neu dobei jekumme met dä Änderung',
'recentchanges-label-minor' => 'Heh dat es en Mini-Änderung',
'recentchanges-label-bot' => 'Di Änderung es fun enem Bot jemaat woode',
'recentchanges-label-unpatrolled' => 'Heh di Änderung es noch nit nohjeloort',
'rcnote' => '{{PLURAL:$1|Heh es de letzte Änderung us|Heh sin de letzte <strong>$1</strong> Änderunge us|Et jit <strong>kei</strong> Änderunge en}} {{PLURAL:$2|däm letzte Daach|de letzte <strong>$2</strong> Dääsch|dä Zick}} vum <strong>$4</strong> aff <strong>$5</strong> Uhr beß jetz.',
-'rcnotefrom' => 'Hee sin bes <strong>$1</strong> fun de Änderunge zick däm <strong>$3</strong> öm <strong>$4</strong> Uhr opjeliss.',
+'rcnotefrom' => 'Hee {{PLURAL:$1|es ein|sin bes op <strong>$1</strong>|es keine}} fun de Änderunge zick dem <strong>$3</strong> öm <strong>$4</strong> Uhr opjelėß.',
'rclistfrom' => 'Zeich de Änderunge vum $1 aan',
'rcshowhideminor' => '$1 klein Mini-Änderunge',
'rcshowhidebots' => '$1 de Bots ehr Änderunge',
Do kanns dann Ding Werk en Sigge enbinge, met Lengks en dä Aate:
* <code>'''<nowiki>[[</nowiki>{{ns:file}}:'''''Beldche'''''.jpg]]'''</code> — för di janze Dattei ze zeije, wi se eß,
* <code>'''<nowiki>[[</nowiki>{{ns:file}}:'''''Beld'''''.svg | '''''200''''' px]]'''</code> — för e Mini-Beldsche met 200 Pixelle Breedt ze zeije,
-* <code>'''<nowiki>[[</nowiki>{{ns:file}}:'''''Su süht dat us'''''.png | left | thumb | '''''ene Tex''''' ]]'''</code> — deiht e 200-Pixel-Mini-Beldsche en ene Kaßte aan der lenke (left) Rand vun dä Sigg un „ene Tex“ onger däm Beldsche,
+* <code>'''<nowiki>[[</nowiki>{{ns:file}}:'''''Su süht dat us'''''.png | left | thumb | '''''ene Tex''''' ]]'''</code> — deiht e 200-Pixel-Mini-Beldsche en ene Kaßte aan der lenke (<i lang=\"en\">left</i>) Rand vun dä Sigg un „ene Tex“ onger däm Beldsche,
* <code>'''<nowiki>[[</nowiki>{{ns:media}}:'''''Esu hürt sich dat aan'''''.ogg]]'''</code> — öm tiräk op en Dattei ze Lenke, ohne se aanzzeije.
Usführlich met alle Müjjelichkeite fings de dat bei de Hölp.",
'upload-permitted' => 'Nor de Dateitüpe <code>$1</code> sin zojelohße.',
(En de [[Special:NewFiles|Jalleri met neu Dateie]] kriß De ene Övverbleck med Belldsche)',
'filename' => 'Dä Name vun dä Datei',
'filedesc' => 'Beschrievungstex un Zosammefassung',
-'fileuploadsummary' => 'Beschrievungstex un Zosammefassung:',
+'fileuploadsummary' => 'Beschrievungstex un Zosammefaßong:',
'filereuploadsummary' => 'Änderunge aan Dateie:',
'filestatus' => 'Urhevver Räächsstatus:',
'filesource' => 'Quell:',
un dann muss de dat Dinge noch ens huhlade.',
'filename-toolong' => 'Name för Dateije künne nit mih wi 240 Bytes lang sind.',
'badfilename' => 'De Datei es en „$1“ ömjedäuf.',
-'filetype-mime-mismatch' => 'Dä Datei ier Ängk vum Name (<code lang="en">.$1</code>) paß nit zo dä MIME-Zoot (<code lang="en">$2</code>)',
+'filetype-mime-mismatch' => 'Dä Datei ier Ängk vum Name (<code lang="en">.$1</code>) paß nit zo dä <i lang="en">MIME</i>-Zoot (<code lang="en">$2</code>)',
'filetype-badmime' => 'Dateie mem MIME-Typ „<code>$1</code>“ wulle mer nit huhjelade krijje.',
'filetype-bad-ie-mime' => 'Di Datei kam_mer nit huhlade, weil der Internet Explorrer se för en „$1“
hallde deiht, wat nit erlaub, un müjjelelscherwies ene jefährlesche Dattei-Typp es.',
'uploadfromurl-queued' => 'Dat Huhlaade es jiz en de Waadeschlang.',
'uploaddisabledtext' => 'Et Huhlade es jesperrt.',
'php-uploaddisabledtext' => 'Et Dateie Huhlade es en PHP affjeschalldt.
-Bes esu joot un donn noh de Enshtellung file_uploads loore.',
+Bes esu joot un donn noh de Enshtellung <i lang="en">file_uploads</i> loore.',
'uploadscripted' => 'En dä Datei es HTML dren oder Code vun enem Skripp, dä künnt Dinge Brauser en do verkihrte Hals krije un usführe.',
'uploadvirus' => 'Esu ene Dress:
<br />
Java-Datteie huhlaade es nit zohjelohße, weil mer domet de Enshtellunge ömjonn kann, di der ẞörver schöze un däm sing Sescherheit jarranteere.',
'upload-source' => 'Wo de Daate herkumme',
'sourcefilename' => 'Datei zem huhlade:',
-'sourceurl' => 'URL för vun eronger ze laade',
+'sourceurl' => '<i lang="en">URL</i> för vun eronger ze laade',
'destfilename' => 'Unger däm Dateiname avspeichere:',
'upload-maxfilesize' => 'Der jrüütßte müjjelesche Ömfang för en Datei es $1.',
'upload-description' => 'Övver di Datei',
Mer wesse nit woröm.
Pröf de URL un versök et noch ens.
Wann et nit flupp, verzäll et enem [[Special:ListUsers/sysop|Wiki-Köbes]].',
-'upload-too-many-redirects' => 'Zoh vill Ömleitunge en däm URL',
+'upload-too-many-redirects' => 'Zoh vill Ömleitunge en däm <i lang="en">URL</i>',
'upload-unknown-size' => 'Mer weße nit, wi jruuß',
-'upload-http-error' => 'Ene HTTP-Fäähler es opjetrodde: $1',
+'upload-http-error' => 'Ene <i lang="en">HTTP</i>-Fäähler es opjetrodde: $1',
'upload-copy-upload-invalid-domain' => 'Fun dä Domain künne mer nix noh heh huh laade. Di es nit zohjelohße.',
# File backend
'lockmanager-fail-acquirelock' => 'Mer kunnte kein Schpärr för „$1“ krijje.',
'lockmanager-fail-openlock' => 'Mer kunnte di Schpärdatei för „$1“ nit opmaache.',
'lockmanager-fail-releaselock' => 'Mer kunnte di Schpärr för „$1“ nit ophävve.',
+'lockmanager-fail-db-bucket' => 'Mer kunnet nit met jenooch <i lang="en">lock databases in bucket$1</i> en Verbendong opnämme.',
'lockmanager-fail-db-release' => 'Mer kunnte di Schpärre för de Daatebangk „$1“ nit ophävve.',
'lockmanager-fail-svr-release' => 'Mer kunnte di Schpärre för dä ẞööver „$1“ nit ophävve.',
Dat Wiki heh es ävver öffentlesch.
Dröm es <code lang="en">img_auth.php</code> zor Sisherheit heh affjeschalldt.',
'img-auth-noread' => 'Dä Metmaacher hät keine Zohjang, öm „$1“ ze lässe.',
-'img-auth-bad-query-string' => 'En däm URL es en onjöltije Aanjab henger em Froorezeishe dren.',
+'img-auth-bad-query-string' => 'En däm <i lang="en">URL</i> es en onjöltije Aanjab henger em Froorezeishe dren.',
# HTTP errors
'http-invalid-url' => 'Dat es en onjöltije URL: $1',
'http-read-error' => 'Et Lässe beim <code lang="en">HTTP</code> es donävve jeange.',
'http-timed-out' => 'Di <code lang="en">HTTP</code>-Aanforderung hät zoh lang jebruch.',
'http-curl-error' => 'Ene Fähler es opjetrodde beim Holle vun däm <code lang="en">URL</code>: $1',
-'http-host-unreachable' => 'Mer sen nit noh dämm URL dorschjekumme.',
+'http-host-unreachable' => 'Mer sen nit noh dämm <i lang="en">URL</i> dorschjekumme.',
'http-bad-status' => 'Bei dä <code lang="en">HTTP</code>-Aanforderung es e Problem opjetrodde: Fähler $1 — $2',
# Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
'upload-curl-error6' => 'Keij Antwoot vun däm <code lang="en">URL</code>.',
-'upload-curl-error6-text' => 'Dä ẞööver för dä URL hät zo lang nit jeantwoot. Bes esu joot un fingk eruß, ov dä övverhoup aam Laufe es, un don_ens jenou pröfe, ov dä URL stemmp.',
+'upload-curl-error6-text' => 'Dä ẞööver för dä <i lang="en">URL</i> hät zo lang nit jeantwoot. Bes esu joot un fingk eruß, ov dä övverhoup aam Laufe es, un don_ens jenou pröfe, ov dä <i lang="en">URL</i> stemmp.',
'upload-curl-error28' => "Dat Huhlade hät zo lang jedooert, do ha'mer't jestopp",
'upload-curl-error28-text' => 'Dä ẞööver för di URL hät zo lang nit jeantwoot.
Bes esu joot un fingk eruß, ov dä övverhoup aam Laufe es.
'filehist-user' => 'Metmaacher',
'filehist-dimensions' => 'Pixelle Breed×Hühte (Dateiömfang)',
'filehist-filesize' => 'Dateiömfang',
-'filehist-comment' => 'Aanmerkung',
+'filehist-comment' => 'Aanmärkong',
'filehist-missing' => 'Di Datei es nit doh',
'imagelinks' => 'Jebruch',
'linkstoimage' => 'Heh {{PLURAL:$1|kütt di Sigg|kumme di $1 Sigge|sin keij Sigge}}, die op heh di Dattei linke {{PLURAL:$1|deiht|dun|dun}}:',
# MIME search
'mimesearch' => 'Dateie üvver dänne ehre <span lang="en">MIME</span>-Typ söke',
-'mimesearch-summary' => 'Op hee dä {{int:nstab-special}} könne de Dateie noh em MIME-Tüpp ußjesöök wäde.
+'mimesearch-summary' => 'Op hee dä {{int:nstab-special}} könne de Dateie noh em <i lang="en">MIME</i>-Tüpp ußjesöök wäde.
Mer moß immer der Medietüp un der Ongertüp aanjevve.
Zem Beispell: <code lang="en">image/jpeg</code>
— kannß donoh op dä Beschrievungssigge von de Dateie loore.',
'usereditcount' => '{{PLURAL:$1|Ein Änderung|$1 Änderunge|Nix jedonn}}',
'usercreated' => '{{GENDER:$3|}}Aanjemelldt aam $1 öm $2 Uhr',
'newpages' => 'Neu Sigge',
-'newpages-username' => 'Metmaacher Name:',
+'newpages-username' => 'Metmaacher_Naame:',
'ancientpages' => 'Atikele zoteet vun Ahl noh Neu',
'move' => 'Ömnenne',
'movethispage' => 'De Sigg ömnenne',
Dä Logböcher ehre Enhald ka'mer all noh de Aat, de Metmaacher,
oder de Sigge ehr Name, un esu, einzel zoteet aanluure.
Bei de Name moß mer op Jruß- un Kleinschreff aachjävve.",
-'logempty' => 'Mer han kein zopass Endräch en däm Logboch.',
+'logempty' => '<i>Mer han kein zopass Endräch en däm Logboch.</i>',
'log-title-wildcard' => 'Sök noh Titelle, di aanfange met …',
'showhideselectedlogentries' => 'Ußjesöhk Endrääsch verschteische udder zeije',
'cachedspecial-refresh-now' => 'De neuste Version.',
# Special:Categories
-'categories' => 'Saachjruppe',
+'categories' => 'Saachjroppe',
'categoriespagetext' => 'Hee {{PLURAL:$1|es nur en Saachjrupp|sin nur Saachjruppe|es kei Saachjrupp}} jeliss, woh jät dren {{PLURAL:$1|es|es|wöhr}}. Mer han_er eije Leßte för de
[[Special:UnusedCategories|Saachjruppe met nix dren]], un de
[[Special:WantedCategories|jewönschte un nit aanjelaate Saachjruppe]].',
Adress en Dinge [[Special:Preferences|ming Enstellunge]] stonn han, öm en E-Mail aan andere Metmaacher ze
schecke.',
'emailuser' => 'E-mail aan dä Metmaacher',
-'emailpage' => 'Verscheck e-mail aan ene Metmaacher',
-'emailpagetext' => 'Wann heh dä Metmaacher en Adräß för sing e-mail aanjejovve hätt en singe Enstellunge,
-un die deit et och, dann kanns De met däm Fomular hee unge en einzel e-mail aan dä Metmaacher schecke.
+'emailpage' => 'Verscheck <i lang="en">e-mail</i> aan ene Metmaacher',
+'emailpagetext' => 'Wann heh dä Metmaacher en Adräß för sing <i lang="en">e-mail</i> aanjejovve hätt en singe Enstellunge,
+un die deit et och, dann kanns De met däm Fomular hee unge en einzel <i lang="en">e-mail</i> aan dä Metmaacher schecke.
-Ding e-mail-Adräß, di De en [[Special:Preferences|Ding eije Enstellunge]] aanjejovve häs,
-di weed als em Afsender sing Adräß en Ding e-mail enjedrage.
-Domet kann, wä di e-mail kritt, drop antwoote, un di Antwood jeiht tirek aan Desch.
+Ding <i lang="en">e-mail</i>-Adräß, di De en [[Special:Preferences|Ding eije Enstellunge]] aanjejovve häs,
+di weed als em Afsender sing Adräß en Ding <i lang="en">e-mail</i> enjedrage.
+Domet kann, wä di <i lang="en">e-mail</i> kritt, drop antwoote, un di Antwood jeiht tirek aan Desch.
Alles klor?',
'usermailererror' => 'Dat E-Mail-Objek jov ene Fähler us:',
'defemailsubject' => 'e-mail fum $1 {{GRAMMAR:fun|{{SITENAME}}}}.',
-'usermaildisabled' => 'De e-mail zwesche Metmaachere es ußjeschalt',
-'usermaildisabledtext' => 'Do kanns kein e-mail aan ander Metmaacher heh en dämm Wiki schecke',
+'usermaildisabled' => 'De <i lang="en">e-mail</i> zwesche Metmaachere es ußjeschalt',
+'usermaildisabledtext' => 'Do kanns kein <i lang="en">e-mail</i> aan ander Metmaacher heh en dämm Wiki schecke',
'noemailtitle' => 'Kein E-Mail Adress',
-'noemailtext' => 'Dä Metmaacher hät kein jöltijje Adreß för sing e-mail enjedrage.<!-- oder hä well kein E-Mail krije. -->',
-'nowikiemailtitle' => 'Kein e-mail zojelohße',
-'nowikiemailtext' => 'Hee dä Metmaacher well kein e-mail vun ander Metmaachere jescheck krijje.',
-'emailnotarget' => 'Et jitt keine Metmaacher met däm Naame, dämm mer en e-mail schecke künnt.',
-'emailtarget' => 'Jiff dä Metmaacher aan, dä di e-mail kritt',
-'emailusername' => 'Metmaacher Name:',
+'noemailtext' => 'Dä Metmaacher hät kein jöltijje Adreß för sing <i lang="en">e-mail</i> enjedrage.<!-- oder hä well kein E-Mail krije. -->',
+'nowikiemailtitle' => 'Kein <i lang="en">e-mail</i> zojelohße',
+'nowikiemailtext' => 'Hee dä Metmaacher well kein <i lang="en">e-mail</i> vun ander Metmaachere jescheck krijje.',
+'emailnotarget' => 'Et jitt keine Metmaacher met däm Naame, dämm mer en <i lang="en">e-mail</i> schecke künnt.',
+'emailtarget' => 'Jiff dä Metmaacher aan, dä di <i lang="en">e-mail</i> kritt',
+'emailusername' => 'Metmaacher_Naame:',
'emailusernamesubmit' => 'Loß Jonn!',
-'email-legend' => 'Scheck en e-mail aan ene andere Metmaacher fum Wiki',
+'email-legend' => 'Scheck en<i lang="en"> e-mail</i> aan ene andere Metmaacher fum Wiki',
'emailfrom' => 'Vun:',
'emailto' => 'Aan:',
'emailsubject' => 'Üvverschreff:',
'emailsend' => 'Avschecke',
'emailccme' => 'Scheck mer en Kopie vun dä E-Mail.',
'emailccsubject' => 'En Kopie vun Dinger E-Mail aan $1: $2',
-'emailsent' => 'De e-mail es ongerwähs',
+'emailsent' => 'De <i lang="en">e-mail</i> es ongerwähs',
'emailsenttext' => 'Ding E-Mail es jetz lossjescheck woode.',
'emailuserfooter' => 'Hee di e-mail hät dä „$1“ an dä „$2“ jescheck, un doför {{GRAMMAR:en dative|{{SITENAME}}}} dat „{{int:emailuser}}“ jebruch.',
# Restrictions (nouns)
'restriction-edit' => 'et Ändere',
'restriction-move' => 'et Ömnenne',
-'restriction-create' => 'Aanläje',
+'restriction-create' => 'Aanlääje',
'restriction-upload' => 'Huhlaade',
# Restriction levels
'sp-contributions-talk' => 'Klaaf',
'sp-contributions-userrights' => 'Räächde verwalde',
'sp-contributions-blocked-notice' => 'Heh dä Metmaacher es em Momang jespert, Dä letzte Enndraach em Logbooch doh drövver kütt jez als ene Henwiiß:',
-'sp-contributions-blocked-notice-anon' => 'Heh di IP-Address es em Momang jesperrt.
+'sp-contributions-blocked-notice-anon' => 'Heh di <i lang="en">IP</i>-Address es em Momang jesperrt.
De neuste Sperr ier Enndraach em Logbooch es:',
'sp-contributions-search' => 'Söök noh Metmaacher ier Beidräg',
'sp-contributions-username' => 'Metmaachername odder IP-Address:',
# Block/unblock
'autoblockid' => 'Automattesche Sperr Nommer $1',
-'block' => 'Metmaacher udder en IP-Addräß sperre',
-'unblock' => 'Don en Sperr för ene Metmaacher udder en IP-Addräß ophävve',
+'block' => 'Metmaacher udder en <i lang="en">IP</i>-Addräß sperre',
+'unblock' => 'Don en Sperr för ene Metmaacher udder en <i lang="en">IP</i>-Addräß ophävve',
'blockip' => 'Metmaacher sperre',
'blockip-title' => 'Metmaacher Schpärre',
'blockip-legend' => 'Metmaacher ov IP-Adresse Sperre',
'blockiptext' => 'Hee kanns De bestemmte Metmaacher oder IP-Adresse sperre, su dat se hee em Wiki nit mieh schrieve und Sigge ändere künne.
Dat sollt nor jedon wääde om sujenannte Vandaale ze bremse. Un mer müsse uns dobei natörlich aan uns [[{{MediaWiki:Policy-url}}|Rejelle]] för esu en Fäll halde.
Drag bei „Aanlass“ ene möchlichs jenaue Jrund en, wöröm dat Sperre passeet. Nenn un Link op de Sigge wo Einer kapott jemaat hät, zem Beispill.',
-'ipadressorusername' => 'IP-Adress oder Metmaacher Name:',
+'ipadressorusername' => '<i lang="en">IP</i>-Adress oder Metmaacher Name:',
'ipbexpiry' => 'Duur, för wie lang',
'ipbreason' => 'Aanlass:',
'ipbreasonotherlist' => 'Ne andere Bejründung',
** esu ene Metmaacher-Name wolle mer nit
* Op en IP-Adräß betrocke Jrönd
** dat es en Proxy-ẞööver övver dänn de Lück zo vill Driß aanjestellt han',
-'ipb-hardblock' => 'Enjelogg Metmaacher dörfe vun heh dä IP-Addräß kein Sigge ändere',
+'ipb-hardblock' => 'Enjelogg Metmaacher dörfe vun heh dä <i lang="en">IP</i>-Addräß kein Sigge ändere',
'ipbcreateaccount' => 'Et Neu-Aanmelde verbeede',
-'ipbemailban' => 'Et e-mail-Verschecke ongerbenge',
+'ipbemailban' => 'Et <i lang="en">e-mail</i>-Verschecke ongerbenge',
'ipbenableautoblock' => 'Dun automatisch de letzte IP-Adress sperre, die dä Metmaacher jehatt hät, un och all die IP-Adresse, vun wo dä versök, jet ze ändere.',
'ipbsubmit' => 'Dun dä Metmaacher sperre',
'ipbother' => 'För en ander Duur:',
'unblocked' => '[[User:$1|$1]] wood widder zojelooße',
'unblocked-range' => 'Dä Berett $1 es nit mieh jesperrt.',
'unblocked-id' => 'De Sperr met dä Nommer $1 es opjehovve',
-'blocklist' => 'De Leß met jesperrte IP-Adräße un Metmaacher',
-'ipblocklist' => 'Leß met jesperrte IP-Adresse un Metmaacher',
+'blocklist' => 'De Leß met jesperrte <i lang="en">IP</i>-Adräße un Metmaacher',
+'ipblocklist' => 'Leß met jesperrte <i lang="en">IP</i>-Adresse un Metmaacher',
'ipblocklist-legend' => 'Ene jesperrte Metmaacher fenge',
'blocklist-userblocks' => 'De einzel Metmaacher ier Sperre ußblende',
'blocklist-tempblocks' => 'De Sperre op Zick ußblende',
-'blocklist-addressblocks' => 'De einzel IP-Addresse ier Sperre ußblende',
+'blocklist-addressblocks' => 'De einzel <i lang="en">IP</i>-Addresse ier Sperre ußblende',
'blocklist-rangeblocks' => 'Don de Sperre för ene Berett ußblände',
'blocklist-timestamp' => 'Zick wann jesperrt',
'blocklist-target' => 'Jesperrt woodt',
'anononlyblock' => 'nor namelose',
'noautoblockblock' => 'automatisch Sperre avjeschalt',
'createaccountblock' => 'neu Aanmelde verbodde',
-'emailblock' => 'e-Mail Schecke verbodde',
+'emailblock' => '<i lang="en">e-Mail</i> Schecke verbodde',
'blocklist-nousertalk' => 'de eije Klaafsigg Änndere verbodde',
'ipblocklist-empty' => 'Do es nix en dä Sperrleß.',
'ipblocklist-no-results' => 'Dä Metmaacher udder di IP-Adrress es janit jesperrt.',
-'blocklink' => 'Sperre',
+'blocklink' => 'schpärre',
'unblocklink' => 'widder freijevve',
'change-blocklink' => 'Sperr ändere',
'contribslink' => 'Beidräch',
'blocklogentry' => 'hät „[[$1]]“ fö de Zick vun $2 jesperrt. $3',
'reblock-logentry' => 'hät di Sperr för dä „[[$1]]“ met dä Duuer fun $2 $3 jeändert',
'blocklogtext' => 'Heh es et Logboch övver et Metmaacher-Sperre un -Freijevve vun Hand.
-Automattesch jesperrte IP-Addräße sin nit heh, ävver en de [[Special:BlockList|{{int:ipblocklist}}]] ze fenge.',
+Automattesch jesperrte <i lang="en>IP</i>-Addräße sin nit heh, ävver en de [[Special:BlockList|{{int:ipblocklist}}]] ze fenge.',
'unblocklogentry' => 'Metmaacher „$1“ freijejovve',
'block-log-flags-anononly' => 'nor de namelose Metmaacher sperre',
'block-log-flags-nocreate' => 'neu Metmaacher aanlääje es verbodde',
'proxyblocker-disabled' => 'Di Funxjon es ußjeschalldt.',
'proxyblockreason' => 'Unger Ding IP_Adress läuf ene offe Proxy.
Dröm kanns De heh em Wiki nix maache.
-Schwaad met Dingem System-Minsch udder Netzwerk-Techniker udder ISP (Internet Service Provider)
+Schwaad met Dingem System-Minsch udder Netzwerk-Techniker udder ISP (<i lang="en">Internet Service Provider</i>)
un verzäll dänne vun däm ärrje Risiko för de Secherheit fun dänne ehr Rääschnere!',
'proxyblocksuccess' => 'Jedonn.',
-'sorbs' => 'DNSBL',
+'sorbs' => '<i lang="en">DNSBL</i>',
'sorbsreason' => 'Ding IP-Adress weed en de DNSbl als ene offe Proxy jeliss. Schwaad met Dingem System-Minsch oder Netzwerk-Techniker (ISP Internet Service Provider) drüvver, un verzäll dänne vun däm Risiko för ehr Secherheit!',
'sorbs_create_account_reason' => 'Ding IP-Adress weed en de DNSbl als ene offe Proxy jeliss. Dröm kanns De Dich heh em Wiki nit als ene neue Metmaacher aanmelde. Schwaad met Dingem System-Minsch oder Netzwerk-Techniker oder (ISP Internet Service Provider) drüvver, un verzäll dänne vun däm Risiko för ehr Secherheit!',
'cant-block-while-blocked' => 'Do kanns ander Metmaacher nit sperre, esu lang wi De sellver jesperrt bes.',
'allmessages-filter-all' => 'ejaal',
'allmessages-filter-modified' => 'heh em Wiki jeändert',
'allmessages-prefix' => 'Name fängk aan met:',
-'allmessages-language' => 'Shprooch:',
+'allmessages-language' => 'Schprooch:',
'allmessages-filter-submit' => 'Lohß Jonn!',
# Thumbnails
'djvu_no_xml' => 'De XML-Date för di DjVu-Datei kunnte mer nit afrofe',
'thumbnail-temp-create' => 'Mer kunnte kein Zweschedattei für Minnibeldscher aanlääje.',
'thumbnail-dest-create' => 'Mer kunnte kein Minnibeldscher faßhallde, woh se hen sulle.',
-'thumbnail_invalid_params' => 'Ene Parameter för et Breefmarke-Belldsche (thumbnail) Maache wohr nit en Odenung',
+'thumbnail_invalid_params' => 'Ene Parameter för et Breefmarke-Belldsche (<i lang="en">thumbnail</i>) Maache wohr nit en Odenung',
'thumbnail_dest_directory' => 'Dat Verzeichnis för dat erin ze donn kunte mer nit aanlääje.',
'thumbnail_image-type' => 'Di Zoot Beld künne mer nit met ömjonn',
-'thumbnail_gd-library' => 'Vun dä GD Projramm_Biplijotheek fäählt en Funkßuhn: „$1“',
+'thumbnail_gd-library' => 'Vun dä <i lang="en">GD</i> Projramm_Biplijotheek fäählt en Funkßuhn: „$1“',
'thumbnail_image-missing' => 'Di Datei schingk nit doh ze sin: <code>$1</code>',
# Special:Import
'javascripttest-pagetext-frameworks' => 'Bes esu jood un söök eine vun dä Prööfömjävvonge us: $1',
'javascripttest-pagetext-skins' => 'Sööke en Bovverfläsch udder et Ußsinn uß, öm di Prööfonge domet ze maache:',
'javascripttest-qunit-intro' => 'Loor noh dä [$1 Dokemäntation övver et Prööfe] op mediawiki.org.',
-'javascripttest-qunit-heading' => 'De Sammlong vum MediaWiki sing JavaSkrep-QUnit-Pröövunge',
+'javascripttest-qunit-heading' => 'De Sammlong vum MediaWiki sing JavaSkrep-<i lang="en">QUnit</i>-Pröövunge',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Don Ding eije Metmaachersigg aanzeije',
'nextdiff' => 'De Änderung donoh zeije →',
# Media information
-'mediawarning' => '<strong>Opjepass</strong>: En dä Datei künnt en <b>jefährlich Projrammstöck</b> dren stecke. Wa\'mer et laufe looße dät, do künnt dä ẞööver, udder Dinge Rääschner, met för de Cracker opjemaht wääde.',
+'mediawarning' => '<strong>Opjepass</strong>: En dä Datei künnt en <b>jefährlich Projrammstöck</b> dren stecke. Wa\'mer et laufe looße dät, do künnt dä ẞööver, udder Dinge Rääschner, met för de <i lang="en">Cracker</i> opjemaht wääde.',
'imagemaxsize' => "Belder nit jrößer maache wie:<br /> ''(op dä Sigge, wo se beschrevve wääde)''",
'thumbsize' => 'Esu breid solle de klein Beldche (Thumbnails/Breefmarke) sin:',
'widthheightpage' => '{{PLURAL:$1|Ei Pixel|$1 Pixelle}} breed × {{PLURAL:$2|Ei Pixel|$2 Pixelle}} huh, {{PLURAL:$3|eij Sigg|$3 Sigge|keij Sigge}}',
'file-info' => 'Dateiömfang: $1, MIME-Tüp: <code>$2</code>',
'file-info-size' => '{{PLURAL:$1|Ei Pixel|$1 Pixelle}} breed × {{PLURAL:$2|Ei Pixel|$2 Pixelle}} huh, de Datei hät $3, dä MIME-Typ es: <code>$4</code>',
-'file-info-size-pages' => '{{PLURAL:$1|Ei Pixel|$1 Pixelle}} breed × {{PLURAL:$2|Ei Pixel|$2 Pixelle}} huh, Ömfang: $3, MIME Zoot: $4, met {{PLURAL:$5|ein Sigg|$5 Sigge|kein Sigge}}',
+'file-info-size-pages' => '{{PLURAL:$1|Ei Pixel|$1 Pixelle}} breed × {{PLURAL:$2|Ei Pixel|$2 Pixelle}} huh, Ömfang: $3, <i lang="en">MIME</i> Zoot: $4, met {{PLURAL:$5|ein Sigg|$5 Sigge|kein Sigge}}',
'file-nohires' => 'Mer han kein hüütere Oplösung vun däm Beld.',
'svg-long-desc' => 'SVG-Datei, de Basis es {{PLURAL:$1|ei Pixel|$1 Pixelle}} breed × {{PLURAL:$2|ei Pixel|$2 Pixelle}} huh, dä Dateiömfang es $3',
'show-big-image' => 'Jröößer Oplöösung',
'exif-artist' => 'Fotojraf odder Maacher',
'exif-copyright' => 'Wä et Urhävverrääsch hät',
'exif-exifversion' => 'Exif-Version',
-'exif-flashpixversion' => 'De ongershtözte Flashpix-Version',
+'exif-flashpixversion' => 'De ongershtözte <i lang="en">Flashpix</i>-Version',
'exif-colorspace' => 'Färveroum',
'exif-componentsconfiguration' => 'Bedüggening fun all de enkele Komponente',
'exif-compressedbitsperpixel' => 'Aat fun de Kompreßjohn fun däm Beld',
'exif-gpsareainformation' => 'Dä Name fum GPS-Jebeet',
'exif-gpsdatestamp' => 'GPS-Dattum',
'exif-gpsdifferential' => 'De Differenzjahl-Bereschtijong fum GPS',
-'exif-jpegfilecomment' => 'Aanmärkong uss ene JPEG-Dattei',
+'exif-jpegfilecomment' => 'Aanmärkong uss ene <i lang="en">JPEG</i>-Dattei',
'exif-keywords' => 'Schlößelwööter',
'exif-worldregioncreated' => 'De Rejoon op de Ääd, woh dat Fotto jeknips wood',
'exif-countrycreated' => 'Et Land, woh dat Fotto jeknips wood',
'exif-contact' => 'Kuntak',
'exif-writer' => 'Schriiver',
'exif-languagecode' => 'Schprooch',
-'exif-iimversion' => 'Dem IIM sing Version',
+'exif-iimversion' => 'Dem <i lang="en">IIM</i> sing Version',
'exif-iimcategory' => 'Saachjrupp udder Zoot',
'exif-iimsupplementalcategory' => 'Extra Saachjroppe udder Zoote',
'exif-datetimeexpires' => 'Nit mieh verwende noh',
'exif-usageterms' => 'Räjelle för et Benöze',
'exif-webstatement' => 'Aanjaabe em Internet övver em Urhävver sing Rääschde',
'exif-originaldocumentid' => 'En eijmohleje Kännong för et Ojinaal',
-'exif-licenseurl' => 'En URL för de Lizänz',
+'exif-licenseurl' => 'En <i lang="en">URL</i> för de Lizänz',
'exif-morepermissionsurl' => 'Övver zohsäzlejje Lizänze',
'exif-attributionurl' => 'Wann De dat Wärk sellver bruchs, leng op',
'exif-preferredattributionname' => 'Wann De dat Wärk bruchs, donn en Danksaarung dobei, aan',
-'exif-pngfilecomment' => 'Aanmärkong uss ene PNG-Dattei',
+'exif-pngfilecomment' => 'Aanmärkong uss ene <i lang="en">PNG</i>-Dattei',
'exif-disclaimer' => 'Et Verwahre jääje Haftong',
'exif-contentwarning' => 'Warnong för em Enhallt',
-'exif-giffilecomment' => 'Aanmärkong uss ene GIF-Dattei',
+'exif-giffilecomment' => 'Aanmärkong uss ene <i lang="en">GIF</i>-Dattei',
'exif-intellectualgenre' => 'De Zoot vun Enhallt',
'exif-subjectnewscode' => 'Der Schlößel (Kood) för et Teema',
-'exif-scenecode' => 'Dä IPTC-Kood för Zoot vun wat mer süht',
+'exif-scenecode' => 'Dä <i lang="en">IPTC</i>-Kood för Zoot vun wat mer süht',
'exif-event' => 'Der jezeishte Aanlaß',
'exif-organisationinimage' => 'De jezeishte Ojanisazjuhn udder Ferma',
'exif-personinimage' => 'Der jezeishte Minsch',
# EXIF attributes
'exif-compression-1' => 'Oohne Kompressjuhn',
-'exif-compression-2' => 'Noh de CCITT ier Jrop 3 kodeet mem eindimänsjonalle aanjepaßte Verfahre noh_m Huffman singe „Läng vum Rötsch“',
-'exif-compression-3' => 'Noh de CCITT ier Jrop 3 als Fax kodeet',
-'exif-compression-4' => 'Noh de CCITT ier Jrop 4 als Fax kodeet',
-'exif-compression-5' => 'LZW',
-'exif-compression-6' => 'JPEG (ahl)',
-'exif-compression-7' => 'JPEG',
-'exif-compression-8' => 'Deflate (Adobe)',
-'exif-compression-32773' => 'PackBits (Macintosh RLE)',
-'exif-compression-32946' => 'Deflate (PKZIP)',
-'exif-compression-34712' => 'JPEG2000',
+'exif-compression-2' => 'Noh de <i lang="en">CCITT</i> ier Jrop 3 kodeet mem eindimänsjonalle aanjepaßte Verfahre noh_m <i lang="en">Huffman</i> singe „Läng vum Rötsch“',
+'exif-compression-3' => 'Noh de <i lang="en">CCITT</i> ier Jrop 3 als Fax kodeet',
+'exif-compression-4' => 'Noh de <i lang="en">CCITT</i> ier Jrop 4 als Fax kodeet',
+'exif-compression-5' => '<i lang="en">LZW</i>',
+'exif-compression-6' => '<i lang="en">JPEG</i> (ahl)',
+'exif-compression-7' => '<i lang="en">JPEG</i>',
+'exif-compression-8' => '<i lang="en">Deflate (Adobe)</i>',
+'exif-compression-32773' => '<i lang="en">PackBits (Macintosh RLE)</i>',
+'exif-compression-32946' => '<i lang="en">Deflate (PKZIP)</i>',
+'exif-compression-34712' => '<i lang="en">JPEG</i>2000',
'exif-copyrighted-true' => 'Häd_en Urhävverrääsch',
-'exif-copyrighted-false' => 'Es en de Allmende (jemeinfrei, public domain)',
+'exif-copyrighted-false' => 'Es en de Allmende (jemeinfrei, <i lang="en">public domain</i>)',
'exif-photometricinterpretation-2' => 'RJB',
-'exif-photometricinterpretation-6' => 'YCbCr',
+'exif-photometricinterpretation-6' => '<i lang="en">YCbCr</i>',
'exif-unknowndate' => 'Dattum onbikannt',
'exif-xyresolution-i' => '{{PLURAL:$1|eine Punk|$1 Punkte|keine Punk}} pro Zoll',
'exif-xyresolution-c' => '{{PLURAL:$1|eine Punk|$1 Punkte|keine Punk}} pro Zenntimeeter',
-'exif-colorspace-1' => 'sRGB',
+'exif-colorspace-1' => '<i lang="en">sRGB</i>',
'exif-colorspace-65535' => 'De Färve sin nit kallibreert',
'exif-componentsconfiguration-0' => 'Jidd_et nit',
'exif-sensingmethod-7' => 'Ene trilinejare sequenzjelle Sensor fö Färve',
'exif-sensingmethod-8' => 'Ene linejare sequenzjelle Sensor fö Färve',
-'exif-filesource-3' => 'Ene onbewääschlesche dijitaale Knepskaste (DSC)',
+'exif-filesource-3' => 'Ene onbewääschlesche dijitaale Knepskaste (<i lang="en">DSC</i>)',
'exif-scenetype-1' => 'Normal — e tirek fotmafeet Beld',
'confirmemail_needlogin' => 'Do muss Dich $1, för de E-Mail Adress ze bestätije.',
'confirmemail_success' => 'Ding E-Mail Adress es jetz bestätich.
Jetz künns De och noch enlogge. Vill Spass!',
-'confirmemail_loggedin' => 'Ding E-Mail Adress es jetz bestätich!',
+'confirmemail_loggedin' => 'Ding Addräß fö de <i lang="en">e-mail</i> es jäz beschtäätesch!',
'confirmemail_error' => 'Beim E-Mail Adress Bestätije es jet donevve jejange, de Bestätijung kunnt nit avjespeichert wääde.',
'confirmemail_subject' => 'Dun Ding e-mail Adress för {{GRAMMAR:Akkusativ|{{SITENAME}}}} bestäteje.',
'confirmemail_body' => 'Künnt jod sin, Do wors et selver, vun de IP_Adress $1 hät sich
Em <span class="plainlinks">[{{fullurl:Special:Log|type=delete&page=}}{{FULLPAGENAMEE}} Logboch vum Sigge-Fottschmieße]</span> künnt der Jrund shtonn.
Wann De de Sigg avspeichere deis, weed se widder aanjelaat.',
'confirmrecreate' => 'Dä Metmaacher [[User:$1|$1]] ([[User talk:$1|Klaaf]]) hät die Sigg fottjeschmesse, nohdäm Do do dran et Ändere aanjefange häs. Dä Jrund:
-: „$2“
+: „<i>$2</i>“
Wells Do jetz met en neu Version die Sigg widder neu aanläje?',
'confirmrecreate-noreason' => 'Dä [[User:$1|$1]] ([[User talk:$1|Klaaf]]) hät heh di Sigg fottjeschmeße, nohdämm Do aanjefange häs, draan ze ändere. Bes esu jood un donn beshtääteje, dat De di Sigg widder neu aanjelaat han wells.',
'recreate' => 'Widder neu aanlääje',
# Signatures
'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Klaafe]])',
-'timezone-utc' => 'UTC',
+'timezone-utc' => '<i lang="en">UTC</i>',
# Core parser functions
'unknown_extension_tag' => '„<code>$1</code>“ es en zosäzlejje Kennzeichnung, die kenne mer nit.',
'version-variables' => 'Variable',
'version-antispam' => 'SPAM verhendere',
'version-skins' => 'Ovverflääsche',
-'version-api' => 'API',
+'version-api' => '<i lang="en">API</i>',
'version-other' => 'Söns',
'version-mediahandlers' => 'Medije-Handler',
'version-hooks' => 'Schnettstelle oder Hooke',
'version-license' => 'Lizänz',
'version-poweredby-credits' => "Dat Wiki heh löp met '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001–$1 $2.",
'version-poweredby-others' => 'sönß wää',
-'version-license-info' => 'MediaWiki es e frei Projramm. Mer kann et unmolesteet wigger verdeile, un mer kann et verändere, wi mer löstich es, wam_mer sesch dobei aan de GNU General Public License (jenerälle öffentlesche Lizänz noh GNU) hallde deiht, wi se vun der Free Software Foundation (Steftung för frei Soffwäer) veröffentlesch woode es. Dobei kam_mer sesch ußsöhke of mer sesch aan de Version 2 dovun hallde deiht, udder öhnz en späädere Fassung.
+'version-license-info' => 'MediaWiki es e frei Projramm. Mer kann et unmolesteet wigger verdeile, un mer kann et verändere, wi mer löstich es, wam_mer sesch dobei aan de <i lang="en">GNU General Public License</i> (jenerälle öffentlesche Lizänz noh GNU) hallde deiht, wi se vun der <i lang="en">Free Software Foundation</i> (Steftung för frei Soffwäer) veröffentlesch woode es. Dobei kam_mer sesch ußsöhke of mer sesch aan de Version 2 dovun hallde deiht, udder öhnz en späädere Fassung.
MediaWiki weed verdeilt met dä Hoffnung, dat et för jet jood es, ävver <span style="text-transform:uppercase">der ohne jeede Jarantie</span>, un esujaa ohne ene unjesaate Jedangke, et künnt <span style="text-transform:uppercase">ze verkoufe</span> sin udder <span style="text-transform:uppercase;">för öhndsene bestemmpte Zweck ze jebruche</span>. Loor Der de jenannte Lizänz aan, wann De mieh Einzelheite weße wells.
-Do sullts en [{{SERVER}}{{SCRIPTPATH}}/COPYING Kopie vun dä GNU General Public License] zosamme met däm Projramm krääje han, un wann nit, schrief aan de: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA udder [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liß se em Internet noh].',
+Do sullts en [{{SERVER}}{{SCRIPTPATH}}/COPYING Kopie vun dä <i lang="en">GNU General Public License</i>] zosamme met däm Projramm krääje han, un wann nit, schrief aan de: <i lang="en">Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA </i> udder [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liß se em Internet noh].',
'version-software' => 'Installeete Soffwäer',
'version-software-product' => 'Produk',
'version-software-version' => 'Version',
-'version-entrypoints' => 'URLs för enzeschteije',
+'version-entrypoints' => '<i lang="en>URLs</i> för enzeschteije',
'version-entrypoints-header-entrypoint' => 'Enschteesch',
-'version-entrypoints-header-url' => 'URL',
+'version-entrypoints-header-url' => '<i lang="en">URL</i>',
# Special:FilePath
'filepath' => 'Medije-Dateie med ier URL zëije',
'filepath-page' => 'Dattëij_Name:',
-'filepath-submit' => 'Lohß jonn!',
+'filepath-submit' => 'Lohß Jonn!',
'filepath-summary' => "Med dä {{int:nstab-special}} hee künnd'Er dä kompläte Paad vun de neuste Version vun ene Datei direk erusfenge.
Die Datei weed jlich aanjezeig, odder med däm paßende Projramm op jemaat.",
'tags' => 'De jöltijje Makeerunge för Änderunge',
'tag-filter' => '[[Special:Tags|Makeerunge]] ußsöke:',
'tag-filter-submit' => 'Beschränke!',
-'tags-title' => 'Makeerunge',
+'tags-title' => 'Makeeronge',
'tags-intro' => 'Heh sin alle de Makeerunge opjeliß, die et Wiki för Änderunge verjevve kann, un wat se bedügge.',
'tags-tag' => 'Dä Makeerung iere Name',
'tags-display-header' => 'Kennzeiche en de Leßte met Änderunge',
'dberr-problems' => 'Deit uns leid, die Sigg heh häd för der Momang e teschnisch Problem.',
'dberr-again' => 'Versök eijfach en e paa Menutte, norr_ens die Sigg afzeroofe.',
'dberr-info' => '(Mer han kei Verbindung noh_m Datebank-ẞööver krijje künne: $1)',
-'dberr-usegoogle' => 'De künnß zweschedorsch ad met Google söke.',
+'dberr-usegoogle' => 'De künnß zweschedorsch ad met <i lang="en">Google</i> söke.',
'dberr-outofdate' => 'Müjjelesch, dat dat Verzeichnes vun uns Sigge do nit janß om neuste Shtannd es.',
'dberr-cachederror' => 'Wat heh noh kütt es en Kopi vum Zwescheshpeisher vun dä Sigg,
die De häs han welle. Se künnt jet ällder un nit mieh aktoäll sin.',
'feedback-cancel' => 'Stopp! Avbreche!',
'feedback-submit' => 'Lohß jonn!',
'feedback-adding' => 'Ben di Röckmäldong op di Sigg aam donn …',
-'feedback-error1' => 'Fähler: dat API säät jät, wat mer nit kenne',
+'feedback-error1' => 'Fähler: dat <i lang="en">API</i> säät jät, wat mer nit kenne',
'feedback-error2' => 'Fähler: de Sigg ze ändere es donävve jejange',
-'feedback-error3' => 'Fähler: dat API joov kein Antwoot',
+'feedback-error3' => 'Fähler: dat <i lang="en">API</i> joov kein Antwoot',
'feedback-thanks' => 'Joot. Dinge Beidraach kütt op die Sigg "[$2 $1]".',
'feedback-close' => 'Jedonn.',
'feedback-bugcheck' => 'Joot. Donn op jeede Vall nohlooer, dat dat bes jäz noch nit [$1 bikannt wohr].',
# API errors
'api-error-badaccess-groups' => 'Do häs nit et Rääsch, Datteije en heh dat Wiki huhzelaade.',
-'api-error-badtoken' => 'Fähler: et Kännzeijsche (token) es kappott.',
-'api-error-copyuploaddisabled' => 'Et Huhlaade vun enem URL es op däm ẞööver heh nit zohjelohße.',
+'api-error-badtoken' => 'Fähler: et Kännzeijsche (<i lang="en">token</i>) es kappott.',
+'api-error-copyuploaddisabled' => 'Et Huhlaade vun enem <i lang="en">URL</i> es op däm ẞööver heh nit zohjelohße.',
'api-error-duplicate' => 'Mer han em Wiki ald {{PLURAL:$1|[$2 en Dattei]|[$2 $1 andere Datteije]|[$2 kein Dattei]}} mem akeraat sellve Enhalldt.',
'api-error-duplicate-archive' => 'Mer hatte {{PLURAL:$1|[$2 en ander Dattei]|[$2 ander Datteije]|[$2 kein ander Dattei]}} heh em Wiki mem sellve Enhalt, ävver se {{PLURAL:$1|es|sen|es}} ald fottjeschmeße woode.',
'api-error-duplicate-archive-popup-title' => 'Ald fottjeschmeße {{PLURAL:$1|es de dubbelte Datei:|sen de dubbelte Dateije:|es kein dubbelte Datteije woode.}}',
'duration-centuries' => '{{PLURAL:$1|e Johrhondert|$1 Johrhonderte|kei Johrhondert}}',
'duration-millennia' => '{{PLURAL:$1|e Johrdousend|$1 Johrdousende|kei Johrdousend}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '{{PLURAL:$4|Dat Dateifommaat|De Dateifommaate|}} $1 wulle mer nit huhjelaade krijje. Älaup {{PLURAL:$3|es|sin_er|}}: $2',
);
'activeusers-noresult' => 'Tu bikarhêner nehate dîtin.',
# Special:Log/newusers
-'newuserlogpage' => 'Ã\87êkirina hesabê nû',
+'newuserlogpage' => 'çêkirina hesabê nû',
# Special:ListGroupRights
'listgrouprights' => 'Mafên koma bikarhêner',
'blocklist-tempblocks' => 'Astengkirinên demkî veşêre',
'blocklist-rangeblocks' => 'Astengkirinên cur bi cur veşêre',
'blocklist-by' => 'Astengkirina rêveber',
-'blocklist-params' => 'Parametreyan asteng bike',
+'blocklist-params' => 'Parametreyên astengkirinê',
'blocklist-reason' => 'Sedem',
'ipblocklist-submit' => 'Lêgerîn',
'ipblocklist-localblock' => 'Astengkirina herêmî',
* @ingroup Language
* @file
*
+ * @author Amahoney
* @author Andrew Dalby
* @author Dferg
* @author Esteban97
'tog-editsectiononrightclick' => 'Paginarum segmenta dextero percussu in titulis redigenda (JavaScript poscitur)',
'tog-showtoc' => 'Indicem plurium quam III segmentorum paginis praebere',
'tog-rememberpassword' => 'Memorare tesserae meae hoc in navigatro inter conventa ({{PLURAL:$1|die|diebus}} $1 tenus)',
-'tog-watchcreations' => 'Paginas quas creo in paginarum custoditarum indicem addere',
-'tog-watchdefault' => 'Paginas quas recenseo in paginarum custoditarum indicem addere',
-'tog-watchmoves' => 'Paginas quas moveo in paginarum custoditarum indicem addere',
-'tog-watchdeletion' => 'Paginas quas deleo in paginarum custoditarum indicem addere',
+'tog-watchcreations' => 'Paginas quas creo et fasciculos quos impono in paginarum custoditarum indicem addere',
+'tog-watchdefault' => 'Paginas et fasciculos quos recenseo in paginarum custoditarum indicem addere',
+'tog-watchmoves' => 'Paginas et fasciculos quos moveo in paginarum custoditarum indicem addere',
+'tog-watchdeletion' => 'Paginas et fasciculos quos deleo in paginarum custoditarum indicem addere',
'tog-minordefault' => 'Notare omnes recensiones quasi minores',
'tog-previewontop' => 'Monstrare praevisum ante capsam recensiti, non post ipsam',
'tog-previewonfirst' => 'Praevisum monstrare recensione incipiente',
'tog-nocache' => 'Sistere paginas apothecare',
-'tog-enotifwatchlistpages' => 'Mittere mihi litteras electronicas si pagina a me custodita mutatur',
+'tog-enotifwatchlistpages' => 'Mittere mihi litteras electronicas si pagina a me custodita vel fasciculus a me custoditus mutatur',
'tog-enotifusertalkpages' => 'Mittere mihi litteras electronicas si mea disputatio mutatur',
'tog-enotifminoredits' => 'Mittere mihi litteras electronicas etiam pro recensionibus minoribus',
'tog-enotifrevealaddr' => 'Monstrare inscriptio mea electronica in nuntiis notificantibus',
'createaccount' => 'Rationem novam creare',
'gotaccount' => "Habesne iam rationem? '''$1'''.",
'gotaccountlink' => 'Conventum aperi',
-'userlogin-resetlink' => 'Tesserae tuae oblitus esne?',
+'userlogin-resetlink' => 'Num tesserae tuae oblitus es?',
'createaccountmail' => 'ab inscriptione electronica',
'createaccountreason' => 'Causa:',
'badretype' => 'Tesserae quas scripsisti inter se non congruunt.',
'loginerror' => 'Error factus est in aperiendo conventum',
'nocookiesnew' => "Ratio usoris creata est, sed conventum non apertum est. {{SITENAME}} ''Cookies'' utitur in usorum conventa aperiendo. Cookies tua debiles sunt. Eis potestatem fac, tum conventum aperi cum nomine usoris tesseraque tua nova.",
'nocookieslogin' => "{{SITENAME}} ''Cookies'' utitur in usorum conventa aperiendo. Cookies tua debiles sunt. Eis potestatem fac, tum conare denuo.",
-'noname' => 'Nominem usoris ratum non designavisti.',
+'noname' => 'Nomen usoris ratum non designavisti.',
'loginsuccesstitle' => 'Conventum prospere apertum est',
'loginsuccess' => "'''Apud {{grammar:accusative|{{SITENAME}}}} agnosceris nomine \"\$1\".'''",
'nosuchuser' => 'Usor "$1" non est.
'userpage-userdoesnotexist' => 'Usor "<nowiki>$1</nowiki>" non est. Visne re vera hanc paginam creare vel recensere?',
'updated' => '(Novata)',
'note' => "'''Nota:'''",
-'previewnote' => "'''Memento hanc paginam solum praevisum esse, neque iam servatam!'''",
+'previewnote' => "'''Memento hanc paginam solum praevisam esse, neque iam servatam!'''",
'editing' => 'Recensio paginae "$1"',
'creating' => 'Creans $1',
'editingsection' => 'Recensens $1 (partem)',
'compareselectedversions' => 'Conferre emendationes selectas',
'showhideselectedversions' => 'Monstrare/celare emendationes selectas',
'editundo' => 'abrogare',
+'diff-multi' => '(Inter has {{PLURAL:$1|una emendatio|$1 emendationes}} ab {{PLURAL:$2|uno usore|$2 usoribus}} {{PLURAL:$1|facta|factae}} non {{PLURAL:$1|videtur|videntur}})',
# Search results
'searchresults' => 'Eventum investigationis',
'upload-unknown-size' => 'Magnitudo ignota',
'license' => 'Typus permissionis:',
-'license-header' => 'Typus permissionis:',
+'license-header' => 'Potestas usoris',
'nolicense' => 'Nulla selecta',
'license-nopreview' => '(Praevisum monstrari non potest)',
* @file
*
* @author ILVI
+ * @author Jewbask
* @author Remember the dot
* @author Runningfridgesrule
* @author Taichi
'missingarticle-rev' => '(nº. de revisión: $1)',
'missingarticle-diff' => '(Dif.: $1, $2)',
'filecopyerror' => 'No se pudo copiar el arxiv "$1" a "$2".',
+'badtitle' => 'Titolo malo',
'badtitletext' => 'El título de la hoja demandada está vazío, no es valible, o es un link interlingua o interwiki incorrecto.
Puede ser que contiene uno o más caracteres que no se pueden usar en los títulos.',
'viewsource' => 'Ver su manadero',
# Login and logout pages
'yourname' => 'Su nombre de usuario',
'yourpassword' => 'Parola',
+'yourpasswordagain' => 'Entra de muevo la parola',
'remembermypassword' => 'Acórdate de mi entrada de usador en este bilgisayar/orddênador (por un maksimum de {{PLURAL:$1|día|días}})',
'login' => 'Entrar',
'nav-login-createaccount' => 'Entrar / Crîar un cuento',
+'loginprompt' => 'Kale tener "cookies" aktivadas enel navegador para enrejistrarse en {{SITENAME}}',
'userlogin' => 'Entrar / Registrarse',
'logout' => 'Salir',
'userlogout' => 'Salir',
'createaccount' => 'Crea un nuevo cuento',
'gotaccount' => "¿Ya tienes un cuento? '''$1'''.",
'gotaccountlink' => 'Entrar',
+'userlogin-resetlink' => 'Olvidates tus detalyos de akseso?',
'createaccountmail' => 'por una letra electrónica',
'userexists' => 'El nombre que entrates ya se usa.
Si puede ser, escoge un otro nombre.',
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los rejistros relasyonados]</span>.',
'userpage-userdoesnotexist-view' => 'El cuento del usador $1 no está enrejistrado.',
'note' => "'''Nota:'''",
-'previewnote' => "'¡Acórdate que esto es sólo una previsualización y daínda no se registró!'''",
+'previewnote' => "¡Akórdate ke esto es sólo una previsualizasion i aínda no se enrejistró!'''
+Los tus trokamientos no se tienen guadrados!",
'editing' => 'Trocando $1',
'editingsection' => 'Trocando $1 (sección)',
'editingcomment' => 'Trocando $1 (kapítůlo)',
'hiddencategories' => 'Esta hoja es un miembro de {{PLURAL:$1|1 kateggoría escondida|$1 kateggorías escondidas}}:',
'nocreate-loggedin' => 'No tienes el permisso de creas hojas nuevas.',
'permissionserrorstext-withaction' => 'No tienes el permiso para $2, por las {{PLURAL:$1|razón|razones}} venideras:',
+'recreate-moveddeleted-warn' => "'''Aviso: Estas kriando una oja la kuala fue efassada antes.'''
+Kale ke penses si es menesterozo editar esta oja.
+El enrejistro de efassado i taxireado para esta oja puede ser meldado aki:",
+'moveddeleted-notice' => "Esta ója fue efassada.
+El ''log'' de efassado i taxireado de la ója es amostrado abasho para dar referensia.",
# Parser/template warnings
'post-expand-template-inclusion-warning' => "'''Avizo:''' La contenencia de xablon está muy grande.
Algunos xablones no van á ser comprendidos.",
'post-expand-template-inclusion-category' => 'Hojas ande la contenencia de xablones está sovrepassada',
+'post-expand-template-argument-warning' => "'''Aviso:''' Esta oja tiene kuanto menos un kampo enel xablon muy lungo.
+Este o estos kampos no van ser amostrados",
+'post-expand-template-argument-category' => 'Ojas ke tienen xablones kon parametros no uzados',
# History pages
'viewpagelogs' => 'Ver los registros de esta hoja',
'currentrev' => "Enderechamiento d'al cavo",
'currentrev-asof' => 'Enderechamiento de alcavo á las $1',
'revisionasof' => 'Enderechamiento á las $1',
+'revision-info' => 'Revision en data $1 por $2',
'previousrevision' => '← Enderechamiento de antes',
'nextrevision' => 'Rêvisión venidera →',
'currentrevisionlink' => 'Revisión actual',
# Revision feed
'history-feed-title' => 'Îstoria de nderechamientos',
+'history-feed-item-nocomment' => '$1 en $2',
# Revision deletion
'rev-delundel' => 'mostra/esconde',
'revertmerge' => 'Apartar',
# Diffs
-'history-title' => 'Istoria de revisiones para «$1»',
+'history-title' => 'Istorya de trokamientos para «$1»',
'lineno' => 'Shurá $1:',
'compareselectedversions' => 'Comparar versiones escogidas',
'editundo' => 'deshaze',
+'diff-multi' => '(No {{PLURAL:$1|es amostrado un trokamiento intermedio echo|son amostrados $1 trokamientos intermedios echos}} por {{PLURAL:$2|un usador|$2 usadores}})',
# Search results
'searchresults' => 'Resultados de la búsqueda',
'nextn-title' => '$1 {{PLURAL:$1|resultado|resultados}} venideros',
'shown-title' => 'Àmostrar $1 {{PLURAL:$1|resultado|resultados}} por hoja',
'viewprevnext' => 'Ver ($1 {{int:pipe-separator}} $2) ($3).',
+'searchmenu-exists' => 'Egziste una oja yamada "[[:$1]]" en esta viki',
'searchmenu-new' => "'''Crîar la hoja «[[:$1]]» en esta viki!'''",
'searchhelp-url' => 'Help:Ayudo',
'searchprofile-articles' => 'Hojas de contènido',
'searchprofile-everything-tooltip' => 'Buscar en todo el contènido (y también hojas de diskusyón)',
'searchprofile-advanced-tooltip' => 'Buscar en espacios de nombres particůlares',
'search-result-size' => '$1 ({{PLURAL:$2|1 biervo|$2 biervos}})',
+'search-result-category-size' => '{{PLURAL:$1|1 miembro|$1 miembros}} ({{PLURAL:$2|1 basho-kateggoria|$2 basho-kateggoria}}, {{PLURAL:$3|1 dossia|$3 dossias}})',
'search-redirect' => '(direksión desde $1)',
'search-section' => '(capítůlo $1)',
'search-suggest' => 'Quisites dezir: $1',
'search-interwiki-more' => '(más)',
'search-mwsuggest-enabled' => 'con consejos',
'search-mwsuggest-disabled' => 'no ay consejos',
+'searchrelated' => 'lisionado',
'searchall' => 'todos',
'showingresultsheader' => "{{PLURAL:$5|Resultado '''$1''' de '''$3'''|Resultados '''$1-$2''' de '''$3'''}} para '''$4'''",
'nonefound' => "'''Nota''': Por defecto sólo se busca en algunos espacios de nombre.
'yourlanguage' => 'Lingua:',
'yournick' => 'Firma mueva:',
'email' => 'Letral',
+'prefs-help-email' => 'El adreso de e-posta es menester para alimpiar la tu parola, si la olvidates',
+'prefs-help-email-others' => 'Endemas puedes eskojer si keres dar pueder a otros usadores de azer kontakto kon ti por modre de e-posta, a traverso de un atamiento en tus ojas de usador i de diskusyon.',
'prefs-signature' => 'Firma',
# Groups
'recentchanges-legend' => 'Opciones encima de los trocamientos frescos',
'recentchanges-summary' => 'Perseguid en esta hoja, los trocamientos de alcabo realizados en la Viki.',
'recentchanges-feed-description' => 'Perseguir los trocamientos más nuevos en el viki en este feed.',
+'recentchanges-label-newpage' => 'Este trokamiento krio una mueva ója',
'recentchanges-label-minor' => 'Esta es un trocamiento chiquitico',
+'recentchanges-label-bot' => 'Este trokamiento fue echo por un bot',
+'recentchanges-label-unpatrolled' => 'Estre trokamiento no esta akavidado',
'rcnote' => "Debaxo {{PLURAL:$1|ay '''1''' trocamiento realizado|están los dal cabo '''$1''' trocamientos realizados}} en {{PLURAL:$2|el dal cabo día|los dal cabo '''$2''' días}}, hasta el $4, $5.",
+'rcnotefrom' => "Debasho se amostran los trokamientos desde '''$2''' (amostrados fina <b>$1</b>)",
'rclistfrom' => 'Mostra los trocamientos nuevos empeçando desde $1',
'rcshowhideminor' => '$1 trocamientos chiquiticos',
'rcshowhidebots' => '$1 bots',
'rcshowhideliu' => '$1 empleadores enrējjistrados',
'rcshowhideanons' => '$1 empleadores anonimes',
+'rcshowhidepatr' => '$1 trokamientos akavidados',
'rcshowhidemine' => '$1 mis ediciones',
'rclinks' => 'Ver los dal cabo $1 trocamientos en los dal cabo $2 días.<br />$3',
'diff' => 'dif',
'recentchangeslinked-feed' => 'Trocamientos conectados',
'recentchangeslinked-toolbox' => 'Trocamientos relatados',
'recentchangeslinked-title' => 'Los trocamientos relacionados con "$1"',
+'recentchangeslinked-noresult' => 'Sin trokamientos en las ójas atadas en la data demandada',
'recentchangeslinked-summary' => "Esto es la lista de los trocamientos de alcavo de las hojas que relatan á una hoja spēcifik (ou de los miembros de la katēggoría spēcifikada).
Las hojas en tu [[Special:Watchlist|lista de akavidamiento]] son escritas '''con letras grexas'''.",
'recentchangeslinked-page' => 'Nombre de la hoja',
'filedesc' => 'Somario',
'uploadedimage' => 'subió «[[$1]]»',
+'license' => 'Lesensia:',
+'license-header' => 'Lesensiamyénto',
+
# Special:ListFiles
'listfiles_name' => 'Nombre',
'listfiles_user' => 'Usuario',
'file-anchor-link' => 'Archivo',
'filehist' => 'La storia del dosya',
'filehist-help' => 'Klika encima de una data/ora para vel el arxivo de esta data.',
+'filehist-revert' => 'aboltar',
'filehist-current' => 'actual',
'filehist-datetime' => 'Data/Ora',
'filehist-thumb' => 'Minyatura',
'filehist-comment' => 'Comentario',
'imagelinks' => 'El uso del dosya',
'linkstoimage' => '{{PLURAL:$1|La hoja venidera da link|Las hojas venideras dan link}} a este arxivo:',
+'nolinkstoimage' => 'Dinguna ója tiene atamientos a esta imej',
'sharedupload' => 'Este arxivo es de $1 i puede ser usado por otros proyectos.',
'sharedupload-desc-here' => 'Esta hoja es de $1 y puede ser usado por otros projetos.
La descripción en su [$2 hoja de descripción del arxivo] está amostrada debaxo.',
# Statistics
'statistics' => 'Estatísticas',
+'disambiguationspage' => 'Template:Aklarasion',
+
# Miscellaneous special pages
'nbytes' => '$1 {{PLURAL:$1|bayt|baytes}}',
'nmembers' => '$1 {{PLURAL:$1|miembro|miembros}}',
'prefixindex' => 'Todas las hojas con prefixo',
+'usercreated' => '{{GENDER:$3|Enrejistrado|Enrejistrada}} el $1 a las $2',
'newpages' => 'Hojas muevas',
'ancientpages' => 'Artikolos mas viejos',
'move' => 'taxirea',
# Special:LinkSearch
'linksearch' => 'Linkes eksternos',
+'linksearch-line' => 'Atamiento para $1 en la ója $2',
# Special:Log/newusers
'newuserlogpage' => 'Registro de creación de usuarios',
# Watchlist
'watchlist' => 'Mi lista de escogidas',
'mywatchlist' => 'Mi lista de akavidamientos',
+'watchlistfor2' => 'Para $1 $2',
'addedwatchtext' => "La hoja «[[:$1]]» fue ajustada a tu [[Special:Watchlist|lista de escogidas]]. Los trocamientos venideros en esta hoja i en tu hoja de diskussión associada se van indicar aí, i la hoja va aparecer '''gordo''' en la hoja de [[Special:RecentChanges|trocamientos freskos]] para hazerla más kolay de detektar.
Cuando queres eliminar la hoja de tu lista de escogidas, piza «Dexar de cudiar» en el menú.",
Si puede ser, confirma que de verdad queres hazer esto, que estás entendiendo las
resultados, i que lo estás haziendo de acorddo con las [[{{MediaWiki:Policy-url}}|Políticas]].',
'actioncomplete' => 'Aksion kompleta',
+'actionfailed' => 'Aksiyon sin reushitá',
'deletedtext' => '"$1" fue efassado.
Mira $2 para un registro de los efassados nuevos.',
'dellogpage' => 'Registro de efassados',
'sp-contributions-newbies' => 'Mostrar solo las ajustamientos de los usuarios nuevos',
'sp-contributions-blocklog' => 'registro de bloqueos',
-'sp-contributions-talk' => 'Diścutir',
+'sp-contributions-uploads' => 'suvidas',
+'sp-contributions-logs' => 'enrejistros',
+'sp-contributions-talk' => 'Diskusyón',
'sp-contributions-search' => 'Buscar ajustamientos',
'sp-contributions-username' => 'Dirección IP o nombre de usuario:',
+'sp-contributions-toponly' => "Amostrar solo revisiones d'alkavo",
'sp-contributions-submit' => 'Buscar',
# What links here
'whatlinkshere-title' => 'Hojas que dan link a "$1"',
'whatlinkshere-page' => 'Hoja:',
'linkshere' => "Las hojas venideras dan link a '''[[:$1]]''':",
+'nolinkshere' => "Dinguna ója tiene atamientos kon '''[[:$1]]'''",
'isredirect' => 'Hoja redirigida',
'istemplate' => 'inclusión',
-'isimage' => 'Link del image',
+'isimage' => 'Atamiento de la dossia',
'whatlinkshere-prev' => '{{PLURAL:$1|de antes|de antes $1}}',
'whatlinkshere-next' => '{{PLURAL:$1|venidera|venideras $1}}',
'whatlinkshere-links' => '← linkes',
'whatlinkshere-hideredirs' => '$1 redirecciones',
'whatlinkshere-hidetrans' => '$1 inclusiones',
'whatlinkshere-hidelinks' => '$1 linkes',
+'whatlinkshere-hideimages' => '$1 atamientos a imejes',
'whatlinkshere-filters' => 'Filtres',
# Block/unblock
# Thumbnails
'thumbnail-more' => 'Engrandece',
+'thumbnail_error' => 'Yerro kriando la imej chika: $1',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Tu hoja de usador',
'watchlisttools-edit' => 'Ver i trocar tu lista de escogidas',
'watchlisttools-raw' => 'Troca tu lista de escogidas en crudo',
+# Core parser functions
+'duplicate-defaultsort' => '\'\'\'Aviso:\'\'\' la klave primaria para ordenamiento "$2" anula la primera "$1"',
+
# Special:Version
'version' => 'Versión',
'version-specialpages' => 'Pajinas espesiales',
'specialpages' => 'Hojas especiales',
'specialpages-group-users' => 'Usadores y derechos',
+# External image whitelist
+'external_image_whitelist' => ' #Desha esta linea ansina komo esta<pre>
+#Mete partes de frasas (solo la parte ke va entre los //) enbasho
+#Eyas van ser komparadas kon las URLs de las dossias ekternas (hotlinked)
+#Akeyos iguales van ser amostrados komo una imej; si no, solo el su atamientoque
+#Las lineas ke empiezan kor «#» son konsideradas komentarios
+#Esta no aze diferente el senso se la letra
+
+#Mete todas las partes de frasas regex enriva de esta linea. Desha esta ansina komo se topa</pre>',
+
# Special:Tags
'tag-filter' => 'Filtro de [[Special:Tags|etiquetas]]:',
'tag-filter-submit' => 'Filtro',
'youhavenewmessages' => 'Dir hutt $1 ($2).',
'newmessageslink' => 'nei Messagen',
'newmessagesdifflink' => 'Lescht Ännerung',
+'youhavenewmessagesfromusers' => 'Dir hutt $1 vu(n) {{PLURAL:$3|engem anere Benotzer|$3 anere Benotzer}} ($2).',
+'youhavenewmessagesmanyusers' => 'Dir hutt $1 vu ville Benotzer ($2)',
+'newmessageslinkplural' => '{{PLURAL:$1|een neie Message|nei Message}}',
+'newmessagesdifflinkplural' => 'lescht {{PLURAL:$1|Ännerung|Ännerungen}}',
'youhavenewmessagesmulti' => 'Dir hutt nei Messagen op $1',
'editsection' => 'änneren',
'editold' => 'änneren',
'remembermypassword' => 'Meng Umeldung op dësem Computer (fir maximal $1 {{PLURAL:$1|Dag|Deeg}}) verhalen',
'securelogin-stick-https' => 'Nom Umelle mat HTTPS verbonn bleiwen',
'yourdomainname' => 'Ären Domain',
+'password-change-forbidden' => 'Dir däerft op dëser Wiki Passwierder net änneren.',
'externaldberror' => 'Entweder ass e Feeler bei der externer Authentifizéierung geschitt, oder Dir däerft Ären externe Benotzerkont net aktualiséieren.',
'login' => 'Aloggen',
'nav-login-createaccount' => 'Aloggen / Benotzerkont uleeën',
'lockmanager-fail-openlock' => 'De Spärfichier fir "$1" konnt net opgemaach ginn.',
'lockmanager-fail-releaselock' => 'D\'Spär fir "$1" konnt net opgehuewe ginn.',
'lockmanager-fail-db-release' => "D'Spären op der Datebank $1 konnten net fräigeschalt ginn.",
+'lockmanager-fail-svr-acquire' => "D'Spären um Server $1 konnten net ofgefrot ginn.",
'lockmanager-fail-svr-release' => "D'Spären um Server $1 konnten net fräigeschalt ginn.",
# ZipDirectoryReader
'disambiguations' => 'Säiten déi op Homonymie-Säite linken',
'disambiguationspage' => 'Template:Homonymie',
-'disambiguations-text' => 'Dës Säite si mat enger Homonymie-Säit verlinkt.
-Sie sollten am beschten op déi eigentlech gemengte Säit verlinkt sinn.<br />
-Eng Säite gëtt als Homonymiesäit behandelt, wa si eng Schabloun benotzt déi vu [[MediaWiki:Disambiguationspage]] verlinkt ass.',
+'disambiguations-text' => "Dës Säite ass mat mindestens enger '''Homonymie-Säit''' verlinkt.
+Si sollte am beschten op déi eigentlech gemengte Säit verlinkt sinn.<br />
+Eng Säite gëtt als Homonymie-Säit behandelt, wa si eng Schabloun benotzt déi vu [[MediaWiki:Disambiguationspage]] verlinkt ass.",
'doubleredirects' => 'Duebel Viruleedungen',
'doubleredirectstext' => 'Op dëser Säit stinn déi Säiten déi op aner Viruleedungssäite viruleeden.
'protectedtitlesempty' => 'Zur Zäit si mat de Parameteren déi Dir uginn hutt keng Säite fir neit Uleeë gespaart.',
'listusers' => 'Benotzerlëscht',
'listusers-editsonly' => 'Nëmme Benotzer mat Ännerunge weisen',
-'listusers-creationsort' => 'No dem Datum vum Uleeë sortéieren',
+'listusers-creationsort' => 'Nom Datum vum Uleeën zortéieren',
'usereditcount' => '$1 {{PLURAL:$1|Ännerung|Ännerungen}}',
'usercreated' => "{{GENDER:$3|De(n)|D'}} $1 ëm $2 Auer ugeluecht",
'newpages' => 'Nei Säiten',
[[Special:UnusedCategories|Netbenotzt Kategorië]] ginn hei net gewisen.
Kuckt och [[Special:WantedCategories|Gewënscht Kategorien]].',
'categoriesfrom' => 'Weis Kategorien ugefaang bei:',
-'special-categories-sort-count' => 'No der Zuel sortéieren',
-'special-categories-sort-abc' => 'alphabetesch sortéieren',
+'special-categories-sort-count' => 'No der Zuel zortéieren',
+'special-categories-sort-abc' => 'alphabetesch zortéieren',
# Special:DeletedContributions
'deletedcontributions' => 'Geläschte Kontributiounen',
'rollback' => 'Ännerungen zrécksetzen',
'rollback_short' => 'Zrécksetzen',
'rollbacklink' => 'Zrécksetzen',
+'rollbacklinkcount' => '{{PLURAL:$1|Eng Ännerung|$1 Ännerungen}} zerécksetzen',
+'rollbacklinkcount-morethan' => 'méi wéi {{PLURAL:$1|Eng Ännerung|$1 Ännerungen}} zerécksetzen',
'rollbackfailed' => 'Zrécksetzen huet net geklappt',
'cantrollback' => 'Lescht Ännerung kann net zréckgesat ginn. De leschten Auteur ass deen eenzegen Auteur vun dëser Säit.',
'alreadyrolled' => 'Déi lescht Ännerung vun der Säit [[:$1]] vum [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);; kann net zeréckgesat ginn;
* <span class="mw-specialpagecached">Spezialsäiten aus dem Tëschespäicher (ka vereelst sinn).</span>',
'specialpages-group-maintenance' => 'Maintenance-Rapporten',
'specialpages-group-other' => 'Aner Spezialsäiten',
-'specialpages-group-login' => 'Aloggen / Umellen',
+'specialpages-group-login' => 'Aloggen / Benotzerkont uleeën',
'specialpages-group-changes' => 'Rezent Ännerungen a Lëschten',
'specialpages-group-media' => 'Medie-Rapporten an eropgeluede Fichieren',
'specialpages-group-users' => 'Benotzer a Rechter',
'api-error-emptypage' => 'Et ass net erlaabt nei, eidel Säiten unzeleeën.',
'api-error-fetchfileerror' => 'Interne Feeler: beim Opruffe vum Fichier huet eppes net fonctionnéiert.',
'api-error-fileexists-forbidden' => 'E Fichier mam Numm "$1" gëtt et schonn an e kann net iwwerschriwwe ginn.',
+'api-error-fileexists-shared-forbidden' => 'E Fichier mam Numm "$1" gëtt et schonn am gedeelte Repertoire an e kann net iwwerschriwwe ginn.',
'api-error-file-too-large' => 'De Fichier deen Dir geschéckt hutt war ze grouss.',
'api-error-filename-tooshort' => 'Den Numm vum Fichier ass ze kuerz.',
'api-error-filetype-banned' => 'Dësen Typ vu Fichier ass net zougelooss.',
'duration-millennia' => '$1 {{PLURAL:$1|Millenaire|Millenairen}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => "D'Spären um Server $1 konnten net ofgefrot ginn.",
+'api-error-filetype-banned-type' => "$1 {{PLURAL:$4|ass e Fichiersformat deen net erlaabt ass|si Fichiersformater déi net erlaabt sinn}}. Erlaabt {{PLURAL:$3|ass de Fichiersformat|sinn d'Fichiersformater}}: $2.",
);
'vector-action-unprotect' => 'Хуьн дегишарун',
'vector-simplesearch-preference' => 'Гегьенш жагъурунин рикIел гъун кутун (кьилди "Вектор" акунар патал)',
'vector-view-create' => 'Туькlуьрун',
-'vector-view-edit' => 'Ð\94Ñ\83Ñ\8cзаÑ\80 Ñ\85Ñ\8aÑ\83вун',
+'vector-view-edit' => 'РаÑ\81ун',
'vector-view-history' => 'Тарихдиз килигун',
'vector-view-view' => 'Кlелун',
'vector-view-viewsource' => 'Чешме къалурун',
'lastmodifiedat' => 'Ччинин эхиримжи масакIа хьун: $1, $2',
'protectedpage' => 'Хвенвай ччин',
'jumpto' => 'ЭлячIун иниз:',
-'jumptonavigation' => 'Навигация',
+'jumptonavigation' => 'Навигаци',
'jumptosearch' => 'Жугъурун',
'pool-queuefull' => 'ТIалабар кIватзавайди ацIа я',
'pool-errorunknown' => 'Малумтушир гъалатI',
'newmessageslink' => 'цlийи чарар',
'newmessagesdifflink' => 'Эхиримжи масакIавилер',
'youhavenewmessagesmulti' => '"$1"-да квез цIийи чарар атанва.',
-'editsection' => 'дÑ\83Ñ\8cзаÑ\80 Ñ\85Ñ\8aÑ\83вун',
+'editsection' => 'РаÑ\81ун',
'editold' => 'Дуьзар хъувун',
'viewsourceold' => 'сифте кьилин коддиз килига',
'editlink' => 'Дуьзар хъувун',
# Watchlist
'watchlist' => 'Зи вилив хуьнин сиягь',
-'mywatchlist' => 'Ð\97и вилив Ñ\85Ñ\83Ñ\8cнин сиягь',
+'mywatchlist' => 'Ð\97и вилив Ñ\85Ñ\8eнин сиягь',
'watchlistfor2' => '$1 $2 патал',
'addedwatchtext' => "Чар \"[[:\$1]]\" тун хъувунай куьн [[Special:Watchlist|watchlist]]. Къвезмай дегишунар и чарчел ва галкlанавай чарчихъ ихтилатар жеда инна, ахъатдава \"сакlус яцlу''''' инна [[Special:RecentChanges|list of recent changes]] гьам кьизил авун.",
'removedwatchtext' => 'Чар "[[:$1]]" Идай чlурнай [[Special:Watchlist|ахтармишунин цlарар]].',
'file-info-size' => '$1 × $2 пикселар, файлдин кьадар: $3, MIME жуьре: $4',
'file-nohires' => 'Идалайни хъсан ери авайд туш',
'svg-long-desc' => 'SVG файл, номилдаказ $1 $2 × пикселяр, файлдин кьадар: $3',
-'show-big-image' => 'Ð\9cадни Ñ\85Ñ\8aÑ\81ан еÑ\80идин Ñ\88икил',
+'show-big-image' => 'ЦlаÑ\80аÑ\84а Ñ\85вена Ñ\82Ñ\83нвай жеÑ\80гедай',
# Bad image list
'bad_image_list' => 'Формат гьихьтинди хьана кlанда:
* @author Aelske
* @author Benopat
* @author Cicero
+ * @author Geitost
* @author Kaganer
* @author Matthias
* @author Ooswesthoesbes
'tog-hidepatrolled' => 'Gemarkeerde wieziginge verberge in recente wieziginge',
'tog-newpageshidepatrolled' => "Gemarkeerde pagina's verberge in de lies mit nuuj pagina's",
'tog-extendwatchlist' => 'Oetgebreide volglies gebroeke óm alle verangeringe te zeen en neet allein de lèste',
-'tog-usenewrc' => 'Oetgebreide recènte verangeringe (Javascript nudig)',
+'tog-usenewrc' => 'Tuun verangeringe per pagina in recènte verangeringe en volglies (Javascript nudig)',
'tog-numberheadings' => 'Köpkes automatisch nummere',
'tog-showtoolbar' => 'Laot edit toolbar zeen',
'tog-editondblclick' => "Bewirk pagina's bie 'ne dobbelklik (JavaScript)",
'tog-editsectiononrightclick' => "Secties bewirke mit 'ne rechtermoesklik op sectietitels (JavaScript nudig)",
'tog-showtoc' => "Inhaudsopgaaf veur pagina's mit mie es 3 köpkes",
'tog-rememberpassword' => 'Mien wachwaord onthouwe veur later sessies (hoegstens $1 {{PLURAL:$1|daag|daag}})',
-'tog-watchcreations' => "Pagina's die ich aanmaak automatisch volge",
-'tog-watchdefault' => "Voog pagina's die se bewirks toe aan dien volglies",
-'tog-watchmoves' => "Pagina's die ich verplaats automatisch volge",
-'tog-watchdeletion' => "Pagina's die ich ewegsjaf automatisch volge",
+'tog-watchcreations' => "Volg autematis pagina's die ich aanmaak en bestenj die ich upload",
+'tog-watchdefault' => "Voog pagina's em bestenj die se bewirks toe aan dien volglies",
+'tog-watchmoves' => "Volg autematis pagina's en bestenj die ich verplaats",
+'tog-watchdeletion' => "Volg autematis pagina's en bestenj die ich ewegsjaf",
'tog-minordefault' => 'Markeer sjtanderd alle bewirkinge es klein',
'tog-previewontop' => 'Veurvertuin baove bewèrkingsveld tuine',
'tog-previewonfirst' => 'Preview laote zien bie de ierste bewirking',
'tog-nocache' => 'Zèt de browserpaginacaching oet',
-'tog-enotifwatchlistpages' => "'ne E-mail nao mich versjikke bie bewirkinge van pagina's op mien volglies",
+'tog-enotifwatchlistpages' => "Versjik 'ne e-mail nao mich bie bewirkinge van pagina's en bestenj op mien volglies",
'tog-enotifusertalkpages' => "'ne E-mail nao mich versjikke es emes mien euverlèkpagina verangert",
-'tog-enotifminoredits' => "'ne E-mail nao mich versjikke bie klein bewirkinge op pagina's op mien volglies",
+'tog-enotifminoredits' => "Versjik mich 'ne e-mail bie klein bewirkinge op pagina's en bestenj op mien volglies",
'tog-enotifrevealaddr' => 'Mien e-mailadres tuine in e-mailberichte',
'tog-shownumberswatching' => "'t Aantal gebroekers tuine die dees pagina volg",
'tog-oldsig' => 'Bestaonde ongerteikening:',
'cannotdelete' => 'Kós de pagina of \'t besjtand "$1" neet ewegsjaffe.
Mesjiens haet emes angers det al gedaon.',
'cannotdelete-title' => 'Pagina "$1" kin neet gewösj waere',
+'delete-hook-aborted' => "'t Wösje is aafgebroke door 'ne 'hook'.
+D'r is gein toelichting besjikbaar.",
'badtitle' => 'Óngeljige paginatitel',
'badtitletext' => 'De opgevraogde pagina is neet besjikbaar of laeg.',
'perfcached' => "De gegaeves koume oet 'n cache en zeen mäögelik neet actueel. 't Geuf {{PLURAL:$1|maximaal ei rizzeltaot|maximaal $1 rizzeltaote}} inne cache.",
d\'n Opgegaeve raej vanne sloetendje admin waar "\'\'$3\'\'".',
'invalidtitle-knownnamespace' => 'Óngèljige titel mit naamruumdje "$2" en teks "$3"',
'invalidtitle-unknownnamespace' => 'Óngèljige titel mit ónbekèndj naamruumdenómmer $1 en teks "$2"',
+'exception-nologin' => 'Neet aangemèld',
+'exception-nologin-text' => 'Óm dees pagina te betrachte of dees hanjeling te kinne doon mós se aangemèldj zeen bie deze wiki.',
# Virus scanner
'virus-badscanner' => "Slechte configuratie: onbekenge virusscanner: ''$1''",
'right-writeapi' => 'Bewèrke via de API',
'right-delete' => "Pagina's verwijdere",
'right-bigdelete' => "Pagina's mit 'n grote gesjiedenis verwijdere",
+'right-deletelogentry' => 'Wösj of plaats trögk specifieke logbookregels',
'right-deleterevision' => "Versies van pagina's verberge",
'right-deletedhistory' => 'Verwijderde versies bekieke, zonder te kinne zeen wat verwijderd is',
'right-deletedtext' => 'Bekieke gewösjde teks en wieziginge tösse verwiedere versies',
'lockmanager-fail-releaselock' => 'Kós de vergrendeling veur "$1" neet opheffe.',
'lockmanager-fail-db-bucket' => 'Kós neet in kontak kómme mit genóg vergrendelingsdatabases in de bucket $1.',
'lockmanager-fail-db-release' => "'t Waar neet meugelik ómme vergrendeling veure database $1 óp tö höffe.",
+'lockmanager-fail-svr-acquire' => "'t Waar neet meugelik ómme vergrendeling oppe server $1 tö kriege.",
'lockmanager-fail-svr-release' => "'t Waar neet meugelik ómme vergrendeling veure server $1 óp tö höffe.",
# ZipDirectoryReader
'spambot_username' => 'MediaWiki spam opruming',
'spam_reverting' => 'Bezig mit trökdrèjje nao de letste versie die gein verwiezing haet nao $1',
'spam_blanking' => "Alle wieziginge mit 'ne link nao $1 waere verwiederd",
+'spam_deleting' => 'Alle wieziginge hawwe links nao $1, wuuertj gewösj',
# Info page
'pageinfo-title' => 'Informatie euver "$1"',
'api-error-empty-file' => 't Bestandj det se perbeers te uploade had gein inhald.',
'api-error-emptypage' => "Doe maags gein nuuj, laeg pagina's aanmake.",
'api-error-fetchfileerror' => "Intern fout: d'r is get fout gegange bie 't óphaole van 't bestandj.",
+'api-error-fileexists-forbidden' => 'd\'r Besteit al e bestandj mitte naam "$1" det neet euversjreve kin waere.',
+'api-error-fileexists-shared-forbidden' => 'd\'r Besteit al e bestandj mitte naam "$1" inne gedeildje repositoir det neet euversjreve kin waere.',
'api-error-file-too-large' => 't Bestandj det se perbeers te uploade waas te groet.',
'api-error-filename-tooshort' => "t Bestandj det se perbeers te uploade had 'ne te kórte bestandjsnaam.",
'api-error-filetype-banned' => 't Bestandj det se perbeers te uploade waas van e neet-toegelaote bestandjstype.',
'duration-centuries' => '$1 {{PLURAL:$1|ieëf|ieëf}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
+# Unknown messages
+'api-error-filetype-banned-type' => "{{PLURAL:$4|'t bestandjstype $1 weurt|De bestandjstypes $1 waere}} neet toegelaote. {{PLURAL:$3|'t Toegelaote bestandjstype is|De toegelaote bestandjstypes zeen}} $2.",
);
NS_CATEGORY_TALK => 'Kategorijos_aptarimas',
);
+$namespaceGenderAliases = array(
+ NS_USER => array( 'male' => 'Naudotojas', 'female' => 'Naudotoja' ),
+ NS_USER_TALK => array( 'male' => 'Naudotojo_aptarimas', 'female' => 'Naudotojos_aptarimas' ),
+);
+
$specialPageAliases = array(
'Allmessages' => array( 'Visi_pranešimai' ),
'Allpages' => array( 'Visi_puslapiai' ),
'youhavenewmessages' => 'Jūs turite $1 ($2).',
'newmessageslink' => 'naujų žinučių',
'newmessagesdifflink' => 'paskutinis pakeitimas',
+'youhavenewmessagesfromusers' => 'Jūs turite $1 nuo {{PLURAL:$3|kito vartotojo|$3 vartotojų}} ($2).',
+'youhavenewmessagesmanyusers' => 'Jūs turite $1 iš daugelio vartotojų ( $2 ) .',
+'newmessageslinkplural' => '{{PLURAL:$1|nauja žinutė|naujos žinutės}}',
+'newmessagesdifflinkplural' => 'paskutinis {{PLURAL:$1|pakeitimas|pakeitimai}}',
'youhavenewmessagesmulti' => 'Turite naujų žinučių $1',
'editsection' => 'redaguoti',
'editold' => 'taisyti',
'cannotdelete' => 'Nepavyko ištrinti puslapio ar failo „$1“.
Galbūt jį jau kažkas kitas ištrynė.',
'cannotdelete-title' => 'Negalite ištrinti puslapio "$1"',
+'delete-hook-aborted' => 'Trynimą atšaukė kabliukas.
+Nebuvo duotas joks paaiškinimas.',
'badtitle' => 'Blogas pavadinimas',
'badtitletext' => 'Nurodytas puslapio pavadinimas buvo neleistinas, tuščias arba neteisingai sujungtas tarpkalbinis arba tarpprojektinis pavadinimas. Jame gali būti vienas ar daugiau simbolių, neleistinų pavadinimuose',
'perfcached' => 'Rodoma išsaugota duomenų kopija, todėl duomenys gali būti ne patys naujausi. Maksimaliai $1 {{PLURAL:$1|rezultatas|rezultatai|rezultatų}} yra saugoma.',
Ją užrakinęs administratorius pateikė šį paaiškinimą: "$3".',
'invalidtitle-knownnamespace' => 'Klaidingas pavadinimas vardų erdvėje "$2" ir tekstu "$3"',
'invalidtitle-unknownnamespace' => 'Klaidingas pavadinimas nežinomoje vardų erdvėje numeriu $1 ir tekstu "$2"',
+'exception-nologin' => 'Neprisijungęs',
+'exception-nologin-text' => 'Šiam puslapiui ar veiksmui reikalingas prisijungimas šioje wiki.',
# Virus scanner
'virus-badscanner' => "Neleistina konfigūracija: nežinomas virusų skeneris: ''$1''",
'remembermypassword' => 'Prisiminti prisijungimo duomenis šiame kompiuteryje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})',
'securelogin-stick-https' => 'Likite prisijungę prie HTTPS po prisijungimo',
'yourdomainname' => 'Jūsų domenas:',
+'password-change-forbidden' => 'Jus negalite keisti slaptažodžių šioje wiki.',
'externaldberror' => 'Yra arba išorinė autorizacijos duomenų bazės klaida arba jums neleidžiama atnaujinti jūsų išorinės paskyros.',
'login' => 'Prisijungti',
'nav-login-createaccount' => 'Prisijungti / sukurti paskyrą',
'right-writeapi' => 'Naudoti rašymo API',
'right-delete' => 'Trinti puslapius',
'right-bigdelete' => 'Ištrinti puslapius su ilga istorija',
+'right-deletelogentry' => 'Naikinti ir anuliuoti konkrečius žurnalo įrašus',
'right-deleterevision' => 'Ištrinti ir atkurti specifines puslapių versijas',
'right-deletedhistory' => 'Žiūrėti ištrintų puslapių istoriją, nerodant susieto teksto',
'right-deletedtext' => 'Peržiūrėti ištrintą tekstą ir skirtumus tarp ištrintų puslapio versijų.',
'rollback' => 'Atmesti keitimus',
'rollback_short' => 'Atmesti',
'rollbacklink' => 'atmesti',
+'rollbacklinkcount' => 'atšaukti $1 {{PLURAL:$1|redagavimą|redagavimus}}',
+'rollbacklinkcount-morethan' => 'atšaukti daugiau nei $1 {{PLURAL:$1|redagavimą|redagavimus}}',
'rollbackfailed' => 'Atmetimas nepavyko',
'cantrollback' => 'Negalima atmesti redagavimo; paskutinis keitęs naudotojas yra šio puslapio autorius.',
'alreadyrolled' => 'Nepavyko atmesti paskutinio [[User:$2|$2]] ([[User talk:$2|Aptarimas]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) daryto puslapio [[:$1]] keitimo;
'javascripttest-pagetext-frameworks' => 'Prašome pasirinkti vieną iš išvardintų testavimo struktūrų: $1',
'javascripttest-pagetext-skins' => 'Pasirinkite naudotojo sąsajos išvaizdą, kuriai atliksite testavimą:',
'javascripttest-qunit-intro' => 'Peržiūrėkite [$1 testavimo dokumentaciją]',
+'javascripttest-qunit-heading' => 'MediaWiki JavaScript QUnit bandymų komplektas',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Jūsų naudotojo puslapis',
'version-software' => 'Įdiegta programinė įranga',
'version-software-product' => 'Produktas',
'version-software-version' => 'Versija',
+'version-entrypoints' => 'Įėjimo taško URL',
+'version-entrypoints-header-entrypoint' => 'Įėjimo taškas',
'version-entrypoints-header-url' => 'URL',
# Special:FilePath
# New logging system
'logentry-delete-delete' => '$1 ištrynė puslapį $3',
'logentry-delete-restore' => '$1 atkūrė puslapį $3',
+'logentry-delete-event' => '$1 pakeistas matomumas {{PLURAL:$5|žurnalo įvykio|$5 žurnalo įvykių}} tarp $3: $4',
+'logentry-delete-revision' => '$1 pakeitė puslapio „$3“ {{PLURAL:$5|versijos|$5 versijų}} matomumą: $4',
+'logentry-delete-event-legacy' => '$1 pakeistas matomumą žurnalo renginiams tarp $3',
+'logentry-delete-revision-legacy' => '$1 pakeistas matomumas pažiūrų puslapio $3',
+'logentry-suppress-delete' => '$1 nuslopino puslapį $3',
+'logentry-suppress-event' => '$1 slaptai pakeistas matomumas {{PLURAL:$5|žurnalo įvykio|$5 žurnalo įvykiu}} tarp $3: $4',
+'logentry-suppress-revision' => '$1 slaptai pakeistas matomumas {{PLURAL:$5|peržiūros|$5 peržiūrų}} puslapyje $3: $4',
+'logentry-suppress-event-legacy' => '$1 slaptai pakeistas matomumas žurnalo įvykių tarp $3',
+'logentry-suppress-revision-legacy' => '$1 slaptai pakeistas matomumas peržiūrų puslapyje $3',
'revdelete-content-hid' => 'turinys paslėptas',
'revdelete-summary-hid' => 'paslėptas keitimo komentaras',
'revdelete-uname-hid' => 'paslėptas naudotojo vardas',
'logentry-move-move-noredirect' => '$1 pervadino puslapį $3 į $4, nepalikdamas nukreipimo',
'logentry-move-move_redir' => '$1 pervadino puslapį iš $3 į $4, vietoje buvusio nukreipimo',
'logentry-move-move_redir-noredirect' => '$1 pervadino puslapį iš $3 į $4, vietoje buvusio nukreipimo, bet nesukurdamas naujo',
+'logentry-patrol-patrol' => '$1 pažymėjo peržiūrą $4 puslapio $3 patruliuojama',
+'logentry-patrol-patrol-auto' => '$1 automatiškai pažymėjo peržiūrą $4 puslapio $3 patruliuojama',
'logentry-newusers-newusers' => '$1 sukūrė naudotojo paskyrą',
'logentry-newusers-create' => '$1 sukūrė naudotojo paskyrą',
'logentry-newusers-create2' => '$1 sukūrė naudotojo paskyrą $3',
# API errors
'api-error-badaccess-groups' => 'Jums neleidžiama įkelti failus į šią wiki.',
+'api-error-badtoken' => 'Vidinė klaida: blogai atpažinimo ženklas.',
'api-error-copyuploaddisabled' => 'Siuntimas pagal URL yra išjungtas šiame serveryje.',
+'api-error-duplicate' => 'Jau {{PLURAL:$1|yra [$2 kitas failas]|yra [$2 kiti failai]}} puslapyje su tuo pačiu turiniu..',
+'api-error-duplicate-archive' => 'Jau {{PLURAL:$1|buvo [$2 kitas failas]|buvo [$2 kitų failų]}} puslapyje su tuo pačiu turiniu, bet {{PLURAL:$1|buvo|buvo}} ištrinti.',
+'api-error-duplicate-archive-popup-title' => 'Dubliuoti {{PLURAL:$1|failą kuris buvo|failus kurie buvo}} jau buvo ištrinti.',
'api-error-duplicate-popup-title' => 'Dubliuoti {{PLURAL:$1|failą|failus}}',
'api-error-empty-file' => 'Pateikta failas buvo tuščias.',
'api-error-emptypage' => 'Kurti naujus, tuščius puslapius neleidžiama.',
'api-error-fetchfileerror' => 'Vidinė klaida: Kažkas nutiko gaunant failą.',
+'api-error-fileexists-forbidden' => 'Failas, kurio pavadinimas "$1" jau egzistuoja, ir negali būti perrašytas.',
+'api-error-fileexists-shared-forbidden' => 'Failas, kurio pavadinimas "$1" jau egzistuoja bendro naudojimo failų saugykloje, ir negali būti perrašytas.',
'api-error-file-too-large' => 'Failą, kurį pateikėte buvo per didelis.',
'api-error-filename-tooshort' => 'Failo vardas yra per trumpas.',
'api-error-filetype-banned' => 'Šis failų tipas yra uždraustas.',
'api-error-hookaborted' => 'Pakeitimą, kurį bandėte atlikti, nutraukė priedas.',
'api-error-http' => 'Vidinė klaida: nepavyko prisijungti prie serverio.',
'api-error-illegal-filename' => 'Failo vardas neleidžiamas.',
+'api-error-internal-error' => 'Vidinė klaida: Kažkas ne taip su jūsų įkėlimo apdorojimu wiki.',
'api-error-invalid-file-key' => 'Vidinė klaida: failas nerastas saugykloje.',
+'api-error-missingparam' => 'Vidinė klaida: Trūksta reikalingų parametrų.',
'api-error-missingresult' => 'Vidinė klaida: nepavyko nustatyti, ar pavyko nukopijuoti.',
'api-error-mustbeloggedin' => 'Jūs turite būti prisijungęs kad galėtumėte įkelti failus.',
'api-error-mustbeposted' => 'Vidinė klaida: prašymas reikalauja HTTP POST.',
'api-error-noimageinfo' => 'Įkelti pavyko, bet serveris nepateikė mums jokios informacijos apie failą.',
+'api-error-nomodule' => 'Vidinė klaida: nėra nustatytas įkėlimų modulis.',
'api-error-ok-but-empty' => 'Vidinė klaida: nėra atsakymo iš serverio.',
'api-error-overwrite' => 'Perrašymas esamą failą neleidžiamas.',
'api-error-stashfailed' => 'Vidinė klaida: serveriui nepavyko išsaugoti laikinąjį failą.',
'duration-centuries' => '$1 {{PLURAL:$1|amžius|amžiai|amžių}}',
'duration-millennia' => '$1 {{PLURAL:$1|tūkstantmetis|tūkstantmečiai|tūkstantmečių}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 nėra {{PLURAL:$4|leistinas failo tipas|leistini failo tipai}}. {{PLURAL:$3|Leistinas failų tipas|Leistini failų tipai}} yra $2.',
);
'tog-hidepatrolled' => 'Slēpt apstiprinātās izmaņas pēdējo izmaiņu sarakstā',
'tog-newpageshidepatrolled' => 'Paslēpt pārbaudītās lapas jauno lapu sarakstā',
'tog-extendwatchlist' => 'Izvērst uzraugāmo lapu sarakstu, lai parādītu visas veiktās izmaiņas (ne tikai pašas svaigākās)',
-'tog-usenewrc' => "Lietot uzlaboto pēdējo izmaiņu lapu (izmanto ''JavaScript'')",
+'tog-usenewrc' => "Grupēt izmaiņas pēc lapas pēdējās izmaiņās un uzraugāmo lapu sarakstā (izmanto ''JavaScript'')",
'tog-numberheadings' => 'Automātiski numurēt virsrakstus',
'tog-showtoolbar' => 'Rādīt rediģēšanas rīkjoslu',
'tog-editondblclick' => "Atvērt rediģēšanas lapu ar dubultklikšķi (izmanto ''JavaScript'')",
'editold' => 'labot',
'viewsourceold' => 'aplūkot kodu',
'editlink' => 'labot',
-'viewsourcelink' => 'Skatīt pirmkodu',
+'viewsourcelink' => 'aplūkot kodu',
'editsectionhint' => 'Rediģēt sadaļu: $1',
'toc' => 'Satura rādītājs',
'showtoc' => 'parādīt',
'edit-no-change' => 'Tavs labojums tika ignorēts, jo tekstā netika izdarītas izmaiņas.',
'edit-already-exists' => 'Nevar izveidot jaunu lapu.
Tā jau eksistē.',
+'defaultmessagetext' => 'Noklusētais ziņojuma teksts',
# Parser/template warnings
'expensive-parserfunction-category' => 'Lapas ar pārāk daudz laikietilpīgiem apstrādes funkciju izsaukumiem',
# Diffs
'history-title' => '"$1" versiju hronoloģija',
+'difference-title' => 'Atšķirības starp "$1" versijām',
'difference-multipage' => '(Atšķirības starp lapām)',
'lineno' => '$1. rindiņa:',
'compareselectedversions' => 'Salīdzināt izvēlētās versijas',
# Namespace 8 related
'allmessages' => 'Visi sistēmas paziņojumi',
'allmessagesname' => 'Nosaukums',
-'allmessagesdefault' => 'Sākotnējais teksts',
+'allmessagesdefault' => 'Noklusētais ziņojuma teksts',
'allmessagescurrent' => 'Pašreizējais teksts',
'allmessagestext' => "Šajā lapā ir visu \"'''MediaWiki:'''\" lapās atrodamo sistēmas paziņojumu uzskaitījums.
Šos paziņojumus var izmainīt tikai admini. Izmainot tos šeit, tie tiks izmainīti tikai šajā mediawiki instalācijā. Lai tos izmainītu visām pārējām, apskatieties [//www.mediawiki.org/wiki/Localisation MediaWiki Localisation] un [//translatewiki.net translatewiki.net].",
'api-error-unknown-code' => 'अबूझ भ्रम:"$1"',
'api-error-uploaddisabled' => 'ऐ विकीपर उपारोपण अशक्त कएल गेल अछि।',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|मान्य संचिका प्रकार नै अछि|मान्य संचिका प्रकार सभ नै अछि}}। मान्य अछि {{PLURAL:$3|संचिका प्रकार अछि|संचिका प्रकार सभ अछि}} $2।',
);
'tog-hidepatrolled' => "Hanitrika ny fanovana voaara-maso ao amin'ny fanovana farany",
'tog-newpageshidepatrolled' => "Hanitsika ny pejy voaara-maso ao amin'ny pejy vaovao",
'tog-extendwatchlist' => 'Hanitatra ny lisitra fanaraham-pejy mba haneho ny fanovana rehetra fa tsy ny vaovao indrindra fotsiny',
-'tog-usenewrc' => "Hampiasa ny fanovana farany nohatsaraina (mila an'i Javascript)",
+'tog-usenewrc' => "Hamondrona ny fanovana araka ny pejy ao amin'ny fanovàna farany ary ao amin'ny pejy arahana (mila Javascript)",
'tog-numberheadings' => 'Asio laharany ny lohateny',
'tog-showtoolbar' => 'Asehoy ny edit toolbar (mila JavaScript)',
'tog-editondblclick' => 'Ovay ny pejy rehefa voatsindry indroa misesy ny totozy (mila JavaScript)',
'tog-editsectiononrightclick' => "Ovay ny fizaràna rehefa manindry ny bokotra havanana amin'ny totozy eo amin'ny lohateny hoe fizaràna (mila JavaScript)",
'tog-showtoc' => "Asehoy ny fanoroan-takila (ho an'ny pejy misy lohateny mihoatra ny 3)",
'tog-rememberpassword' => "Tadidio ny tenimiafiko eto amin'ity solosaina ity (mandritry ny andro $1 fara-fahabetsany){{PLURAL:}}",
-'tog-watchcreations' => "Ampina ao anarin'ny pejy fanaraha-maso ny pejy amboariko",
-'tog-watchdefault' => "Atsofohy ao amin'ny lisitry ny pejy arahinao maso ny pejy izay ovainao na foroninao",
-'tog-watchmoves' => "Ampina ao anatin'ny pejiko fanaraha-maso ny pejy soloiko anarana",
-'tog-watchdeletion' => "Ampina anatin'ny pejy fanaraha-maso ny pejy nofafako",
+'tog-watchcreations' => 'Hanaraka ny pejy foronoko ary ny rakitra ampidiriko',
+'tog-watchdefault' => 'Hanaraka ny pejy ary ny rakitra ovaiko',
+'tog-watchmoves' => 'Hanaraka ny pejy ary ny rakitra ovaiko anarana',
+'tog-watchdeletion' => 'Hanaraka ny pejy ary ny rakitra voafafako',
'tog-minordefault' => 'Mariho ho madinika foana aloha ny fanovana rehetra',
'tog-previewontop' => "Asehoy alohan'ny boaty fanovana ny tsipalotra",
'tog-previewonfirst' => "Asehoy ny tsipalotra amin'ny fanovana voalohany",
'tog-nocache' => 'Tsy alefa ny fanehoana ny pejy voasitriky ny mpitety',
-'tog-enotifwatchlistpages' => 'Andefaso imailaka aho rehefa misy miova ny pejy',
+'tog-enotifwatchlistpages' => 'Andefasana imailaka rehefa voaova ny pejy na ny rakitra arahako',
'tog-enotifusertalkpages' => 'Andefaso imailaka aho rehefa miova ny pejin-dresako',
-'tog-enotifminoredits' => 'Andefaso imailaka aho na dia fanovana madinika ihany aza',
+'tog-enotifminoredits' => "Andefasana imailaka na dia fanovana madinika aza no atao amin'ny pejy sy ny rakitra",
'tog-enotifrevealaddr' => "Asehoy ny adiresy imailako any amin'ny imailaka fampilazana",
'tog-shownumberswatching' => "Asehoy ny isan'ny mpikambana manara-maso ny pejy",
'tog-oldsig' => "Topi-mason'ny sonia :",
'badarticleerror' => "Tsy azo atao eto amin'ity pejy ity io asa io.",
'cannotdelete' => "Tsy afaka fafàna ny pejy na ny rakitra « $1 ».
Mety efa nataon'ny hafa angamba ny famafàna.",
+'cannotdelete-title' => 'Tsy afaka mamafa ny pejy "$1"',
'badtitle' => 'Tsy mety ny lohateny',
'badtitletext' => "Tsy mety io anaram-pejy nangatahinao io na tsy misy n'inon'inona na rohy dikan-teny vahiny misy diso tsipelina.",
-'perfcached' => 'Ireto angona ireto dia nalaina tao anaty cache koa mety ho efa lany daty. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts' => "Aminy ''cache'' daholo ny ''data'' misy ato, tamin'ny $1 ny data tato no natao ''mise à jour''. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.",
+'perfcached' => "Ao amin'ny voatakona ireo data manaraka ireo ary mety tsy voavao. $1{{PLURAL:}} ihany no isan'ireo zavatra voatahiry ao amin'ny voatakona",
+'perfcachedts' => "Ao amin'ny voatakona (cache) ny data aseho, ary tamin'ny $1 izy no navaozina farany. $4{{PLURAL:}} no isan'ny valim-pikarohana ao amin'ilay voatakona.",
'querypage-no-updates' => "Tsy nalefa ny ''mise à jour'' (update) hoan'ity pejy ity.
Mety tsy misy fifandraisana amin'ny zavamisy ankehitriny ny zavamisy ao anatin'ity pejy ity..",
'wrong_wfQuery_params' => "Misy tsy fetezana amin'ny wfQuery()<br />
Asa : $1<br />
fangatahana : $2",
'viewsource' => 'Hijery fango',
+'viewsource-title' => "Hijery ny fangon'i $1",
'actionthrottled' => 'Tao voafetra',
'actionthrottledtext' => "Mba hiady amin'ny spam, ny hatetika momba ny fanaovana io otao io dia ferana ho foifoy, ary niaotra io fetra io ianao.
Andramo indray afaka minitra vitsivitsy.",
'protectedpagetext' => 'Voaaro mba tsy hisy hanova ity pejy ity.',
'viewsourcetext' => "Azonao atao no mijery sy mandrika ny votoatin'ity pejy ity :",
+'viewyourtext' => "Azonao atao ny mijery ary mandika ny fangon'ny '''fanovanao''' tamin'ity pejy ity:",
'protectedinterface' => "Manome lahatsoratra ho an'ny rindrankajy ity pejy ity ary voaaro izy ity mba tsy hisy hanararaotra",
'editinginterface' => "'''Tandremo :''' manova pejy ampiasan'ny lôjisialy wiki ianao. Mety hita ny mpikambana sàsany izy io. Rehefa tia mandika teny ianao, jereo ny volavola MediaWiki ho an'ny internationalisation ny hafatra [//translatewiki.net/wiki/Main_Page?setlang=fr translatewiki.net].",
'sqlhidden' => '(nafenina ny requête SQL)',
'ns-specialprotected' => "Tsy afaka ovaina ny pejy anatin'ny toeran'anarana « {{ns:special}} » .",
'titleprotected' => "Voaaron'i [[User:$1|$1]] ity lohateny ity mba tsy hamorona pejy mitondra ity anarana ity.
Ny antony napetraka dia : « ''$2'' ».",
+'exception-nologin' => 'Tsy tafiditra',
# Virus scanner
'virus-badscanner' => "Diso : Tsy fantatray ny mpitady virus ''$1''",
'emailconfirmlink' => 'Hamarino ny adiresy imailakao',
'invalidemailaddress' => 'Tsy mety io imailaka nalefanao io satria tsy manaraka ny firafitra tokony ho izy.
Azafady manomeza adiresy voasoratra tsara na avelao ho banga io toerana io.',
+'cannotchangeemail' => "Tsy afaka ovaina eto amin'ity wiki ity ny adiresy imailaky ny kaonty.",
'accountcreated' => 'Kaonty voaforona',
'accountcreatedtext' => "Voasokatra ilay kaonty hoan'i $1.",
'createaccount-title' => "Fanokafana kaonty ho an'ny/i {{SITENAME}}",
'passwordreset-pretext' => '{{PLURAL:$1}}Mampidira singa data eo ambany',
'passwordreset-username' => 'Anaram-pikambana :',
'passwordreset-domain' => 'Vala (domain) :',
+'passwordreset-capture' => 'Hijery ny imailaka vokany ?',
'passwordreset-email' => 'Adiresy imailaka :',
'passwordreset-emailtitle' => "Antsipirihan'ny kaonty eo amin'i {{SITENAME}}",
'passwordreset-emailtext-ip' => "Nisy olona (mety ianao ihany angamba, avy amin'ity adiresy IP ity: $1) nangataka fampahalalana manokana mikasika ny kaontinao eo amin'i {{SITENAME}} ($4). {{PLURAL:$3|Ity|Ireto}} adiresy imailaka {{PLURAL:$3|Ity|Ireto}} dia mampiasa ity adiresy imailaka ity :
'passwordreset-emailelement' => 'Anaram-pikambana : $1
Tenimiafina miserana : $2',
'passwordreset-emailsent' => 'Nalefa ny imailaka fampatsiahivana.',
+'passwordreset-emailsent-capture' => 'Lasa ilay imailaka fahatadidiana, izay aseho eo ambany.',
+'passwordreset-emailerror-capture' => "Voaforona ilay imailaka fitadidiana, izay aseho eo ambany, fa tsy nahomby anefa ny fandefasana azy any amin'ny mpikambana : $1",
# Special:ChangeEmail
'changeemail' => 'Hanova ny adiresy imailaka',
Tadidio fa mampiasa soramadinika ny lohatenin'ny pejinao manan-tovana *.css sy *.js, ohatra {{ns:user}}:Foo/vector.css fa tsy {{ns:user}}:Foo/Vector.css.",
'updated' => '(Nohavaozina)',
'note' => "'''Fanamarihana:'''",
-'previewnote' => "'''Topi-maso ihany ity hitanao ity, tsy mbola voatahiry ny fanovana nataonao!'''",
+'previewnote' => "'''Fantaro fa topi-maso fotsiny ity.'''
+Mbola tsy voatahiry ny fanovanao !",
+'continue-editing' => 'Tohizana ny fanovana',
'previewconflict' => "
Ity topi-maso ity no mifanaraka amin'ny lahatsoratra ao amin'ny faritra eo ambony,
ary toy izao no ho fisehon'ny pejy raha misafidy ny hitahiry azy ianao.",
Ny zava-tsoratanao eto dia vokatr'asa naverinao soratana na nodikainao tany amina loharano ao amin'ny vala sarababem-bahoaka na loharano malalaka hafa (Jereo $1 ho an'ny antsipirihany).
'''Aza mampiasa tahirin-kevitra tsy nahazoan-dalana!'''",
-'longpageerror' => "'''Tsi-fetezana : Ny lanjan’ny lahatsoratrao dia $1 Ko, mihoatra ny $2 Ko, ilay fetra napetraka. Tsy afaka tahirizina ilay lahatsoratra.'''",
+'longpageerror' => "'''Hadisoana : Ny tahirin-tsoratra nalefanao dia manana halava {{PLURAL:$1|iray|$1}} kilooktety, izay lava kokoa nohon'ny fetra avo indridra izay natao ho {{PLURAL:$2|iray|$2}} kilooktety.'''
+Tsy afaka tahirizina ilay tahirin-tsoratra.",
'readonlywarning' => "'''FAMPITANDREMANA: Nohidiana noho ny antony fikolokoloana aloha ny banky angona,
koa tsy afaka mitahiry ny fanovana nataonao aloha ianao izao. Angamba tokony hanao Couper coller aloha
ianao dia tehirizo anaty rakitra ny fanovanao mandra-paha.'''
'edit-no-change' => "Tsy norarahian'ny rindrankajy ny fanovanao satria tsy nanova ny lahatsoratra ianao.",
'edit-already-exists' => 'Tsy afaka amboarina ilay pejy vaovao.
Efa misy izy.',
+'defaultmessagetext' => 'Hafatra raha tsy misy',
# Parser/template warnings
'expensive-parserfunction-warning' => 'Tandremo : Betsaka loatra ny fanantsoana ny tao parser.
'parser-template-loop-warning' => 'endrika vono hita tao : [[$1]]',
'parser-template-recursion-depth-warning' => "Fetran'ny halalin'ny fiantsoana endrika voahoatra ($1).",
'language-converter-depth-warning' => "Mihoatra ny fetran-kalalin'ny mpamadika teny ($1)",
+'node-count-exceeded-category' => "Pejy izay ahitana fihoatran'ny isam-patotra (node)",
+'node-count-exceeded-warning' => 'Pejy manana isam-patotra mihoatra',
+'expansion-depth-exceeded-category' => 'Pejy manana halalim-panitarana mihoatra',
+'expansion-depth-exceeded-warning' => 'Pejy manana halalim-panitarana mihoatra',
+'parser-unstrip-loop-warning' => 'Nahitana tondro mifolaka tsy azo vahana',
# "Undo" feature
'undo-success' => 'Ho voafafa io fanovana io. Marino tsara ny fanovana eo ambany, ary tehirizo rehefa vita.',
# Suppression log
'suppressionlog' => 'tatitr’asa momban’ny famafana pejy',
-'suppressionlogtext' => "Ity ny lisitra ny famafàna sy ny fanakanana asitrika amin'ny mpandrindra.
-Hijery ny [[Special:BlockList|lisitra ny adiresy IP sy mpikambana voasakana]] ho an'ny lisitra ny voaraoka sy ny fanakanana mbola miasa.",
+'suppressionlogtext' => "Ity ny lisitry ny famafana ary ny sakana mikasika ny votoatiny asitrika amin'ny mpandrindra. Jereo ny [[Special:BlockList|lisitry ny sakana]] ho an'ny lisitry ny fandroahana ary ny sakana mbola mihatra amin'izao fotoana.",
# History merging
'mergehistory' => 'Atsonika ny tantara ny pejy',
# Diffs
'history-title' => "Tantara ny endrik'i « $1 »",
+'difference-title' => "$1 : Fahasamihafan'ny santiôna roa",
+'difference-title-multipage' => 'Fahasamihafan\'ny pejy "$1" ary "$2"',
'difference-multipage' => "(Fahasamihafan'ny pejy)",
'lineno' => 'Andalana $1:',
'compareselectedversions' => 'Ampitahao ireo version voafidy',
'prefs-beta' => 'Fitaovana beta',
'prefs-datetime' => 'Daty sy ora',
'prefs-labs' => 'Fitaovana « labs »',
+'prefs-user-pages' => 'Pejim-pikambana',
'prefs-personal' => 'Mombamomba anao',
'prefs-rc' => 'Vao niova',
'prefs-watchlist' => 'Lisitry ny pejy arahana-maso',
'userrights-lookup-user' => 'Handrindra vondrom-pikambana',
'userrights-user-editname' => 'Manomeza solonanarana:',
'editusergroup' => "Hanova satan'ny mpikambana",
+'editinguser' => "Fanovana ny zon'ny mpikambana '''[[user:$1|$1]]''' $2",
'userrights-editusergroup' => 'Hanova vondrom-pikambana',
'saveusergroups' => 'Tehirizo ny vondrom-pikambana',
'userrights-groupsmember' => "Mpikambana amin'ny vondrona:",
'userrights-groupsmember-auto' => "Mpikambana tsy dia voalazan'i :",
+'userrights-groups-help' => 'Azonao atao ny manova ny vondrona isian\'ity mpikambana ity.
+* Ny boaty voa-"check" dia midika fa ao amin\'io vondrona io ilay mpikambana.
+* Ny boaty tsy voa-"check" dia midika fa tsy ao amin\'io vondrona io ilay mpikambana.
+* Ny * dia fa tsy azonao esorina amin\'ilay vondrona nampianao/nesorinao ilay mpikambana.',
'userrights-reason' => 'Antony :',
'userrights-no-interwiki' => "Tsy manana alalana manova ny alalan'ny mpikambana eny amin'ny wiki hafa ianao.",
'userrights-nodatabase' => 'Tsy eto akaiky na tsy misy ny banky angona « $1 ».',
'group-suppress' => 'Mpitondra',
'group-all' => '(izy rehetra)',
-'group-user-member' => 'Mpikambana',
-'group-autoconfirmed-member' => 'Mpikambana voasoratra',
-'group-bot-member' => 'Mpikambana rôbô',
-'group-sysop-member' => 'Mpandrindra',
-'group-bureaucrat-member' => 'Borôkraty',
-'group-suppress-member' => 'Mpitondra',
+'group-user-member' => '{{GENDER:$1|mpikambana}}',
+'group-autoconfirmed-member' => '{{GENDER:$1|Mpikambana voamarina ho azy}}',
+'group-bot-member' => '{{GENDER:$1|Mpikambana rôbô}}',
+'group-sysop-member' => '{{GENDER:$1|Mpandrindra}}',
+'group-bureaucrat-member' => '{{GENDER:$1|Borôkraty}}',
+'group-suppress-member' => '{{GENDER:$1|Mpitondra}}',
'grouppage-user' => '{{ns:project}}:Mpikambana',
'grouppage-autoconfirmed' => '{{ns:project}}:Mpikambana Voamafy',
'right-writeapi' => 'Mampiasa ny API fifanovana ny wiki',
'right-delete' => 'Mamafa pejy',
'right-bigdelete' => 'Mamafa pejy manana tantara be',
+'right-deletelogentry' => "Hamafa ary hamerina iditra manokana ao amin'ny laogy.",
'right-deleterevision' => 'Mamafa ny version manokana-na pejy iray',
'right-deletedhistory' => 'Mijery ny tantaram-pejy voafafa fa tsy lahatsorany',
'right-deletedtext' => "Mijery ny lahatsoratra voafafa sy ny fampitahana anelanelan'ny santiôna voafafa",
'right-siteadmin' => 'Manidy sy manokatra ny banky angona',
'right-override-export-depth' => "Mamoaka ny pejy miaraka amin'ny zana-pejy hatramin'ny ambaratonga fahadimy",
'right-sendemail' => "Mandefa imailaka any amin'ny mpikambana hafa",
+'right-passwordreset' => 'Hijery ny imailaka famerenana ny tenimiafina',
# User rights log
'rightslog' => 'Tatitr’asa momban’ny fanovana satam-pikambana',
'action-suppressionlog' => 'hijery io tao tsy sarababem-bahoaka',
'action-block' => 'manakana am-panoratana ny mpikambana iray',
'action-protect' => "manova ny fanovàn'ity pejy ity",
+'action-rollback' => "Manafoana haingana ny fanovan'ny mpikambana farany nanova pejy iray",
'action-import' => 'mampiditra ity pejy ity avy amina wiki hafa',
'action-importupload' => 'hampiditra ity pejy ity avy amina rakitra nampidirina',
'action-patrol' => 'marihana ho hita ity version ity',
'action-userrights' => "hanova ny fahefan'ny mpikambana rehetra",
'action-userrights-interwiki' => "hanova ny fahefan'ny mpikambana any amin'ny wiki hafa",
'action-siteadmin' => 'Manidy sy manokatra ny banky angona',
+'action-sendemail' => 'handefa imailaka',
# Recent changes
'nchanges' => '{{PLURAL:$1|fanovana|fanovana}} $1',
'number_of_watching_users_pageview' => '[$1 {{PLURAL:$1|mpikambana|mpikambana}} manara-maso]',
'rc_categories' => 'Ferana amin\'ireto sokajy ireto ihany (saraho amin\'ny "|")',
'rc_categories_any' => 'Tsy misy fetrany',
+'rc-change-size-new' => "$1{{PLURAL:}} oktety taorian'ny fanovana",
'newsectionsummary' => '/* $1 */ fizarana vaovao',
'rc-enhanced-expand' => 'Jereo ny detail (mila JavaScript)',
'rc-enhanced-hide' => 'Asitriho ny adidiny sy ny antsipiriany',
+'rc-old-title' => 'noforonina tamin\'ilay lohateny "$1"',
# Recent changes linked
'recentchangeslinked' => 'Novaina',
'ignorewarnings' => 'Aza mihaino fampitandremana',
'minlength1' => 'Ny anaran-drakitra dia tokony manana litera iray fara-fahakeliny',
'illegalfilename' => 'Misy litera tsy mety amin\'ny lohateny ny anaran\'ilay rakita "$1". Azafady soloy ny anaran\'ny rakitra dia andramo alefa indray.',
+'filename-toolong' => 'Tsy afaka mihoatra ny 240 oktety ny anaran-drakitra.',
'badfilename' => 'Novana ho "$1" ny anaran\'ny rakitra.',
'filetype-mime-mismatch' => "Ny karazan-drakitra dia tsy miady amin'ny karazana MIME.",
'filetype-badmime' => 'Ny karazan-drakitra MIME « $1 » dia tsy afaka ampidirina.',
'upload-unknown-size' => 'tsy fantatra ny habe',
'upload-http-error' => 'Nisy tsy fetezana HTTP nitranga : $1',
+# File backend
+'backend-fail-stream' => 'Tsy afaka mamaky ilay rakitra $1.',
+'backend-fail-backup' => 'Tsy afaka mitahiry ilay rakitra $1.',
+'backend-fail-hashes' => "Tsy azo ilay hash an-drakitra ho an'ny fampitahana.",
+'backend-fail-delete' => 'Tsy afaka mamafa ilay rakitra $1.',
+'backend-fail-alreadyexists' => 'Efa misy ilay rakitra $1.',
+'backend-fail-store' => 'Tsy afaka mitahiry ilay rakitra $1 anaty $2.',
+'backend-fail-copy' => 'Tsy afaka mandika ilay rakitra $1 anaty $2.',
+'backend-fail-move' => "Tsy afaka manova ny toeran'ilay raktira avy amin'i $1 mankany amin'i $2.",
+'backend-fail-opentemp' => 'Tsy afaka manokatra ilay rakitra miserana.',
+'backend-fail-writetemp' => "Tsy afaka manoratra ao anatin'ilay rakitra miserana.",
+'backend-fail-closetemp' => 'Tsy afaka manidy ilay rakitra miserana.',
+'backend-fail-read' => 'Tsy afaka mamaky ilay rakitra $1.',
+'backend-fail-create' => "Tsy afaka manoratra anatin'ilay rakitra $1.",
+'backend-fail-readonly' => 'Amin\'izao fotoana dia famakiana ihany ny fitahirizana terminal an\'i "$1". "\'\'$2\'\'" no antony nomena',
+'backend-fail-connect' => 'Tsy afaka mifandray amin\'ny terminal fitahirizana "$1".',
+'backend-fail-internal' => 'Hadisoana tsy fantatra tao anatin\'ny terminal fitahirizana "$1".',
+'backend-fail-usable' => "Tsy afaka manoratra ilay rakitra $1 noho ny tsifiampian'ny zo na noho ny petra-drakitra tsy misy.",
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Tsy afaka miantso ilay banky angona laogy ho an\'ny terminal fitahirizana "$1".',
+'filejournal-fail-dbquery' => 'Tsy afaka manavao ny banky angona laogy ho an\'ilay terminal fitahirizana "$1".',
+
+# Lock manager
+'lockmanager-notlocked' => 'Tsy afaka manalahidy an\'i "$1" ; tsy voahidy ilay izy.',
+'lockmanager-fail-closelock' => 'Tsy afaka manidy ilay rakitra fanidiana ho an\'i "$1".',
+'lockmanager-fail-deletelock' => 'Tsy afaka manidy ilay rakitra fanidiana ho an\'i "$1"',
+'lockmanager-fail-acquirelock' => 'Tsy afaka maka ilay rakitra fanidiana ho an\'i "$1"',
+'lockmanager-fail-openlock' => 'Tsy afaka manokatra ilay rakitra fanidiana ho an\'i "$1".',
+'lockmanager-fail-releaselock' => 'Tsy afaka mamela ilay fanidiana ho an\'i "$1"',
+'lockmanager-fail-db-bucket' => "Tsy ampy ny isan'ireo banky angona fanidiana voaantso anatin'ny baketra (godet) $1.",
+'lockmanager-fail-db-release' => "Tsy afaka mamela ny fanidiana eo amin'ny banky angona $1.",
+'lockmanager-fail-svr-acquire' => "Tsy afaka maka ny fanidiana eo amin'ny lohamilina $1.",
+'lockmanager-fail-svr-release' => "Tsy afaka mamela ny fanidiana eo amin'ny banky angona $1.",
+
# ZipDirectoryReader
'zip-file-open-error' => "Nitrangana hadisoana teo am-panokafana ilay rakitra ZIP ho an'ny fanamarinana.",
'zip-wrong-format' => "Tsy ZIP ny karazan-drakitr'ilay rakitra voatonona.",
'img-auth-public' => "Ny asa ataon'i img_auth.php dia maneho ny rakitry ny wiki an'olona.
ity wiki ity dia no-regler-na ho sarababem-bahoaka.",
'img-auth-noread' => "Tsy manana ny alalam-pamakiana ilay mpikambana eo amin'ny « $1 ».",
+'img-auth-bad-query-string' => 'Manana tohintsora-kataka tsy manara-penitra ilay URL.',
# HTTP errors
'http-invalid-url' => 'URL diso : $1',
** Rakitra efa misy',
'filedelete-edit-reasonlist' => 'Hanova ny antom-pamafàna',
'filedelete-maintenance' => 'Ny famafana sy ny famerenan-drakitra dia tsy alefa mandritra ny fikojakojana.',
+'filedelete-maintenance-title' => 'Tsy afaka mamafa ilay rakitra',
# MIME search
'mimesearch' => 'Fikarohana MIME',
'mostimages' => "Misy firohizana betsaka amin'ny sary",
'mostrevisions' => 'Lahatsoratra niova im-betsaka indrindra',
'prefixindex' => "Pejy manomboka amin'ny...",
+'prefixindex-namespace' => 'Ny pejy rehetra mitondra ny tovona (anaran-tsehatra $1)',
'shortpages' => 'Pejy fohy',
'longpages' => 'Pejy lavabe',
'deadendpages' => 'Pejy tsy mirohy',
'protect-level-sysop' => 'Sysops ihany',
'protect-summary-cascade' => 'Fiarovana an-driana',
'protect-expiring' => "Miala amin'ny $1",
+'protect-expiring-local' => 'mitsahatra ny $1',
'protect-expiry-indefinite' => 'tsiefa',
'protect-cascade' => "Miaro ny pejy ao anatin'ity pejy ity (cascading protection)",
'protect-cantedit' => "Tsy afaka manolo ny sokaji-piarovan'ity pejy ity ianao satria tsy manana ny sata ilaina",
Vakio ny [[Special:Log/delete|laogim-pamafana]] ho an'ny lisitry ny famafana sy ny famerenana pejy.",
'undelete-header' => 'Jereo ny [[Special:Log/delete|laogim-pamafana]] rehefa hanalisitra ny pejy vao voafafa.',
+'undelete-search-title' => 'Hitady pejy voafafa',
'undelete-search-box' => 'Hitady pejy voafafa',
'undelete-search-prefix' => "Asehoy ny pejy manomboka amin'ny :",
'undelete-search-submit' => 'Fikarohana',
'undelete-cleanup-error' => 'Tsy fetezana teo am-pamafana ilay rakitra an-tahiry tsy miasa « $1 ».',
'undelete-missing-filearchive' => "Tsy afaka atao ny mamerina ilay rakitra tahiry miaraka amin'ny ID $1 satria tsy ao amin'ny banky angona izy io.
Mety efa naverina angamba izy io.",
+'undelete-error' => 'Pejin-kadisoam-panafoanana',
'undelete-error-short' => 'Tsi-fetezana teo am-pamerenana ilay rakitra : $1',
'undelete-error-long' => 'Nisy tsi-fetezana nitranga teo am-pamerenana ilay rakitra :
# Namespace form on various pages
'namespace' => 'Anaran-tsehatra :',
'invert' => 'Ampifamadiho ny safidy',
+'namespace_association' => 'Anaran-tsehatra nampiarahana',
'blanknamespace' => '(fotony)',
# Contributions
'whatlinkshere-filters' => 'sivana',
# Block/unblock
+'autoblockid' => 'Fanakanana mandeha ho azy #$1',
'block' => 'Hanakana ilay mpikambana',
'unblock' => "Hanala ny sakan'ilay mpikambana",
'blockip' => 'Sakano ny mpikambana',
'unblocklink' => 'esory ny sakana',
'change-blocklink' => 'ovay ny fanakanana',
'contribslink' => "fandraisan'anjara",
+'emaillink' => 'Handefa imailaka',
'autoblocker' => "Voasakana satria ny adiresy IP-nao dia vao avy nampiasain'i \"[[User:\$1|\$1]]\". Ny anton'ny fanakanana dia: \"'''\$2'''\"",
'blocklogpage' => "Tantaran'ny sakana",
'blocklog-showlog' => 'Efa voasakana ity mpikambana ity taloha.
'ipb_already_blocked' => 'Efa voasakana « $1 »',
'ipb-needreblock' => 'Efa voasakana i $1. Tianao ovaina ve ny parametatra ?',
'ipb-otherblocks-header' => '{{PLURAL:$1}}sakana hafa',
+'unblock-hideuser' => "Tsy azonao atao ny manala ny sakan'ity mpikambana ity, satria nafenina ny anaram-pikambany.",
'ipb_cant_unblock' => 'Tsy fetezana : Marik ny fanakanana $1 tsy hita.
Mety efa natao angamba ny fanalana sakana.',
'ipb_blocked_as_range' => "Hadisoana : tsy nosakanana manokana ny adiresy IP $1 ka noho izany tsy afaka alàna ny sakany.
miampy fampahalalana momba ny fanovana farany fotsiny ve sa miaraka amin'ny tantaran'ny fanovana rehetra.
Etsy amin'ny toerana farany dia afaka mampiasa rohy ihany koa ianao, ohatra [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] ho an'ny [[{{MediaWiki:Mainpage}}]].",
+'exportall' => 'Hamoaka ny pejy rehetra',
'exportcuronly' => "Ny votoatiny ankehitriny ihany no haondrana fa tsy miaraka amin'ny tantarany iray manontolo",
'exportnohistory' => "
----
'jumpto' => 'Куснаш:',
'jumptonavigation' => 'навигацийыш',
'jumptosearch' => 'кычалмашшке',
+'pool-errorunknown' => 'Палыдыме йоҥылыш',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
'aboutsite' => '{{SITENAME}} нерген',
'filecopyerror' => '«$1» гыч «$2» файлыш копийым ышташ лийдыме.',
'fileexistserror' => '«$1» файлыш возыкым ышташ лийдыме: файл уло.',
'unexpected' => 'Келшыдыме кугыт: «$1»=«$2».',
+'cannotdelete-title' => '"$1" лаштыкым шӧраш ок лий',
'badtitle' => 'Сай огыл лӱм',
'badtitletext' => 'Йодмо лаштыкын лӱмжӧ йоҥылыш, але яра, але йылме кокла але интер-вики лӱмжӧ йоҥылыш. Ала лӱмыштӧ кӱлдымӧ тамга улыт.',
'viewsource' => 'Тӱҥалтыш текст',
$messages = array(
# User preference toggles
'tog-underline' => 'Потцртување на врски:',
-'tog-justify' => 'Ð\94воÑ\81Ñ\82Ñ\80аниÑ\87но поÑ\80амнÑ\83ваÑ\9aе на паÑ\80агÑ\80аÑ\84иÑ\82е',
+'tog-justify' => 'Ð\9fоÑ\80амнÑ\83ваÑ\9aе на паÑ\81Ñ\83Ñ\81иÑ\82е по Ñ\88иÑ\80инаÑ\82а на Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а',
'tog-hideminor' => 'Скривај ги ситните уредувања во скорешните промени',
'tog-hidepatrolled' => 'Скриј испатролирани уредувања во скорешните промени',
'tog-newpageshidepatrolled' => 'Скриј испатролирани страници од списокот на нови страници',
'tog-extendwatchlist' => 'Прошири го список на набљудувања за приказ на сите промени, не само скорешните',
'tog-usenewrc' => 'Промени во групи по страници во списокот на скорешни промени (бара JavaScript)',
-'tog-numberheadings' => 'Ð\90вÑ\82о нÑ\83меÑ\80иÑ\80аÑ\9aе на заглавијата',
+'tog-numberheadings' => 'Ð\9dÑ\83меÑ\80иÑ\80аÑ\98 ги заглавијата',
'tog-showtoolbar' => 'Прикажи алатник за уредување (JavaScript)',
'tog-editondblclick' => 'Уредување на страници при двојно кликнување (JavaScript)',
'tog-editsection' => 'Овозможи уредување на заглавија преку врските [уреди]',
'tog-shownumberswatching' => 'Прикажи го бројот на корисници кои набљудуваат',
'tog-oldsig' => 'Постоечки потпис:',
'tog-fancysig' => 'Сметај го потписот за викитекст (без автоматска врска)',
-'tog-externaleditor' => 'По основно користи надворешен уредник (само за стручњаци, потребно е посебно нагодување на сметачот. [//www.mediawiki.org/wiki/Manual:External_editors?uselang=mk Повеќе информации.])',
+'tog-externaleditor' => 'По основно користи надворешен уредувач (само за стручњаци, потребно е посебно нагодување на сметачот. [//www.mediawiki.org/wiki/Manual:External_editors?uselang=mk Повеќе информации.])',
'tog-externaldiff' => 'По основно користи надворешен програм за споредување верзии (само за стручњаци, потребно е специјално нагодување на сметачот. [//www.mediawiki.org/wiki/Manual:External_editors?uselang=mk Повеќе информации.])',
'tog-showjumplinks' => 'Овозможи врски на пристапност „скокни на“',
'tog-uselivepreview' => 'Користи преглед во живо (JavaScript, експериментално)',
'youhavenewmessages' => 'Имате $1 ($2).',
'newmessageslink' => 'нови пораки',
'newmessagesdifflink' => 'скорешна промена',
+'youhavenewmessagesfromusers' => 'Имате $1 од {{PLURAL:$3|еден корисник|$3 корисници}} ($2).',
+'youhavenewmessagesmanyusers' => 'Имате $1 од многу корисници ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|нова порака|нови пораки}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|последна промена|последни промени}}',
'youhavenewmessagesmulti' => 'Имате нови пораки на $1',
'editsection' => 'уреди',
'editsection-brackets' => '[$1]',
'remembermypassword' => 'Запомни ме на овој сметач (највеќе $1 {{PLURAL:$1|ден|дена}})',
'securelogin-stick-https' => 'Останете поврзани со HTTPS по одјавата',
'yourdomainname' => 'Вашиот домен:',
+'password-change-forbidden' => 'Не можете да ја менувате лозинката на ова вики.',
'externaldberror' => 'Настана грешка при надворешното најавување на базата или пак немате дозвола да ја подновите вашата надворешна сметка.',
'login' => 'Најава',
'nav-login-createaccount' => 'Најава / регистрација',
Затоа мораме да ја користиме неговата бројчена IP-адреса за да го препознаеме.
Една ваква IP-адреса може да ја делат повеќе корисници.
Ако сте анонимен корисник и сметате дека кон вас се упатени нерелевантни коментари, тогаш [[Special:UserLogin/signup|создајте корисничка сметка]] или [[Special:UserLogin|најавете се]] за да избегнете поистоветување со други анонимни корисници во иднина.''",
-'noarticletext' => 'Моментално нема текст на оваа страница.
-Можете да направите [[Special:Search/{{PAGENAME}}|пребарување за овој наслов на страница]] во други страници,
-<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} пребарување во дневниците],
-или [{{fullurl:{{FULLPAGENAME}}|action=edit}} да ја уредите оваа страница]</span>.',
-'noarticletext-nopermission' => 'Нема текст на оваа страница.
-Можете да го [[Special:Search/{{PAGENAME}}|пребарате овој наслов]] во други страници,
-или да ги <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} пребарате соодветните дневници]</span>.',
+'noarticletext' => 'Таква страница сè уште не постои.
+Можете да проверите [[Special:Search/{{PAGENAME}}|дали насловот се споменува]] во други статии,
+да ги <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} пребарате дневниците],
+или да [{{fullurl:{{FULLPAGENAME}}|action=edit}} ја создадете]</span>.',
+'noarticletext-nopermission' => 'Таква страница сè уште не постои.
+Можете да проверите [[Special:Search/{{PAGENAME}}|дали насловот се споменува]] во други статии,
+или да ги <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} пребарате дневниците]</span>.',
+'missing-revision' => 'Не ја пронајдов ревизијата бр. $1 на страницата со наслов „{{PAGENAME}}“.
+
+Ова обично се должи на застарена врска за разлики што води кон избришана страница.
+Повеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].',
'userpage-userdoesnotexist' => 'Корисничката сметка „<nowiki>$1</nowiki>“ не е регистрирана.
Ве молиме размислете дали навистина сакате да ја создадете/уредите оваа страница.',
'userpage-userdoesnotexist-view' => 'Корисничката сметка „$1“ не е регистрирана.',
'expansion-depth-exceeded-warning' => 'Страницата ја надмина длабочината на проширувањето',
'parser-unstrip-loop-warning' => 'Утврдена е јамка',
'parser-unstrip-recursion-limit' => 'Пречекорена е границата на рекурзија ($1)',
+'converter-manual-rule-error' => 'Пронајдов грешка во правилото за рачно претворање на јазик',
# "Undo" feature
'undo-success' => 'Уредувањето може да се откаже.
'editundo' => 'откажи',
'diff-multi' => '({{PLURAL:$1|Не е прикажана една меѓувремена ревизија|Не се прикажани $1 меѓувремени ревизии}} од {{PLURAL:$2|еден корисник|$2 корисници}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Не е прикажана една меѓувремена ревизија направена|Не се прикажани $1 меѓувремени ревизии направени}} од повеќе од $2 {{PLURAL:$2|корисник|корисници}})',
+'difference-missing-revision' => 'Не пронајдов {{PLURAL:$2|една ревизија|$2 ревизии}} од оваа разлика ($1).
+
+Ова обично се должи на застарена врска за разлики што води кон избришана страница.
+Повеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].',
# Search results
'searchresults' => 'Резултати од пребарувањето',
'right-writeapi' => 'Можност за пишување на API',
'right-delete' => 'Бришење страници',
'right-bigdelete' => 'Бришење страници со долга историја',
+'right-deletelogentry' => 'Бришење и враќање на конкретни ставки во дневник',
'right-deleterevision' => 'Бришење и враќање на конкретни ревизии на страници',
'right-deletedhistory' => 'Прегледување на записи во историја на бришења, без придружниот текст',
'right-deletedtext' => 'Прегледување на избришан текст и промени помеѓу избришани ревизии',
'disambiguations' => 'Страници што водат до страници за појаснување',
'disambiguationspage' => 'Template:Појаснување',
-'disambiguations-text' => "СледниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86и имааÑ\82 вÑ\80Ñ\81ки кои водаÑ\82 до '''страница за појаснување'''.
-Наместо тоа тие треба да водат до соодветната тема.<br />
-СÑ\82Ñ\80аниÑ\86а Ñ\81е Ñ\82Ñ\80еÑ\82иÑ\80а како Ñ\81Ñ\82Ñ\80аниÑ\86а за поÑ\98аÑ\81нÑ\83ваÑ\9aе ако Ñ\82аа го коÑ\80иÑ\81Ñ\82и Ñ\88аблоноÑ\82 коÑ\98 е наведен [[MediaWiki:Disambiguationspage|Ñ\82Ñ\83ка]]",
+'disambiguations-text' => "СледниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86и имааÑ\82 баÑ\80ем по една вÑ\80Ñ\81ка Ñ\88Ñ\82о води до '''страница за појаснување'''.
+Наместо тоа, може да имаат врска до посоодветната тема.<br />
+Ð\95дна Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81е Ñ\81меÑ\82а за Ñ\81Ñ\82Ñ\80аниÑ\86а за поÑ\98аÑ\81нÑ\83ваÑ\9aе ако го коÑ\80иÑ\81Ñ\82и Ñ\88аблоноÑ\82 Ñ\88Ñ\82о води од [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Двојни пренасочувања',
'doubleredirectstext' => 'Оваа страница ги прикажува пренасочувачките страници до други пренасочувачки страници.
'rollback' => 'Отповикај промени',
'rollback_short' => 'Отповикај',
'rollbacklink' => 'отповикај',
+'rollbacklinkcount' => 'отповикај $1 {{PLURAL:$1|уредување|уредувања}}',
+'rollbacklinkcount-morethan' => 'отповикај повеќе од $1 {{PLURAL:$1|уредување|уредувања}}',
'rollbackfailed' => 'Отповикувањето не успеа',
'cantrollback' => 'Уредувањето не може да се отповика.
Последниот уредник е воедно и единствениот автор на страницата.',
'whatlinkshere' => 'Што води овде',
'whatlinkshere-title' => 'Страници со врски што водат до „$1“',
'whatlinkshere-page' => 'Страница:',
-'linkshere' => "СледниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86и имааÑ\82 вÑ\80Ñ\81ка до '''[[:$1]]''':",
+'linkshere' => "СледниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86и водаÑ\82 кон â\80\9e'''[[:$1]]'''â\80\9c:",
'nolinkshere' => "Нема страници што водат кон '''[[:$1]]'''.",
'nolinkshere-ns' => "Нема страници што водат кон '''[[:$1]]''' во избраниот именски простор.",
'isredirect' => 'пренасочувачка страница',
'import-interwiki-templates' => 'Вклучи ги сите шаблони',
'import-interwiki-submit' => 'Увези',
'import-interwiki-namespace' => 'Целен именски простор:',
-'import-upload-filename' => 'Ð\98ме на подаÑ\82оÑ\82екаÑ\82а:',
+'import-upload-filename' => 'Ð\9fодаÑ\82оÑ\82ека:',
'import-comment' => 'Коментар:',
'importtext' => 'Извезете ја податотеката од изворното вики користејќи ја [[Special:Export|алатката за извоз]].
Зачувајте ја на вашиот сметач и подигнете ја овде.',
# Media information
'mediawarning' => "'''Предупредување''': Оваа податотека може да содржи штетен код.
Ако ја користите, ова може да му наштети на вашиот систем.",
-'imagemaxsize' => "Ограничување на големина на слика:<br />''(на нивните описни страници)''",
+'imagemaxsize' => "Ограничување на големината на сликите:<br />''(на нивните описни страници)''",
'thumbsize' => 'Големина на минијатурата:',
'widthheight' => '$1 × $2',
'widthheightpage' => '$1 × $2, $3 {{PLURAL:$3|страница|страници}}',
'exif-whitebalance' => 'Рамнотежа на бело',
'exif-digitalzoomratio' => 'Сооднос на дигиталното приближување (зум)',
'exif-focallengthin35mmfilm' => 'Еквивалентно фокусно растојание за 35 мм филм',
-'exif-scenecapturetype' => 'Тип на сликање сцена',
+'exif-scenecapturetype' => 'Тип на сликаната сцена',
'exif-gaincontrol' => 'Контрола на сцената',
'exif-contrast' => 'Контраст',
'exif-saturation' => 'Заситеност',
'fileduplicatesearch' => 'Барање на дуплирани податотеки',
'fileduplicatesearch-summary' => 'Пребарување на дуплирани податотеки по тарабни вредности.',
'fileduplicatesearch-legend' => 'Барање на дупликат',
-'fileduplicatesearch-filename' => 'Ð\98ме на подаÑ\82оÑ\82екаÑ\82а:',
+'fileduplicatesearch-filename' => 'Ð\9fодаÑ\82оÑ\82ека:',
'fileduplicatesearch-submit' => 'Барај',
'fileduplicatesearch-info' => '$1 × $2 пиксели<br />Големина на податотеката: $3<br />MIME-тип: $4',
'fileduplicatesearch-result-1' => 'Податотеката „$1“ нема истоветни дупликати.',
'duration-centuries' => '$1 {{PLURAL:$1|век|века}}',
'duration-millennia' => '$1 {{PLURAL:$1|милениум|милениуми}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 не {{PLURAL:$4|е допуштен тип на податотека|се допуштени типови на податотека}}. {{PLURAL:$3|Допуштен е|Допуштени се}} $2.',
);
'youhavenewmessages' => 'താങ്കൾക്ക് $1 ഉണ്ട് ($2).',
'newmessageslink' => 'പുതിയ സന്ദേശങ്ങൾ',
'newmessagesdifflink' => 'അവസാന മാറ്റം',
+'youhavenewmessagesfromusers' => 'താങ്കൾക്ക് {{PLURAL:$3|മറ്റൊരു ഉപയോക്താവ്|മറ്റ് $3 ഉപയോക്താക്കൾ}} $1 ചേർത്തിട്ടുണ്ട് ($2).',
+'youhavenewmessagesmanyusers' => 'താങ്കൾക്ക് പലർ $1 ചേർത്തിട്ടുണ്ട് ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|പുതിയ സന്ദേശം|പുതിയ സന്ദേശങ്ങൾ}}',
+'newmessagesdifflinkplural' => 'അവസാന {{PLURAL:$1|മാറ്റം|മാറ്റങ്ങൾ}}',
'youhavenewmessagesmulti' => 'താങ്കൾക്ക് $1 താളിൽ പുതിയ സന്ദേശങ്ങൾ ഉണ്ട്',
'editsection' => 'തിരുത്തുക',
'editold' => 'തിരുത്തുക',
'remembermypassword' => 'എന്റെ പ്രവേശനം ഈ ബ്രൗസറിൽ ({{PLURAL:$1|ഒരു ദിവസം|$1 ദിവസം}}) ഓർത്തുവെക്കുക',
'securelogin-stick-https' => 'പ്രവേശനത്തിനു ശേഷവും എച്ച്.റ്റി.റ്റി.പി.എസ്. തന്നെ ഉപയോഗിക്കുക',
'yourdomainname' => 'താങ്കളുടെ ഡൊമെയിൻ:',
+'password-change-forbidden' => 'ഈ വിക്കിയിൽ രഹസ്യവാക്കുകൾ മാറ്റാനാവില്ല.',
'externaldberror' => 'ഒന്നുകിൽ ഡേറ്റാബേസ് സാധൂകരണത്തിൽ പ്രശ്നം ഉണ്ടായിരുന്നു അല്ലെങ്കിൽ നവീകരിക്കുവാൻ താങ്കളുടെ ബാഹ്യ അംഗത്വം താങ്കളെ അനുവദിക്കുന്നില്ല.',
'login' => 'പ്രവേശിക്കുക',
'nav-login-createaccount' => 'പ്രവേശിക്കുക / അംഗത്വമെടുക്കുക',
'noarticletext-nopermission' => 'ഇപ്പോൾ ഈ താളിൽ എഴുത്തുകളൊന്നും ഇല്ല.
താങ്കൾക്ക് മറ്റു താളുകളിൽ [[Special:Search/{{PAGENAME}}|ഈ താളിന്റെ തലക്കെട്ടിനായി തിരയാവുന്നതാണ്]],
അല്ലെങ്കിൽ <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ബന്ധപ്പെട്ട രേഖകൾ പരിശോധിക്കാവുന്നതാണ്]</span>.',
+'missing-revision' => '"{{PAGENAME}}" എന്ന താളിന്റെ #$1 എന്ന നാൾപ്പതിപ്പ് നിലവിലില്ല.
+
+മായ്ക്കപ്പെട്ട താളിന്റെ കാലഹരണപ്പെട്ട നാൾവഴി കണ്ണി ഉപയോഗിച്ചാലാണ് സാധാരണ ഇങ്ങനെ സംഭവിക്കുക.
+കൂടുതൽ വിവരങ്ങൾ [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} മായ്ക്കൽ രേഖയിൽ] കാണാവുന്നതാണ്.',
'userpage-userdoesnotexist' => '"<nowiki>$1</nowiki>" എന്ന ഉപയോക്താവ് അംഗത്വമെടുത്തിട്ടില്ല. ഈ താൾ സൃഷ്ടിക്കണമോ എന്നതു പരിശോധിക്കുക.',
'userpage-userdoesnotexist-view' => '"$1" എന്ന അംഗത്വം നിലവിലില്ല.',
'blocked-notice-logextract' => 'ഈ ഉപയോക്താവ് ഇപ്പോൾ തടയപ്പെട്ടിരിക്കുകയാണ്.
'editundo' => 'മാറ്റം തിരസ്ക്കരിക്കുക',
'diff-multi' => '(ഇടയ്ക്ക് {{PLURAL:$2|ഒരു ഉപയോക്താവ്|$2 ഉപയോക്താക്കൾ}} ചെയ്ത {{PLURAL:$1|ഒരു പതിപ്പ്|$1 പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല.)',
'diff-multi-manyusers' => '(ഇടയ്ക്ക് {{PLURAL:$2|ഒന്നിലധികം|$2 എണ്ണത്തിലധികം}} ഉപയോക്താക്കൾ ചെയ്തിട്ടുള്ള {{PLURAL:$1|ഒരു പതിപ്പ്|$1 പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല.)',
+'difference-missing-revision' => 'ഈ വ്യത്യാസത്തിൽ ($1) {{PLURAL:$2|ഒരു നാൾപ്പതിപ്പ്|$2 നാൾപ്പതിപ്പുകൾ}} കാണാനായില്ല.
+
+മായ്ക്കപ്പെട്ട താളിന്റെ കാലഹരണപ്പെട്ട നാൾവഴി കണ്ണി ഉപയോഗിച്ചാലാണ് സാധാരണ ഇങ്ങനെ സംഭവിക്കുക.
+കൂടുതൽ വിവരങ്ങൾ [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} മായ്ക്കൽ രേഖയിൽ] കാണാവുന്നതാണ്.',
# Search results
'searchresults' => 'തിരച്ചിലിന്റെ ഫലം',
'right-writeapi' => 'തിരുത്തുക എ.പി.ഐ.യുടെ ഉപയോഗം',
'right-delete' => 'താളുകൾ മായ്ക്കുക',
'right-bigdelete' => 'വലിയ നാൾവഴിയുള്ള താളുകൾ മായ്ക്കുക',
+'right-deletelogentry' => 'രേഖയിലെ പ്രത്യേക ഉൾപ്പെടുത്തലുകൾ മായ്ക്കുകയോ പുനഃസ്ഥാപിക്കുകയോ ചെയ്യുക',
'right-deleterevision' => 'താളിന്റെ പ്രത്യേക പതിപ്പുകൾ മായ്ക്കുക പുനഃസ്ഥാപിക്കുക',
'right-deletedhistory' => 'മായ്ക്കപ്പെട്ട വിവരങ്ങൾ ബന്ധപ്പെട്ട എഴുത്തുകൾ ഇല്ലാതെ കാണുക',
'right-deletedtext' => 'മായ്ക്കപ്പെട്ട എഴുത്തും താളിന്റെ മായ്ക്കപ്പെട്ട പതിപ്പുകൾ തമ്മിലുള്ള വ്യത്യാസവും കാണുക',
'backend-fail-connect' => '"$1" ശേഖരണ ബാക്കെൻഡുമായി ബന്ധപ്പെടാൻ കഴിഞ്ഞില്ല.',
'backend-fail-internal' => '"$1" എന്ന സ്റ്റോറേജ് ബാക്കെൻഡിൽ അപരിചിതമായ പിഴവ് സംഭവിച്ചു.',
'backend-fail-contenttype' => '"$1" എന്നതിൽ സംഭരിച്ചിരിക്കുന്ന പ്രമാണത്തിന്റെ ഉള്ളടക്ക തരം നിർണ്ണയിക്കാനായില്ല.',
+'backend-fail-batchsize' => 'ശേഖരണ ബാക്ക്എൻഡിൽ $1 ഫയൽ {{PLURAL:$1|പ്രവൃത്തി|പ്രവൃത്തികൾ}} ചെയ്യാൻ നൽകിയിരുന്നു; അതിന്റെ പരിധി $2 {{PLURAL:$2|പ്രവൃത്തി|പ്രവൃത്തികൾ}} ആണ്.',
'backend-fail-usable' => 'ആവശ്യമായത്ര അനുമതിയില്ലാത്തതു കൊണ്ടോ ഡയറക്റ്ററികൾ/കണ്ടൈനറുകൾ ഇല്ലാത്തതു കൊണ്ടോ പ്രമാണം $1 എഴുതിച്ചേർക്കാൻ കഴിഞ്ഞില്ല.',
# File journal errors
'filejournal-fail-dbquery' => '"$1" എന്ന ശേഖരണ ബാക്ക്എൻഡിനായി ജേണൽ ഡേറ്റാബേസ് പുതുക്കാൻ കഴിഞ്ഞില്ല.',
# Lock manager
-'lockmanager-notlocked' => '"$1" à´\8eà´¨àµ\8dനതിലàµ\86 à´ªàµ\82à´\9fàµ\8dà´\9fൽ അഴിക്കാൻ കഴിഞ്ഞില്ല; അത് പൂട്ടിയിട്ടില്ല.',
+'lockmanager-notlocked' => '"$1" à´\8eà´¨àµ\8dനതിലàµ\86 à´ªàµ\82à´\9fàµ\8dà´\9fàµ\8d അഴിക്കാൻ കഴിഞ്ഞില്ല; അത് പൂട്ടിയിട്ടില്ല.',
'lockmanager-fail-closelock' => '"$1" എന്നതിന്റെ പൂട്ടൽ പ്രമാണം അടയ്ക്കാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-deletelock' => '"$1" എന്നതിന്റെ പൂട്ടൽ പ്രമാണം നീക്കംചെയ്യാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-acquirelock' => '"$1" എന്നതിന്റെ പൂട്ട് ലഭ്യമാക്കാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-releaselock' => '"$1" എന്നതിന്റെ പൂട്ട് വിടുവിക്കാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-db-bucket' => '$1 എന്ന ബക്കറ്റിൽ ആവശ്യത്തിനു പൂട്ടൽ ഡേറ്റാബേസുകളെ ബന്ധപ്പെടാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-db-release' => '$1 ഡേറ്റാബേസിലെ പൂട്ടലുകൾ വിടുവിക്കാൻ കഴിഞ്ഞില്ല.',
+'lockmanager-fail-svr-acquire' => '$1 സെർവറിൽ പൂട്ട് ലഭ്യമാക്കാൻ കഴിഞ്ഞില്ല.',
'lockmanager-fail-svr-release' => '$1 സെർവറിലെ പൂട്ടലുകൾ വിടുവിക്കാൻ കഴിഞ്ഞില്ല.',
# ZipDirectoryReader
'disambiguations' => 'വിവക്ഷിത താളുകളിലേയ്ക്ക് കണ്ണിചേർത്തിരിക്കുന്ന താളുകൾ',
'disambiguationspage' => 'Template:വിവക്ഷകൾ',
-'disambiguations-text' => 'താഴെ കൊടുത്തിരിക്കുന്ന താളുകൾ വിവക്ഷിതങ്ങൾ താളിലേക്കു കണ്ണി ചേർക്കപ്പെട്ടിരിക്കുന്നു. അതിനു പകരം അവ ലേഖനതാളുകളിലേക്കു കണ്ണി ചേക്കേണ്ടതാണ്. <br /> ഒരു താളിനെ വിവക്ഷിത താൾ ആയി പരിഗണിക്കണമെങ്കിൽ അതു [[MediaWiki:Disambiguationspage]] എന്ന താളിൽ നിന്നു കണ്ണി ചേർക്കപ്പെട്ട ഒരു ഫലകം ഉപയോഗിക്കണം.',
+'disambiguations-text' => "താഴെക്കൊടുത്തിരിക്കുന്ന താളുകളിൽ '''വിവക്ഷിതങ്ങൾ താളിലേയ്ക്ക്''' കുറഞ്ഞത് ഒരു കണ്ണിയുണ്ട്. അവ അനുയോജ്യമായ താളിലേയ്ക്ക് കണ്ണിചേർക്കപ്പെടേണ്ടതാവാം. <br />
+[[MediaWiki:Disambiguationspage]] എന്ന താളിൽ കണ്ണി ചേർത്തിട്ടുള്ള ഫലകം ഉപയോഗിക്കുന്ന താളുകളെ വിവക്ഷിതങ്ങൾ താളായി കണക്കാക്കുന്നു.",
'doubleredirects' => 'ഇരട്ട തിരിച്ചുവിടലുകൾ',
'doubleredirectstext' => 'ഈ താളിൽ ഒരു തിരിച്ചുവിടലിൽ നിന്നും മറ്റു തിരിച്ചുവിടൽ താളുകളിലേയ്ക്ക് പോകുന്ന താളുകൾ കൊടുത്തിരിക്കുന്നു. ഓരോ വരിയിലും ഒന്നാമത്തേയും രണ്ടാമത്തേയും തിരിച്ചുവിടൽ താളിലേക്കുള്ള കണ്ണികളും, രണ്ടാമത്തെ തിരിച്ചുവിടൽ താളിൽ നിന്നു ശരിയായ ലക്ഷ്യതാളിലേക്കുള്ള കണ്ണികളും ഉൾക്കൊള്ളുന്നു.
'rollback' => 'തിരുത്തലുകൾ റോൾബാക്ക് ചെയ്യുക',
'rollback_short' => 'റോൾബാക്ക്',
'rollbacklink' => 'റോൾബാക്ക്',
+'rollbacklinkcount' => '{{PLURAL:$1|ഒരു തിരുത്തൽ|$1 തിരുത്തലുകൾ}} മുൻപ്രാപനം ചെയ്യുക',
+'rollbacklinkcount-morethan' => '{{PLURAL:$1|ഒന്നിലധികം തിരുത്തൽ|$1 എണ്ണത്തിലധികം തിരുത്തലുകൾ}} മുൻപ്രാപനം ചെയ്യുക',
'rollbackfailed' => 'റോൾബാക്ക് പരാജയപ്പെട്ടു',
'cantrollback' => 'തിരുത്തൽ തിരസ്കരിക്കുവാൻ സാധിക്കുകയില്ല. ഒരു ഉപയോക്താവ് മാത്രമാണ് ഈ താളിൽ സംഭാവന ചെയ്തിരിക്കുന്നത്.',
'alreadyrolled' => '[[:$1]] എന്ന താളിൽ [[User:$2|$2]] ([[User talk:$2|സംവാദം]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) നടത്തിയ തിരുത്തലുകൾ മുൻപ്രാപനം ചെയ്യാൻ സാധിക്കുന്നതല്ല. മറ്റാരോ താൾ തിരുത്തുകയോ മുൻപ്രാപനം ചെയ്യുകയോ ചെയ്തിരിക്കുന്നു.
* <span class="mw-specialpagecached">പ്രാദേശികമായി സംഭരിച്ചുപയോഗിക്കുന്ന പ്രത്യേക താളുകൾ.</span>',
'specialpages-group-maintenance' => 'പരിചരണം ആവശ്യമായവ',
'specialpages-group-other' => 'മറ്റു പ്രത്യേക താളുകൾ',
-'specialpages-group-login' => 'à´ªàµ\8dà´°à´µàµ\87ശിà´\95àµ\8dà´\95àµ\81à´\95 / à´\85à´\82à´\97à´¤àµ\8dà´µà´\82 à´\8eടുക്കുക',
+'specialpages-group-login' => 'à´ªàµ\8dà´°à´µàµ\87ശിà´\95àµ\8dà´\95àµ\81à´\95 / à´\85à´\82à´\97à´¤àµ\8dവമàµ\86ടുക്കുക',
'specialpages-group-changes' => 'പുതിയ മാറ്റങ്ങളും രേഖകളും',
'specialpages-group-media' => 'മീഡിയ രേഖകളും അപ്ലോഡുകളും',
'specialpages-group-users' => 'ഉപയോക്താക്കളും അവകാശങ്ങളും',
'duration-centuries' => '{{PLURAL:$1|ഒരു നൂറ്റാണ്ട്|$1 നൂറ്റാണ്ട്}}',
'duration-millennia' => '{{PLURAL:$1|ഒരു സഹസ്രാബ്ദം|$1 സഹസ്രാബ്ദം}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|അനുവദനീയമല്ലാത്ത തരം പ്രമാണമാണ്|അനുവദനീയമല്ലാത്ത തരങ്ങളിലുള്ള പ്രമാണങ്ങളാണ്}}. $2 {{PLURAL:$3|തരത്തിലുള്ള പ്രമാണം|തരങ്ങളിലുള്ള പ്രമാണങ്ങൾ}} ആണ് അഭിലഷണീയം.',
);
'tog-hidepatrolled' => 'Сүүлийн өөрчлөлтүүдэд манагдсан засваруудыг нуух',
'tog-newpageshidepatrolled' => 'Шинэ хуудсуудын жагсаалтаас манагдаж буй хуудсуудыг нуух',
'tog-extendwatchlist' => 'Хянах жагсаалтаа сүүлийн л засваруудыг бус бүх засварыг харуулахаар томруулах',
-'tog-usenewrc' => 'Сүүлийн өөрчлөлтүүдийн сайжруулсан хэлбэрийг ашиглах (ЖаваСкрипт хэрэглэгдэнэ)',
+'tog-usenewrc' => 'Сүүлийн үед хуудсанд хийсэн Грүп өөрчлөлтүүд (ЖаваСкрипт хэрэглэгдэнэ)',
'tog-numberheadings' => 'Гарчигуудыг автоматаар дэс дугаарлах',
'tog-showtoolbar' => 'Засварлах түүлбарыг үзүүлэх (ЖаваСкрипт)',
'tog-editondblclick' => 'Хоёр удаа дараад хуудсыг засварлах (ЖаваСкрипт)',
'lockmanager-fail-releaselock' => '"$1" साठी लॉक उघडू शकत नाही',
'lockmanager-fail-db-bucket' => '$1 बास्केट मधील कुलूप बंद डेटाबेसशी पुरेसा संपर्क होवू शकत नाही',
'lockmanager-fail-db-release' => '"$1" डाटाबेस वरील लॉक उघडू शकत नाही',
+'lockmanager-fail-svr-acquire' => 'सर्व्हर "$1" वरील कुलूप उघडू शकत नाही',
'lockmanager-fail-svr-release' => 'सर्व्हर "$1" वरील् लॉक उघडू शकत नाही',
# ZipDirectoryReader
'duration-centuries' => '$1 {{PLURAL:$1|शतकापूर्वी|शतकांपूर्वी }}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'सर्व्हर "$1" वरील कुलूप उघडू शकत नाही',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ही परवानगी नसलेल्या प्रकारची संचिका आहे.|ह्या परवानगी नसलेल्या प्रकारच्या संचिका आहेत.}} $2 {{PLURAL:$3|ही परवानगी असलेल्या प्रकारची संचिका आहे|ह्या परवानगी असलेल्या प्रकारच्या संचिका आहेत}}.',
);
'tog-editsectiononrightclick' => 'Bolehkan penyuntingan bahagian dengan mengklik kanan pada tajuk bahagian (JavaScript)',
'tog-showtoc' => 'Tunjukkan isi kandungan (bagi rencana yang melebihi 3 tajuk)',
'tog-rememberpassword' => 'Ingat log masuk saya di pelayar ini (tidak melebihi $1 {{PLURAL:$1|hari|hari}})',
-'tog-watchcreations' => 'Tambahkan laman yang saya cipta ke dalam senarai pantau',
-'tog-watchdefault' => 'Tambahkan laman yang saya sunting ke dalam senarai pantau',
-'tog-watchmoves' => 'Tambahkan laman yang saya pindahkan ke dalam senarai pantau',
-'tog-watchdeletion' => 'Tambahkan laman yang saya hapuskan ke dalam senarai pantau',
+'tog-watchcreations' => 'Tambahkan halaman-halaman yang saya buat dan fail-fail yang saya muat naik ke dalam senarai pantau',
+'tog-watchdefault' => 'Tambahkan halaman-halaman dan fail-fail yang saya sunting ke dalam senarai pantau',
+'tog-watchmoves' => 'Tambahkan halaman-halaman dan fail-fail yang saya pindahkan ke dalam senarai pantau',
+'tog-watchdeletion' => 'Tambahkan halaman-halaman dan fail-fail yang saya hapuskan ke dalam senarai pantau',
'tog-minordefault' => 'Tandakan semua suntingan sebagai kecil secara asali',
'tog-previewontop' => 'Tunjukkan pralihat di atas kotak sunting',
'tog-previewonfirst' => 'Tunjukkan pralihat pada suntingan pertama',
'tog-nocache' => 'Lumpuhkan pengagregatan laman',
-'tog-enotifwatchlistpages' => 'E-melkan saya apabila berlaku perubahan pada laman yang dipantau',
+'tog-enotifwatchlistpages' => 'E-mel kepada saya tentang perubahan pada halaman-halaman dan fail-fail dalam senarai pantau saya',
'tog-enotifusertalkpages' => 'E-melkan saya apabila berlaku perubahan pada laman perbincangan saya',
-'tog-enotifminoredits' => 'Juga e-melkan saya apabila berlaku penyuntingan kecil',
+'tog-enotifminoredits' => 'Juga e-mel kepada saya tentang suntingan kecil pada halaman-halaman dan fail-fail',
'tog-enotifrevealaddr' => 'Serlahkan alamat e-mel saya dalam e-mel pemberitahuan',
'tog-shownumberswatching' => 'Tunjukkan bilangan pemantau',
'tog-oldsig' => 'Tanda tangan yang sedia ada:',
'youhavenewmessages' => 'Anda mempunyai $1 ($2).',
'newmessageslink' => 'pesanan baru',
'newmessagesdifflink' => 'perubahan terakhir',
+'youhavenewmessagesfromusers' => 'Anda menerima $1 daripada {{PLURAL:$3|seorang|$3 orang}} pengguna lain ($2).',
+'youhavenewmessagesmanyusers' => 'Anda menerima $1 daripada ramai pengguna ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|pesanan|pesanan-pesanan}} baru',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|perubahan|perubahan-perubahan}} terkini',
'youhavenewmessagesmulti' => 'Anda telah menerima pesanan baru pada $1',
'editsection' => 'sunting',
'editsection-brackets' => '[$1]',
'remembermypassword' => 'Ingat log masuk saya pada pelayar ini (tidak melebihi $1 {{PLURAL:$1|hari|hari}})',
'securelogin-stick-https' => 'Kekal disambungkan ke HTTPS selepas log masuk',
'yourdomainname' => 'Domain anda:',
+'password-change-forbidden' => 'Anda tidak dapat mengubah kata laluan di wiki ini.',
'externaldberror' => 'Berlaku ralat pangkalan data bagi pengesahan luar atau anda tidak dibenarkan mengemaskinikan akaun luar anda.',
'login' => 'Log masuk',
'nav-login-createaccount' => 'Log masuk / buka akaun',
'noarticletext-nopermission' => 'Tiada teks dalam laman ini ketika ini.
Anda boleh [[Special:Search/{{PAGENAME}}|mencari tajuk laman ini]] dalam laman lain,
atau <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log yang berkaitan]</span>.',
+'missing-revision' => 'Semakan #$1 pada halaman "{{PAGENAME}}" tidak wujud.
+
+Hal ini biasanya disebabkan oleh pautan sejarah yang lapuk ke halaman yang sudah dihapuskan.
+Butirannya boleh didapati di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].',
'userpage-userdoesnotexist' => 'Akaun pengguna "<nowiki>$1</nowiki>" tidak berdaftar. Sila pastikan sama ada anda mahu mencipta/menyunting laman ini.',
'userpage-userdoesnotexist-view' => 'Akaun pengguna "$1" tidak berdaftar.',
'blocked-notice-logextract' => 'Pengguna ini sedang disekat.
'expansion-depth-exceeded-warning' => 'Laman terlebih dalam peluasan',
'parser-unstrip-loop-warning' => 'Gelung unstrip dikesan',
'parser-unstrip-recursion-limit' => 'Had rekursi unstrip dilampaui ($1)',
+'converter-manual-rule-error' => 'Ralat dikesan dalam aturan penukaran bahasa manual',
# "Undo" feature
'undo-success' => 'Suntingan ini boleh dibatalkan. Sila semak perbandingan di bawah untuk mengesahkan bahawa anda betul-betul mahu melakukan tindakan ini, kemudian simpan perubahan tersebut.',
'editundo' => 'batal',
'diff-multi' => '($1 {{PLURAL:$1|semakan pertengahan|semakan pertengahan}} oleh $2 {{PLURAL:$2|pengguna|pengguna}} tidak dipaparkan)',
'diff-multi-manyusers' => '($1 {{PLURAL:$1|semakan pertengahan|semakan pertengahan}} oleh lebih daripada $2 {{PLURAL:$2|pengguna|pengguna}} tidak dipaparkan)',
+'difference-missing-revision' => '{{PLURAL:$2|Satu semakan|$2 semakan}} bagi perbezaan ini ($1) tidak ditemui.
+
+Hal ini biasanya disebabkan oleh pautan perbezaan yang lapuk ke halaman yang sudah dihapuskan.
+Butirannya boleh didapati di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].',
# Search results
'searchresults' => 'Hasil carian',
'right-writeapi' => 'Menggunakan API tulis',
'right-delete' => 'Menghapuskan laman',
'right-bigdelete' => 'Menghapuskan laman bersejarah',
+'right-deletelogentry' => 'Memadamkan dan memulihkan entri log tertentu',
'right-deleterevision' => 'Menghapuskan dan memulihkan semula mana-mana semakan bagi sesebuah laman',
'right-deletedhistory' => 'Melihat senarai entri sejarah yang telah dihapuskan, tetapi tanpa teks yang berkaitan',
'right-deletedtext' => 'Melihat teks yang telah dihapuskan dan perubahan antara semakan-semakan yang telah dihapuskan',
'lockmanager-fail-releaselock' => 'Kunci untuk "$1" tidak dapat dikeluarkan.',
'lockmanager-fail-db-bucket' => 'Di baldi $1 tidak dapat dihubungi pangkalan data selak yang secukupnya.',
'lockmanager-fail-db-release' => 'Selak-selak tidak dapat dikeluarkan di pangkalan data $1.',
+'lockmanager-fail-svr-acquire' => 'Selak-selak tidak dapat diperoleh di pelayan $1.',
'lockmanager-fail-svr-release' => 'Selak-selak tidak dapat dikeluarkan di pelayan $1.',
# ZipDirectoryReader
'disambiguations' => 'Laman-laman yang berpaut dengan laman penyahkekaburan',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "Laman-laman berikut mengandungi pautan ke '''laman penyahtaksaan'''. Pautan ini sepatutnya ditujukan kepada topik yang sepatutnya.<br />Sesebuah laman dianggap sebagai laman penyahtaksaan jika ia menggunakan templat yang dipaut dari [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Laman-laman berikut mengandungi sekurang-kurangnya satu pautan ke '''laman penyahkekaburan'''.
+Pautan ini sepatutnya ditujukan ke topik yang sepatutnya.<br />
+Sesebuah laman dianggap sebagai laman penyahkekaburan jika ia menggunakan templat yang dipaut dari [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Lencongan berganda',
'doubleredirectstext' => 'Yang berikut ialah senarai laman yang melencong ke laman lencongan lain. Setiap baris mengandungi pautan ke laman lencongan pertama dan kedua, serta baris pertama bagi teks lencongan kedua, lazimnya merupakan laman sasaran "sebenar", yang sepatutnya ditujui oleh lencongan pertama.
'rollback' => 'Undurkan suntingan.',
'rollback_short' => 'Undur',
'rollbacklink' => 'undur',
+'rollbacklinkcount' => 'mengundurkan $1 {{PLURAL:$1|suntingan}}',
+'rollbacklinkcount-morethan' => 'mengundurkan lebih daripada $1 {{PLURAL:$1|suntingan}}',
'rollbackfailed' => 'Pengunduran gagal',
'cantrollback' => 'Suntingan tersebut tidak dapat dibalikkan: penyumbang terakhir adalah satu-satunya pengarang bagi rencana ini.',
'alreadyrolled' => 'Suntingan terakhir bagi [[:$1]] oleh [[User:$2|$2]] ([[User talk:$2|Perbualan]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) tidak dapat dibalikkan; terdapat pengguna lain yang telah menyunting atau membalikkan laman itu.
* <span class="mw-specialpagecached">Laman khas tercache (mungkin lapuk).</span>',
'specialpages-group-maintenance' => 'Laporan penyenggaraan',
'specialpages-group-other' => 'Laman khas lain',
-'specialpages-group-login' => 'Log masuk / daftar',
+'specialpages-group-login' => 'Log masuk / buka akaun',
'specialpages-group-changes' => 'Perubahan terkini dan log',
'specialpages-group-media' => 'Laporan media dan muat naik',
'specialpages-group-users' => 'Pengguna dan hak',
'duration-millennia' => '$1 alaf',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Selak-selak tidak dapat diperoleh di pelayan $1.',
+'api-error-filetype-banned-type' => '$1 merupakan {{PLURAL:$4|jenis|jenis-jenis}} fail yang dilarang. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang dibenarkan ialah $2.',
);
'tog-hidepatrolled' => 'Aħbi l-modifiki verifikati fit-tibdil riċenti',
'tog-newpageshidepatrolled' => 'Aħbi l-paġni verifikati mil-lista tal-paġni l-ġodda',
'tog-extendwatchlist' => "Espandi l-lista ta' osservazzjoni biex turi t-tibdil kollu, u mhux biss dak riċenti",
-'tog-usenewrc' => 'Uża t-tibdil riċenti avvanzat (bżonn tal-JavaScript)',
+'tog-usenewrc' => "Iġbor il-modifiki skont il-paġna fit-tibdil riċenti u fil-lista ta' osservazzjonijiet (bżonn tal-Javascript)",
'tog-numberheadings' => 'Numerazzjoni awtomatika tat-titli tas-sezzjonijiet',
'tog-showtoolbar' => 'Uri l-kolonna tal-għodda għall-immodifikar (bżonn tal-JavaScript)',
'tog-editondblclick' => "Immodifika l-paġni permezz ta' klikk doppju (bżonn tal-JavaScript)",
'tog-editsectiononrightclick' => "L-immodifikar ta' sezzjonijiet bi klikk lemini fuq it-titli tas-sezzjonijiet (bżonn tal-JavaScript)",
'tog-showtoc' => "Uri l-werrej (għal paġni b'iktar minn 3 sezzjonijiet)",
'tog-rememberpassword' => "Ftakar il-login tiegħi fuq dan il-browżer (għal massimu ta' {{PLURAL:$1|ġurnata|$1 ġurnata}})",
-'tog-watchcreations' => "Żid il-paġni li noħloq fil-lista ta' osservazzjoni tiegħi",
-'tog-watchdefault' => "Żid il-paġni li nimmodifika fil-lista ta' osservazzjoni personali",
+'tog-watchcreations' => "Żid il-paġni li noħloq u l-fajls li ntella' fil-lista ta' osservazzjoni tiegħi",
+'tog-watchdefault' => "Żid il-paġni u l-fajls li nimmodifika fil-lista ta' osservazzjoni personali",
'tog-watchmoves' => "Żid il-paġni li mmexxi fil-lista ta' osservazzjoni tiegħi",
'tog-watchdeletion' => "Żid il-paġni li nħassar mal-lista ta' osservazzjoni tiegħi",
'tog-minordefault' => 'Immarka awtomatikament kull modifika bħala waħda minuri',
'tog-nocache' => 'Iddiżattiva l-cache tal-paġni tal-browser',
'tog-enotifwatchlistpages' => "Ibgħatli ittra-e kull meta sseħħ modifika fuq paġna li tinsab fil-lista ta' osservazzjoni tiegħi",
'tog-enotifusertalkpages' => "Ibgħatli ittra-e kull meta l-paġna ta' diskussjoni tiegħi tiġi modifikata",
-'tog-enotifminoredits' => 'Ibgħatli wkoll ittra-e għall-modifiki minuri fuq paġni',
+'tog-enotifminoredits' => 'Ibgħatli wkoll ittra-e għall-modifiki minuri fuq paġni u fajls',
'tog-enotifrevealaddr' => "Ikxef l-indirizz tal-posta elettronika tiegħi fil-messaġġi ta' avviż",
'tog-shownumberswatching' => "Uri n-numru ta' utenti li qegħdin isegwu din il-paġna",
'tog-oldsig' => 'Firma attwali:',
Din il-modifika ma ġietx aċċettata sabiex ma jkunx hemm żballji fit-test tal-paġna. Dan xi kultant jiġri minħabba li qiegħed tuża servizz difettuż anonimu li huwa bbażat fuq il-web ta' prokura.'''",
'edit_form_incomplete' => "'''Ċerti parti tal-formola tal-modifika ma laħqux is-server; iċċekkja jekk il-modifiki tiegħek humiex intatti u erġa' pprova.'''",
'editing' => "Modifika ta' $1",
+'creating' => 'Qiegħed toħloq $1',
'editingsection' => "Modifika ta' $1 (sezzjoni)",
'editingcomment' => 'Qed jiġi editjat $1 (sezzjoni ġdida)',
'editconflict' => "Kunflitt t'editjar: $1",
# Diffs
'history-title' => 'Kronoloġija tal-modifiki ta\' "$1"',
'difference-title' => 'Differenza bejn ir-reviżjonijiet ta\' "$1"',
+'difference-title-multipage' => 'Differenza bejn il-paġni "$1" u "$2"',
'difference-multipage' => '(Differenzi bejn il-paġni)',
'lineno' => 'Linja $1:',
'compareselectedversions' => 'Qabbel il-verżjonijiet magħżula',
'prefs-beta' => 'Karatteristiċi tal-Beta',
'prefs-datetime' => 'Data u ħin',
'prefs-labs' => 'Karatteristiċi tal-laboratorji',
+'prefs-user-pages' => 'Paġni tal-utent',
'prefs-personal' => 'Profil tal-utent',
'prefs-rc' => 'Modifiki riċenti',
'prefs-watchlist' => 'Osservazzjoni speċjali',
'right-writeapi' => 'Uża API sabiex tagħmel modifiki fil-wiki',
'right-delete' => 'Ħassar paġni',
'right-bigdelete' => "Ħassar paġni b'kronoloġija kbira",
+'right-deletelogentry' => "Ħassar u reġġa' lura daħliet speċifi tar-reġistru",
'right-deleterevision' => 'Ħassar reviżjonijiet speċifiki tal-paġni',
'right-deletedhistory' => 'Uri r-reviżjonijiet tal-kronoloġija li huma mħassra mingħajr it-test assoċjat.',
'right-deletedtext' => 'Jara test imħassar u modifiki bejn reviżjonijiet imħassra',
'number_of_watching_users_pageview' => '[osservat minn {{PLURAL:$1|$1 utent|$1 utent}}]',
'rc_categories' => 'Illimita għall-kategoriji (issepara b\' "|")',
'rc_categories_any' => 'Kwalunkwe',
+'rc-change-size-new' => '$1 {{PLURAL:$1|byte|bytes}} wara l-modifika',
'newsectionsummary' => '/* $1 */ sezzjoni ġdida',
'rc-enhanced-expand' => 'Uri d-dettalji (hemm bżonn tal-JavaScript)',
'rc-enhanced-hide' => 'Aħbi d-dettalji',
'upload-unknown-size' => 'Dimensjoni mhux magħrufa',
'upload-http-error' => 'Qam żball HTTP: $1',
+# File backend
+'backend-fail-delete' => 'Il-fajl "$1" ma setax jiġi mħassar.',
+'backend-fail-alreadyexists' => 'Il-fajl "$1" diġà jeżisti.',
+
# ZipDirectoryReader
'zip-file-open-error' => 'Qam żball waqt il-ftuħ tal-fajl għall-kontrolli ZIP.',
'zip-wrong-format' => 'Il-fajl speċifikat ma kienx fajl ZIP.',
** Fajl duplikat",
'filedelete-edit-reasonlist' => 'Immodifika r-raġunijiet għat-tħassir',
'filedelete-maintenance' => "It-tħassir u r-restawr ta' fajls huwa diżattivat għall-mument minħabba xi manutenzjoni.",
+'filedelete-maintenance-title' => 'Il-fajl ma jistax jiġi mħassar',
# MIME search
'mimesearch' => 'Fittex fil-bażi għal tip MIME',
'statistics-users-active-desc' => 'Utenti li wettqu azzjoni fl-aħħar {{PLURAL:$1|ġurnata|$1 ġurnata}}',
'statistics-mostpopular' => 'Il-paġni l-aktar miżjura',
-'disambiguations' => "Paġni ta' diżambigwazzjoni",
+'disambiguations' => "Paġni li jorbtu lejn paġni ta' diżambigwazzjoni",
'disambiguationspage' => 'Template:diżambig',
'disambiguations-text' => "Il-Paġni li jinsabu f'din lista huma parti minn '''paġna ta' diżambigwazzjoni''' b'hekk għandhom jiġu relatati mas-suġġett preċiż minflok. <br />
Paġna tiġi stimata paġna ta' diżambigwazzjoni dawk kollha li jagħmlu użu mit-template elenkat f'[[MediaWiki:Disambiguationspage]]",
# Special:Log
'specialloguserlabel' => 'Azzjoni effettwata minn:',
-'speciallogtitlelabel' => 'Titlu:',
+'speciallogtitlelabel' => 'Azzjoni effetwata fuq:',
'log' => 'Reġistri',
'all-logs-page' => 'Ir-reġistri pubbliċi kollha',
'alllogstext' => "Preżentazzjoni unifikata tar-reġistri kollha ta' {{SITENAME}}. Tista' tqassar il-kriterji ta' tfittxija billi tagħżel it-tip ta' reġistru, l-isem tal-utent, jew il-paġna affetwata (it-tnejn tal-aħħar huma sensittivi għal kif jinkitbu l-karattri).",
'logempty' => "Ir-reġistru m'għandu l-ebda element li jaqbel mat-tfittxija tiegħek.",
'log-title-wildcard' => "Tfittxija ta' titli li jibdew b'dan it-test",
+'showhideselectedlogentries' => 'Uri/aħbi daħliet magħżula tar-reġistru',
# Special:AllPages
'allpages' => 'Il-paġni kollha',
'allpagesprefix' => 'Uri l-paġni bil-prefiss:',
'allpagesbadtitle' => "It-titlu indikat għal dil-paġna mhuwiex validu jew inkella fih xi prefiss interlingwa jew interwiki. Għaldaqstant, jista' ikun fih xi karratru(i) li ma jistgħux jintużaw fit-titli.",
'allpages-bad-ns' => 'In-namespace "$1" ma jeżistix fuq {{SITENAME}}.',
+'allpages-hide-redirects' => 'Aħbi rindirizzi',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => "Qiegħed tara verżjoni ta' din il-paġna memorizzata fil-''cache'', li tista' tkun antika sa massimu ta' $1.",
+'cachedspecial-refresh-now' => 'Uri l-iktar riċenti.',
# Special:Categories
'categories' => 'Kategoriji',
'sp-deletedcontributions-contribs' => 'kontribuzzjonijiet',
# Special:LinkSearch
-'linksearch' => 'Ħoloq esterni',
+'linksearch' => 'Fittex ħoloq esterni',
'linksearch-pat' => "Mudell ta' tfittxija:",
'linksearch-ns' => 'Spazju tal-isem:',
'linksearch-ok' => 'Fittex',
'protect-othertime' => 'Ħin ieħor:',
'protect-othertime-op' => 'ħin ieħor',
'protect-existing-expiry' => 'Skadenza attwali: $2, $3',
-'protect-otherreason' => 'Raġunijiet oħra/addizjonali:',
+'protect-otherreason' => 'Raġunijiet oħra/addizzjonali:',
'protect-otherreason-op' => 'Raġuni oħra',
'protect-dropdown' => '*Raġunijiet komuni għall-protezzjoni
** Vandaliżmu eċċessiv
'ipbcreateaccount' => "Impedixxi ħolqien ta' kontijiet oħrajn",
'ipbemailban' => 'Impedixxi utenti milli jkunu jistgħu jibgħatu posta elettronika',
'ipbenableautoblock' => 'Awtomatikament blokka l-aħħar indirizz tal-IP użat minn dan l-utent, u IP suċċessivi li jipprovaw jagħmlu modifiki',
-'ipbsubmit' => 'Imblokk lil dan l-utent',
+'ipbsubmit' => 'Imblokka lil dan l-utent',
'ipbother' => 'Ħin ieħor:',
'ipboptions' => 'sagħtejn:2 hours,ġurnata 1:1 day,3 ġranet:3 days,ġimgħa 1:1 week,ġimgħatejn:2 weeks,xahar 1:1 month,3 xhur:3 months,6 xhur:6 months,sena 1:1 year,infinita:infinite',
'ipbotheroption' => 'ieħor',
'movetalk' => "Mexxi wkoll il-paġna ta' diskussjoni",
'move-subpages' => 'Mexxi s-sottopaġni (sa $1)',
'move-talk-subpages' => "Mexxi is-sottopaġni kollha tal-paġna ta' diskussjoni (sa $1)",
-'movepage-page-exists' => 'Il-Paġna $1 ġa teżisti u ma tistax tiġi awtomatikament miktub fuqha.',
+'movepage-page-exists' => 'Il-paġna $1 diġà teżisti u ma tistax tiġi miktuba fuqha awtomatikament.',
'movepage-page-moved' => 'Il-Paġna $1 ġiet imċaqilqa għal $2.',
'movepage-page-unmoved' => 'Il-Paġna $1 ma setgħatx tiġi mċaqilqa għal $2.',
'movepage-max-pages' => "Ġie mċaqlaq in-numru massimu ta' {{PLURAL:$1|paġna u ma jistax jiġi mċaqlaq aktar awtomatikament|$1 paġni u ma jistgħux jiġu mċaqilqa aktar awtomatikament.}}",
'import-invalid-interwiki' => 'Ma jistax jiġi importat mill-wiki indikata.',
'import-error-edit' => 'Il-paġna "$1" ma ġietx impurtata minħabba li mintix awtorizzat li timmodifikaha.',
'import-error-create' => 'Il-paġna "$1" ma ġietx impurtata minħabba li mintix awtorizzat li toħloqha.',
+'import-error-invalid' => 'Il-paġna "$1" ma ġietx impurtata minħabba li isimha hi invalida.',
# Import log
'importlogpage' => 'Importazzjoni',
'import-logentry-interwiki' => 'Trasferixxejt minn wiki ieħor il-paġna $1',
'import-logentry-interwiki-detail' => '{{PLURAL:$1|reviżjoni|$1 reviżjonijiet}} minn $2',
+# JavaScriptTest
+'javascripttest' => 'Testjar tal-JavaScript',
+'javascripttest-disabled' => 'Din il-funzjoni ma ġietx abilitata fuq din il-wiki.',
+'javascripttest-pagetext-noframework' => 'Din il-paġna hi riservata għall-eżekuzzjoni tat-testijiet tal-JavaScript.',
+'javascripttest-pagetext-frameworks' => 'Jekk jogħġbok agħżel wieħed mill-oqsma tal-ittestjar: $1',
+'javascripttest-pagetext-skins' => 'Agħżel aspett grafiku fuq liema tesegwixxi testijiet:',
+'javascripttest-qunit-intro' => 'Ara d-[$1 dokumentazzjoni dwar it-test] fuq mediawiki.org.',
+
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Il-paġna tal-utent tiegħek',
'tooltip-pt-anonuserpage' => "Il-Paġna tal-utent ta' dan l-indirizz tal-IP",
'tooltip-diff' => 'Uri liem modifiki għamilt fit-test.',
'tooltip-compareselectedversions' => "Ara d-differenzi bejn iż-żewġ verżjonijiet magħżula ta' din il-paġna.",
'tooltip-watch' => "Żid din il-paġna mal-lista ta' osservazzjoni tiegħek",
+'tooltip-watchlistedit-normal-submit' => 'Neħħi t-titli',
+'tooltip-watchlistedit-raw-submit' => "Aġġorna l-lista ta' osservazzjoni",
'tooltip-recreate' => "Erġa' oħloq din il-paġna minkejja li kienet ġiet imħassra",
'tooltip-upload' => "Ibda tella'",
'tooltip-rollback' => '"Rollback" tannulla l-modifiki li saru mill-aħħar kontributur fuq din il-paġna, permezz ta\' sempliċi klikk',
'spambot_username' => 'Tindif tal-MedjaWiki mill-ispam',
'spam_reverting' => "Erġa' lura għall-aħħar verżjoni li m'għandiex link għal $1",
'spam_blanking' => 'Paġna svojtjata, kull verżjoni kellu link għal $1',
+'spam_deleting' => 'Paġna mħassra, ir-reviżjonijiet kollha kellhom ħoloq lejn $1',
# Info page
'pageinfo-title' => 'Informazzjoni għal "$1"',
# Flash modes
'exif-flash-fired-0' => "Il-''flash'' ma ħariġx",
'exif-flash-fired-1' => "Il-''flash'' ħareġ",
+'exif-flash-function-1' => "''Flash'' diżattivata",
+'exif-flash-redeye-1' => 'modalità riduzzjoni tal-għajnejn ħomor',
'exif-focalplaneresolutionunit-2' => 'pulzier',
'iranian-calendar-m9' => 'Azar',
'iranian-calendar-m10' => 'Dey',
+# Signatures
+'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskussjoni]])',
+
# Core parser functions
'unknown_extension_tag' => 'Estensjoni tat-tag mhux magħrufa "$1"',
'duplicate-defaultsort' => '\'\'\'Twissija:\'\'\' iċ-ċavetta tal-issortjar oriġinali "$2" tissostitwixxi dik preċedenti "$1".',
'version-software' => 'Softwer installat',
'version-software-product' => 'Prodott',
'version-software-version' => 'Verżjoni',
+'version-entrypoints' => "URL ta' aċċess",
+'version-entrypoints-header-entrypoint' => "Punt ta' dħul",
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'Post tal-fajl',
# New logging system
'logentry-delete-delete' => '$1 ħassar il-paġna $3',
+'logentry-delete-restore' => "$1 reġġa' lura l-paġna $3",
+'logentry-delete-event' => "$1 biddel il-viżibilità ta' {{PLURAL:$5|azzjoni tar-reġistru|$5 azzjonijiet tar-reġistru}} ta' $3: $4",
'revdelete-restricted' => "limiti applikati 'l amministraturi",
'revdelete-unrestricted' => "neħħi l-limiti 'l amministraturi",
'logentry-move-move' => '$1 mexxa l-paġna $3 għal $4',
'logentry-move-move-noredirect' => '$1 mexxa l-paġna $3 għal $4 mingħajr ma ħalla rindirizz',
'logentry-move-move_redir' => '$1 mexxa l-paġna $3 għal $4 fuq rindrizz',
'logentry-move-move_redir-noredirect' => '$1 mexxa l-paġna $3 għal $4 fuq rindirizz mingħajr ma ħalla rindirizz',
+'logentry-newusers-newusers' => '$1 ħoloq kont tal-utent',
+'logentry-newusers-create' => '$1 ħoloq kont tal-utent',
+'logentry-newusers-create2' => '$1 ħoloq kont tal-utent $3',
+'logentry-newusers-autocreate' => 'Il-kont $1 ġie maħluq awtomatikament',
'newuserlog-byemail' => "il-password intbagħtet permezz ta' posta elettronika",
# Feedback
'feedback-bugcheck' => 'Tajjeb ħafna! Ivverifika li mhux diġà fost id-[$1 difetti magħrufa].',
'feedback-bugnew' => 'Ikkontrollajt. Irrapporta d-difett',
+# API errors
+'api-error-fileexists-forbidden' => 'Fajl bl-isem "$1" diġà jeżisti, u ma jistax jiġi miktub fuqu.',
+'api-error-fileexists-shared-forbidden' => 'Fajl bl-isem "$1" diġà jeżisti fir-repożitorju maqsum u ma jistax jiġi miktub fuqu.',
+'api-error-file-too-large' => 'Il-fajl magħżul huwa wisq kbir.',
+'api-error-filename-tooshort' => 'L-isem tal-fajl huwa qasir wisq.',
+'api-error-filetype-banned' => "It-tip ta' fajl mhuwiex aċċettat.",
+'api-error-filetype-missing' => 'L-isem tal-fajl jonqsu l-estensjoni.',
+'api-error-illegal-filename' => 'L-isem tal-fajl mhuwiex permess.',
+'api-error-uploaddisabled' => "It-tlugħ ta' fajls mhuwiex attivat fuq din il-wiki.",
+'api-error-verification-error' => "Dan il-fajl jista' jkun imħassar, jew għandu l-estensjoni l-ħażina.",
+
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|sekonda|sekondi}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minuta|minuti}}',
+'duration-hours' => '$1 {{PLURAL:$1|siegħa|sigħat}}',
+'duration-days' => '$1 {{PLURAL:$1|jum|jiem}}',
+'duration-weeks' => '$1 {{PLURAL:$1|ġimgħa|ġimgħat}}',
+'duration-years' => '$1 {{PLURAL:$1|sena|snin}}',
+'duration-decades' => '$1 {{PLURAL:$1|deċennju|deċennji}}',
+'duration-centuries' => '$1 {{PLURAL:$1|seklu|sekli}}',
+'duration-millennia' => '$1 {{PLURAL:$1|millennju|millennji}}',
+
);
* @author Hanzaw
* @author Hintha
* @author Lagoonaing
+ * @author Liangent
* @author Lionslayer
* @author Minnyoonthit
* @author Myanmars
'loginprompt' => '{{SITENAME}} သို့ လော့အင်ဝင်ရန် ကွတ်ကီးသုံးနိုင်ရန် ပြုလုပ်ပေးထားရမည်။',
'userlogin' => 'Log in ဝင်ရန်/ အကောင့် လုပ်ရန်',
'userloginnocreate' => 'Log in ဝင်ရန်',
-'logout' => 'Log out ထွက်ရန်',
-'userlogout' => 'Log out ထွက်ရန်',
+'logout' => 'ထွက်ရန်',
+'userlogout' => 'ထွက်ရန်',
'notloggedin' => 'logged in ဝင်မထားပါ',
'nologin' => 'အကောင့်မရှိဘဲ ဖြစ်နေပါသလား။ $1။',
'nologinlink' => 'အကောင့်လုပ်ရန်',
'tog-watchmoves' => 'Legg til sider og filer jeg flytter til min overvåkingsliste',
'tog-watchdeletion' => 'Legg til sider og filer jeg sletter i min overvåkingsliste',
'tog-minordefault' => 'Merk i utgangspunktet alle redigeringer som mindre',
-'tog-previewontop' => 'Vis forhåndsvisningen ovenfor redigeringsboksen',
+'tog-previewontop' => 'Vis forhåndsvisningen over redigeringsboksen',
'tog-previewonfirst' => 'Vis forhåndsvisning når du begynner å redigere',
'tog-nocache' => 'Deaktiver nettlesermellomlagring av sider («caching»)',
'tog-enotifwatchlistpages' => 'Send meg en e-post når sider og filer på overvåkningslisten min blir endret',
Det siste loggelementet er oppgitt under som referanse:",
'semiprotectedpagewarning' => "'''Merk:''' Denne siden har blitt låst slik at kun registrerte brukere kan endre den.
Det siste loggelementet er oppgitt under som referanse:",
-'cascadeprotectedwarning' => "'''Advarsel:''' Denne siden har blitt låst slik at kun brukere med administratorrettigheter kan redigere den, fordi den inkluderes på følgende dypbeskyttede sider:<!--{{PLURAL:$1}}-->",
+'cascadeprotectedwarning' => "'''Advarsel:''' Denne siden har blitt låst slik at kun brukere med administratorrettigheter kan redigere den, fordi den inkluderes på følgende dypbeskyttede {{PLURAL:$1|sider}}:",
'titleprotectedwarning' => "'''Advarsel: Denne siden har blitt låst slik at [[Special:ListGroupRights|spesielle rettigheter]] kreves for å opprette den.'''
Det siste loggelementet er oppgitt under som referanse:",
'templatesused' => '{{PLURAL:$1|Mal|Maler}} som brukes på denne siden:',
'expansion-depth-exceeded-category' => 'Sider hvor hvor ekspansjonsdybden er overskredet',
'expansion-depth-exceeded-warning' => 'Sida har overskredet ekspansjonsdybden',
'parser-unstrip-loop-warning' => '«Unstrip»-loop påvist',
+'parser-unstrip-recursion-limit' => 'Rekursjonsgrense for taggfjerning overskredet ($1)',
# "Undo" feature
'undo-success' => 'Redigeringen kan omgjøres. Sjekk sammenligningen under for å bekrefte at du vil gjøre dette, og lagre endringene for å fullføre omgjøringen.',
'deletedhist' => 'Slettet historikk',
'revdelete-hide-current' => 'Feil under skjuling av objektet datert $2, $1: dette er den gjeldende revisjonen.
Den kan ikke skjules.',
-'revdelete-show-no-access' => 'Feil under visning av objekt datert $2, $1: dette objektet har blitt markert "begrenset".
+'revdelete-show-no-access' => 'Feil under visning av objekt datert $2, $1: dette objektet har blitt markert «begrenset».
Du har ikke tilgang til det.',
-'revdelete-modify-no-access' => 'Feil under endring av objekt datert $2, $1: dette objektet har blitt markert "begrenset".
+'revdelete-modify-no-access' => 'Feil under endring av objekt datert $2, $1: dette objektet har blitt markert «begrenset».
Du har ikke tilgang til det.',
'revdelete-modify-missing' => 'Feil under endring av objekt ID $1: det mangler i databasen!',
'revdelete-no-change' => "'''Advarsel:''' objektet datert $2 $1 hadde allerede synlighetsinnstillingene du forespurte.",
'lineno' => 'Linje $1:',
'compareselectedversions' => 'Sammenlign valgte revisjoner',
'showhideselectedversions' => 'Vis/skjul valgte versjoner',
-'editundo' => 'angre',
+'editundo' => 'fjern',
'diff-multi' => '({{PLURAL:$1|Én mellomrevisjon|$1 mellomrevisjoner}} av {{PLURAL:$2|én bruker|$2 brukere}} vises ikke)',
'diff-multi-manyusers' => '({{PLURAL:$1|Én mellomrevisjon|$1 mellomrevisjoner}} av mer enn $2 {{PLURAL:$2|bruker|brukere}} vises ikke)',
# Search results
-'searchresults' => 'Søkeresultat',
-'searchresults-title' => 'Søkeresultat for «$1»',
+'searchresults' => 'Søkeresultater',
+'searchresults-title' => 'Søkeresultater for «$1»',
'searchresulttext' => 'For mer informasjon om søking i {{SITENAME}}, se [[{{MediaWiki:Helppage}}|{{int:help}}]].',
'searchsubtitle' => "Du søkte etter '''[[:$1]]''' ([[Special:Prefixindex/$1|alle sider som begynner med «$1»]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|alle sider som lenker til «$1»]])",
'searchsubtitleinvalid' => "Du søkte etter '''$1'''",
'search-redirect' => '(omdirigering $1)',
'search-section' => '(avsnitt $1)',
'search-suggest' => 'Mente du: $1',
-'search-interwiki-caption' => 'Søsterprosjekt',
-'search-interwiki-default' => '$1-resultat:',
+'search-interwiki-caption' => 'Søsterprosjekter',
+'search-interwiki-default' => '$1-resultater:',
'search-interwiki-more' => '(mer)',
'search-mwsuggest-enabled' => 'med forslag',
'search-mwsuggest-disabled' => 'ingen forslag',
'showingresultsheader' => "{{PLURAL:$5|Resultat '''$1''' av '''$3'''|Resultat '''$1 - $2''' av '''$3'''}} for '''$4'''",
'nonefound' => "'''Merk''': Som standard søkes det kun i enkelte navnerom.
For å søke i alle, bruk prefikset ''all:'' (inkluderer diskusjonssider, maler, osv), eller bruk det ønskede navnerommet som prefiks.",
-'search-nonefound' => 'Det var ingen resultater som passet til søket.',
+'search-nonefound' => 'Ingen resultater passet til søket.',
'powersearch' => 'Avansert søk',
'powersearch-legend' => 'Avansert søk',
'powersearch-ns' => 'Søk i navnerom:',
'prefs-watchlist-days' => 'Dager som skal vises i overvåkningslisten:',
'prefs-watchlist-days-max' => 'Maksimalt $1 {{PLURAL:$1|dag|dager}}',
'prefs-watchlist-edits' => 'Antall redigeringer som skal vises i utvidet overvåkningsliste:',
-'prefs-watchlist-edits-max' => 'Maksimum antall: 1000',
+'prefs-watchlist-edits-max' => 'Maksimalt antall: 1000',
'prefs-watchlist-token' => 'Nøkkel for overvåkningsliste',
'prefs-misc' => 'Diverse',
'prefs-resetpass' => 'Endre passord',
'stub-threshold' => 'Grense for <span class="mw-stub-example">stubblenkeformatering</span>:',
'stub-threshold-disabled' => 'Deaktivert',
'recentchangesdays' => 'Antall dager som skal vises i siste endringer:',
-'recentchangesdays-max' => '(maksimum $1 {{PLURAL:$1|dag|dager}})',
+'recentchangesdays-max' => 'Maks $1 {{PLURAL:$1|dag|dager}}',
'recentchangescount' => 'Antall redigeringer som skal vises som standard:',
'prefs-help-recentchangescount' => 'Dette inkluderer nylige endringer, sidehistorikk og logger.',
'prefs-help-watchlist-token' => 'Om du fyller ut dette feltet med et hemmelig tall, vil det lages en RSS-liste for overvåkningslisten din.
'timezoneregion-asia' => 'Asia',
'timezoneregion-atlantic' => 'Atlanterhavet',
'timezoneregion-australia' => 'Australia',
-'timezoneregion-europe' => 'Europe',
-'timezoneregion-indian' => 'Det indiske hav',
+'timezoneregion-europe' => 'Europa',
+'timezoneregion-indian' => 'Indiahavet',
'timezoneregion-pacific' => 'Stillehavet',
'allowemail' => 'Tillat andre å sende meg e-post',
'prefs-searchoptions' => 'Søkealternativ',
'prefs-signature' => 'Signatur',
'prefs-dateformat' => 'Datoformat',
'prefs-timeoffset' => 'Tidsforskyvning',
-'prefs-advancedediting' => 'Avanserte alternativ',
+'prefs-advancedediting' => 'Avanserte alternativer',
'prefs-advancedrc' => 'Avanserte alternativ',
-'prefs-advancedrendering' => 'Avanserte alternativ',
-'prefs-advancedsearchoptions' => 'Avanserte alternativ',
-'prefs-advancedwatchlist' => 'Avanserte alternativ',
-'prefs-displayrc' => 'Visningsalternativ',
+'prefs-advancedrendering' => 'Avanserte alternativer',
+'prefs-advancedsearchoptions' => 'Avanserte alternativer',
+'prefs-advancedwatchlist' => 'Avanserte alternativer',
+'prefs-displayrc' => 'Visningsalternativer',
'prefs-displaysearchoptions' => 'Visningsalternativer',
'prefs-displaywatchlist' => 'Visningsalternativer',
'prefs-diffs' => 'Forskjeller',
'right-createpage' => 'Opprette sider (som ikke er diskusjonssider)',
'right-createtalk' => 'Opprette diskusjonssider',
'right-createaccount' => 'Opprette nye kontoer',
-'right-minoredit' => 'Marker endringer som mindre',
+'right-minoredit' => 'Markere endringer som mindre',
'right-move' => 'Flytte sider',
'right-move-subpages' => 'Flytte sider med undersider',
'right-move-rootuserpages' => 'Flytte hovedbrukersider',
'right-writeapi' => 'Redigere via API',
'right-delete' => 'Slette sider',
'right-bigdelete' => 'Slette sider med stor historikk',
+'right-deletelogentry' => 'Slett og gjenopprett spesifikke loggoppføringer',
'right-deleterevision' => 'Slette og gjenopprette enkeltrevisjoner av sider',
'right-deletedhistory' => 'Se slettet sidehistorikk uten tilhørende sidetekst',
-'right-deletedtext' => 'Vis slettet tekst og endringer mellom slettede versjoner',
+'right-deletedtext' => 'Vise slettet tekst og endringer mellom slettede versjoner',
'right-browsearchive' => 'Søke i slettede sider',
'right-undelete' => 'Gjenopprette sider',
'right-suppressrevision' => 'Se og gjenopprette skjulte siderevisjoner',
'right-hideuser' => 'Blokkere et brukernavn og skjule det fra det offentlige',
'right-ipblock-exempt' => 'Kan redigere fra blokkerte IP-adresser',
'right-proxyunbannable' => 'Kan redigere fra blokkerte proxyer',
-'right-unblockself' => 'Fjern blokkering av seg selv',
-'right-protect' => 'Endre beskyttelsesnivåer',
+'right-unblockself' => 'Fjerne blokkering av seg selv',
+'right-protect' => 'Endre beskyttelsesnivåer og redigere beskyttete sider',
'right-editprotected' => 'Redigere beskyttede sider',
'right-editinterface' => 'Redigere brukergrensesnittet',
'right-editusercssjs' => 'Redigere andre brukeres CSS- og JS-filer',
# User rights log
'rightslog' => 'Brukerrettighetslogg',
-'rightslogtext' => 'Dette er en logg over forandringer i brukerrettigheter.',
+'rightslogtext' => 'Dette er en logg over endringer av brukerrettigheter.',
'rightslogentry' => 'endret gruppe for $1 fra $2 til $3',
'rightslogentry-autopromote' => 'ble automatisk forfremmet fra $2 til $3',
'rightsnone' => '(ingen)',
'action-suppressionlog' => 'se denne private loggen',
'action-block' => 'blokkere denne brukeren fra å redigere',
'action-protect' => 'endre denne sidens beskyttelsesnivåer',
-'action-rollback' => 'tilbakestill raskt endringene til den siste brukeren som redigerte en bestemt side',
+'action-rollback' => 'raskt tilbakestille endringene til den siste brukeren som redigerte en bestemt side',
'action-import' => 'importere denne siden fra en annen wiki',
'action-importupload' => 'importere denne siden fra en opplastet fil',
'action-patrol' => 'merke andre brukeres redigeringer som patruljert',
'action-userrights' => 'redigere alle brukerrettigheter',
'action-userrights-interwiki' => 'endre brukerrettigheter for brukere på andre wikier',
'action-siteadmin' => 'låse eller låse opp databasen',
-'action-sendemail' => 'send e-post',
+'action-sendemail' => 'sende e-poster',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|endring|endringer}}',
'recentchanges' => 'Siste endringer',
-'recentchanges-legend' => 'Alternativ for siste endringer',
-'recentchanges-summary' => 'Vis de siste endringene til denne siden',
+'recentchanges-legend' => 'Alternativer for siste endringer',
+'recentchanges-summary' => 'På denne siden vises de siste endringene i wikien.',
'recentchanges-feed-description' => 'Følg med på siste endringer i denne wikien med denne matingen.',
'recentchanges-label-newpage' => 'Denne redigeringen opprettet en ny side',
'recentchanges-label-minor' => 'Dette er en mindre endring',
'recentchanges-label-bot' => 'Denne redigeringen ble gjort av en bot',
'recentchanges-label-unpatrolled' => 'Denne redigeringen har ikke blitt patruljert ennå',
'rcnote' => "Nedenfor vises {{PLURAL:$1|'''1''' endring|de siste '''$1''' endringene}} fra {{PLURAL:$2|det siste døgnet|de siste '''$2''' døgnene}}, per $5 $4.",
-'rcnotefrom' => "Nedenfor er endringene fra '''$2''' (opp til '''$1''' vises).",
+'rcnotefrom' => "Nedenfor er endringene siden '''$2''' (opp til '''$1''' vises).",
'rclistfrom' => 'Vis nye endringer med start fra $1',
'rcshowhideminor' => '$1 mindre endringer',
'rcshowhidebots' => '$1 roboter',
'number_of_watching_users_pageview' => '[$1 overvåkende {{PLURAL:$1|bruker|brukere}}]',
'rc_categories' => 'Begrens til kategorier (skilletegn: «|»)',
'rc_categories_any' => 'Alle',
-'rc-change-size-new' => '$1 {{PLURAL:$1|byte|bytes}} etter endring',
+'rc-change-size-new' => '$1 {{PLURAL:$1|byte}} etter endring',
'newsectionsummary' => '/* $1 */ ny seksjon',
'rc-enhanced-expand' => 'Vis detaljer (krever JavaScript)',
'rc-enhanced-hide' => 'Skjul detaljer',
'rollback' => 'Fjern redigeringer',
'rollback_short' => 'Tilbakestill',
'rollbacklink' => 'tilbakestill',
+'rollbacklinkcount' => 'tilbakestill {{PLURAL:$1|én endring|$1 endringer}}',
+'rollbacklinkcount-morethan' => 'tilbakestill mer enn $1 {{PLURAL:$1|endring|endringer}}',
'rollbackfailed' => 'Kunne ikke tilbakestille',
'cantrollback' => 'Kan ikke fjerne redigering; den siste brukeren er den eneste forfatteren.',
'alreadyrolled' => 'Kan ikke fjerne den siste redigeringen på [[$1]] av [[User:$2|$2]] ([[User talk:$2|diskusjon]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); en annen har allerede redigert siden eller fjernet redigeringen.
'spambot_username' => 'MediaWikis spamopprydning',
'spam_reverting' => 'Tilbakestiller til siste versjon uten lenke til $1',
'spam_blanking' => 'Alle revisjoner inneholdt lenke til $1, tømmer siden',
+'spam_deleting' => 'Sletter alle revisjoner med lenker til $1',
# Info page
'pageinfo-title' => 'Informasjon om «$1»',
'api-error-empty-file' => 'Filen du sendte inn var tom.',
'api-error-emptypage' => 'Det er ikke tillatt å opprette nye, tomme sider.',
'api-error-fetchfileerror' => 'Intern feil: Noe gikk galt ved henting av denne filen.',
+'api-error-fileexists-forbidden' => 'En fil med navnet «$1» finnes allerede, og kan ikke overskrives.',
+'api-error-fileexists-shared-forbidden' => 'En fil med navnet «$1» finnes allerede i det delte filsystemet, og kan ikke overskrives.',
'api-error-file-too-large' => 'Filen du la inn var for stor.',
'api-error-filename-tooshort' => 'Filnavnet er for kort.',
'api-error-filetype-banned' => 'Denne filtypen er ikke tillatt.',
'duration-centuries' => '$1 {{PLURAL:$1|århundre|århundrer}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennier}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '{{PLURAL:$4|Filtypen|Filtypene}} $1 er ikke {{PLURAL:$4|tillatt|tillatte}}. {{PLURAL:$3|Tillatt filtype|Tillatte filtyper}} er $2.',
);
* @ingroup Language
* @file
*
+ * @author Geitost
* @author Kaganer
* @author Purodha
* @author Slomox
*
* @author Erwin
* @author Erwin85
+ * @author Geitost
* @author Jens Frank
* @author Kaganer
* @author Purodha
'tog-justify' => "Alinea's uutvullen",
'tog-hideminor' => 'Kleine wiezigingen verbargen in leste wiezigingen',
'tog-hidepatrolled' => 'Wiezigingen die emarkeerd bin verbargen in leste wiezigingen',
-'tog-newpageshidepatrolled' => "Pagina's die emarkeerd bin, verbargen in de lieste mit nieje artikels",
+'tog-newpageshidepatrolled' => 'Ziejen die emarkeerd bin, verbargen in de lieste mit nieje artikels',
'tog-extendwatchlist' => 'Volglieste uutbreien, zodat alle wiezigingen zichtbaor bin, en niet allinnig de leste wieziging',
-'tog-usenewrc' => "Groepeer wiezigingen per pagina in de liesten leste wiezigingen en mien volglieste (hierveur he'j JavaScript neudig)",
+'tog-usenewrc' => 'Groepeer wiezigingen per zied in "Leste wiezigingen" en "Mien volglieste" (hierveur he\'j JavaScript neudig)',
'tog-numberheadings' => 'Koppen vanzelf nummeren',
'tog-showtoolbar' => 'Laot de warkbalke zien',
'tog-editondblclick' => 'Mit dubbelklik bewarken (JavaScript)',
'tog-editsectiononrightclick' => 'Bewarkgedeelte mit rechtermuusknoppe bewarken (JavaScript)',
'tog-showtoc' => 'Samenvatting laoten zien van de zaken die an bod koemen (mit meer as dree onderwarpen)',
'tog-rememberpassword' => 'Vanzelf anmelden (hooguut $1 {{PLURAL:$1|dag|dagen}})',
-'tog-watchcreations' => "Pagina's die'k anmake en bestaanden die'k opsture op mien volglieste zetten",
-'tog-watchdefault' => "Pagina's en bestaanden die'k wiezige op mien volglieste zetten",
-'tog-watchmoves' => "Pagina's en bestaanden die'k herneume op mien volglieste zetten",
-'tog-watchdeletion' => "Pagina's en bestaanden die'k vortdo op mien volglieste zetten",
+'tog-watchcreations' => "Spul wa'k anmake op mien volglieste zetten",
+'tog-watchdefault' => "Spul wa'k bewarke op mien volglieste zetten",
+'tog-watchmoves' => "Spul wa'k herneume op mien volglieste zetten",
+'tog-watchdeletion' => "Spul wa'k vortdo op mien volglieste zetten",
'tog-minordefault' => "Markeer alle veraanderingen as 'kleine wieziging'",
-'tog-previewontop' => 'De naokiekpagina boven t bewarkingsveld zetten',
+'tog-previewontop' => 'De naokiekzied boven t bewarkingsveld zetten',
'tog-previewonfirst' => 'Naokieken bie eerste wieziging',
'tog-nocache' => 'De tussenopslag van de webkieker uutzetten',
-'tog-enotifwatchlistpages' => 'Stuur mien n berichjen over pagina- of bestaandswiezigingen uut mien volglieste.',
-'tog-enotifusertalkpages' => 'Stuur mien n berichjen as mien overlegpagina ewiezigd is.',
-'tog-enotifminoredits' => "Stuur mien oek n berichjen bie kleine bewarkingen van pagina's en bestaanden",
+'tog-enotifwatchlistpages' => 'Stuur mien n berichjen over zied- of bestaandswiezigingen uut mien volglieste.',
+'tog-enotifusertalkpages' => 'Stuur mien n berichjen as mien overlegzied ewiezigd is.',
+'tog-enotifminoredits' => 'Stuur mien oek n berichjen bie kleine bewarkingen van ziejen en bestaanden',
'tog-enotifrevealaddr' => 'Mien netpostadres laoten zien in netposttiejigen',
-'tog-shownumberswatching' => 't Antal gebrukers bekieken die disse pagina volgt',
+'tog-shownumberswatching' => 't Antal gebrukers bekieken die disse zied volgt',
'tog-oldsig' => 'Bestaonde haandtekening:',
'tog-fancysig' => 'Ondertekening zien as wikitekste (zonder automatiese verwiezing)',
'tog-externaleditor' => 'Standard n externe tekstbewarker gebruken (allinnig veur gevorderden - veur disse funksie bin spesiale instellingen neudig. [//www.mediawiki.org/wiki/Manual:External_editors Meer informasie]).',
'tog-externaldiff' => 'Standard n extern vergeliekingsprogramma gebruken (allinnig veur gevorderden - veur disse funksie bin spesiale instellingen neudig. [//www.mediawiki.org/wiki/Manual:External_editors Meer informasie]).',
'tog-showjumplinks' => '"Gao naor"-verwiezingen toelaoten',
-'tog-uselivepreview' => 'Gebruuk "rechtstreeks naokieken" (mö\'j JavaScript veur hebben - experimenteel)',
+'tog-uselivepreview' => 'Gebruuk "rechtstreeks naokieken" (mu\'j JavaScript veur hebben - experimenteel)',
'tog-forceeditsummary' => 'Geef n melding bie n lege samenvatting',
'tog-watchlisthideown' => 'Verbarg mien eigen bewarkingen',
'tog-watchlisthidebots' => 'Verbarg botgebrukers',
'tog-watchlisthidepatrolled' => 'Wiezigingen die emarkeerd bin op volglieste verbargen',
'tog-nolangconversion' => 't Ummezetten van variaanten uutschakelen',
'tog-ccmeonemails' => 'Stuur mien kopieën van berichten an aandere gebrukers',
-'tog-diffonly' => 'Laot de pagina-inhoud niet onder de an-egeven wiezigingen zien.',
+'tog-diffonly' => 'Laot de inhoud van ziejen niet onder de an-egeven wiezigingen zien.',
'tog-showhiddencats' => 'Laot verbörgen kategorieën zien',
-'tog-noconvertlink' => 'Paginanaamkonversie uutschakelen',
+'tog-noconvertlink' => 'Ziednaamkonversie uutschakelen',
'tog-norollbackdiff' => 'Wiezigingen vortlaoten nao t weerummedreien',
'underline-always' => 'Altied',
'hidden-category-category' => 'Verbörgen kategorieën',
'category-subcat-count' => '{{PLURAL:$2|Disse kategorie hef de volgende subkategorie.|Disse kategorie hef de volgende {{PLURAL:$1|subkategorie|$1 subkategorieën}}, van in totaal $2.}}',
'category-subcat-count-limited' => 'Disse kategorie hef de volgende {{PLURAL:$1|subkategorie|$1 subkategorieën}}.',
-'category-article-count' => "{{PLURAL:$2|In disse kategorie steet allinnig de volgende pagina.|De volgende {{PLURAL:$1|pagina steet|$1 pagina's staon}} in disse kategorie, van in totaal $2.}}",
-'category-article-count-limited' => "In disse kategorie {{PLURAL:$1|steet de volgende pagina|staon de volgende $1 pagina's}}.",
+'category-article-count' => '{{PLURAL:$2|In disse kategorie steet allinnig de volgende zied.|De volgende {{PLURAL:$1|zied steet|$1 ziejen staon}} in disse kategorie, van in totaal $2.}}',
+'category-article-count-limited' => 'In disse kategorie {{PLURAL:$1|steet de volgende zied|staon de volgende $1 ziejen}}.',
'category-file-count' => 'In disse kategorie {{PLURAL:$2|steet t volgende bestaand|staon de volgende $1 bestaanden, van in totaal $2}}.',
'category-file-count-limited' => 'In disse kategorie {{PLURAL:$1|steet t volgende bestaand|staon de volgende $1 bestaanden}}.',
'listingcontinuesabbrev' => '(vervolg)',
-'index-category' => "Pagina's die indexeerd bin",
-'noindex-category' => "Pagina's die niet indexeerd bin",
-'broken-file-category' => "Pagina's mit verkeerde bestaandsverwiezingen",
+'index-category' => 'Spul wat al indexeerd is',
+'noindex-category' => 'Spul wat nog niet indexeerd is',
+'broken-file-category' => 'Ziejen mit verkeerde bestaandsverwiezingen',
'about' => 'Informasie',
'article' => 'Artikel',
'newwindow' => '(niej vienster)',
'cancel' => 'Aofbreken',
'moredotdotdot' => 'Meer...',
-'mypage' => 'Mien gebrukerspagina',
+'mypage' => 'Mien gebrukerszied',
'mytalk' => 'Mien overleg',
-'anontalk' => 'Overlegpagina veur dit IP-adres',
+'anontalk' => 'Overlegzied veur dit IP-adres',
'navigation' => 'Navigasie',
'and' => ' en',
'qbfind' => 'Zeuken',
'qbbrowse' => 'Blaojen',
'qbedit' => 'Bewark',
-'qbpageoptions' => 'Pagina-opsies',
-'qbpageinfo' => 'Pagina-informasie',
+'qbpageoptions' => 'Disse zied',
+'qbpageinfo' => 'Ziedinformasie',
'qbmyoptions' => 'Veurkeuren',
-'qbspecialpages' => "Spesiale pagina's",
+'qbspecialpages' => 'Spesiale ziejen',
'faq' => 'Vragen die vake esteld wörden',
'faqpage' => 'Project:Vragen die vake esteld wörden',
'view' => 'Lezen',
'edit' => 'Bewarken',
'create' => 'Anmaken',
-'editthispage' => 'Pagina bewarken',
-'create-this-page' => 'Disse pagina anmaken',
+'editthispage' => 'Disse zied bewarken',
+'create-this-page' => 'Disse zied anmaken',
'delete' => 'Vortdoon',
-'deletethispage' => 'Disse pagina vortdoon',
+'deletethispage' => 'Disse zied vortdoon',
'undelete_short' => '$1 {{PLURAL:$1|versie|versies}} weerummeplaotsen',
'viewdeleted_short' => '{{PLURAL:$1|Eén versie die vortedaon is|$1 versies die vortedaon bin}} bekieken',
'protect' => 'Beveiligen',
'protect_change' => 'wiezigen',
'protectthispage' => 'Beveiligen',
'unprotect' => 'Beveiliging wiezigen',
-'unprotectthispage' => 'Beveiliging van disse pagina wiezigen',
-'newpage' => 'Nieje pagina',
-'talkpage' => 'Overlegpagina',
+'unprotectthispage' => 'Beveiliging van disse zied wiezigen',
+'newpage' => 'Nieje zied',
+'talkpage' => 'Overlegzied',
'talkpagelinktext' => 'Overleg',
-'specialpage' => 'Spesiale pagina',
+'specialpage' => 'Spesiale zied',
'personaltools' => 'Persoonlike instellingen',
'postcomment' => 'Niej onderwarp',
'articlepage' => 'Artikel',
'talk' => 'Overleg',
-'views' => 'Aspekten/aksies',
+'views' => 'Weergaven',
'toolbox' => 'Hulpmiddels',
-'userpage' => 'gebrukerspagina',
-'projectpage' => 'Bekiek projektpagina',
-'imagepage' => 'Bestaandspagina bekieken',
+'userpage' => 'gebrukerszied',
+'projectpage' => 'Bekiek projektzied',
+'imagepage' => 'Bestaandszied bekieken',
'mediawikipage' => 'Tiejige bekieken',
'templatepage' => 'Mal bekieken',
-'viewhelppage' => 'Hulppagina bekieken',
-'categorypage' => 'Kategoriepagina bekieken',
-'viewtalkpage' => 'Bekiek overlegpagina',
+'viewhelppage' => 'Hulpzied bekieken',
+'categorypage' => 'Kategoriezied bekieken',
+'viewtalkpage' => 'Bekiek overlegzied',
'otherlanguages' => 'Aandere talen',
'redirectedfrom' => '(deurestuurd vanaof "$1")',
-'redirectpagesub' => 'Deurstuurpagina',
-'lastmodifiedat' => 'Disse pagina is t lest ewiezigd op $1 um $2.',
-'viewcount' => 'Disse pagina is $1 {{PLURAL:$1|keer|keer}} bekeken.',
-'protectedpage' => 'Beveiligden pagina',
+'redirectpagesub' => 'Deurstuurzied',
+'lastmodifiedat' => 'Disse zied is t lest ewiezigd op $1 um $2.',
+'viewcount' => 'Disse zied is $1 {{PLURAL:$1|keer|keer}} bekeken.',
+'protectedpage' => 'Beveiligden zied',
'jumpto' => 'Gao naor:',
'jumptonavigation' => 'navigasie',
'jumptosearch' => 'zeuk',
'view-pool-error' => "De servers bin noen overbelast.
-Te veule meensen proberen disse pagina te bekieken.
-Wacht even veurda'j opniej toegang proberen te kriegen tot disse pagina.
+Te veule meensen proberen disse zied te bekieken.
+Wacht even veurda'j opniej toegang proberen te kriegen tot disse zied.
$1",
'pool-timeout' => 'Wachttied tiejens t wachten op vergrendeling',
'badaccess-groups' => 'Disse aksie kan allinnig uutevoerd wörden deur gebrukers uut {{PLURAL:$2|de groep|één van de groepen}}: $1.',
'versionrequired' => 'Versie $1 van MediaWiki is neudig',
-'versionrequiredtext' => 'Versie $1 van MediaWiki is neudig um disse pagina te gebruken. Zie [[Special:Version|Versie]].',
+'versionrequiredtext' => 'Versie $1 van MediaWiki is neudig um disse zied te gebruken. Zie [[Special:Version|Versie]].',
-'ok' => 'Oké',
+'ok' => 'Best',
'retrievedfrom' => 'Van "$1"',
'youhavenewmessages' => 'Je hebben $1 ($2).',
'newmessageslink' => 'nieje berichten',
'site-atom-feed' => '$1 Atom-voer',
'page-rss-feed' => '"$1" RSS-voer',
'page-atom-feed' => '"$1" Atom-voer',
-'red-link-title' => '$1 (pagina besteet nog niet)',
+'red-link-title' => '$1 (zied besteet nog niet)',
'sort-descending' => 'Aoflopend sorteren',
'sort-ascending' => 'Oplopend sorteren',
'nstab-main' => 'Artikel',
'nstab-user' => 'Gebruker',
'nstab-media' => 'Media',
-'nstab-special' => 'Spesiale pagina',
-'nstab-project' => 'Projektpagina',
+'nstab-special' => 'Spesiale zied',
+'nstab-project' => 'Projektzied',
'nstab-image' => 'Bestaand',
'nstab-mediawiki' => 'Tiejige',
'nstab-template' => 'Mal',
'nosuchactiontext' => 'De opdrachte in t webadres in ongeldig.
Je hebben t webadres misschien verkeerd in-etikt of de verkeerde verwiezing evolgd.
Dit kan oek dujen op n fout in de programmatuur van {{SITENAME}}.',
-'nosuchspecialpage' => 'Der besteet gien spesiale pagina mit disse naam',
-'nospecialpagetext' => "<strong>Disse spesiale pagina wörden niet herkend deur de programmatuur.</strong>
+'nosuchspecialpage' => 'Der besteet gien spesiale zied mit disse naam',
+'nospecialpagetext' => "<strong>Disse spesiale zied wörden niet herkend deur de programmatuur.</strong>
-n Lieste mit bestaonde spesiale pagina ku'j vienen op [[Special:SpecialPages|{{int:specialpages}}]].",
+n Lieste mit bestaonde spesiale ziejen ku'j vienen op [[Special:SpecialPages|{{int:specialpages}}]].",
# General errors
'error' => 'Foutmelding',
vanuut de funksie "$2"
De databanke gaf de volgende foutmelding: "$3: $4"',
'laggedslavemode' => '<strong>Waorschuwing:</strong> t is meugelik dat leste wiezigingen in de tekste van dit artikel nog niet verwarkt bin.',
-'readonly' => 'De databanke is beveilig',
-'enterlockreason' => 'Waorumme en veur hoe lange is e eblokkeerd?',
+'readonly' => 'De databanke is beveiligd',
+'enterlockreason' => 'Waorumme en veur hoe lange is t eblokkeerd?',
'readonlytext' => "De databanke van {{SITENAME}} is noen esleuten veur nieje bewarkingen en wiezigingen, warschienlik veur bestaandsonderhoud. De verantwoordelike systeembeheerder gaf hierveur de volgende reden op: '''$1'''",
-'missing-article' => 'In de databanke steet gien tekste veur de pagina "$1" die der wel in zol mutten staon ($2).
+'missing-article' => 'In de databanke steet gien tekste veur de zied "$1" die der wel in zol mutten staon ($2).
-Dit kan koemen deurda\'j n ouwe verwiezing naor t verschil tussen twee versies van n pagina volgen of n versie opvragen die vortedaon is.
+Dit kan koemen deurda\'j n ouwe verwiezing naor t verschil tussen twee versies van n zied volgen of n versie opvragen die vortedaon is.
As dat niet zo is, dan he\'j misschien n fout in de programmatuur evunnen.
-Meld t dan effen bie n [[Special:ListUsers/sysop|systeembeheerder]] van {{SITENAME}} en vermeld derbie de internetverwiezing van disse pagina.',
+Meld t dan effen bie n [[Special:ListUsers/sysop|systeembeheerder]] van {{SITENAME}} en vermeld derbie de internetverwiezing van disse zied.',
'missingarticle-rev' => '(versienummer: $1)',
'missingarticle-diff' => '(Wieziging: $1, $2)',
'readonly_lag' => 'De databanke is automaties beveilig, zodat de ondergeschikten servers zich kunnen synchroniseren mit de sentrale server.',
'fileexistserror' => 'Kon niet schrieven naor t bestaand "$1": t bestaand besteet al',
'unexpected' => 'Onverwachten weerde: "$1"="$2".',
'formerror' => 'Fout: kon formulier niet versturen',
-'badarticleerror' => 'Disse haandeling kan op disse pagina niet uutevoerd wörden.',
-'cannotdelete' => 'De pagina of t bestaand "$1" kon niet vortedaon wörden.
+'badarticleerror' => 'Disse haandeling kan op disse zied niet uutevoerd wörden.',
+'cannotdelete' => 'De zied of t bestaand "$1" kon niet vortedaon wörden.
t Kan ween dat n aander t al vortedaon hef.',
-'cannotdelete-title' => 'Pagina "$1" kan niet vortedaon wörden',
+'cannotdelete-title' => 'Zied "$1" kan niet vortedaon wörden',
'delete-hook-aborted' => 't Vortdoon wörden in t wiere eschopt deur n MediaWiki-programmatuuruutbreiding.
Der is gien veerdere informasie beschikbaor.',
'badtitle' => 'Ongeldige naam',
-'badtitletext' => 'De naam van de op-evreugen pagina is niet geldig, leeg, of n interwiki-verwiezing naor n onbekende of ongeldige wiki.',
+'badtitletext' => 'De naam van de op-evreugen zied is niet geldig, leeg, of n interwiki-verwiezing naor n onbekende of ongeldige wiki.',
'perfcached' => 'Disse gegevens koemen uut t tussengeheugen en bin misschien niet aktueel. Der {{PLURAL:$1|is hooguut een resultaot|bin hooguut $1 resultaoten}} beschikbaor in t tussengeheugen.',
'perfcachedts' => 'Disse gegevens koemen uut t tussengeheugen die veur t lest bie-ewörken is op $2 um $3. Der {{PLURAL:$4|is hooguut een resultaot|bin hooguut $4 resultaoten}} beschikbaor in t tussengeheugen.',
-'querypage-no-updates' => "'''Disse pagina wörden niet meer bie-ewörken.'''",
+'querypage-no-updates' => "'''Disse zied wörden niet meer bie-ewörken.'''",
'wrong_wfQuery_params' => 'Parameters veur wfQuery() waren verkeerd<br />
Funksie: $1<br />
Zeukopdrachte: $2',
'viewsource' => 'Brontekste bekieken',
'viewsource-title' => 'Bron bekieken van $1',
-'actionthrottled' => 'Haandeling tegen-ehuilen',
+'actionthrottled' => 'Haandeling tegenehöllen',
'actionthrottledtext' => "As maotregel tegen t plaotsen van ongewunste verwiezingen, is t antal keren da'j disse haandeling in n korte tied uutvoeren kunnen beteund. Je hebben de limiet overschrejen. Probeer t over n antal minuten weer.",
-'protectedpagetext' => 'Disse pagina is beveiligd um bewarkingen te veurkoemen.',
-'viewsourcetext' => 'Je kunnen de brontekste van disse pagina bewarken en bekieken:',
-'viewyourtext' => "Je kunnen '''joew bewarkingen''' an de brontekste van disse pagina bekieken en kopiëren:",
-'protectedinterface' => 'Op disse pagina steet n tekste die gebruukt wörden veur systeemteksten van de wiki. Allinnig beheerders kunnen disse pagina bewarken.',
-'editinginterface' => "'''Waorschuwing:''' je bewarken n pagina die gebruukt wörden deur de programmatuur. Wa'j hier wiezigen, is van invleud op de hele wiki. Overweeg veur vertalingen um [//translatewiki.net/wiki/Main_Page?setlang=nds-nl translatewiki.net] te gebruken, t vertalingsprojekt veur MediaWiki.",
+'protectedpagetext' => 'Disse zied is beveiligd um bewarkingen te veurkoemen.',
+'viewsourcetext' => 'Je kunnen de brontekste van disse zied bewarken en bekieken:',
+'viewyourtext' => "Je kunnen '''joew bewarkingen''' an de brontekste van disse zied bekieken en kopiëren:",
+'protectedinterface' => 'Op disse zied steet n tekste die gebruukt wörden veur systeemteksten van de wiki. Allinnig beheerders kunnen disse zied bewarken.',
+'editinginterface' => "'''Waorschuwing:''' je bewarken n zied die gebruukt wörden deur de programmatuur. Wa'j hier wiezigen, is van invleud op de hele wiki. Overweeg veur vertalingen um [//translatewiki.net/wiki/Main_Page?setlang=nds-nl translatewiki.net] te gebruken, t vertalingsprojekt veur MediaWiki.",
'sqlhidden' => '(SQL-zeukopdrachte verbörgen)',
-'cascadeprotected' => 'Disse pagina is beveiligd umdat t veurkump in de volgende {{PLURAL:$1|pagina|pagina\'s}}, die beveiligd {{PLURAL:$1|is|bin}} mit de "kaskade"-opsie:
+'cascadeprotected' => 'Disse zied is beveiligd umdat t veurkömp in de volgende {{PLURAL:$1|zied|ziejen}}, die beveiligd {{PLURAL:$1|is|bin}} mit de "kaskade"-opsie:
$2',
-'namespaceprotected' => "Je maggen gien pagina's in de '''$1'''-naamruumte bewarken.",
-'customcssprotected' => 'Je kunnen disse CSS-pagina niet bewarken, umdat der persoonlike instellingen van n aandere gebruker in staon.',
-'customjsprotected' => 'Je kunnen disse JavaScript-pagina niet bewarken, umdat der persoonlike instellingen van n aandere gebruker in staon.',
-'ns-specialprotected' => "Spesiale pagina's kunnen niet bewarkt wörden.",
-'titleprotected' => "t Anmaken van disse pagina is beveiligd deur [[User:$1|$1]].
+'namespaceprotected' => "Je maggen gien ziejen in de '''$1'''-naamruumte bewarken.",
+'customcssprotected' => 'Je kunnen disse CSS-zied niet bewarken, umdat der persoonlike instellingen van n aandere gebruker in staon.',
+'customjsprotected' => 'Je kunnen disse JavaScript-zied niet bewarken, umdat der persoonlike instellingen van n aandere gebruker in staon.',
+'ns-specialprotected' => 'Spesiale ziejen kunnen niet bewarkt wörden.',
+'titleprotected' => "t Anmaken van disse zied is beveiligd deur [[User:$1|$1]].
De op-egeven reden is ''$2''.",
'filereadonlyerror' => 'Kon t bestaand "$1" niet anpassen umdat de bestaandsmap "$2" op dit moment op allinnig-lezen steet.
'invalidtitle-knownnamespace' => 'Ongeldige titel mit naamruumte "$2" en tekste "$3"',
'invalidtitle-unknownnamespace' => 'Ongeldige titel mit onbekend naamruumtenummer $1 en tekste "$2"',
'exception-nologin' => 'Niet an-emeld',
-'exception-nologin-text' => "Um disse pagina te bekieken of disse haandeling uut te kunnen voeren mu'j an-emeld ween bie disse wiki.",
+'exception-nologin-text' => "Um disse zied te bekieken of disse haandeling uut te kunnen voeren mu'j an-emeld ween bie disse wiki.",
# Virus scanner
'virus-badscanner' => "Slichte konfigurasie: onbekend antivirusprogramma: ''$1''",
'logouttext' => "'''Je bin noen aofemeld.'''
Je kunnen {{SITENAME}} noen anoniem gebruken of je eigen [[Special:UserLogin|opniej anmelden]] onder disse of n aandere gebrukersnaam.
-t Kan ween dat der wat pagina's bin die weeregeven wörden asof je an-emeld bin totda'j t tussengeheugen van joew webkieker leegmaken.",
+t Kan ween dat der wat ziejen bin die weeregeven wörden asof je an-emeld bin totda'j t tussengeheugen van joew webkieker leegmaken.",
'welcomecreation' => '== Welkom, $1! ==
Joew gebrukersnaam is an-emaakt.
Vergeet niet joew [[Special:Preferences|veurkeuren veur {{SITENAME}}]] in te stellen.',
Zet ze an, en meld daornao an mit de nieje gegevens.',
'nocookieslogin' => 't Anmelden is mislokt umdat de webkieker gien scheumbestaanden (cookies) an hef staon. Probeer t aksepteren van scheumbestaanden an te zetten en daornao opniej an te melden.',
'nocookiesfornew' => "De gebruker is niet an-emaakt, umdat de bron niet bevestigd kon wörden.
-Zörg derveur da'j scheumbestaanden (cookies) an hebben staon, herlaot disse pagina en probeer t opniej.",
+Zörg derveur da'j scheumbestaanden (cookies) an hebben staon, herlaoj disse zied en probeer t opniej.",
'noname' => 'Je mutten n gebrukersnaam opgeven.',
'loginsuccesstitle' => 'Suksesvol an-emeld',
'loginsuccess' => 'Je bin noen an-emeld bie {{SITENAME}} as "$1".',
'noemailcreate' => 'Je mutten n geldig netpostadres opgeven',
'passwordsent' => 'Der is n niej wachtwoord verstuurd naor t netpostadres van gebruker "$1". Meld an, a\'j t wachtwoord ontvangen.',
'blocked-mailpassword' => "Dit IP-adres is eblokkeerd. Dit betekent da'j niet bewarken kunnen en dat {{SITENAME}} joew wachtwoord niet weerummehaolen kan, dit wörden edaon um misbruuk tegen te gaon.",
-'eauthentsent' => "Der is n bevestigingsberich naor t op-egeven netpostadres verstuurd. Veurdat der veerdere berichten naor dit netpostadres verstuurd kunnen wörden, mö'j de instruksies volgen in t toe-esturen berich, um te bevestigen da'j joe eigen daodwarkelik an-emeld hebben.",
+'eauthentsent' => "Der is n bevestigingsberich naor t op-egeven netpostadres verstuurd. Veurdat der veerdere berichten naor dit netpostadres verstuurd kunnen wörden, mu'j de instruksies volgen in t toe-esturen berich, um te bevestigen da'j joe eigen daodwarkelik an-emeld hebben.",
'throttled-mailpassword' => 'In {{PLURAL:$1|t leste ure|de leste $1 uren}} is der al n wachtwoordherinnering estuurd.
Um misbruuk te veurkoemen wörden der mer één wachtwoordherinnering per {{PLURAL:$1|ure|$1 uren}} verstuurd.',
'mailerror' => 'Fout bie t versturen van bericht: $1',
# Change password dialog
'resetpass' => 'Wachtwoord wiezigen',
-'resetpass_announce' => "Je bin an-emeld mit n veurlopige kode die mit de netpost toe-estuurd wörden. Um t anmelden te voltooien, mö'j n niej wachtwoord invoeren:",
+'resetpass_announce' => "Je bin an-emeld mit n veurlopige kode die mit de netpost toe-estuurd wörden. Um t anmelden te voltooien, mu'j n niej wachtwoord invoeren:",
'resetpass_text' => '<!-- Tekste hier invoegen -->',
'resetpass_header' => 'Wachtwoord wiezigen',
'oldpassword' => "Wachtwoord da'j noen hebben",
'resetpass_submit' => 'Voer t wachtwoord in en meld je an',
'resetpass_success' => 'Joew wachtwoord is suksesvol ewiezigd Je wörden noen an-emeld...',
'resetpass_forbidden' => 'Wachtwoorden kunnen niet ewiezigd wörden',
-'resetpass-no-info' => "Je mutten an-emeld ween veurda'j disse pagina gebruken kunnen.",
+'resetpass-no-info' => "Je mutten an-emeld ween veurda'j disse zied gebruken kunnen.",
'resetpass-submit-loggedin' => 'Wachtwoord wiezigen',
'resetpass-submit-cancel' => 'Aofbreken',
'resetpass-wrong-oldpass' => "t Veurlopige wachtwoord of t wachtwoord da'j noen hebben is ongeldig.
$2
-{{PLURAL:$3|Dit tiedelike wachtwoord vervuilt|Disse tiedelike wachtwoorden vervallen}} over {{PLURAL:$5|één dag|$5 dagen}}.
+{{PLURAL:$3|Dit tiedelike wachtwoord vervölt|Disse tiedelike wachtwoorden vervallen}} over {{PLURAL:$5|één dag|$5 dagen}}.
Meld je eigen noen an en wiezig t wachtwoord. A'j dit verzeuk niet zelf edaon hebben, of a'j t oorspronkelike wachtwoord nog kennen en t niet wiezigen willen, negeer dit bericht dan en blief joew ouwe wachtwoord gebruken.",
'passwordreset-emailtext-user' => "De gebruker $1 van {{SITENAME}} hef joew gebrukersgegevens veur {{SITENAME}} ($4) op-evreugen vanaof t IP-adres $1.
De volgende {{PLURAL:$3|gebruker is|gebrukers bin}} ekoppeld an dit netpostadres:
$2
-{{PLURAL:$3|Dit tiedelike wachtwoord vervuilt|Disse tiedelike wachtwoorden vervallen}} over {{PLURAL:$5|één dag|$5 dagen}}.
+{{PLURAL:$3|Dit tiedelike wachtwoord vervölt|Disse tiedelike wachtwoorden vervallen}} over {{PLURAL:$5|één dag|$5 dagen}}.
Meld je eigen noen an en wiezig t wachtwoord. A'j dit verzeuk niet zelf edaon hebben, of a'j t oorspronkelike wachtwoord nog kennen en t niet wiezigen willen, negeer dit bericht dan en blief joew ouwe wachtwoord gebruken.",
'passwordreset-emailelement' => 'Gebrukersnaam: $1
Tiedelik wachtwoord: $2',
# Special:ChangeEmail
'changeemail' => 'Wiezig netpostadres',
'changeemail-header' => 'Netpostadres wiezigen',
-'changeemail-text' => "Vul dit formulier in um joew netpostadres te wiezigen. Um disse wieziging te bevestigen mö'j je wachtwoord invoeren.",
-'changeemail-no-info' => 'Je mutten an-emeld ween um drekt toegang te hebben tot disse pagina.',
+'changeemail-text' => "Vul dit formulier in um joew netpostadres te wiezigen. Um disse wieziging te bevestigen mu'j je wachtwoord invoeren.",
+'changeemail-no-info' => 'Je mutten an-emeld ween um drekt toegang te hebben tot disse zied.',
'changeemail-oldemail' => 't Ouwe netpostadres:',
'changeemail-newemail' => 't Nieje netpostadres:',
'changeemail-none' => '(gien)',
'summary' => 'Samenvatting:',
'subject' => 'Onderwarp:',
'minoredit' => 'kleine wieziging',
-'watchthis' => 'volg disse pagina',
-'savearticle' => 'Pagina opslaon',
+'watchthis' => 'volg disse zied',
+'savearticle' => 'Zied opslaon',
'preview' => 'Naokieken',
'showpreview' => 'Bewarking naokieken',
'showlivepreview' => 'Drekte weergave',
'showdiff' => 'Verschil bekieken',
'anoneditwarning' => "'''Waorschuwing:''' je bin niet an-emeld.
-Joew IP-adres zal op-esleugen wörden a'j wiezigingen op disse pagina anbrengen.",
+Joew IP-adres zal op-esleugen wörden a'j wiezigingen op disse zied anbrengen.",
'anonpreviewwarning' => "''Je bin niet an-emeld.''
-''Deur de bewarking op te slaon wörden joew IP-adres op-esleugen in de paginageschiedenisse.''",
+''Deur de bewarking op te slaon wörden joew IP-adres op-esleugen in de ziedgeschiedenisse.''",
'missingsummary' => "'''Herinnering:''' je hebben gien samenvatting op-egeven veur de bewarking. A'j noen weer op ''Opslaon'' klikken wörden de bewarking zonder samenvatting op-esleugen.",
'missingcommenttext' => 'Plaots joew opmarking hieronder.',
'missingcommentheader' => "'''Waorschuwing:''' je hebben der gien onderwarptitel bie ezet. A'j noen weer op \"{{int:savearticle}}\" klikken, dan wörden de bewarking op-esleugen zonder onderwarptitel.",
Joew IP-adres is $3 en joew blokkeernummer is $5.
Geef disse nummers deur a\'j kontakt mit ene opnemen over de blokkering.',
'blockednoreason' => 'gien reden op-egeven',
-'whitelistedittext' => "Um pagina's te kunnen wiezigen, mö'j $1 ween",
+'whitelistedittext' => "Um ziejen te kunnen wiezigen, mu'j $1 ween",
'confirmedittext' => "Je mutten je netpostadres bevestigen veurda'j bewarken kunnen. Vul je adres in en bevestig t via [[Special:Preferences|mien veurkeuren]].",
'nosuchsectiontitle' => 'Disse seksie besteet niet',
'nosuchsectiontext' => 'Je proberen n seksie te bewarken dat niet besteet.
t Kan ween dat t herneumd is of dat t vortedaon is to jie t an t bekieken waren.',
'loginreqtitle' => 'Anmelden verplicht',
'loginreqlink' => 'Anmelden',
-'loginreqpagetext' => 'Je mutten $1 um disse pagina te bekieken.',
+'loginreqpagetext' => 'Je mutten $1 um disse zied te bekieken.',
'accmailtitle' => 'Wachtwoord is verstuurd.',
'accmailtext' => "Der is n willekeurig wachtwoord veur [[User talk:$1|$1]] verstuurd naor $2.
-t Wachtwoord veur disse gebruker kan ewiezigd wörden deur de pagina ''[[Special:ChangePassword|wachtwoord wiezigen]]'' te gebruken.",
+t Wachtwoord veur disse gebruker kan ewiezigd wörden deur de zied ''[[Special:ChangePassword|wachtwoord wiezigen]]'' te gebruken.",
'newarticle' => '(Niej)',
-'newarticletext' => "Disse pagina besteet nog niet.
-In t veld hieronder ku'j wat schrieven um disse pagina an te maken (meer informasie vie'j op de [[{{MediaWiki:Helppage}}|hulppagina]]).
+'newarticletext' => "Disse zied besteet nog niet.
+In t veld hieronder ku'j wat schrieven um disse zied an te maken (meer informasie vie'j op de [[{{MediaWiki:Helppage}}|hulpzied]]).
A'j hier per ongelok terechtekeumen bin gebruuk dan de knoppe '''veurige''' um weerumme te gaon.",
-'anontalkpagetext' => "---- ''Disse overlegpagina heurt bie n anonieme gebruker die nog gien gebrukersnaam hef, of t niet gebruuk. We gebruken daorumme t IP-adres um hum of heur te herkennen, mer t kan oek ween dat meerdere personen t zelfde IP-adres gebruken, en da'j hiermee berichten ontvangen die niet veur joe bedoeld bin. A'j dit veurkoemen willen, dan ku'j t bes [[Special:UserLogin/signup|n gebrukersnaam anmaken]] of [[Special:UserLogin|anmelden]].''",
-'noarticletext' => 'Der steet noen gien tekste op disse pagina.
-Je kunnen [[Special:Search/{{PAGENAME}}|de titel opzeuken]] in aandere pagina\'s,
+'anontalkpagetext' => "---- ''Disse overlegzied heurt bie n anonieme gebruker die nog gien gebrukersnaam hef, of t niet gebruukt. We gebruken daorumme t IP-adres um hum of heur te herkennen, mer t kan oek ween dat meerdere personen t zelfde IP-adres gebruken, en da'j hiermee berichten ontvangen die niet veur joe bedoeld bin. A'j dit veurkoemen willen, dan ku'j t best [[Special:UserLogin/signup|n gebrukersnaam anmaken]] of [[Special:UserLogin|anmelden]].''",
+'noarticletext' => 'Der steet noen gien tekste op disse zied.
+Je kunnen [[Special:Search/{{PAGENAME}}|de titel opzeuken]] in aandere ziejen,
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} zeuken in de logboeken],
-of [{{fullurl:{{FULLPAGENAME}}|action=edit}} disse pagina bewarken]</span>.',
-'noarticletext-nopermission' => 'Op disse pagina steet gien tekste.
-Je kunnen [[Special:Search/{{PAGENAME}}|zeuken naor disse term]] in aandere pagina\'s of
+of [{{fullurl:{{FULLPAGENAME}}|action=edit}} disse zied bewarken]</span>.',
+'noarticletext-nopermission' => 'Op disse zied steet gien tekste.
+Je kunnen [[Special:Search/{{PAGENAME}}|zeuken naor disse term]] in aandere ziejen of
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboeken deurzeuken]</span>.',
-'userpage-userdoesnotexist' => 'Je bewarken n gebrukerspagina van n gebruker die niet besteet (gebruker "<nowiki>$1</nowiki>"). Kiek effen nao o\'j disse pagina wel anmaken/bewarken willen.',
+'userpage-userdoesnotexist' => 'Je bewarken n gebrukerszied van n gebruker die niet besteet (gebruker "<nowiki>$1</nowiki>"). Kiek effen nao o\'j disse zied wel anmaken/bewarken willen.',
'userpage-userdoesnotexist-view' => 'Gebruker "$1" steet hier niet in-eschreven',
'blocked-notice-logextract' => 'Disse gebruker is op t moment eblokkeerd.
De leste regel uut t blokkeerlogboek steet hieronder as referensie:',
*'''Opera:''' leeg t tussengeheugen in ''Extra → Voorkeuren\"",
'usercssyoucanpreview' => "'''Tip:''' gebruuk de knoppe \"{{int:showpreview}}\" um joew nieje css/js nao te kieken veurda'j t opslaon.",
'userjsyoucanpreview' => "'''Tip:''' gebruuk de knoppe \"{{int:showpreview}}\" um joew nieje css/js nao te kieken veurda'j t opslaon.",
-'usercsspreview' => "'''Dit is allinnig n kontrole van joew persoonlike CSS.'''
+'usercsspreview' => "'''Dit is allinnig n naokieksel van joew persoonlike CSS.'''
'''t Is nog niet op-esleugen!'''",
'userjspreview' => "'''Denk deran da'j joew persoonlike JavaScript allinnig nog mer an t bekieken bin, t is nog niet op-esleugen!'''",
'sitecsspreview' => "'''Je bin allinnig mer de CSS an t naokieken.'''
'''t Is nog niet op-esleugen!'''",
'sitejspreview' => "'''Je bin allinnig mer de JavaScript-kode an t naokieken.'''
'''t Is nog niet op-esleugen!'''",
-'userinvalidcssjstitle' => "'''Waorschuwing:''' der is gien uutvoering mit de naam \"\$1\". Vergeet niet dat joew eigen .css- en .js-pagina's beginnen mit n kleine letter, bv. \"{{ns:user}}:Naam/'''v'''ector\" in plaotse van \"{{ns:user}}:Naam/'''V'''ector.css\".",
+'userinvalidcssjstitle' => "'''Waorschuwing:''' der is gien uutvoering mit de naam \"\$1\". Vergeet niet dat joew eigen .css- en .js-ziejen beginnen mit n kleine letter, bv. \"{{ns:user}}:Naam/'''v'''ector\" in plaotse van \"{{ns:user}}:Naam/'''V'''ector.css\".",
'updated' => '(Bewark)',
'note' => "'''Opmarking:'''",
-'previewnote' => "'''Waort je: dit is n kontrolepagina.'''
+'previewnote' => "'''Waort je: dit is n naokiekzied.'''
Joew tekste is niet op-esleugen!",
'continue-editing' => 'Deurgaon mit bewarken',
-'previewconflict' => "Disse versie laot zien hoe de tekste in t bovenste veld deruut kump te zien a'j de tekste opslaon.",
+'previewconflict' => "Disse versie löt zien hoe de tekste in t bovenste veld deruut kömp te zien a'j de tekste opslaon.",
'session_fail_preview' => "'''De bewarking kan niet verwarkt wörden wegens n verlies an data.'''
Probeer t laoter weer.
-As t probleem dan nog steeds veurkump, probeer dan [[Special:UserLogout|opniej an te melden]].",
+As t probleem dan nog steeds veurkömp, probeer dan [[Special:UserLogout|opniej an te melden]].",
'session_fail_preview_html' => "'''De bewarking kan niet verwarkt wörden wegens n verlies an data.'''
-''Umdat in {{SITENAME}} roewe HTML in-eschakeld is, is de weergave dervan verbörgen um te veurkoemen dat t JavaScript an-evuilen wörden.''
+''Umdat in {{SITENAME}} roewe HTML in-eschakeld is, is de weergave dervan verbörgen um te veurkoemen dat t JavaScript an-evöllen wörden.''
'''As dit n legitieme wieziging is, probeer t dan opniej.'''
-As t dan nog problemen geef, probeer dan um [[Special:UserLogout|opniej an te melden]].",
-'token_suffix_mismatch' => "'''De bewarking is eweigerd umdat de webkieker de leestekens in t bewarkingstoken verkeerd behaandeld hef. De bewarking is eweigerd um verminking van de paginatekste te veurkoemen. Dit gebeurt soms as der n web-ebaseerden proxydienst gebruukt wörden waor fouten in zitten.'''",
+As t dan nog problemen gif, probeer dan um [[Special:UserLogout|opniej an te melden]].",
+'token_suffix_mismatch' => "'''De bewarking is eweigerd umdat de webkieker de leestekens in t bewarkingstoken verkeerd behaandeld hef. De bewarking is eweigerd um verminking van de ziedtekste te veurkoemen. Dit gebeurt soms as der n web-ebaseerden proxydienst gebruukt wörden waor fouten in zitten.'''",
'edit_form_incomplete' => "'''Partie delen van t bewarkingsformulier hebben de server niet bereikt. Kiek eers nao of de bewarkingen kloppen en probeer t opniej.'''",
-'editing' => 'Bewark: $1',
-'creating' => 'Bezig mit t anmaken van $1',
-'editingsection' => 'Bewark: $1 (deelpagina)',
-'editingcomment' => 'Bewark: $1 (niej onderwarp)',
-'editconflict' => 'Bewarkingskonflikt: $1',
-'explainconflict' => "'''NB:''' n aander hef disse pagina ewiezigd naoda'j an disse bewarking begunnen bin.
-t Bovenste bewarkingsveld laot de pagina zien zo as t noen is.
-Daoronder (bie \"Wiezigingen\") staon de verschillen tussen joew versie en de op-esleugen pagina.
+'editing' => 'Bewarken: $1',
+'creating' => 'Anmaken: $1',
+'editingsection' => 'Bewarken: $1 (deelzied)',
+'editingcomment' => 'Bewarken: $1 (niej onderwarp)',
+'editconflict' => 'Tegelieke bewörken: $1',
+'explainconflict' => "'''NB:''' n aander hef disse zied ewiezigd naoda'j an disse bewarking begunnen bin.
+t Bovenste bewarkingsveld löt de zied zien zo as t noen is.
+Daoronder (bie \"Wiezigingen\") staon de verschillen tussen joew versie en de op-esleugen zied.
Helemaole onderan (bie \"Joew tekste\") steet nog n bewarkingsveld mit joew versie.
Je zullen je eigen wiezigingen in de nieje tekste in mutten passen.
'''Allinnig''' de tekste in t bovenste veld wörden beweerd a'j noen kiezen veur \"{{int:savearticle}}\".",
'yourtext' => 'Joew tekste',
'storedversion' => 'Op-esleugen versie',
-'nonunicodebrowser' => "'''Waorschuwing: de webkieker kan niet goed overweg mit unikode, schakel over op n aandere webkieker um de wiezigingen an te brengen!'''",
-'editingold' => "'''Waorschuwing: je bewarken noen n ouwe versie van disse pagina. A'j de wiezigingen opslaon, gaon alle niejere versies verleuren.'''",
+'nonunicodebrowser' => "'''Waorschuwing: joew webkieker kan niet goed mit unikode uut de voten, schakel over op n aandere webkieker um de wiezigingen an te brengen!'''",
+'editingold' => "'''Waorschuwing: je bewarken noen n ouwe versie van disse zied. A'j de wiezigingen opslaon, bi'j alle niejere versies kwiet.'''",
'yourdiff' => 'Wiezigingen',
'copyrightwarning' => "Waort je dat alle biedragen an {{SITENAME}} vrie-egeven mutten wörden onder de \$2 (zie \$1 veur meer informasie).
A'j niet willen dat joew tekste deur aander volk bewarkt en verspreid kan wörden, slao de tekste dan niet op.<br />
-Deur op \"Pagina opslaon\" te klikken beleuf je ons da'j disse tekste zelf eschreven hebben, of over-eneumen hebben uut n vrieje, openbaore bron.<br />
+Deur op \"Zied opslaon\" te klikken beleuf je ons da'j disse tekste zelf eschreven hebben, of over-eneumen hebben uut n vrieje, openbaore bron.<br />
'''Gebruuk gien spul mit auteursrechten, a'j daor gien toestemming veur hebben!'''",
'copyrightwarning2' => "Waort je dat alle biedragen an {{SITENAME}} deur aander volk bewarkt of vortedaon kan wörden. A'j niet willen dat joew tekste deur aander volk bewarkt wörden, slao de tekste dan niet op.<br />
-Deur op \"Pagina opslaon\" te klikken beleuf je ons da'j disse tekste zelf eschreven hebben, of over-eneumen hebben uut n vrieje, openbaore bron (zie \$1 veur meer informasie).
+Deur op \"Zied opslaon\" te klikken beleuf je ons da'j disse tekste zelf eschreven hebben, of over-eneumen hebben uut n vrieje, openbaore bron (zie \$1 veur meer informasie).
'''Gebruuk gien spul mit auteursrechten, a'j daor gien toestemming veur hebben!'''",
'longpageerror' => "'''Foutmelding: de tekste die'j opslaon willen is {{PLURAL:$1|een kilobyte|$1 kilobytes}}. Dit is groter as t toe-estaone maximum van $2 kilobytes. Joew tekste kan niet op-esleugen wörden.'''",
-'readonlywarning' => "'''Waorschuwing: De databanke is op dit moment in onderhoud; t is daorumme niet meugelik um pagina's te wiezigen.
-Je kunnen de tekste t beste bie joew eigen systeem opslaon en laoter opniej proberen de pagina te bewarken.'''
+'readonlywarning' => "'''Waorschuwing: De databanke is op dit moment in onderhoud; t is daorumme niet meugelik um ziejen te wiezigen.
+Je kunnen de tekste t beste bie joew eigen systeem opslaon en laoter opniej proberen de zied te bewarken.'''
As grund is angeven: $1",
-'protectedpagewarning' => "'''Waorschuwing: disse pagina is beveiligd, zodat allinnig beheerders t kunnen wiezigen.'''
+'protectedpagewarning' => "'''Waorschuwing: disse zied is beveiligd, zodat allinnig beheerders t kunnen wiezigen.'''
De leste logboekregel steet hieronder:",
-'semiprotectedpagewarning' => "'''Let op:''' disse pagina is beveiligd en ku'j allinnig bewarken a'j n eregistreerden gebruker bin.
+'semiprotectedpagewarning' => "'''Let op:''' disse zied is beveiligd en ku'j allinnig bewarken a'j n eregistreerden gebruker bin.
De leste logboekregel steet hieronder:",
-'cascadeprotectedwarning' => "'''Waorschuwing:''' disse pagina is beveiligd, zodat allinnig beheerders disse pagina kunnen bewarken, dit wörden edaon umdat disse pagina veurkump in de volgende {{PLURAL:$1|kaskade-beveiligden pagina|kaskade-beveiligden pagina's}}:",
-'titleprotectedwarning' => "'''Waorschuwing: disse pagina is beveilig. Je hebben [[Special:ListGroupRights|bepaolde rechten]] neudig um t an te kunnen maken.'''
+'cascadeprotectedwarning' => "'''Waorschuwing:''' disse zied is beveiligd, zodat allinnig beheerders disse zied bewarken kunnen, dit wörden edaon umdat disse zied veurkömp in de volgende {{PLURAL:$1|kaskade-beveiligden zied|kaskade-beveiligden ziejen}}:",
+'titleprotectedwarning' => "'''Waorschuwing: disse zied is beveiligd. Je hebben [[Special:ListGroupRights|bepaolde rechten]] neudig um t an te kunnen maken.'''
De leste logboekregel steet hieronder:",
-'templatesused' => '{{PLURAL:$1|Mal|Mallen}} die op disse pagina gebruukt wörden:',
+'templatesused' => '{{PLURAL:$1|Mal|Mallen}} die op disse zied gebruukt wörden:',
'templatesusedpreview' => '{{PLURAL:$1|Mal|Mallen}} die in disse bewarking gebruukt wörden:',
'templatesusedsection' => '{{PLURAL:$1|Mal|Mallen}} die in dit subkopjen gebruukt wörden:',
-'template-protected' => '(beveilig)',
-'template-semiprotected' => '(semibeveilig)',
-'hiddencategories' => 'Disse pagina vuilt in de volgende verbörgen {{PLURAL:$1|kategorie|kategorieën}}:',
+'template-protected' => '(beveiligd)',
+'template-semiprotected' => '(half-beveiligd)',
+'hiddencategories' => 'Disse zied völt in de volgende verbörgen {{PLURAL:$1|kategorie|kategorieën}}:',
'edittools' => '<!-- Disse tekste steet onder de bewarkings- en bestaandinlaodformulieren. -->',
-'nocreatetitle' => "t Anmaken van pagina's is beteund",
-'nocreatetext' => "Disse webstee hef de meugelikheid um nieje pagina's an te maken beteund. Je kunnen pagina's die al bestaon wiezigen of je kunnen je [[Special:UserLogin|anmelden of n gebrukerspagina anmaken]].",
-'nocreate-loggedin' => "Je hebben gien toestemming um nieje pagina's an te maken.",
+'nocreatetitle' => 't Anmaken van nieje ziejen is beteund',
+'nocreatetext' => 'Disse webstee hef de meugelikheid um nieje ziejen an te maken beteund. Je kunnen ziejen die al bestaon wiezigen of je kunnen je [[Special:UserLogin|anmelden of n gebrukerszied anmaken]].',
+'nocreate-loggedin' => 'Je hebben gien toestemming um nieje ziejen an te maken.',
'sectioneditnotsupported-title' => 't Bewarken van seksies wörden niet ondersteund',
-'sectioneditnotsupported-text' => 'Je kunnen op disse pagina gien seksies bewarken.',
+'sectioneditnotsupported-text' => 'Je kunnen op disse zied gien seksies bewarken.',
'permissionserrors' => 'Fouten mit de rechten',
'permissionserrorstext' => 'Je maggen of kunnen dit niet doon. De {{PLURAL:$1|reden|redens}} daorveur {{PLURAL:$1|is|bin}}:',
'permissionserrorstext-withaction' => 'Je hebben gien rech um $2, mit de volgende {{PLURAL:$1|reden|redens}}:',
-'recreate-moveddeleted-warn' => "'''Waorschuwing: je maken n pagina an die eerder al vortedaon is.'''
+'recreate-moveddeleted-warn' => "'''Waorschuwing: je maken n zied an die eerder al vortedaon is.'''
-Bedenk eers of t neudig is um disse pagina veerder te bewarken.
-Veur de dudelikheid steet hieronder t vortdologboek en t herneumlogboek veur disse pagina:",
-'moveddeleted-notice' => 'Disse pagina is vortedaon.
+Bedenk eerst of t neudig is um disse zied veerder te bewarken.
+Veur de dudelikheid steet hieronder t vortdologboek en t herneumlogboek veur disse zied:",
+'moveddeleted-notice' => 'Disse zied is vortedaon.
Hieronder steet de informasie uut t vortdologboek en t herneumlogboek.',
'log-fulllog' => 't Hele logboek bekieken',
'edit-hook-aborted' => 'De bewarking is aofebreuken deur n hook.
Der is gien reden op-egeven.',
-'edit-gone-missing' => 'De pagina kon niet bie-ewörken wörden.
+'edit-gone-missing' => 'De zied kon niet bie-ewörken wörden.
t Schient dat t vortedaon is.',
-'edit-conflict' => 'Bewarkingskonflikt.',
+'edit-conflict' => 'Tegelieke bewörken.',
'edit-no-change' => 'Joew bewarking is enegeerd, umdat der gien wieziging an de tekste edaon is.',
-'edit-already-exists' => 'De pagina kon niet an-emaakt wörden.
+'edit-already-exists' => 'De zied kon niet an-emaakt wörden.
t Besteet al.',
'defaultmessagetext' => 'Standardtekste',
# Parser/template warnings
-'expensive-parserfunction-warning' => 'Waorschuwing: disse pagina gebruukt te veule kostbaore parserfunksies.
+'expensive-parserfunction-warning' => 'Waorschuwing: disse zied gebruukt te veule kostbaore parserfunksies.
Noen {{PLURAL:$1|is|bin}} t der $1, terwiel t der minder as $2 {{PLURAL:$2|mut|mutten}} ween.',
-'expensive-parserfunction-category' => "Pagina's die te veule kostbaore parserfunksies gebruken",
+'expensive-parserfunction-category' => 'Ziejen die te veule kostbaore parserfunksies gebruken',
'post-expand-template-inclusion-warning' => 'Waorschuwing: de grootte van de in-evoegden mal is te groot.
Sommigen mallen wörden niet in-evoegd.',
-'post-expand-template-inclusion-category' => "Pagina's die over de maximumgrootte veur in-evoegden mallen hinne gaon",
-'post-expand-template-argument-warning' => 'Waorschuwing: disse pagina gebruuk tenminsten één parameter in n mal, die te groot is as t uuteklap wörden. Disse parameters wörden vorteleuten.',
-'post-expand-template-argument-category' => "Pagina's mit ontbrekende malelementen",
+'post-expand-template-inclusion-category' => 'Ziejen die over de maximumgrootte veur in-evoegden mallen hinne gaon',
+'post-expand-template-argument-warning' => 'Waorschuwing: disse zied gebruuk tenminsten één parameter in n mal, die te groot is as t uuteklap wörden. Disse parameters wörden vorteleuten.',
+'post-expand-template-argument-category' => 'Ziejen mit ontbrekende malelementen',
'parser-template-loop-warning' => 'Der is n kringloop in mallen waoreneumen: [[$1]]',
'parser-template-recursion-depth-warning' => 'Der is over de rekursiediepte veur mallen is hinne gaon ($1)',
'language-converter-depth-warning' => 'Je hebben t dieptelimiet veur de taalumzetter bereikt ($1)',
-'node-count-exceeded-category' => "Pagina's die t knuppenantal overschrejen hebben.",
-'node-count-exceeded-warning' => 'Op de pagina is t maximale antal nodes overschrejen',
-'expansion-depth-exceeded-category' => "Pagina's waor de expansiediepte overschrejen is",
-'expansion-depth-exceeded-warning' => 'In disse pagina staon te veul mallen',
+'node-count-exceeded-category' => 'Ziejen die t knuppenantal overschrejen hebben.',
+'node-count-exceeded-warning' => 'Op de zied is t maximale antal nodes overschrejen',
+'expansion-depth-exceeded-category' => 'Ziejen waor de expansiediepte overschrejen is',
+'expansion-depth-exceeded-warning' => 'Op disse zied staon te veule mallen',
'parser-unstrip-loop-warning' => 'Der is n "unstrip"-lusse evunnen',
'parser-unstrip-recursion-limit' => 'De rekursielimiet ($1) veur "unstrip" is overschrejen',
# "Undo" feature
-'undo-success' => 'De bewarking kan weerummedreid wörden. Kiek de vergelieking hieronder nao um der wisse van de ween dat alles goed is, en slao de de pagina op um de bewarking weerumme te dreien.',
+'undo-success' => 'De bewarking kan weerummedreid wörden. Kiek de vergelieking hieronder nao um der wisse van de ween dat alles goed is, en slao de de zied op um de bewarking weerumme te dreien.',
'undo-failure' => 'De wieziging kon niet weerummedreid wörden umdat t ondertussen awweer ewiezigd is.',
'undo-norev' => 'De bewarking kon niet weerummedreid wörden, umdat t niet besteet of vortedaon is.',
'undo-summary' => 'Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|overleg]]) weerummedreid.',
De deur $3 op-egeven reden is ''$2''",
# History pages
-'viewpagelogs' => 'Bekiek logboeken veur disse pagina',
-'nohistory' => 'Der bin gien eerdere versies van disse pagina.',
+'viewpagelogs' => 'Bekiek logboeken veur disse zied',
+'nohistory' => 'Der bin gien eerdere versies van disse zied.',
'currentrev' => 'Leste versie',
'currentrev-asof' => 'Leste versie van $1',
'revisionasof' => 'Versie op $1',
# Revision feed
'history-feed-title' => 'Wiezigingsoverzichte',
-'history-feed-description' => 'Wiezigingsoverzichte veur disse pagina op de wiki',
+'history-feed-description' => 'Wiezigingsoverzichte veur disse zied op de wiki',
'history-feed-item-nocomment' => '$1 op $2',
-'history-feed-empty' => "De op-evreugen pagina besteet niet. t Kan ween dat disse pagina vortedaon is of dat t herneumd is. Probeer te [[Special:Search|zeuken]] naor soortgelieke nieje pagina's.",
+'history-feed-empty' => 'De op-evreugen zied besteet niet. t Kan ween dat disse zied vortedaon is of dat t herneumd is. Probeer te [[Special:Search|zeuken]] naor soortgelieke nieje ziejen.',
# Revision deletion
'rev-deleted-comment' => '(bewarkingsopmarking vortedaon)',
'revdel-restore' => 'Zichtbaorheid wiezigen',
'revdel-restore-deleted' => 'vortedaone versies',
'revdel-restore-visible' => 'zichtbaore versies',
-'pagehist' => 'Paginageschiedenisse',
+'pagehist' => 'Ziedgeschiedenisse',
'deletedhist' => 'Geschiedenisse die vortehaold is',
'revdelete-hide-current' => 'Fout bie t verbargen van t objekt van $1 um $2 uur: dit is de versie van noen.
Disse versie kan niet verbörgen wörden.',
Je hebben gien toegang tot dit objekt.',
'revdelete-modify-no-access' => 'Fout bie t wiezigen van t objekt van $1 um $2 uur: dit objekt is emarkeerd as "beveilig".
Je hebben gien toegang tot dit objekt.',
-'revdelete-modify-missing' => 'Fout bie t wiezigen van versienummer $1: t kump niet veur in de databanke!',
+'revdelete-modify-missing' => 'Fout bie t wiezigen van versienummer $1: t kömp niet veur in de databanke!',
'revdelete-no-change' => "'''Waorschuwing:''' t objekt van $1 um $2 uur had al de an-egeven zichtbaorheidsinstellingen.",
'revdelete-concurrent-change' => 'Fout bie t wiezigen van t objekt van $1 um $2 uur: de staotus is inmiddels ewiezigd deur n aander.
Kiek de logboeken nao.',
# Suppression log
'suppressionlog' => 'Verbargingslogboek',
-'suppressionlogtext' => "In de onderstaande lieste staon de vortedaone pagina's en blokkeringen die veur beheerders verbörgen bin.
-In de [[Special:BlockList|blokkeerlieste]] bin de blokkeringen, die noen van toepassige bin, te bekieken.",
+'suppressionlogtext' => 'In de onderstaande lieste staon de vortedaone ziejen en blokkeringen die veur beheerders verbörgen bin.
+In de [[Special:BlockList|blokkeerlieste]] bin de blokkeringen, die noen van toepassige bin, te bekieken.',
# History merging
-'mergehistory' => "Geschiedenisse van pagina's bie mekaar doon",
-'mergehistory-header' => "Via disse pagina ku'j versies uut de geschiedenisse van n bronpagina mit n niejere pagina samenvoegen. Zörg derveur dat disse versies uut de geschiedenisse histories juus bin.",
-'mergehistory-box' => "Geschiedenisse van twee pagina's bie mekaar doon:",
-'mergehistory-from' => 'Bronpagina:',
-'mergehistory-into' => 'Bestemmingspagina:',
+'mergehistory' => 'Geschiedenisse van ziejen bie mekaar doon',
+'mergehistory-header' => "Via disse zied ku'j versies uut de geschiedenisse van n bronzied mit n niejere zied samenvoegen. Zörg derveur dat disse versies uut de geschiedenisse histories juus bin.",
+'mergehistory-box' => 'Geschiedenisse van twee ziejen bie mekaar doon:',
+'mergehistory-from' => 'Bronzied:',
+'mergehistory-into' => 'Bestemmingszied:',
'mergehistory-list' => 'Bewarkingsgeschiedenisse die bie mekaar edaon kan wörden',
'mergehistory-merge' => 'De volgende versies van [[:$1]] kunnen samenevoegd wörden naor [[:$2]]. Gebruuk de kolom mit keuzerondjes um allinnig de versies die emaak bin op en veur de an-egeven tied samen te voegen. Let op dat t gebruken van de navigasieverwiezingen disse kolom zal herinstellen.',
'mergehistory-go' => 'Bekiek bewarkingen die bie mekaar edaon kunnen wörden',
'mergehistory-submit' => 'Versies bie mekaar doon',
'mergehistory-empty' => 'Der bin gien versies die samenevoegd kunnen wörden.',
'mergehistory-success' => '$3 {{PLURAL:$3|versie|versies}} van [[:$1]] bin suksesvol samenevoegd naor [[:$2]].',
-'mergehistory-fail' => 'Kan gien geschiedenisse samenvoegen, kiek opniej de pagina- en tiedparameters nao.',
-'mergehistory-no-source' => 'Bronpagina $1 besteet niet.',
-'mergehistory-no-destination' => 'Bestemmingspagina $1 besteet niet.',
-'mergehistory-invalid-source' => 'De bronpagina mut n geldige titel ween.',
-'mergehistory-invalid-destination' => 'De bestemmingspagina mut n geldige titel ween.',
+'mergehistory-fail' => 'Kan gien geschiedenisse samenvoegen, kiek opniej de zied- en tiedparameters nao.',
+'mergehistory-no-source' => 'Bronzied $1 besteet niet.',
+'mergehistory-no-destination' => 'Bestemmingszied $1 besteet niet.',
+'mergehistory-invalid-source' => 'De bronzied mut n geldige titel ween.',
+'mergehistory-invalid-destination' => 'De bestemmingszied mut n geldige titel ween.',
'mergehistory-autocomment' => '[[:$1]] samenevoegd naor [[:$2]]',
'mergehistory-comment' => '[[:$1]] samenevoegd naor [[:$2]]: $3',
-'mergehistory-same-destination' => 'De bronpagina en doelpagina kunnen niet t zelfde ween',
+'mergehistory-same-destination' => 'De bronzied en doelzied kunnen niet t zelfde ween',
'mergehistory-reason' => 'Reden:',
# Merge log
'mergelog' => 'Samenvoegingslogboek',
'pagemerge-logentry' => 'voegen [[$1]] naor [[$2]] samen (versies tot en mit $3)',
'revertmerge' => 'Samenvoeging weerummedreien',
-'mergelogpagetext' => "Hieronder zie'j n lieste van de leste samenvoegingen van n paginageschiedenisse naor n aandere.",
+'mergelogpagetext' => "Hieronder zie'j n lieste van de leste samenvoegingen van n ziedgeschiedenisse naor n aandere.",
# Diffs
'history-title' => 'Versiegeschiedenisse van "$1"',
'difference-title' => 'Verschil tussen versies van "$1"',
-'difference-title-multipage' => 'Verschil tussen pagina\'s "$1" en "$2"',
-'difference-multipage' => "(Verschil tussen pagina's)",
+'difference-title-multipage' => 'Verschil tussen ziejen "$1" en "$2"',
+'difference-multipage' => '(Verschil tussen ziejen)',
'lineno' => 'Regel $1:',
'compareselectedversions' => 'Vergeliek de ekeuzen versies',
'showhideselectedversions' => 'Ekeuzen versies bekieken/verbargen',
'searchresults' => 'Zeukresultaoten',
'searchresults-title' => 'Zeukresultaoten veur "$1"',
'searchresulttext' => 'Veur meer informasie over zeuken op {{SITENAME}}, zie [[{{MediaWiki:Helppage}}|{{int:help}}]].',
-'searchsubtitle' => 'Je zöchten naor \'\'\'[[:$1]]\'\'\' ([[Special:Prefixindex/$1|alle pagina\'s die beginnen mit "$1"]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|alle pagina\'s die verwiezen naor "$1"]])',
+'searchsubtitle' => 'Je zöchten naor \'\'\'[[:$1]]\'\'\' ([[Special:Prefixindex/$1|alle ziejen die beginnen mit "$1"]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|alle ziejen die verwiezen naor "$1"]])',
'searchsubtitleinvalid' => 'Veur zeukopdrachte "$1"',
'toomanymatches' => 'Der waren te veule resultaoten. Probeer n aandere zeukopdrachte.',
'titlematches' => 'Overeenkomst mit t onderwarp',
'nextn' => 'volgende {{PLURAL:$1|$1}}',
'prevn-title' => '{{PLURAL:$1|Veurig resultaot|Veurige $1 resultaoten}}',
'nextn-title' => '{{PLURAL:$1|Volgend resultaot|Volgende $1 resultaoten}}',
-'shown-title' => 'Laot $1 {{PLURAL:$1|resultaot|resultaoten}} per pagina zien',
+'shown-title' => 'Laot $1 {{PLURAL:$1|resultaot|resultaoten}} per zied zien',
'viewprevnext' => '($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-legend' => 'Zeukopsies',
-'searchmenu-exists' => "* Pagina '''[[$1]]'''",
-'searchmenu-new' => "'''De pagina \"[[:\$1]]\" op disse wiki anmaken!'''",
+'searchmenu-exists' => "'''Der is n zied mit de naam \"[[:\$1]]\" op disse wiki.'''",
+'searchmenu-new' => "'''De zied \"[[:\$1]]\" op disse wiki anmaken!'''",
'searchhelp-url' => 'Help:Inhold',
-'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Paginanamen mit dit veurvoegsel laoten zien]]',
+'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Ziednamen mit dit veurvoegsel laoten zien]]',
'searchprofile-articles' => 'Artikels',
-'searchprofile-project' => "Hulp- en projektpagina's",
+'searchprofile-project' => 'Hulp- en projektziejen',
'searchprofile-images' => 'Multimedia',
'searchprofile-everything' => 'Alles',
'searchprofile-advanced' => 'Uutebreid',
'searchprofile-articles-tooltip' => 'Zeuken in $1',
'searchprofile-project-tooltip' => 'Zeuken in $1',
'searchprofile-images-tooltip' => 'Zeuken naor bestaanden',
-'searchprofile-everything-tooltip' => "Alle inhoud deurzeuken (oek overlegpagina's)",
+'searchprofile-everything-tooltip' => 'Alle inhoud deurzeuken (oek overlegziejen)',
'searchprofile-advanced-tooltip' => 'Zeuken in de an-egeven naamruumtes',
'search-result-size' => '$1 ({{PLURAL:$2|1 woord|$2 woorden}})',
'search-result-category-size' => '{{PLURAL:$1|1 kategorielid|$1 kategorielejen}} ({{PLURAL:$2|1 onderkategorie|$2 onderkategorieën}}, {{PLURAL:$3|1 bestaand|$3 bestaanden}})',
'showingresults' => "Hieronder {{PLURAL:$1|steet '''1''' resultaot|staon '''$1''' resultaoten}} <b>$1</b> vanaof nummer <b>$2</b>.",
'showingresultsnum' => "Hieronder {{PLURAL:$3|steet '''1''' resultaot|staon '''$3''' resultaoten}} vanaof nummer '''$2'''.",
'showingresultsheader' => "{{PLURAL:$5|Resultaot '''$1''' van '''$3'''|Resultaoten '''$1 - $2''' van '''$3'''}} veur '''$4'''",
-'nonefound' => "<strong>Let wel:</strong> standard wörden niet alle naamruumtes deurzöcht. A'j in zeukopdrachte as veurvoegsel \"''all:'' gebruken wörden alle pagina's deurzöcht (oek overlegpagina's, mallen en gao zo mer deur). Je kunnen oek n naamruumte as veurvoegsel gebruken.",
+'nonefound' => "<strong>Let wel:</strong> standard wörden niet alle naamruumtes deurzöcht. A'j in zeukopdrachte as veurvoegsel \"''all:'' gebruken wörden alle ziejen deurzöcht (oek overlegziejen, mallen en gao zo mer deur). Je kunnen oek n naamruumte as veurvoegsel gebruken.",
'search-nonefound' => 'Der bin gien resultaoten veur de zeukopdrachte.',
'powersearch' => 'Zeuk',
'powersearch-legend' => 'Uutebreid zeuken',
'searchdisabled' => 'Zeuken in {{SITENAME}} is niet meugelik. Je kunnen gebruukmaken van Google. De gegevens over {{SITENAME}} bin misschien niet bie-ewörken.',
# Quickbar
-'qbsettings' => 'Paginalieste',
+'qbsettings' => 'Lieste mit ziejen',
'qbsettings-none' => 'Gien',
'qbsettings-fixedleft' => 'Links, vaste',
'qbsettings-fixedright' => 'Rechts, vaste',
'prefs-beta' => 'Bètafunksies',
'prefs-datetime' => 'Daotum en tied',
'prefs-labs' => 'Alphafunksies',
-'prefs-user-pages' => "Gebrukerspagina's",
+'prefs-user-pages' => 'Gebrukersziejen',
'prefs-personal' => 'Gebrukersgegevens',
'prefs-rc' => 'Leste wiezigingen',
'prefs-watchlist' => 'Volglieste',
'prefs-changeemail' => 'Netpostadres wiezigen',
'prefs-setemail' => 'Stel n netpostadres in',
'prefs-email' => 'Instellingen veur netpost',
-'prefs-rendering' => 'Paginaweergave',
+'prefs-rendering' => 'Ziedweergave',
'saveprefs' => 'Veurkeuren opslaon',
'resetprefs' => 'Standardveurkeuren herstellen',
'restoreprefs' => 'Alle standardinstellingen weerummezetten',
'rows' => 'Regels',
'columns' => 'Kolommen',
'searchresultshead' => 'Zeukresultaoten',
-'resultsperpage' => 'Antal zeukresultaoten per pagina',
+'resultsperpage' => 'Antal zeukresultaoten per zied',
'stub-threshold' => 'Verwiezingsformattering van <a href="#" class="stub">beginnetjes</a>:',
-'stub-threshold-disabled' => 'uuteschakeld',
-'recentchangesdays' => 'Antal dagen die de lieste "leste wiezigingen" laot zien:',
+'stub-threshold-disabled' => 'uutezet',
+'recentchangesdays' => 'Antal dagen die "Leste wiezigingen" löt zien:',
'recentchangesdays-max' => '(hooguut $1 {{PLURAL:$1|dag|dagen}})',
'recentchangescount' => 'Standard antal bewarkingen um te laoten zien:',
-'prefs-help-recentchangescount' => "Dit geldt veur leste wiezigingen, paginageschiedenisse en logboekpagina's",
+'prefs-help-recentchangescount' => 'Dit geldt veur leste wiezigingen, ziedgeschiedenisse en logboekziejen',
'prefs-help-watchlist-token' => "A'j in dit veld n geheime kode invullen, dan maakt t RSS-voer an veur joew volglieste.
Iederene die disse kode weet kan joew volglieste bekieken, kies dus n veilige kode.
Je kunnen oek disse egenereren standardkode gebruken: $1",
'prefs-custom-css' => 'Persoonlike CSS',
'prefs-custom-js' => 'Persoonlike JS',
'prefs-common-css-js' => 'Edeelden CSS/JS veur elke vormgeving:',
-'prefs-reset-intro' => 'Je kunnen disse pagina gebruken um joew veurkeuren naor de standardinstellingen weerumme te zetten.
+'prefs-reset-intro' => 'Je kunnen disse zied gebruken um joew veurkeuren naor de standardinstellingen weerumme te zetten.
Disse haandeling kan niet ongedaonemaakt wörden.',
'prefs-emailconfirm-label' => 'Netpostbevestiging:',
'prefs-textboxsize' => 'Aofmetingen bewarkingsscharm',
'yourrealname' => 'Echte naam (niet verplicht)',
'yourlanguage' => 'Taal veur systeemteksten',
'yourvariant' => 'Taalvariaant veur inhoud:',
-'prefs-help-variant' => "Joew veurkeursvariaant of -spelling um de inhoudspagina's van disse wiki in weer te geven.",
+'prefs-help-variant' => 'Joew veurkeursvariaant of -spelling um de inhoudsziejen van disse wiki in weer te geven.',
'yournick' => 'Alias veur ondertekeningen',
-'prefs-help-signature' => 'Reaksies op de overlegpagina\'s mutten ondertekend wörden mit "<nowiki>~~~~</nowiki>", dit wörden dan ummezet in joew ondertekening mit daorbie de daotum en tied van de bewarking.',
+'prefs-help-signature' => 'Reaksies op de overlegziejen mutten ondertekend wörden mit "<nowiki>~~~~</nowiki>", dit wörden dan ummezet in joew ondertekening mit daorbie de daotum en tied van de bewarking.',
'badsig' => 'Ongeldige haandtekening; HTML naokieken.',
'badsiglength' => 'Joew haandtekening is te lang.
t Mut minder as {{PLURAL:$1|letter|letters}} hebben.',
'email' => 'Privéberichten',
'prefs-help-realname' => "* Echte naam (niet verplicht): a'j disse opsie invullen zu'w joew echte naam gebruken um erkenning te geven veur joew warkzaamheen.",
'prefs-help-email' => "n Netpostadres is niet verplicht, mer zo ku'w wel joew wachtwoord toesturen veur a'j t vergeten bin.",
-'prefs-help-email-others' => "Je kunnen oek aandere meensen de meugelikheid geven um kontakt mit joe op te nemen mit n verwiezing op joew gebrukers- en overlegpagina zonder da'j de identiteit pries hoeven te geven.",
+'prefs-help-email-others' => "Je kunnen oek aandere meensen de meugelikheid geven um kontakt mit joe op te nemen mit n verwiezing op joew gebrukers- en overlegzied zonder da'j de identiteit pries hoeven te geven.",
'prefs-help-email-required' => "Hier he'w n netpostadres veur neudig.",
'prefs-info' => 'Baosisinformasie',
'prefs-i18n' => 'Taalinstellingen',
'grouppage-suppress' => '{{ns:project}}:Toezichte',
# Rights
-'right-read' => "Pagina's bekieken",
-'right-edit' => "Pagina's bewarken",
-'right-createpage' => "Pagina's anmaken",
-'right-createtalk' => "Overlegpagina's anmaken",
+'right-read' => 'Ziejen bekieken',
+'right-edit' => 'Ziejen bewarken',
+'right-createpage' => 'Ziejen anmaken',
+'right-createtalk' => 'Overlegziejen anmaken',
'right-createaccount' => 'Nieje gebrukers anmaken',
'right-minoredit' => 'Bewarkingen markeren as klein',
-'right-move' => "Pagina's herneumen",
-'right-move-subpages' => "Pagina's samen mit subpagina's verplaotsen",
-'right-move-rootuserpages' => "Gebrukerspagina's van t hoogste nivo herneumen",
+'right-move' => 'Ziejen herneumen',
+'right-move-subpages' => 'Ziejen samen mit de ziejen die deronder hangen verplaotsen',
+'right-move-rootuserpages' => 'Gebrukersziejen van t hoogste nivo herneumen',
'right-movefile' => 'Bestaanden herneumen',
-'right-suppressredirect' => 'Gien deurverwiezing anmaken op de ouwe naam as n pagina herneumd wörden',
+'right-suppressredirect' => 'Gien deurverwiezing anmaken op de ouwe naam as n zied herneumd wörden',
'right-upload' => 'Bestaanden opsturen',
'right-reupload' => 'n Bestaond bestaand overschrieven',
'right-reupload-own' => "Bestaanden overschrieven die'j der zelf bie ezet hebben",
'right-reupload-shared' => 'Media uut de edeelden mediadatabanke plaotselik overschrieven',
'right-upload_by_url' => 'Bestaanden inlaojen via n webadres',
-'right-purge' => 't Tussengeheugen van n pagina legen',
+'right-purge' => 't Tussengeheugen van n zied legen',
'right-autoconfirmed' => 'Behaandeld wörden as n an-emelde gebruker',
'right-bot' => 'Behaandeld wörden as n eautomatiseerd preces',
-'right-nominornewtalk' => "Kleine bewarkingen an n overlegpagina leien niet tot n melding 'nieje berichten'",
+'right-nominornewtalk' => "Kleine bewarkingen an n overlegzied leien niet tot n melding 'nieje berichten'",
'right-apihighlimits' => 'Hoge API-limieten gebruken',
'right-writeapi' => 'Bewarken via de API',
-'right-delete' => "Pagina's vortdoon",
-'right-bigdelete' => "Pagina's mit n grote geschiedenisse vortdoon",
-'right-deleterevision' => "Versies van pagina's verbargen",
+'right-delete' => 'Ziejen vortdoon',
+'right-bigdelete' => 'Ziejen mit n grote geschiedenisse vortdoon',
+'right-deletelogentry' => 'Bepaolde logboekregels vortdoon en weerummeplaotsen',
+'right-deleterevision' => 'Versies van ziejen verbargen',
'right-deletedhistory' => 'Vortedaone versies bekieken, zonder te kunnen zien wat der vortedaon is',
'right-deletedtext' => 'Bekiek vortedaone tekste en wiezigingen tussen vortedaone versies',
-'right-browsearchive' => "Vortedaone pagina's bekieken",
-'right-undelete' => "Vortedaone pagina's weerummeplaotsen",
+'right-browsearchive' => 'Vortedaone ziejen bekieken',
+'right-undelete' => 'Vortedaone ziejen weerummeplaotsen',
'right-suppressrevision' => 'Verbörgen versies bekieken en weerummeplaotsen',
'right-suppressionlog' => 'Niet-publieke logboeken bekieken',
'right-block' => 'Aandere gebrukers de meugelikheid ontnemen um te bewarken',
'right-blockemail' => 'n Gebruker t recht ontnemen um berichjes te versturen',
-'right-hideuser' => 'n Gebruker veur de overige gebrukers verbargen',
+'right-hideuser' => 'n Gebruker veur de aandere gebrukers verbargen',
'right-ipblock-exempt' => 'IP-blokkeringen ummezeilen',
'right-proxyunbannable' => "Blokkeringen veur proxy's gelden niet",
'right-unblockself' => 'Eigen gebruker deblokkeren',
'right-protect' => "Beveiligingsnivo's wiezigen",
-'right-editprotected' => "Beveiligden pagina's bewarken",
+'right-editprotected' => 'Beveiligden ziejen bewarken',
'right-editinterface' => 't {{SITENAME}}-uterlik bewarken',
'right-editusercssjs' => 'De CSS- en JS-bestaanden van aandere gebrukers bewarken',
'right-editusercss' => 'De CSS-bestaanden van aandere gebrukers bewarken',
'right-edituserjs' => 'De JS-bestaanden van aandere gebrukers bewarken',
-'right-rollback' => 'Gauw de leste bewarking(en) van n gebruker an n pagina weerummedreien',
+'right-rollback' => 'Gauw de leste bewarking(en) van n gebruker an n zied weerummedreien',
'right-markbotedits' => 'Weerummedreien bewarkingen markeren as botbewarkingen',
'right-noratelimit' => 'Hef gien tiedsaofhankelike beparkingen',
-'right-import' => "Pagina's uut aandere wiki's invoeren",
-'right-importupload' => "Pagina's vanuut n bestaand invoeren",
-'right-patrol' => 'Bewarkingen as ekontroleerd markeren',
-'right-autopatrol' => 'Bewarkingen wörden automaties as ekontroleerd emarkeerd',
-'right-patrolmarks' => 'Kontroletekens in leste wiezigingen bekieken',
-'right-unwatchedpages' => "Bekiek n lieste mit pagina's die niet op n volglieste staon",
-'right-mergehistory' => "De geschiedenisse van pagina's bie mekaar doon",
+'right-import' => "Ziejen uut aandere wiki's invoeren",
+'right-importupload' => 'Ziejen vanuut n bestaand invoeren',
+'right-patrol' => 'Bewarkingen as nao-ekeken markeren',
+'right-autopatrol' => 'Bewarkingen wörden automaties op nao-ekeken ezet',
+'right-patrolmarks' => 'Naokiektekens in "Leste wiezigingen" bekieken',
+'right-unwatchedpages' => 'Bekiek n lieste mit ziejen die niet op n volglieste staon',
+'right-mergehistory' => 'De geschiedenisse van ziejen bie mekaar doon',
'right-userrights' => 'Alle gebrukersrechten bewarken',
'right-userrights-interwiki' => "Gebrukersrechten van gebrukers in aandere wiki's wiezigen",
'right-siteadmin' => 'De databanke blokkeren en weer vriegeven',
-'right-override-export-depth' => "Pagina's uutvoeren, oek de pagina's waor naor verwezen wörden, tot n diepte van 5",
+'right-override-export-depth' => 'Ziejen uutvoeren, oek de ziejen waor naor verwezen wörden, tot n diepte van 5',
'right-sendemail' => 'Bericht versturen naor aandere gebrukers',
'right-passwordreset' => 'Bekiek netpostberichten veur t opniej instellen van joew wachtwoord',
'rightsnone' => '(gien)',
# Associated actions - in the sentence "You do not have permission to X"
-'action-read' => 'disse pagina lezen',
-'action-edit' => 'disse pagina bewarken',
-'action-createpage' => "pagina's schrieven",
-'action-createtalk' => "overlegpagina's anmaken",
+'action-read' => 'disse zied lezen',
+'action-edit' => 'disse zied bewarken',
+'action-createpage' => 'ziejen schrieven',
+'action-createtalk' => 'overlegziejen anmaken',
'action-createaccount' => 'disse gebruker anmaken',
'action-minoredit' => 'disse bewarking as klein markeren',
-'action-move' => 'disse pagina herneumen',
-'action-move-subpages' => "disse pagina en de biebeheurende subpagina's herneumen",
-'action-move-rootuserpages' => "gebrukerspagina's van t hoogste nivo herneumen",
+'action-move' => 'disse zied herneumen',
+'action-move-subpages' => 'disse zied en de biebeheurende ziejen die deronder hangen herneumen',
+'action-move-rootuserpages' => 'gebrukersziejen van t hoogste nivo herneumen',
'action-movefile' => 'dit bestaand herneumen',
'action-upload' => 'dit bestaand opsturen',
'action-reupload' => 'dit bestaonde bestaand overschrieven',
'action-reupload-shared' => 'n aander bestaand over dit bestaand uut de edeelden mediadatabanke hinne zetten.',
'action-upload_by_url' => 'dit bestaand vanaof n webadres inlaojen',
'action-writeapi' => 'de schrief-API bewarken',
-'action-delete' => 'disse pagina vortdoon',
+'action-delete' => 'disse zied vortdoon',
'action-deleterevision' => 'disse versie vortdoon',
-'action-deletedhistory' => 'de vortedaone versies van disse pagina bekieken',
-'action-browsearchive' => "vortedaone pagina's zeuken",
-'action-undelete' => 'disse pagina weerummeplaotsen',
+'action-deletedhistory' => 'de vortedaone versies van disse zied bekieken',
+'action-browsearchive' => 'vortedaone ziejen zeuken',
+'action-undelete' => 'disse zied weerummeplaotsen',
'action-suppressrevision' => 'disse verbörgen versie bekieken en weerummeplaotsen',
'action-suppressionlog' => 'dit bescharmde logboek bekieken',
'action-block' => 'disse gebruker blokkeren',
-'action-protect' => 't beveiligingsnivo van disse pagina anpassen',
-'action-rollback' => 'bewarkingen van de leste gebruker die n pagina hef ewiezigd rap weerummedreien',
-'action-import' => 'disse pagina van n aandere wiki invoeren',
-'action-importupload' => 'disse pagina invoeren vanaof n op-estuurd bestaand',
-'action-patrol' => 'bewarkingen van aander volk as ekontroleerd markeren',
-'action-autopatrol' => 'eigen bewarkingen as ekontroleerd markeren',
-'action-unwatchedpages' => "bekiek de liest mit pagina's die niet evolgd wörden",
-'action-mergehistory' => 'de geschiedenisse van disse pagina samenvoegen',
+'action-protect' => 't beveiligingsnivo van disse zied anpassen',
+'action-rollback' => 'bewarkingen van de leste gebruker die n zied hef ewiezigd rap weerummedreien',
+'action-import' => 'disse zied van n aandere wiki invoeren',
+'action-importupload' => 'disse zied invoeren vanaof n op-estuurd bestaand',
+'action-patrol' => 'bewarkingen van aandere luui op nao-ekeken zetten',
+'action-autopatrol' => 'eigen bewarkingen op nao-ekeken zetten',
+'action-unwatchedpages' => 'bekiek de lieste mit ziejen die niet evolgd wörden',
+'action-mergehistory' => 'de geschiedenisse van disse zied samenvoegen',
'action-userrights' => 'alle gebrukersrechten bewarken',
'action-userrights-interwiki' => "de rechten van gebrukers op aandere wiki's bewarken",
'action-siteadmin' => 'de databanke blokkeren of vriegeven',
'nchanges' => '$1 {{PLURAL:$1|wieziging|wiezigingen}}',
'recentchanges' => 'Leste wiezigingen',
'recentchanges-legend' => 'Opsies veur leste wiezigingen',
-'recentchanges-summary' => 'Op disse pagina ku-j de leste wiezigingen van disse wiki bekieken.',
+'recentchanges-summary' => "Op disse zied ku'j de leste wiezigingen van disse wiki bekieken.",
'recentchanges-feed-description' => 'Zeuk naor de alderleste wiezingen op disse wiki in disse voer.',
-'recentchanges-label-newpage' => 'Mit disse bewarking is n nieje pagina an-emaakt',
+'recentchanges-label-newpage' => 'Mit disse bewarking is n nieje zied an-emaakt',
'recentchanges-label-minor' => 'Dit is n kleine wieziging',
'recentchanges-label-bot' => 'Disse bewarking is uutevoerd deur n bot',
'recentchanges-label-unpatrolled' => 'Disse bewarking is nog niet nao-ekeken',
'recentchangeslinked-feed' => 'Volg verwiezigingen',
'recentchangeslinked-toolbox' => 'Volg verwiezigingen',
'recentchangeslinked-title' => 'Wiezigingen verwaant an $1',
-'recentchangeslinked-noresult' => 'Gien wiezigingen of pagina waornaor verwezen wörden in disse periode.',
-'recentchangeslinked-summary' => "Op disse spesiale pagina steet n lieste mit de leste wieziginen op pagina's waornaor verwezen wörden. Pagina's op [[Special:Watchlist|joew volglieste]] staon '''vet-edrokt'''.",
-'recentchangeslinked-page' => 'Paginanaam:',
-'recentchangeslinked-to' => "Bekiek wiezigingen op pagina's mit verwiezingen naor disse pagina",
+'recentchangeslinked-noresult' => 'Gien wiezigingen of zied waornaor verwezen wörden in disse periode.',
+'recentchangeslinked-summary' => "Op disse spesiale zied steet n lieste mit de leste wieziginen op ziejen waornaor verwezen wörden. Ziejen op [[Special:Watchlist|joew volglieste]] staon '''vet-edrokt'''.",
+'recentchangeslinked-page' => 'Ziednaam:',
+'recentchangeslinked-to' => 'Bekiek wiezigingen op ziejen mit verwiezingen naor disse zied',
# Upload
'upload' => 'Bestaand opsturen',
'uploadbtn' => 'Bestaand opsturen',
-'reuploaddesc' => 'Weerumme naor t bestaandinlaodformulier.',
+'reuploaddesc' => 'Weerumme naor de opstuurzied',
'upload-tryagain' => 'Bestaandsbeschrieving biewarken',
'uploadnologin' => 'Niet an-emeld',
'uploadnologintext' => 'Je mutten [[Special:UserLogin|an-emeld]] ween um bestaanden toe te kunnen voegen.',
-'upload_directory_missing' => 'De bestaandinlaodmap ($1) is vort en kon niet an-emaakt wörden deur de webserver.',
+'upload_directory_missing' => 'De inlaojmap veur bestaanden ($1) is vort en kon niet an-emaakt wörden deur de webserver.',
'upload_directory_read_only' => "Op t moment ku'j gien bestaanden opsturen vanwegen techniese problemen ($1).",
'uploaderror' => 'Fout bie t inlaojen van t bestaand',
'upload-recreate-warning' => "'''Waorschuwing: der is n bestaand mit disse naam vortedaon of herneumd.'''
-Hieronder steet t vortdologboek en t herneumlogboek veur disse pagina:",
+Hieronder steet t vortdologboek en t herneumlogboek veur disse zied:",
'uploadtext' => "Gebruuk t formulier hieronder um bestaanden op te sturen.
Um bestaanden te bekieken of te zeuken die eerder al op-estuurd bin, ku'j naor de [[Special:FileList|bestaandslieste]] gaon.
Bestaanden en media die nao t vortdoon opniej op-estuurd wörden ku'j in de smiezen houwen in t [[Special:Log/upload|logboek mit nieje bestaanden]] en t [[Special:Log/delete|vortdologboek]].
-Um t bestaand in te voegen in n pagina ku'j één van de volgende kodes gebruken:
+Um t bestaand in te voegen in n zied ku'j één van de volgende kodes gebruken:
* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Bestaand.jpg]]</nowiki>'''
* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Bestaand.png|alternetieve tekste]]</nowiki>'''
* '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Bestaand.ogg]]</nowiki>''' drekte verwiezing naor n bestaand.",
'windows-nonascii-filename' => 'Disse wiki ondersteunt gien bestaandsnamen mit spesiale tekens.',
'fileexists' => "n Bestaand mit disse naam besteet al; voeg t bestaand onder n aandere naam toe.
'''<tt>[[:$1]]</tt>''' [[$1|thumb]]",
-'filepageexists' => "De beschrievingspagina veur dit bestaand bestung al op '''<tt>[[:$1]]</tt>''', mer der besteet nog gien bestaand mit disse naam.
-De samenvatting die'j op-egeven hebben zal niet op de beschrievingspagina koemen.
-Bewark de pagina haandmaotig um joew beschrieving daor weer te geven.
+'filepageexists' => "De beschrievingszied veur dit bestaand bestung al op '''<tt>[[:$1]]</tt>''', mer der besteet nog gien bestaand mit disse naam.
+De samenvatting die'j op-egeven hebben zal niet op de beschrievingszied koemen.
+Bewark de zied haandmaotig um joew beschrieving daor weer te geven.
[[$1|thumb]]",
'fileexists-extension' => "n Bestaand mit n soortgelieke naam besteet al: [[$2|thumb]]
* Naam van t bestaand da'j derbie zetten wollen: '''<tt>[[:$1]]</tt>'''
'savefile' => 'Bestaand opslaon',
'uploadedimage' => 'Op-estuurd: [[$1]]',
'overwroteimage' => 'Nieje versie van "[[$1]]" op-estuurd',
-'uploaddisabled' => 't Opsturen van bestaanden is uuteschakeld.',
-'copyuploaddisabled' => 't Opsturen van bestaanden via n webadres is uuteschakeld.',
+'uploaddisabled' => 't Opsturen van bestaanden is uutezet.',
+'copyuploaddisabled' => 't Opsturen van bestaanden via n webadres is uutezet.',
'uploadfromurl-queued' => 'Joew bestaand is in de wachtrie ezet.',
-'uploaddisabledtext' => 't Opsturen van bestaanden is uuteschakeld.',
-'php-uploaddisabledtext' => 't Opsturen van PHP-bestaanden is uuteschakeld. Kiek de instellingen veur t opsturen van bestaanden effen nao.',
+'uploaddisabledtext' => 't Opsturen van bestaanden is uutezet.',
+'php-uploaddisabledtext' => 't Opsturen van PHP-bestaanden is uutezet. Kiek de instellingen veur t opsturen van bestaanden effen nao.',
'uploadscripted' => 'In dit bestaand steet HTML- of skriptkode die verkeerd elezen kan wörden deur de webkieker.',
'uploadvirus' => 'In dit bestaand zit n virus! Gegevens: $1',
'uploadjava' => 't Bestaand is n ZIP-bestaand waor n Java .class-bestaand in zit.
$1',
'upload-warning-subj' => 'Waorschuwing veur t opsturen van bestaanden',
'upload-warning-msg' => 'Der was n probleem mit t inlaojen van t bestaand [$2].
-Gao weerumme naor t [[Special:Upload/stash/$1|bestaandinlaodformulier]] um dit probleem te verhelpen.',
+Gao weerumme naor t [[Special:Upload/stash/$1|opstuurformulier]] um dit probleem te verhelpen.',
'upload-proto-error' => 'Verkeerd protokol',
'upload-proto-error-text' => 'Um op disse maniere bestaanden toe te voegen mutten webadressen beginnen mit <code>http://</code> of <code>ftp://</code>.',
'lockmanager-fail-releaselock' => 'Kon vergrendeling van "$1" der niet aof haolen.',
'lockmanager-fail-db-bucket' => 'Kon niet in kontakt koemen mit genog vergrendelingsdatabanken in de nemmer $1.',
'lockmanager-fail-db-release' => 'Kon de vergrendeling veur de databanke $1 niet deraof haolen.',
+'lockmanager-fail-svr-acquire' => 'Kon gien vergrendeling op server $1 zetten.',
'lockmanager-fail-svr-release' => 'Kon de vergrendeling veur de server $1 der niet aof haolen.',
# ZipDirectoryReader
-'zip-file-open-error' => 'Der is wat fout egaon bie t los doon van t bestaand veur de ZIP-kontrole.',
+'zip-file-open-error' => 'Der is wat fout egaon bie t los doon van t bestaand veur de ZIP-kontraole.',
'zip-wrong-format' => 't Op-egeven bestaand was gien ZIP-bestaand.',
'zip-bad' => 't Bestaand is beschaodig of is n onleesbaor ZIP-bestaand.
-De veiligheid kan niet ekontroleerd wörden.',
+De veiligheid kan niet nao-ekeken wörden.',
'zip-unsupported' => 't Bestaand is n ZIP-bestaand dat gebruukmaak van ZIP-meugelikheen die MediaWiki niet ondersteunt.
-De veiligheid kan niet ekontroleerd wörden.',
+De veiligheid kan niet nao-ekeken wörden.',
# Special:UploadStash
'uploadstash' => 'Verbörgen bestaanden',
-'uploadstash-summary' => 'Disse pagina geef toegang tot bestaanden die op-estuurd bin of nog op-estuurd wörden mer nog niet beschikbaor emaakt bin op de wiki. Disse bestaanden bin allinnig zichtbaor veur de gebruker die ze opstuurt.',
+'uploadstash-summary' => 'Disse zied gif toegang tot bestaanden die op-estuurd bin of nog op-estuurd wörden mer nog niet beschikbaor emaakt bin op de wiki. Disse bestaanden bin allinnig zichtbaor veur de gebruker die ze opstuurt.',
'uploadstash-clear' => 'Verbörgen bestaanden vortdoon',
'uploadstash-nofiles' => 'Der bin gien verbörgen bestaanden.',
-'uploadstash-badtoken' => 't Uutvoeren van de haandeling is mislokt. Dit kump warschienlik deurdat joew bewarkingsreferensies verleupen bin. Probeer t opniej.',
+'uploadstash-badtoken' => 't Uutvoeren van de haandeling is mislokt. Dit kömp waorschienlik deurdat joew bewarkingsreferensies verleupen bin. Probeer t opniej.',
'uploadstash-errclear' => 't Vortdoon van de bestaanden is mislokt.',
'uploadstash-refresh' => 'Lieste mit bestaanden biewarken',
'invalid-chunk-offset' => 'Ongeldig startpunt',
Joew server is niet in-esteld um disse informasie deur te geven.
Misschien gebruukt t CGI, en dan wörden img_auth niet ondersteund.
Zie https://www.mediawiki.org/wiki/Manual:Image_Authorization veur meer informasie.',
-'img-auth-notindir' => 't Op-evreugen pad is niet de in-estelde bestaandinlaodmap',
-'img-auth-badtitle' => 'Kon gien geldige paginanaam maken van "$1".',
+'img-auth-notindir' => 't Op-evreugen pad is niet bekend in de in-estelden inlaojmap',
+'img-auth-badtitle' => 'Kon gien geldige ziednaam maken van "$1".',
'img-auth-nologinnWL' => 'Je bin niet an-emeld en "$1" steet niet op de witte lieste.',
'img-auth-nofile' => 'Bestaand "$1" besteet niet.',
'img-auth-isdir' => 'Je proberen de map "$1" binnen te koemen.
'img-auth-streaming' => 'Bezig mit t streumen van "$1".',
'img-auth-public' => 't Doel van img_auth.php is de uutvoer van bestaanden van n besleuten wiki.
Disse wiki is in-esteld as publieke wiki.
-Um beveiligingsredens is img_auth.php uuteschakeld.',
+Um beveiligingsredens is img_auth.php uutezet.',
'img-auth-noread' => 'De gebruker hef gien leestoegang tot "$1".',
'img-auth-bad-query-string' => 'In t webadres steet n ongeldige zeukopdrachte.',
'upload_source_file' => ' (een bestaand op de hardeschieve)',
# Special:ListFiles
-'listfiles-summary' => "Op disse spesiale pagina ku'j alle bestaanden bekieken die lestens op-estuurd bin.
-As disse pagina efilterd wörden op gebruker, zie'j allinnig bestaanden waor de gebruker de leste versie van hef op-estuurd.",
+'listfiles-summary' => "Op disse spesiale zied ku'j alle bestaanden bekieken die lestens op-estuurd bin.
+As disse zied efilterd wörden op gebruker, zie'j allinnig bestaanden waor de gebruker de leste versie van hef op-estuurd.",
'listfiles_search_for' => 'Zeuk naor bestaand:',
'imgfile' => 'bestaand',
'listfiles' => 'Bestaandslieste',
'filehist-dimensions' => 'Grootte',
'filehist-filesize' => 'Bestaandsgrootte',
'filehist-comment' => 'Opmarkingen',
-'filehist-missing' => 'Bestaand ontbreekt',
+'filehist-missing' => 'Bestaand ontbrik',
'imagelinks' => 'Bestaandsgebruuk',
-'linkstoimage' => "Dit bestaand wörden gebruukt op de volgende {{PLURAL:$1|pagina|$1 pagina's}}:",
+'linkstoimage' => 'Dit bestaand wörden gebruukt op de volgende {{PLURAL:$1|zied|$1 ziejen}}:',
'linkstoimage-more' => 'Der {{PLURAL:$2|is|bin}} meer as $1 {{PLURAL:$1|verwiezing|verwiezingen}} naor dit bestaand.
-De volgende lieste geef allinnig de eerste {{PLURAL:$1|verwiezing|$1 verwiezingen}} naor dit bestaand weer.
+De volgende lieste gif allinnig de eerste {{PLURAL:$1|verwiezing|$1 verwiezingen}} naor dit bestaand weer.
De [[Special:WhatLinksHere/$2|hele lieste]] is oek beschikbaor.',
'nolinkstoimage' => 'Bestaand is niet in gebruuk.',
'morelinkstoimage' => '[[Special:WhatLinksHere/$1|Meer verwiezingen]] naor dit bestaand bekieken.',
'sharedupload-desc-there' => "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten. Bekiek de [$2 beschrieving van t bestaand] veur meer informasie.",
'sharedupload-desc-here' => "Dit is n edeeld bestaand op $1 en ku'j oek gebruken veur aandere projekten. De [$2 beschrieving van t bestaand] dergindse, steet hieronder.",
'sharedupload-desc-edit' => 'Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.
-Je kunnen de [$2 pagina mit de bestaandsbeschrieving] daor bewarken.',
+Je kunnen de [$2 zied mit de bestaandsbeschrieving] daor bewarken.',
'sharedupload-desc-create' => 'Dit besatand kömp van $1 en kan oek in aandere projekten gebruukt wörden.
-Je kunnen de [$2 pagina mit de bestaandsbeschrieving] daor bewarken.',
+Je kunnen de [$2 zied mit de bestaandsbeschrieving] daor bewarken.',
'filepage-nofile' => 'Der besteet gien bestaand mit disse naam.',
'filepage-nofile-link' => 'Der besteet gien bestaand mit disse naam, mer je kunnen t [$1 opsturen].',
'uploadnewversion-linktext' => 'n Niejere versie van dit bestaand opsturen.',
'filedelete-nofile-old' => "Der is gien versie van '''$1''' in t archief mit de an-egeven eigenschappen.",
'filedelete-otherreason' => 'Aandere reden:',
'filedelete-reason-otherlist' => 'Aandere reden',
-'filedelete-reason-dropdown' => "*Veulveurkoemende redens veur t vortdoon van pagina's
+'filedelete-reason-dropdown' => "*Veulveurkoemende redens veur t vortdoon van ziejen
** Auteursrechtenschending
** Dit bestaand he'w dubbel",
'filedelete-edit-reasonlist' => 'Reden veur t vortdoon bewarken',
# MIME search
'mimesearch' => 'Zeuken op MIME-type',
-'mimesearch-summary' => 'Op disse spesiale pagina kunnen de bestaanden naor t MIME-type efiltreerd wörden. In de invoer mut altied t media- en subtype staon, bieveurbeeld: <tt>aofbeelding/jpeg</tt>.',
+'mimesearch-summary' => 'Op disse spesiale zied kunnen de bestaanden naor t MIME-type efiltreerd wörden. In de invoer mut altied t media- en subtype staon, bieveurbeeld: <tt>aofbeelding/jpeg</tt>.',
'mimetype' => 'MIME-type:',
'download' => 'binnenhaolen',
# Unwatched pages
-'unwatchedpages' => "Pagina's die niet evolgd wörden",
+'unwatchedpages' => 'Ziejen die niet evolgd wörden',
# List redirects
'listredirects' => 'Lieste van deurverwiezingen',
# Unused templates
'unusedtemplates' => 'Ongebruukten mallen',
-'unusedtemplatestext' => 'Hieronder staon alle pagina\'s in de naamruumte "{{ns:template}}" die nargens gebruukt wörden.
+'unusedtemplatestext' => 'Hieronder staon alle ziejen in de naamruumte "{{ns:template}}" die nargens gebruukt wörden.
Vergeet niet de verwiezingen nao te kieken veurda\'j de mal vortdoon.',
'unusedtemplateswlh' => 'aandere verwiezingen',
# Random page
-'randompage' => 'Zo mer n artikel',
-'randompage-nopages' => "Der staon gien pagina's in de {{PLURAL:$2|naamruumte|naamruumtes}}: $1.",
+'randompage' => 'Netzelde welk artikel',
+'randompage-nopages' => 'Der staon gien ziejen in de {{PLURAL:$2|naamruumte|naamruumtes}}: $1.',
# Random redirect
-'randomredirect' => 'Zo mer n deurverwiezing',
+'randomredirect' => 'Netzelde welke deurverwiezing',
'randomredirect-nopages' => 'Der staon gien deurverwiezingen in de naamruumte "$1".',
# Statistics
'statistics' => 'Staotistieken',
-'statistics-header-pages' => 'Paginastaotistieken',
+'statistics-header-pages' => 'Ziedstaotistieken',
'statistics-header-edits' => 'Bewarkingsstaotistieken',
'statistics-header-views' => 'Staotistieken bekieken',
'statistics-header-users' => 'Gebrukerstaotistieken',
'statistics-header-hooks' => 'Overige staotistieken',
-'statistics-articles' => "Inhouwelike pagina's",
-'statistics-pages' => "Pagina's",
-'statistics-pages-desc' => "Alle pagina's in de wiki, oek overlegpagina's, deurverwiezingen, en gao zo mer deur.",
+'statistics-articles' => 'Inhouwelike ziejen',
+'statistics-pages' => 'Ziejen',
+'statistics-pages-desc' => 'Alle ziejen in de wiki, oek overlegziejen, deurverwiezingen, en gao zo mer deur.',
'statistics-files' => 'Bestaanden',
-'statistics-edits' => 'Paginabewarkingen vanaof t begin van {{SITENAME}}',
-'statistics-edits-average' => 'Gemiddeld antal bewarkingen per pagina',
-'statistics-views-total' => "Totaal antal weeregeven pagina's",
-'statistics-views-total-desc' => "t Bekieken van niet-bestaonde pagina's en spesiale pagina's zitten der niet bie in",
-'statistics-views-peredit' => "Weeregeven pagina's per bewarking",
+'statistics-edits' => 'Ziedbewarkingen vanaof t begin van {{SITENAME}}',
+'statistics-edits-average' => 'Gemiddeld antal bewarkingen per zied',
+'statistics-views-total' => 'Totaal antal weeregeven ziejen',
+'statistics-views-total-desc' => 't Bekieken van niet-bestaonde ziejen en spesiale ziejen zitten der niet bie in',
+'statistics-views-peredit' => 'Weeregeven ziejen per bewarking',
'statistics-users' => 'In-eschreven [[Special:ListUsers|gebrukers]]',
'statistics-users-active' => 'Aktieve gebrukers',
'statistics-users-active-desc' => 'Gebrukers die de veurbieje {{PLURAL:$1|dag|$1 dagen}} n haandeling uutevoerd hebben',
-'statistics-mostpopular' => "Meestbekeken pagina's",
+'statistics-mostpopular' => 'Meestbekeken ziejen',
-'disambiguations' => "Pagina's die verwiezen naor deurverwiespagina's",
+'disambiguations' => 'Ziejen die verwiezen naor deurverwiesziejen',
'disambiguationspage' => 'Template:Dv',
-'disambiguations-text' => "De onderstaonde pagina's verwiezen naor n '''deurverwiespagina'''. Disse verwiezingen mutten eigenliks rechtstreeks verwiezen naor t juuste onderwarp.
+'disambiguations-text' => "De onderstaonde ziejen verwiezen naor n '''deurverwieszied'''. Disse verwiezingen mutten eigenliks rechtstreeks verwiezen naor t juuste onderwarp.
-Pagina's wörden ezien as n deurverwiespagina, as de mal gebruukt wörden die vermeld steet op [[MediaWiki:Disambiguationspage]]",
+Ziejen wörden ezien as n deurverwieszied, as de mal gebruukt wörden die vermeld steet op [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Dubbele deurverwiezingen',
-'doubleredirectstext' => "Op disse lieste staon alle pagina's die deurverwiezen naor aandere deurverwiezingen.
-Op elke regel steet de eerste en de tweede deurverwiezing, daorachter steet de doelpagina van de tweede deurverwiezing.
-Meestentieds is leste pagina de gewunste doelpagina, waor oek de eerste pagina heer zol mutten liejen.",
+'doubleredirectstext' => 'Op disse lieste staon alle ziejen die deurverwiezen naor aandere deurverwiezingen.
+Op elke regel steet de eerste en de tweede deurverwiezing, daorachter steet de doelzied van de tweede deurverwiezing.
+Meestentieds is leste zied de gewunste doelzied, waor oek de eerste zied heer zol mutten liejen.',
'double-redirect-fixed-move' => '[[$1]] is herneumd en is noen n deurverwiezing naor [[$2]]',
'double-redirect-fixed-maintenance' => 'Verbeteren van dubbele deurverwiezing van [[$1]] naor [[$2]].',
'double-redirect-fixer' => 'Deurverwiezingsverbeteraar',
'brokenredirects' => 'Ebreuken deurverwiezingen',
-'brokenredirectstext' => 'Disse deurverwiezingen verwiezen naor n niet-bestaonde pagina.',
+'brokenredirectstext' => 'Disse deurverwiezingen verwiezen naor n niet-bestaonde zied.',
'brokenredirects-edit' => 'bewark',
'brokenredirects-delete' => 'vortdoon',
-'withoutinterwiki' => "Pagina's zonder verwiezingen naor aandere talen",
-'withoutinterwiki-summary' => "De volgende pagina's verwiezen niet naor versies in n aandere taal.",
+'withoutinterwiki' => 'Ziejen zonder verwiezingen naor aandere talen',
+'withoutinterwiki-summary' => 'De volgende ziejen verwiezen niet naor versies in n aandere taal.',
'withoutinterwiki-legend' => 'Veurvoegsel',
'withoutinterwiki-submit' => 'Bekieken',
'nmembers' => '$1 {{PLURAL:$1|onderwarp|onderwarpen}}',
'nrevisions' => '$1 {{PLURAL:$1|versie|versies}}',
'nviews' => '{{PLURAL:$1|1 keer|$1 keer}} bekeken',
-'nimagelinks' => "Wörden op {{PLURAL:$1|één pagina|$1 pagina's}} gebruukt",
-'ntransclusions' => "wörden op {{PLURAL:$1|één pagina|$1 pagina's}} gebruukt",
-'specialpage-empty' => 'Disse pagina is leeg.',
-'lonelypages' => "Weespagina's",
-'lonelypagestext' => "Naor disse pagina's wörden niet verwezen vanuut {{SITENAME}} en ze bin oek nargens in-evoegd.",
-'uncategorizedpages' => "Pagina's zonder kategorie",
+'nimagelinks' => 'Wörden op {{PLURAL:$1|één zied|$1 ziejen}} gebruukt',
+'ntransclusions' => 'wörden op {{PLURAL:$1|één zied|$1 ziejen}} gebruukt',
+'specialpage-empty' => 'Disse zied is leeg.',
+'lonelypages' => 'Weesziejen',
+'lonelypagestext' => 'Naor disse ziejen wörden niet verwezen vanuut {{SITENAME}} en ze bin oek nargens in-evoegd.',
+'uncategorizedpages' => 'Ziejen zonder kategorie',
'uncategorizedcategories' => 'Kategorieën zonder kategorie',
'uncategorizedimages' => 'Bestaanden zonder kategorie',
'uncategorizedtemplates' => 'Mallen zonder kategorie',
'unusedcategories' => 'Ongebruukten kategorieën',
'unusedimages' => 'Ongebruukten bestaanden',
-'popularpages' => 'Populaire artikels',
+'popularpages' => 'Populaere artikels',
'wantedcategories' => 'Gewunste kategorieën',
-'wantedpages' => "Gewunste pagina's",
-'wantedpages-badtitle' => 'Ongeldige paginanaam in resultaot: $1',
+'wantedpages' => 'Gewunste ziejen',
+'wantedpages-badtitle' => 'Ongeldige ziednaam in resultaot: $1',
'wantedfiles' => 'Gewunste bestaanden',
-'wantedfiletext-cat' => "De volgende bestaanden wörden gebruukt mer bestaon niet. Bestaanden van externe databanken kunnen op-eneumen ween in de lieste, ondanks dat ze bestaon. Soortgelieke vals positieven wörden <del>deurehaold weeregeven</del>. Pagina's die niet-bestaonde bestaanden insluten staon op de pagina [[:$1]].",
+'wantedfiletext-cat' => 'De volgende bestaanden wörden gebruukt mer bestaon niet. Bestaanden van externe databanken kunnen op-eneumen ween in de lieste, ondanks dat ze bestaon. Soortgelieke vals positieven wörden <del>deurehaold weeregeven</del>. Ziejen die niet-bestaonde bestaanden insluten staon op de zied [[:$1]].',
'wantedfiletext-nocat' => 'De volgende bestaanden wörden gebruukt mer bestaon niet. Bestaanden van externe databanken kunnen op-eneumen ween in de lieste, ondanks dat ze bestaon. Soortgelieke vals positieven wörden <del>deurehaold weeregeven</del>.',
'wantedtemplates' => 'Gewunste mallen',
-'mostlinked' => "Pagina's waor t meest naor verwezen wörden",
+'mostlinked' => 'Ziejen waor t meest naor verwezen wörden',
'mostlinkedcategories' => 'Meestgebruukten kategorieën',
'mostlinkedtemplates' => 'Meestgebruukten mallen',
'mostcategories' => 'Artikels mit de meeste kategorieën',
'mostimages' => 'Meestgebruukten bestaanden',
'mostrevisions' => 'Artikels mit de meeste bewarkingen',
-'prefixindex' => "Alle pagina's op veurvoegsel",
-'prefixindex-namespace' => "Alle pagina's mit t veurvoegsel (naamruumte $1)",
+'prefixindex' => 'Alle ziejen op veurvoegsel',
+'prefixindex-namespace' => 'Alle ziejen mit t veurvoegsel (naamruumte $1)',
'shortpages' => 'Korte artikels',
'longpages' => 'Lange artikels',
-'deadendpages' => "Pagina's zonder verwiezingen",
-'deadendpagestext' => "De onderstaonde pagina's verwiezen niet naor aandere pagina's in disse wiki.",
-'protectedpages' => "Pagina's die beveiligd bin",
+'deadendpages' => 'Ziejen zonder verwiezingen',
+'deadendpagestext' => 'De onderstaonde ziejen verwiezen niet naor aandere ziejen in disse wiki.',
+'protectedpages' => 'Ziejen die beveiligd bin',
'protectedpages-indef' => 'Allinnig blokkeringen zonder verloopdaotum',
'protectedpages-cascade' => 'Allinnig beveiligingen mit de kaskadeopsie',
-'protectedpagestext' => "De volgende pagina's bin beveiligd en kunnen niet herneumd of bewarkt wörden.",
-'protectedpagesempty' => "Der bin op t moment gien beveiligden pagina's",
-'protectedtitles' => 'Paginanamen die beveiligd bin',
-'protectedtitlestext' => "De volgende pagina's bin beveiligd, zodat ze niet opniej an-emaakt kunnen wörden",
+'protectedpagestext' => 'De volgende ziejen bin beveiligd en kunnen niet herneumd of bewarkt wörden.',
+'protectedpagesempty' => 'Der bin op t moment gien beveiligden ziejen',
+'protectedtitles' => 'Ziednamen die beveiligd bin',
+'protectedtitlestext' => 'De volgende ziejen bin beveiligd, zodat ze niet opniej an-emaakt kunnen wörden',
'protectedtitlesempty' => 'Der bin noen gien titels beveiligd die an disse veurweerden voldoon.',
'listusers' => 'Gebrukerslieste',
'listusers-editsonly' => 'Allinnig gebrukers mit bewarkingen laoten zien',
t Kan ween dat der drekt verwezen wörden naor n bestaand.
n Bestaand kan hier dus verkeerd op-eneumen ween.",
'unusedcategoriestext' => 'De onderstaonde kategorieën bin an-emaakt mer bin niet in gebruuk.',
-'notargettitle' => 'Gien pagina op-egeven',
-'notargettext' => 'Je hebben niet op-egeven veur welke pagina je disse funksie bekieken willen.',
-'nopagetitle' => 'Doelpagina besteet niet',
-'nopagetext' => "De pagina die'j herneumen willen besteet niet.",
+'notargettitle' => 'Gien zied op-egeven',
+'notargettext' => 'Je hebben niet op-egeven veur welke zied je disse funksie bekieken willen.',
+'nopagetitle' => 'Doelzied besteet niet',
+'nopagetext' => "De zied die'j herneumen willen besteet niet.",
'pager-newer-n' => '{{PLURAL:$1|1 niejere|$1 niejere}}',
'pager-older-n' => '{{PLURAL:$1|1 ouwere|$1 ouwere}}',
'suppress' => 'Toezichte',
-'querypage-disabled' => 'Disse spesiale pagina is uuteschakeld um prestasieredens.',
+'querypage-disabled' => 'Disse spesiale zied is uutezet um prestasieredens.',
# Book sources
'booksources' => 'Boekinformasie',
# Special:Log
'specialloguserlabel' => 'Uutvoerende gebruker:',
-'speciallogtitlelabel' => 'Doel (paginanaam of gebruker):',
+'speciallogtitlelabel' => 'Doel (ziednaam of gebruker):',
'log' => 'Logboeken',
'all-logs-page' => 'Alle publieke logboeken',
'alllogstext' => 'Dit is t kombinasielogboek van {{SITENAME}}.
'showhideselectedlogentries' => 'Ekeuzen logboekregels laoten zien of verbargen',
# Special:AllPages
-'allpages' => "Alle pagina's",
+'allpages' => 'Alle ziejen',
'alphaindexline' => '$1 tot $2',
-'nextpage' => 'Volgende pagina ($1)',
-'prevpage' => 'Veurige pagina ($1)',
-'allpagesfrom' => "Laot pagina's zien vanaof:",
-'allpagesto' => "Laot pagina's zien tot:",
+'nextpage' => 'Volgende zied ($1)',
+'prevpage' => 'Veurige zied ($1)',
+'allpagesfrom' => 'Laot ziejen zien vanaof:',
+'allpagesto' => 'Laot ziejen zien tot:',
'allarticles' => 'Alle artikels',
-'allinnamespace' => "Alle pagina's (naamruumte $1)",
-'allnotinnamespace' => "Alle pagina's (niet in naamruumte $1)",
+'allinnamespace' => 'Alle ziejen (naamruumte $1)',
+'allnotinnamespace' => 'Alle ziejen (niet in naamruumte $1)',
'allpagesprev' => 'veurige',
'allpagesnext' => 'volgende',
'allpagessubmit' => 'Zeuk',
-'allpagesprefix' => "Pagina's bekieken die beginnen mit:",
-'allpagesbadtitle' => 'De op-egeven paginanaam is ongeldig of der steet n interwikiveurvoegsel in. Meugelikerwieze staon der karakters in de naam die niet gebruukt maggen wörden in paginanamen.',
+'allpagesprefix' => 'Ziejen bekieken die beginnen mit:',
+'allpagesbadtitle' => 'De op-egeven ziednaam is ongeldig of der steet n interwikiveurvoegsel in. Meugelikerwieze staon der karakters in de naam die niet gebruukt maggen wörden in ziednamen.',
'allpages-bad-ns' => '{{SITENAME}} hef gien "$1"-naamruumte.',
'allpages-hide-redirects' => 'Deurverwiezingen verbargen',
# SpecialCachedPage
-'cachedspecial-viewing-cached-ttl' => 'Je bekieken noen n versie uut t tussengeheugen van disse pagina, die hooguut $1 oud is.',
-'cachedspecial-viewing-cached-ts' => 'Je bekieken noen n versie uut t tussengeheugen van disse pagina, t kan ween dat t niet helemaole bie de tied is.',
+'cachedspecial-viewing-cached-ttl' => 'Je bekieken noen n versie uut t tussengeheugen van disse zied, die hooguut $1 oud is.',
+'cachedspecial-viewing-cached-ts' => 'Je bekieken noen n versie uut t tussengeheugen van disse zied, t kan ween dat t niet helemaole bie de tied is.',
'cachedspecial-refresh-now' => 'Leste bekieken.',
# Special:Categories
'categories' => 'Kategorieën',
-'categoriespagetext' => "De de volgende {{PLURAL:$1|kategorie steet|kategorieën staon}} pagina's of mediabestaanden.
+'categoriespagetext' => "De de volgende {{PLURAL:$1|kategorie steet|kategorieën staon}} ziejen of mediabestaanden.
[[Special:UnusedCategories|ongebruukten kategorieën]] zie'j hier niet.
Zie oek [[Special:WantedCategories|gewunste kategorieën]].",
'categoriesfrom' => 'Laot kategorieën zien vanaof:',
# Special:ListGroupRights
'listgrouprights' => 'Rechten van gebrukersgroepen',
-'listgrouprights-summary' => "Op disse pagina staon de gebrukersgroepen van disse wiki beschreven, mit de biebeheurende rechten.
+'listgrouprights-summary' => "Op disse zied staon de gebrukersgroepen van disse wiki beschreven, mit de biebeheurende rechten.
Meer informasie over de rechten ku'j [[{{MediaWiki:Listgrouprights-helppage}}|hier vienen]].",
'listgrouprights-key' => '* <span class="listgrouprights-granted">Rech toe-ewezen</span>
* <span class="listgrouprights-revoked">Rech in-etrökken</span>',
'nowatchlist' => 'Gien artikels in volglieste.',
'watchlistanontext' => '$1 is verplicht um joew volglieste te bekieken of te wiezigen.',
'watchnologin' => 'Niet an-emeld',
-'watchnologintext' => "Um je volglieste an te passen mö'j eers [[Special:UserLogin|an-emeld]] ween.",
+'watchnologintext' => "Um je volglieste an te passen mu'j eerst [[Special:UserLogin|an-emeld]] ween.",
'addwatch' => 'Op mien volglieste zetten',
-'addedwatchtext' => "De pagina \"[[:\$1]]\" steet noen op joew [[Special:Watchlist|volglieste]].
-Toekomstige wiezigingen op disse pagina en de overlegpagina zullen hier vermeld wörden, oek zullen disse pagina's '''vet-edrokt''' ween in de lieste mit de [[Special:RecentChanges|leste wiezigingen]] zoda'j t makkeliker zien kunnen.",
+'addedwatchtext' => "De zied \"[[:\$1]]\" steet noen op joew [[Special:Watchlist|volglieste]].
+Toekomstige wiezigingen op disse zied en de overlegzied zullen hier vermeld wörden, oek zullen disse ziejen '''vet-edrokt''' ween in de lieste mit de [[Special:RecentChanges|leste wiezigingen]] zoda'j t makkeliker zien kunnen.",
'removewatch' => 'Van mien volglieste aofhaolen',
-'removedwatchtext' => 'De pagina "[[:$1]]" is van [[Special:Watchlist|joew volglieste]] aofehaold.',
+'removedwatchtext' => 'De zied "[[:$1]]" is van [[Special:Watchlist|joew volglieste]] aofehaold.',
'watch' => 'Volgen',
-'watchthispage' => 'Volg disse pagina',
+'watchthispage' => 'Volg disse zied',
'unwatch' => 'Niet volgen',
'unwatchthispage' => 'Niet volgen',
'notanarticle' => 'Gien artikel',
'notvisiblerev' => 'Bewarking is vortedaon',
-'watchnochange' => "Gien van de pagina's op joew volglieste is in disse periode ewiezigd",
-'watchlist-details' => "Der {{PLURAL:$1|steet één pagina|staon $1 pagina's}} op joew volglieste, zonder de overlegpagina's mee-erekend.",
+'watchnochange' => 'Gien van de ziejen op joew volglieste is in disse periode ewiezigd',
+'watchlist-details' => 'Der {{PLURAL:$1|steet één zied|staon $1 ziejen}} op joew volglieste, zonder de overlegziejen mee-erekend.',
'wlheader-enotif' => 'Je kriegen bericht per netpost',
-'wlheader-showupdated' => "* Pagina's die sinds joew leste bezeuk bie-ewörken bin, staon '''vet-edrokt'''.",
-'watchmethod-recent' => "leste wiezigingen an t naokieken op pagina's die'j volgen",
+'wlheader-showupdated' => "* Ziejen die sinds joew leste bezeuk bie-ewörken bin, staon '''vet-edrokt'''.",
+'watchmethod-recent' => "leste wiezigingen an t naokieken op ziejen die'j volgen",
'watchmethod-list' => 'Kik joew nao volglieste veur de leste wiezigingen',
-'watchlistcontains' => "Der {{PLURAL:$1|steet 1 pagina|staon $1 pagina's}} op joew volglieste.",
+'watchlistcontains' => 'Der {{PLURAL:$1|steet 1 zied|staon $1 ziejen}} op joew volglieste.',
'iteminvalidname' => "Verkeerde naam '$1'",
'wlnote' => 'Hieronder {{PLURAL:$1|steet de leste wieziging|staon de leste $1 wiezigingen}} in {{PLURAL:$2|t aofeleupen ure|de leste $2 uren}} vanaof $3 um $4.',
-'wlshowlast' => 'Laot de aofeleupen $1 ure $2 dagen $3 zien',
+'wlshowlast' => 'Laot de veurbieje $1 uur $2 dagen $3 zien',
'watchlist-options' => 'Opsies veur de volglieste',
# Displayed when you click the "watch" button and it is in the process of watching
'watcherrortext' => 'Der is n fout op-etrejen tiejens t wiezigen van joew volgliesinstellingen veur "$1".',
'enotif_mailer' => '{{SITENAME}}-berichgevingssysteem',
-'enotif_reset' => "Markeer alle pagina's as bezöcht.",
-'enotif_newpagetext' => 'Dit is n nieje pagina.',
+'enotif_reset' => 'Markeer alle ziejen as bezöcht.',
+'enotif_newpagetext' => 'Dit is n nieje zied.',
'enotif_impersonal_salutation' => '{{SITENAME}}-gebruker',
'changed' => 'ewiezigd',
'created' => 'an-emaakt',
-'enotif_subject' => '{{SITENAME}}-pagina $PAGETITLE is $CHANGEDORCREATED deur $PAGEEDITOR',
+'enotif_subject' => '{{SITENAME}}-zied $PAGETITLE is $CHANGEDORCREATED deur $PAGEEDITOR',
'enotif_lastvisited' => 'Zie $1 veur alle wiezigingen sinds joew leste bezeuk.',
'enotif_lastdiff' => 'Zie $1 um disse wieziging te bekieken.',
'enotif_anon_editor' => 'anonieme gebruker $1',
'enotif_body' => 'Huj $WATCHINGUSERNAME,
-De pagina $PAGETITLE op {{SITENAME}} is $CHANGEDORCREATED op $PAGEEDITDATE deur $PAGEEDITOR, zie $PAGETITLE_URL veur de leste versie.
+De zied $PAGETITLE op {{SITENAME}} is $CHANGEDORCREATED op $PAGEEDITDATE deur $PAGEEDITOR, zie $PAGETITLE_URL veur de leste versie.
$NEWPAGE
Netpost: $PAGEEDITOR_EMAIL
Wiki: $PAGEEDITOR_WIKI
-Je kriegen veerder gien berichten, behalven a\'j disse pagina bezeuken.
-Op joew volglieste ku\'j veur alle pagina\'s die\'j volgen de waorschuwingsinstellingen deraof haolen.
+Je kriegen veerder gien berichten, behalven a\'j disse zied bezeuken.
+Op joew volglieste ku\'j veur alle ziejen die\'j volgen de waorschuwingsinstellingen deraof haolen.
Groeten van t {{SITENAME}}-waorschuwingssysteem.
Je kunnen de volgliestinstellingen wiezigen op:
{{canonicalurl:{{#special:EditWatchlist}}}}
-Je kunnen de pagina van joew volglieste aofhaolen deur op de volgende verwiezing te klikken:
+Je kunnen de zied van joew volglieste aofhaolen deur op de volgende verwiezing te klikken:
$UNWATCHURL
Opmarkingen en veerdere hulpe:
'deletepage' => 'Vortdoon',
'confirm' => 'Bevestigen',
'excontent' => "De tekste was: '$1'",
-'excontentauthor' => "De tekste was: '$1' (pagina an-emaakt deur: [[Special:Contributions/$2|$2]])",
-'exbeforeblank' => "veurdat disse pagina leegemaakt wörden stung hier: '$1'",
-'exblank' => 'Pagina was leeg',
+'excontentauthor' => "De tekste was: '$1' (zied an-emaakt deur: [[Special:Contributions/$2|$2]])",
+'exbeforeblank' => "veurdat disse zied leegemaakt wörden stung hier: '$1'",
+'exblank' => 'Zied was leeg',
'delete-confirm' => '"$1" vortdoon',
'delete-legend' => 'Vortdoon',
-'historywarning' => "'''Waorschuwing''': de pagina die'j vortdoon, hef $1 {{PLURAL:$1|versie|versies}}:",
-'confirmdeletetext' => "Je staon op t punt n pagina en de geschiedenisse dervan vort te doon.
+'historywarning' => "'''Waorschuwing''': de zied die'j vortdoon, hef $1 {{PLURAL:$1|versie|versies}}:",
+'confirmdeletetext' => "Je staon op t punt n zied en de geschiedenisse dervan vort te doon.
Bevestig hieronder dat dit inderdaod de bedoeling is, da'j de gevolgen begriepen en dat t akkedeert mit t [[{{MediaWiki:Policy-url}}|beleid]].",
'actioncomplete' => 'Uutevoerd',
'actionfailed' => 'De haandeling is mislokt.',
-'deletedtext' => 't Artikel "$1" is vortedaon. Zie de "$2" veur n lieste van pagina\'s die as lest vortedaon bin.',
+'deletedtext' => 't Artikel "$1" is vortedaon. Zie de "$2" veur n lieste van ziejen die as lest vortedaon bin.',
'dellogpage' => 'Vortdologboek',
-'dellogpagetext' => "Hieronder steet n lieste van pagina's en bestaanden die as lest vortedaon bin.",
+'dellogpagetext' => 'Hieronder steet n lieste van ziejen en bestaanden die as lest vortedaon bin.',
'deletionlog' => 'Vortdologboek',
'reverted' => 'Eerdere versie hersteld',
'deletecomment' => 'Reden:',
'deleteotherreason' => 'Aandere/extra reden:',
'deletereasonotherlist' => 'Aandere reden',
-'deletereason-dropdown' => "*Redens veur t vortdoon van pagina's
+'deletereason-dropdown' => '*Redens veur t vortdoon van ziejen
** Op vrage van de auteur
** Schending van de auteursrechten
-** Vandelisme",
+** Vandelisme',
'delete-edit-reasonlist' => 'Redens veur t vortdoon bewarken',
-'delete-toobig' => "Disse pagina hef n lange bewarkingsgeschiedenisse, meer as $1 {{PLURAL:$1|versie|versies}}.
-t Vortdoon van dit soort pagina's is mit rechten bepark um t per ongelok versteuren van de warking van {{SITENAME}} te veurkoemen.",
-'delete-warning-toobig' => 'Disse pagina hef n lange bewarkingsgeschiedenisse, meer as $1 {{PLURAL:$1|versie|versies}}.
-Woart je: t vortdoon van disse pagina kan de warking van de databanke van {{SITENAME}} versteuren.
+'delete-toobig' => 'Disse zied hef n lange bewarkingsgeschiedenisse, meer as $1 {{PLURAL:$1|versie|versies}}.
+t Vortdoon van dit soort ziejen is mit rechten bepark um t per ongelok versteuren van de warking van {{SITENAME}} te veurkoemen.',
+'delete-warning-toobig' => 'Disse zied hef n lange bewarkingsgeschiedenisse, meer as $1 {{PLURAL:$1|versie|versies}}.
+Woart je: t vortdoon van disse zied kan de warking van de databanke van {{SITENAME}} versteuren.
Wees veurzichtig',
# Rollback
'rollbacklink' => 'Weerummedreien',
'rollbackfailed' => 'Wieziging herstellen is mislokt',
'cantrollback' => 'De wiezigingen konnen niet hersteld wörden; der is mer 1 auteur.',
-'alreadyrolled' => 'Kan de leste wieziging van de pagina [[:$1]] deur [[User:$2|$2]] ([[User talk:$2|Overleg]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); niet weerummedreien.
-n Aander hef disse pagina al bewarkt of hersteld naor n eerdere versie.
+'alreadyrolled' => 'Kan de leste wieziging van de zied [[:$1]] deur [[User:$2|$2]] ([[User talk:$2|Overleg]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); niet weerummedreien.
+n Aander hef disse zied al bewarkt of hersteld naor n eerdere versie.
-De leste bewarking op disse pagina is edaon deur [[User:$3|$3]] ([[User talk:$3|Overleg]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
+De leste bewarking op disse zied is edaon deur [[User:$3|$3]] ([[User talk:$3|Overleg]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
'editcomment' => "De bewarkingssamenvatting was: ''$1''.",
'revertpage' => 'Wiezigingen deur [[Special:Contributions/$2|$2]] hersteld tot de versie nao de leste wieziging deur $1',
'revertpage-nouser' => 'Wiezigingen deur (gebrukersnaam vortedaon) weerummedreid naor de leste versie deur [[User:$1|$1]]',
# Edit tokens
'sessionfailure-title' => 'Sessiefout',
-'sessionfailure' => 'Der is n probleem mit joew anmeldsessie. De aksie is stop-ezet uut veurzörg tegen n beveiligingsrisico (dat besteet uut t meugelike "kraken" van disse sessie). Gao weerumme naor de veurige pagina, laoj disse pagina opniej en probeer t nog es.',
+'sessionfailure' => 'Der is n probleem mit joew anmeldsessie. De aksie is stop-ezet uut veurzörg tegen n beveiligingsrisico (dat besteet uut t meugelike "kraken" van disse sessie). Gao weerumme naor de veurige zied, laoj disse zied opniej en probeer t nog es.',
# Protect
'protectlogpage' => 'Beveiligingslogboek',
-'protectlogtext' => "Hieronder staon de leste wiezigingen veur t blokkeren en vriegeven van artikels en pagina's.
-Zie de [[Special:ProtectedPages|lieste mit pagina's die beveiligd bin]] veur t hele overzichte.",
+'protectlogtext' => 'Hieronder staon de leste wiezigingen veur t blokkeren en vriegeven van artikels en ziejen.
+Zie de [[Special:ProtectedPages|lieste mit ziejen die beveiligd bin]] veur t hele overzichte.',
'protectedarticle' => '[[$1]] is beveiligd',
'modifiedarticleprotection' => 'beveiligingsnivo van "[[$1]]" ewiezigd',
'unprotectedarticle' => 'hef de beveiliging van "[[$1]]" deraof ehaold',
'protect-title-notallowed' => 'Beveiligingsnivo veur "$1" bekieken',
'prot_1movedto2' => '[[$1]] is ewiezigd naor [[$2]]',
'protect-badnamespace-title' => 'Niet te beveiligen naamruumte',
-'protect-badnamespace-text' => "Pagina's in disse naamruumte kunnen niet beveiligd wörden.",
+'protect-badnamespace-text' => 'Ziejen in disse naamruumte kunnen niet beveiligd wörden.',
'protect-legend' => 'Beveiliging bevestigen',
'protectcomment' => 'Reden:',
'protectexpiry' => 'Duur',
'protect_expiry_invalid' => 'Verlooptied is ongeldig.',
'protect_expiry_old' => 'De verlooptied is al veurbie.',
'protect-unchain-permissions' => 'Overige beveiligingsinstellingen beschikbaor maken',
-'protect-text' => "Hier ku'j t beveiligingsnivo veur de pagina '''$1''' instellen.",
-'protect-locked-blocked' => "Je kunnen beveiligingsnivo's niet wiezigen terwiel je eblokkeerd bin. Hier bin de instellingen zo as ze noen bin veur de pagina '''$1''':",
+'protect-text' => "Hier ku'j t beveiligingsnivo veur de zied '''$1''' instellen.",
+'protect-locked-blocked' => "Je kunnen beveiligingsnivo's niet wiezigen terwiel je eblokkeerd bin. Hier bin de instellingen zo as ze noen bin veur de zied '''$1''':",
'protect-locked-dblock' => "Beveiligingsnivo's kunnen effen niet ewiezigd wörden umdat de databanke noen beveiligd is.
-Hier staon de instellingen zo as ze noen bin veur de pagina '''$1''':",
-'protect-locked-access' => "Je hebben gien rechten um t beveilingsnivo van pagina's te wiezigen.
-Hier staon de instellingen zo as ze noen bin veur de pagina '''$1''':",
-'protect-cascadeon' => "Disse pagina wörden beveiligd, umdat t op-eneumen is in de volgende {{PLURAL:$1|pagina|pagina's}} die beveiligd {{PLURAL:$1|is|bin}} mit de kaskadeopsie. Je kunnen t beveiligingsnivo van disse pagina anpassen, mer dat hef gien invleud op de kaskadebeveiliging.",
+Hier staon de instellingen zo as ze noen bin veur de zied '''$1''':",
+'protect-locked-access' => "Je hebben gien rechten um t beveilingsnivo van ziejen te wiezigen.
+Hier staon de instellingen zo as ze noen bin veur de zied '''$1''':",
+'protect-cascadeon' => 'Disse zied wörden beveiligd, umdat t op-eneumen is in de volgende {{PLURAL:$1|zied|ziejen}} die beveiligd {{PLURAL:$1|is|bin}} mit de kaskadeopsie. Je kunnen t beveiligingsnivo van disse zied anpassen, mer dat hef gien invleud op de kaskadebeveiliging.',
'protect-default' => 'Veur alle gebrukers',
'protect-fallback' => 'Hierveur is t rech "$1" neudig',
'protect-level-autoconfirmed' => 'Blokkeer nieje en anonieme gebrukers',
'protect-level-sysop' => 'Allinnig beheerders',
'protect-summary-cascade' => 'kaskade',
'protect-expiring' => 'löp aof op $1 (UTC)',
-'protect-expiring-local' => 'vervuilt op $1',
+'protect-expiring-local' => 'vervölt op $1',
'protect-expiry-indefinite' => 'onbepark',
-'protect-cascade' => "Kaskadebeveiliging (beveilig alle pagina's en mallen die in disse pagina op-eneumen bin)",
-'protect-cantedit' => "Je kunnen t beveiligingsnivo van disse pagina niet wiezigen, umda'j gien rechten hebben um t te bewarken.",
+'protect-cascade' => 'Kaskadebeveiliging (beveilig alle ziejen en mallen die in disse zied op-eneumen bin)',
+'protect-cantedit' => "Je kunnen t beveiligingsnivo van disse zied niet wiezigen, umda'j gien rechten hebben um t te bewarken.",
'protect-othertime' => 'Aandere tiedsduur:',
'protect-othertime-op' => 'aandere tiedsduur',
'protect-existing-expiry' => 'Bestaonde verloopdaotum: $2 $3',
** Vandelisme
** Ongewunste verwiezingen plaotsen
** Bewarkingsoorlog
-** Pagina mit veul bezeukers',
+** Zied mit veule bezeukers',
'protect-edit-reasonlist' => 'Redens veur beveiliging bewarken',
'protect-expiry-options' => '1 uur:1 hour,1 dag:1 day,1 weke:1 week,2 weken:2 weeks,1 maond:1 month,3 maonden:3 months,6 maonden:6 months,1 jaor:1 year,onbeparkt:infinite',
'restriction-type' => 'Toegang',
'restriction-level-all' => 'alles',
# Undelete
-'undelete' => "Vortedaone pagina's bekieken",
-'undeletepage' => "Vortedaone pagina's bekieken en weerummeplaotsen",
+'undelete' => 'Vortedaone ziejen bekieken',
+'undeletepage' => 'Vortedaone ziejen bekieken en weerummeplaotsen',
'undeletepagetitle' => "'''Hieronder staon de vortedaone bewarkingen van [[:$1]]'''.",
-'viewdeletedpage' => "Bekiek vortedaone pagina's",
-'undeletepagetext' => "Hieronder {{PLURAL:$1|steet de pagina die vortedaon is|staon de pagina's die vortedaon bin}} en vanuut t archief weerummeplaots {{PLURAL:$1|kan|kunnen}} wörden.",
+'viewdeletedpage' => 'Bekiek vortedaone ziejen',
+'undeletepagetext' => 'Hieronder {{PLURAL:$1|steet de zied die vortedaon is|staon de ziejen die vortedaon bin}} en vanuut t archief weerummeplaotst {{PLURAL:$1|kan|kunnen}} wörden.',
'undelete-fieldset-title' => 'Versies weerummeplaotsen',
-'undeleteextrahelp' => "Laot alle vakjes leeg en klik op '''''{{int:undeletebtn}}''''' um de hele pagina mit alle veurgeschiedenisse weerumme te plaotsen.
+'undeleteextrahelp' => "Laot alle vakjes leeg en klik op '''''{{int:undeletebtn}}''''' um de hele zied mit alle veurgeschiedenisse weerumme te plaotsen.
Vink de versies die weerummeplaotsen willen an en klik op '''''{{int:undeletebtn}}''''' um bepaolde versies weerumme te zetten.",
'undeleterevisions' => '$1 {{PLURAL:$1|versie|versies}} earchiveerd',
-'undeletehistory' => "A'j n pagina weerummeplaotsen, wörden alle versies as ouwe versies weerummeplaots.
-As der al n nieje pagina mit de zelfde naam an-emaakt is, zullen disse versies as ouwe versies weerummeplaotst wörden, mer de op-esleugen versie zal niet ewiezigd wörden.",
-'undeleterevdel' => "Herstellen kan niet as daor de leste versie van de pagina of t bestaand gedeeltelik mee vortedaon wörden.
-In dat geval mö'j de leste versie as zichtbaor instellen.",
-'undeletehistorynoadmin' => 'Disse pagina is vortedaon. De reden hierveur steet hieronder, samen mit de informasie van de gebrukers die dit artikel ewiezigd hebben veurdat t vortedaon is. De tekste van t artikel is allinnig zichtbaor veur beheerders.',
+'undeletehistory' => "A'j n zied weerummeplaotsen, wörden alle versies as ouwe versies weerummeplaots.
+As der al n nieje zied mit de zelfde naam an-emaakt is, zullen disse versies as ouwe versies weerummeplaotst wörden, mer de op-esleugen versie zal niet ewiezigd wörden.",
+'undeleterevdel' => "Herstellen kan niet as daor de leste versie van de zied of t bestaand gedeeltelik mee vortedaon wörden.
+In dat geval mu'j de leste versie as zichtbaor instellen.",
+'undeletehistorynoadmin' => 'Disse zied is vortedaon. De reden hierveur steet hieronder, samen mit de informasie van de gebrukers die dit artikel ewiezigd hebben veurdat t vortedaon is. De tekste van t artikel is allinnig zichtbaor veur beheerders.',
'undelete-revision' => 'Vortedaone versies van $1 (per $4 um $5) deur $3:',
-'undeleterevision-missing' => "Ongeldige of ontbrekende versie. t Is meugelik da'j n verkeerde verwiezing gebruken of dat disse pagina weerummeplaotst is of dat t uut t archief ewist is.",
+'undeleterevision-missing' => "Ongeldige of ontbrekende versie. t Is meugelik da'j n verkeerde verwiezing gebruken of dat disse zied weerummeplaotst is of dat t uut t archief ewist is.",
'undelete-nodiff' => 'Gien eerdere versie evunnen.',
'undeletebtn' => 'Weerummeplaotsen',
'undeletelink' => 'bekiek/weerummeplaotsen',
'undeletedrevisions' => '$1 {{PLURAL:$1|versie|versies}} weerummeplaotst',
'undeletedrevisions-files' => '{{PLURAL:$1|1 versie|$1 versies}} en {{PLURAL:$2|1 bestaand|$2 bestaanden}} bin weerummeplaotst',
'undeletedfiles' => '{{PLURAL:$1|1 bestaand|$1 bestaanden}} weerummeplaotst',
-'cannotundelete' => 'Weerummeplaotsen van t bestaand is mislokt; n aander hef disse pagina misschien al weerummeplaotst.',
+'cannotundelete' => 'Weerummeplaotsen van t bestaand is mislokt; n aander hef disse zied misschien al weerummeplaotst.',
'undeletedpage' => "'''$1 is weerummeplaotst'''
-Bekiek t [[Special:Log/delete|vortdologboek]] veur n overzichte van pagina's die kortens vortedaon en weerummeplaotst bin.",
+Bekiek t [[Special:Log/delete|vortdologboek]] veur n overzichte van ziejen die kortens vortedaon en weerummeplaotst bin.",
'undelete-header' => 'Zie t [[Special:Log/delete|vortdologboek ]] veur spul dat krek vortedaon is.',
-'undelete-search-title' => "Vortedaone pagina's zeuken",
-'undelete-search-box' => "Deurzeuk vortedaone pagina's",
-'undelete-search-prefix' => "Bekiek pagina's vanaof:",
+'undelete-search-title' => 'Vortedaone ziejen zeuken',
+'undelete-search-box' => 'Deurzeuk vortedaone ziejen',
+'undelete-search-prefix' => 'Bekiek ziejen vanaof:',
'undelete-search-submit' => 'Zeuk',
-'undelete-no-results' => "Gien pagina's evunnen in t archief mit vortedaone pagina's.",
+'undelete-no-results' => 'Gien ziejen evunnen in t archief mit vortedaone ziejen.',
'undelete-filename-mismatch' => 'Bestaandsversie van t tiedstip $1 kon niet hersteld wörden: bestaandsnaam kloppen niet',
'undelete-bad-store-key' => 'Bestaandsversie van t tiedstip $1 kon niet hersteld wörden: t bestaand was der al niet meer veurdat t vortedaon wörden.',
'undelete-cleanup-error' => 'Fout bie t herstellen van t ongebruukten archiefbestaand "$1".',
'undelete-missing-filearchive' => 't Lokten niet um ID $1 weerumme te plaotsen umdat t niet in de databanke is.
Misschien is t al weerummeplaotst.',
-'undelete-error' => 'Der is wat fout egaon bie t vortdoon van de pagina',
+'undelete-error' => 'Der is wat fout egaon bie t vortdoon van de zied',
'undelete-error-short' => 'Fout bie t herstellen van t bestaand: $1',
'undelete-error-long' => 'Fouten bie t herstellen van t bestaand:
# Namespace form on various pages
'namespace' => 'Naamruumte:',
-'invert' => 'seleksie ummekeren',
-'tooltip-invert' => "Vink dit vakjen an um wiezigingen an pagina's binnen de ekeuzen naamruumte te verbargen (en de biebeheurende naamruumte as dat an-evinkt is)",
+'invert' => 'Seleksie ummekeren',
+'tooltip-invert' => 'Vink dit vakjen an um wiezigingen an ziejen binnen de ekeuzen naamruumte te verbargen (en de biebeheurende naamruumte as dat an-evinkt is)',
'namespace_association' => 'Naamruumte die hieran ekoppeld is',
'tooltip-namespace_association' => 'Vink dit vakjen an um oek de overlegnaamruumte, of in t ummekeren geval de naamruumte zelf, derbie te doon die bie disse naamruumte heurt.',
'blanknamespace' => '(Heufdnaamruumte)',
'sp-contributions-submit' => 'Zeuk',
# What links here
-'whatlinkshere' => 'Verwiezingen naor disse pagina',
-'whatlinkshere-title' => 'Pagina\'s die verwiezen naor "$1"',
-'whatlinkshere-page' => 'Pagina:',
-'linkshere' => "Disse pagina's verwiezen naor '''[[:$1]]''':",
-'nolinkshere' => "Gien enkele pagina verwies naor '''[[:$1]]'''.",
-'nolinkshere-ns' => "Gien enkele pagina verwiest naor '''[[:$1]]''' in de ekeuzen naamruumte.",
+'whatlinkshere' => 'Verwiezingen naor disse zied',
+'whatlinkshere-title' => 'Ziejen die verwiezen naor "$1"',
+'whatlinkshere-page' => 'Zied:',
+'linkshere' => "Disse ziejen verwiezen naor '''[[:$1]]''':",
+'nolinkshere' => "Gien enkele zied verwis naor '''[[:$1]]'''.",
+'nolinkshere-ns' => "Gien enkele zied verwis naor '''[[:$1]]''' in de ekeuzen naamruumte.",
'isredirect' => 'deurverwiezing',
'istemplate' => 'in-evoegd as mal',
'isimage' => 'bestaandsverwiezing',
'blockip' => 'Gebruker blokkeren',
'blockip-title' => 'Gebruker blokkeren',
'blockip-legend' => 'n Gebruker of IP-adres blokkeren',
-'blockiptext' => "Gebruuk dit formulier um n IP-adres of gebrukersnaam te blokkeren. t Is bedoeld um vandelisme te veurkoemen en mit in akkerderen mit t [[{{MediaWiki:Policy-url}}|beleid]]. Geef hieronder n reden op (bieveurbeeld op welke pagina's de vandelisme epleeg is)",
+'blockiptext' => 'Gebruuk dit formulier um n IP-adres of gebrukersnaam te blokkeren. t Is bedoeld um vandelisme te veurkoemen en mit in akkerderen mit t [[{{MediaWiki:Policy-url}}|beleid]]. Geef hieronder n reden op (bieveurbeeld op welke ziejen de vandelisme epleeg is)',
'ipadressorusername' => 'IP-adres of gebrukersnaam',
'ipbexpiry' => 'Verlöp nao',
'ipbreason' => 'Reden:',
'ipbreasonotherlist' => 'aandere reden',
-'ipbreason-dropdown' => "*Algemene redens veur t blokkeren
+'ipbreason-dropdown' => '*Algemene redens veur t blokkeren
** valse informasie invoeren
-** pagina's leegmaken
+** ziejen leegmaken
** ongewunste verwiezingen plaotsen
** onzinteksten schrieven
** targerieje of naor gedrag
** misbruuk vanaof meerdere profielen
-** ongewunste gebrukersnaam",
+** ongewunste gebrukersnaam',
'ipb-hardblock' => 'Veurkoemen dat an-emelde gebrukers vanaof dit IP-adres kunnen bewarken',
'ipbcreateaccount' => 'Veurkom t anmaken van gebrukersprofielen',
'ipbemailban' => 'Veurkom dat bepaolde gebrukers berichten versturen',
'ipbotheroption' => 'aanders',
'ipbotherreason' => 'Aandere/extra reden:',
'ipbhidename' => 'Verbarg de gebrukersnaam in bewarkingen en liesten',
-'ipbwatchuser' => 'Gebrukerspagina en overlegpagina op volglieste zetten',
-'ipb-disableusertalk' => 'Veurkoemen dat disse gebruker tiejens de blokkering de eigen overlegpagina kan bewarken',
+'ipbwatchuser' => 'Gebrukerszied en overlegzied op volglieste zetten',
+'ipb-disableusertalk' => 'Veurkoemen dat disse gebruker tiejens de blokkering de eigen overlegzied kan bewarken',
'ipb-change-block' => 'De gebruker opniej blokkeren mit disse instellingen',
'ipb-confirm' => 'Blokkering bevestigen',
'badipaddress' => 'Ongeldig IP-adres of onbestaonde gebrukersnaam',
'blocklist-rangeblocks' => 'IP-adresblokkeringen verbargen',
'blocklist-timestamp' => 'Tiedstip',
'blocklist-target' => 'Doel',
-'blocklist-expiry' => 'Vervuilt',
+'blocklist-expiry' => 'Vervölt',
'blocklist-by' => 'Eblokkeerd deur',
'blocklist-params' => 'Blokkeringsparameters',
'blocklist-reason' => 'Reden',
'noautoblockblock' => 'autoblok niet aktief',
'createaccountblock' => 'anmaken van n gebrukersprofiel is eblokkeerd',
'emailblock' => 't versturen van berichten is eblokkeerd',
-'blocklist-nousertalk' => 'kan zien eigen overlegpagina niet bewarken',
+'blocklist-nousertalk' => 'kan zien eigen overlegzied niet bewarken',
'ipblocklist-empty' => 'De blokkeerlieste is leeg.',
'ipblocklist-no-results' => 't Op-evreugen IP-adres of de gebrukersnaam is niet eblokkeerd.',
'blocklink' => 'blokkeren',
'change-blocklink' => 'blokkering wiezigen',
'contribslink' => 'biedragen',
'emaillink' => 'netpostbericht sturen',
-'autoblocker' => 'Vanzelf eblokkeerd umdat t IP-adres overenekump mit t IP-adres van [[User:$1|$1]], die eblokkeerd is mit as reden: "$2"',
+'autoblocker' => 'Vanzelf eblokkeerd umdat t IP-adres overenekömp mit t IP-adres van [[User:$1|$1]], die eblokkeerd is mit as reden: "$2"',
'blocklogpage' => 'Blokkeerlogboek',
'blocklog-showlog' => 'Disse gebruker is al eerder eblokkeerd.
t Blokkeerlogboek steet hieronder as referensie:',
'blocklogtext' => "Hier zie'j n lieste van de leste blokkeringen en deblokkeringen. Automatiese blokkeringen en deblokkeringen koemen niet in t logboek te staon. Zie de [[Special:BlockList|blokkeerlieste]] veur de lieste van adressen die noen eblokkeerd bin.",
'unblocklogentry' => 'blokkering van $1 is op-eheven',
'block-log-flags-anononly' => 'allinnig anoniemen',
-'block-log-flags-nocreate' => 'anmaken van gebrukersprofielen uuteschakeld',
-'block-log-flags-noautoblock' => 'autoblokkeren uuteschakeld',
+'block-log-flags-nocreate' => 'anmaken van gebrukersprofielen uutezet',
+'block-log-flags-noautoblock' => 'autoblokkeren uutezet',
'block-log-flags-noemail' => 't versturen van berichten is eblokkeerd',
-'block-log-flags-nousertalk' => 'kan zien eigen overlegpagina niet bewarken',
+'block-log-flags-nousertalk' => 'kan zien eigen overlegzied niet bewarken',
'block-log-flags-angry-autoblock' => 'uutebreide automatiese blokkering in-eschakeld',
'block-log-flags-hiddenname' => 'gebrukersnaam verbörgen',
-'range_block_disabled' => 'De meugelikheid veur beheerders um n groep adressen te blokkeren is uuteschakeld.',
+'range_block_disabled' => 'De meugelikheid veur beheerders um n groep adressen te blokkeren is uutezet.',
'ipb_expiry_invalid' => 'De op-egeven verlooptied is ongeldig.',
'ipb_expiry_temp' => 'Blokkeringen veur verbörgen gebrukers mutten permanent ween.',
'ipb_hide_invalid' => 'Kan disse gebruker niet verbargen; warschienlik hef e al te veule bewarkingen emaakt.',
'ip_range_toolarge' => 'Groeps-IP-adressen die groter bin as /$1, bin niet toe-estaon.',
'blockme' => 'Mien blokkeren',
'proxyblocker' => 'Proxyblokker',
-'proxyblocker-disabled' => 'Disse funksie is uuteschakeld.',
-'proxyblockreason' => "Dit is n automatiese preventieve blokkering umda'j gebruuk maken van n open proxyserver.",
+'proxyblocker-disabled' => 'Disse funksie is uutezet.',
+'proxyblockreason' => "Dit is n automatiese preventieve blokkering umda'j gebruukmaken van n open proxyserver.",
'proxyblocksuccess' => 'Suksesvol.',
'sorbsreason' => "Joew IP-adres is op-eneumen as open proxyserver in de zwarte lieste van DNS die'w veur {{SITENAME}} gebruken.",
'sorbs_create_account_reason' => "Joew IP-adres is op-eneumen as open proxyserver in de zwarte lieste van DNS, die'w veur {{SITENAME}} gebruken.
-Je kunnen gien gebrukerspagina anmaken.",
+Je kunnen gien gebrukerszied anmaken.",
'cant-block-while-blocked' => "Je kunnen aandere gebrukers niet blokkeren a'j zelf oek eblokkeerd bin.",
'cant-see-hidden-user' => "De gebruker die'j proberen te blokkeren is al eblokkeerd en verbörgen.
Umda'j gien rech hebben um gebrukers te verbargen, ku'j de blokkering van de gebruker niet bekieken of bewarken.",
# Developer tools
'lockdb' => 'Databanke blokkeren',
'unlockdb' => 'Databanke vriegeven',
-'lockdbtext' => "Waorschuwing: a'j de databanke blokkeren dan kan der gienene meer pagina's bewarken, zien veurkeuren wiezingen of wat aanders doon waorveur der wiezigingen in de databanke neudig bin.",
+'lockdbtext' => "Waorschuwing: a'j de databanke blokkeren dan kan der gienene meer ziejen bewarken, zien veurkeuren wiezingen of wat aanders doon waorveur der wiezigingen in de databanke neudig bin.",
'unlockdbtext' => 'Vriegeven van de databanke maak alle bewarkingen weer meugelik.
Mut de databanke vrie-egeven wörden?',
'lockconfirm' => 'Ja, ik wille de databanke blokkeren.',
# Move page
'move-page' => 'Herneum "$1"',
-'move-page-legend' => 'Pagina herneumen',
-'movepagetext' => "Mit dit formulier ku'j de pagina n nieje naam geven, de geschiedenisse geet dan vanzelf mee.
-De ouwe naam zal automaties n deurverwiezing wörden naor de nieje pagina.
+'move-page-legend' => 'Zied herneumen',
+'movepagetext' => "Mit dit formulier ku'j de zied n nieje naam geven, de geschiedenisse geet dan vanzelf mee.
+De ouwe naam zal automaties n deurverwiezing wörden naor de nieje zied.
Deurverwiezingen naor de ouwe naam kunnen automaties ewiezigd wörden.
A'j derveur kiezen um dat niet te doon, kiek t dan effen nao of der [[Special:DoubleRedirects|dubbele]] en [[Special:BrokenRedirects|ebreuken deurverwiezingen]] bin ontstaon.
t Is an joe um derveur te zörgen dat de deurverwiezingen naor de goeie naam gaon.
-n Pagina kan '''allinnig''' herneumd wörden as de nieje naam niet besteet of t n deurverwiezing is zonder veerdere geschiedenisse.
-Dit betekent da'j n pagina weer naor de ouwe naam kunnen herneumen, a'j bieveurbeeld n fout emaakt hebben, zonder da'j de bestaonde pagina overschrieven.
+n Zied kan '''allinnig''' herneumd wörden as de nieje naam niet besteet of t n deurverwiezing is zonder veerdere geschiedenisse.
+Dit betekent da'j n zied weer naor de ouwe naam kunnen herneumen, a'j bieveurbeeld n fout emaakt hebben, zonder da'j de bestaonde zied overschrieven.
'''WAORSCHUWING!'''
-Veur populaire pagina's kan t herneumen drastiese en onveurziene gevolgen hebben.
+Veur populaere ziejen kan t herneumen drastiese en onveurziene gevolgen hebben.
Zörg derveur da'j de gevolgen overzien veurda'j veerder gaon.",
-'movepagetext-noredirectfixer' => "Mit dit formulier ku'j de pagina n nieje naam geven, de geschiedenisse geet dan vanzelf mee.
-De ouwe naam zal automaties n deurverwiezing wörden naor de nieje pagina.
+'movepagetext-noredirectfixer' => "Mit dit formulier ku'j de zied n nieje naam geven, de geschiedenisse geet dan vanzelf mee.
+De ouwe naam zal automaties n deurverwiezing wörden naor de nieje zied.
Kiek oek effen nao of der gien [[Special:DoubleRedirects|dubbele]] of [[Special:BrokenRedirects|ebreuken deurverwiezingen]] bin ontstaon.
t Is an joe um derveur te zörgen dat de deurverwiezingen naor de goeie naam gaon.
-n Pagina kan '''allinnig''' herneumd wörden as de nieje naam niet besteet of t n deurverwiezing is zonder veerdere geschiedenisse.
-Dit betekent da'j n pagina weer naor de ouwe naam kunnen herneumen, a'j bieveurbeeld n fout emaakt hebben, zonder da'j de bestaonde pagina overschrieven.
+n Zied kan '''allinnig''' herneumd wörden as de nieje naam niet besteet of t n deurverwiezing is zonder veerdere geschiedenisse.
+Dit betekent da'j n zied weer naor de ouwe naam kunnen herneumen, a'j bieveurbeeld n fout emaakt hebben, zonder da'j de bestaonde zied overschrieven.
'''WAORSCHUWING!'''
-Veur popelaire pagina's kan t herneumen drastiese en onveurziene gevolgen hebben.
+Veur populaere ziejen kan t herneumen drastiese en onveurziene gevolgen hebben.
Zörg derveur da'j de gevolgen overzien veurda'j veerder gaon.",
-'movepagetalktext' => "De overlegpagina die derbie heurt krig oek n nieje titel, mer '''niet''' in de volgende gevallen:
-* As de pagina in n aandere naamruumte eplaots wörden
-* As der al n niet-lege overlegpagina besteet onder de aandere naam
+'movepagetalktext' => "De overlegzied die derbie heurt krig oek n nieje titel, mer '''niet''' in de volgende gevallen:
+* As de zied in n aandere naamruumte eplaotst wörden
+* As der al n niet-lege overlegzied besteet onder de aandere naam
* a'j t onderstaonde vinkjen vorthaolen",
'movearticle' => 'Herneum',
-'moveuserpage-warning' => "'''Waorschuwing:''' Je staon op t punt um n gebrukerspagina te herneumen. Allinnig disse pagina zal herneumd wörden, '''niet''' de gebruker.",
+'moveuserpage-warning' => "'''Waorschuwing:''' Je staon op t punt um n gebrukerszied te herneumen. Allinnig disse zied zal herneumd wörden, '''niet''' de gebruker.",
'movenologin' => 'Niet an-emeld.',
-'movenologintext' => 'Je mutten [[Special:UserLogin|an-emeld]] ween um de naam van n pagina te wiezigen.',
-'movenotallowed' => "Je hebben gien rechten um pagina's te herneumen.",
+'movenologintext' => 'Je mutten [[Special:UserLogin|an-emeld]] ween um de naam van n zied te wiezigen.',
+'movenotallowed' => 'Je hebben gien rechten um ziejen te herneumen.',
'movenotallowedfile' => 'Je hebben gien rechten um bestaanden te herneumen.',
-'cant-move-user-page' => "Je hebben gien rechten um gebrukerspagina's te herneumen.",
-'cant-move-to-user-page' => "Je hebben gien rechten um n pagina naor n gebrukerspagina te herneumen. Herneumen naor n subpagina ma'j wel doon.",
+'cant-move-user-page' => 'Je hebben gien rechten um gebrukersziejen te herneumen.',
+'cant-move-to-user-page' => "Je hebben gien rechten um n zied naor n gebrukerszied te herneumen. Herneumen naor n zied die deronder völt ma'j wel doon.",
'newtitle' => 'Nieje naam',
-'move-watch' => 'volg disse pagina',
+'move-watch' => 'volg disse zied',
'movepagebtn' => 'Herneum',
'pagemovedsub' => 'Naamwieziging suksesvol',
'movepage-moved' => '\'\'\'"$1" is ewiezigd naor "$2"\'\'\'',
'movepage-moved-redirect' => 'Der is n deurverwiezing an-emaakt.',
'movepage-moved-noredirect' => 'Der is gien deurverwiezing an-emaakt.',
-'articleexists' => 'Onder disse naam besteet al n pagina. Kies n aandere naam.',
-'cantmove-titleprotected' => 'Je kunnen gien pagina naor disse titel herneumen, umdat de nieje titel beveiligd is tegen t anmaken dervan.',
-'talkexists' => "De pagina zelf is herneumd, mer de overlegpagina kon niet verherneumd wörden, umdat de doelnaam al n niet-lege overlegpagina had. Kombineer de overlegpagina's mit de haand.",
+'articleexists' => 'Onder disse naam besteet al n zied. Kies n aandere naam.',
+'cantmove-titleprotected' => 'Je kunnen gien zied naor disse titel herneumen, umdat de nieje titel beveiligd is tegen t anmaken dervan.',
+'talkexists' => 'De zied zelf is herneumd, mer de overlegzied kon niet verherneumd wörden, umdat de doelnaam al n niet-lege overlegzied had. Kombineer de overlegziejen mit de haand.',
'movedto' => 'wiezigen naor',
-'movetalk' => 'De overlegpagina oek wiezigen, as t meuglik is.',
-'move-subpages' => "Herneum subpagina's (tot en mit $1)",
-'move-talk-subpages' => "Herneum subpagina's van overlegpagina's (tot en mit $1)",
-'movepage-page-exists' => 'De pagina $1 besteet al en kan niet automaties vortedaon wörden.',
-'movepage-page-moved' => 'De pagina $1 is herneumd naor $2.',
-'movepage-page-unmoved' => 'De pagina $1 kon niet herneumd wörden naor $2.',
-'movepage-max-pages' => "t Maximale antal automaties te herneumen pagina's is bereikt ({{PLURAL:$1|$1|$1}}).
-De overige pagina's wörden niet automaties herneumd.",
+'movetalk' => 'De overlegzied oek wiezigen, as t meuglik is.',
+'move-subpages' => 'Herneum de ziejen die deronder hangen (tot en mit $1)',
+'move-talk-subpages' => 'Herneum de ziejen die onder de overlegziejen hangen (tot en mit $1)',
+'movepage-page-exists' => 'De zied $1 besteet al en kan niet automaties vortedaon wörden.',
+'movepage-page-moved' => 'De zied $1 is herneumd naor $2.',
+'movepage-page-unmoved' => 'De zied $1 kon niet herneumd wörden naor $2.',
+'movepage-max-pages' => 't Maximale antal automaties te herneumen ziejen is bereikt ({{PLURAL:$1|$1|$1}}).
+De aandere ziejen wörden niet automaties herneumd.',
'movelogpage' => 'Herneumlogboek',
-'movelogpagetext' => "Hieronder steet n lieste mit pagina's die herneumd bin.",
-'movesubpage' => "{{PLURAL:$1|Subpagina|Subpagina's}}",
-'movesubpagetext' => "De {{PLURAL:$1|subpagina|$1 subpagina's}} van disse pagina vie'j hieronder.",
-'movenosubpage' => "Disse pagina hef gien subpagina's.",
+'movelogpagetext' => 'Hieronder steet n lieste mit ziejen die herneumd bin.',
+'movesubpage' => '{{PLURAL:$1|Zied die deronder hank|Ziejen die deronder hangen}}',
+'movesubpagetext' => "De {{PLURAL:$1|zied die onder disse zied hank|$1 ziejen die onder disse zied hangen}} vie'j hieronder.",
+'movenosubpage' => 'Onder disse zied hangen gien aandere ziejen.',
'movereason' => 'Reden:',
'revertmove' => 'Weerummedreien',
'delete_and_move' => 'Vortdoon en herneumen',
'delete_and_move_text' => '==Mut vortedaon wörden==
<div style="color: red"> Onder de nieje naam "[[:$1]]" besteet al n artikel. Wi\'j t vortdoon um plaotse te maken veur t herneumen?</div>',
-'delete_and_move_confirm' => 'Ja, disse pagina vortdoon',
+'delete_and_move_confirm' => 'Ja, disse zied vortdoon',
'delete_and_move_reason' => 'Vortedaon vanwegen de herneuming van "[[$1]]"',
'selfmove' => 'De naam kan niet ewiezigd wörden naor de naam die t al hef.',
-'immobile-source-namespace' => 'Pagina\'s in de naamruumte "$1" kunnen niet herneumd wörden',
-'immobile-target-namespace' => 'Pagina\'s kunnen niet herneumd wörden naor de naamruumte "$1"',
-'immobile-target-namespace-iw' => 'n Interwikiverwiezing is gien geldige bestemming veur t herneumen van n pagina.',
-'immobile-source-page' => 'Disse pagina kan niet herneumd wörden.',
-'immobile-target-page' => 'Kan niet herneumd wörden naor disse paginanaam.',
-'imagenocrossnamespace' => 'n Mediabestaand kan niet naor n aandere naamruumte verplaots wörden',
+'immobile-source-namespace' => 'Ziejen in de naamruumte "$1" kunnen niet herneumd wörden',
+'immobile-target-namespace' => 'Ziejen kunnen niet herneumd wörden naor de naamruumte "$1"',
+'immobile-target-namespace-iw' => 'n Interwikiverwiezing is gien geldige bestemming veur t herneumen van n zied.',
+'immobile-source-page' => 'Disse zied kan niet herneumd wörden.',
+'immobile-target-page' => 'Kan niet herneumd wörden naor disse ziednaam.',
+'imagenocrossnamespace' => 'n Mediabestaand kan niet naor n aandere naamruumte verplaotst wörden',
'nonfile-cannot-move-to-file' => 'Je kunnen niet herneumen van en naor de bestaandsnaamruumte',
'imagetypemismatch' => 'De nieje bestaandsextensie is niet gelieke an t bestaandstype',
'imageinvalidfilename' => 'De nieje bestaandsnaam is ongeldig',
'fix-double-redirects' => 'Alle deurverwiezingen die naor de ouwe titel verwiezen, herneumen naor de nieje titel',
'move-leave-redirect' => 'n Deurverwiezing achterlaoten',
-'protectedpagemovewarning' => "'''Waorschuwing:''' disse pagina kan allinnig deur beheerders herneumd wörden.",
-'semiprotectedpagemovewarning' => "'''Waorschuwing:''' disse pagina kan allinnig deur eregistreerden gebrukers herneumd wörden.
+'protectedpagemovewarning' => "'''Waorschuwing:''' disse zied kan allinnig deur beheerders herneumd wörden.",
+'semiprotectedpagemovewarning' => "'''Waorschuwing:''' disse zied kan allinnig deur eregistreerden gebrukers herneumd wörden.
De leste logboekregel steet hieronder:",
'move-over-sharedrepo' => "== t Bestaand besteet al ==
[[:$1]] besteet al in de edeelden mediadatabanke. A'j n bestaand naor disse titel herneumen, dan ku'j t edeelden bestaand niet gebruken.",
Kies n aandere bestaandsnaam.',
# Export
-'export' => "Pagina's uutvoeren",
-'exporttext' => "De tekste en geschiedenisse van n pagina of n antal pagina's kunnen in XML-formaot uutevoerd wörden. Dit bestaand ku'j daornao uutvoeren naor n aandere MediaWiki deur de [[Special:Import|invoerpagina]] te gebruken.
+'export' => 'Ziejen uutvoeren',
+'exporttext' => "De tekste en geschiedenisse van n zied of n koppel ziejen kunnen in XML-formaot uutevoerd wörden. Dit bestaand ku'j daornao uutvoeren naor n aandere MediaWiki deur de [[Special:Import|invoerzied]] te gebruken.
-Zet in t onderstaonde veld de namen van de pagina's die'j uutvoeren willen, één pagina per regel, en geef an o'j alle versies mit de bewarkingssamenvatting uutvoeren willen of allinnig de leste versies mit de bewarkingssamenvatting.
+Zet in t onderstaonde veld de namen van de ziejen die'j uutvoeren willen, één zied per regel, en gif an o'j alle versies mit de bewarkingssamenvatting uutvoeren willen of allinnig de leste versies mit de bewarkingssamenvatting.
-A'j dat leste doon willen dan ku'j oek n verwiezing gebruken, bieveurbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] veur de pagina \"[[{{MediaWiki:Mainpage}}]]\".",
-'exportall' => "Alle pagina's uutvoeren",
+A'j dat leste doon willen dan ku'j oek n verwiezing gebruken, bieveurbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] veur de zied \"[[{{MediaWiki:Mainpage}}]]\".",
+'exportall' => 'Alle ziejen uutvoeren',
'exportcuronly' => 'Allinnig de actuele versie, niet de veurgeschiedenisse',
'exportnohistory' => "----
-'''NB:''' t uutvoeren van de hele geschiedenisse is uuteschakeld vanwegen prestasieredens.",
-'exportlistauthors' => 'De hele auteurslieste opnemen veur elke pagina',
+'''NB:''' t uutvoeren van de hele geschiedenisse is uutezet vanwegen prestasieredens.",
+'exportlistauthors' => 'De hele auteurslieste opnemen veur elke zied',
'export-submit' => 'Uutvoeren',
-'export-addcattext' => "Pagina's derbie doon uut de kategorie:",
+'export-addcattext' => 'Ziejen derbie doon uut de kategorie:',
'export-addcat' => 'Derbie doon',
-'export-addnstext' => "Pagina's uut de volgende naamruumte derbie doon:",
+'export-addnstext' => 'Ziejen uut de volgende naamruumte derbie doon:',
'export-addns' => 'Derbie doon',
'export-download' => 'As bestaand opslaon',
'export-templates' => 'Mit mallen derbie',
-'export-pagelinks' => "Pagina's waor naor verwezen wörden opnemen tot:",
+'export-pagelinks' => 'Ziejen waor naor verwezen wörden opnemen tot:',
# Namespace 8 related
'allmessages' => 'Alle systeemteksten',
'allmessagescurrent' => 'De leste versie',
'allmessagestext' => "Hieronder steet n lieste mit alle systeemteksten in de MediaWiki-naamruumte.
Kiek oek effen bie [//www.mediawiki.org/wiki/Localisation MediaWiki-lokalisasie] en [//translatewiki.net translatewiki.net] a'j biedragen willen an de algemene vertaling veur MediaWiki.",
-'allmessagesnotsupportedDB' => "Disse pagina kan niet gebruukt wörden umdat '''\$wgUseDatabaseMessages''' uuteschakeld is.",
+'allmessagesnotsupportedDB' => "Disse zied kan niet gebruukt wörden umdat '''\$wgUseDatabaseMessages''' uutezet is.",
'allmessages-filter-legend' => 'Filter',
'allmessages-filter' => 'Filtreer op wiezigingen:',
'allmessages-filter-unmodified' => 'niet ewiezigd',
# Thumbnails
'thumbnail-more' => 'vergroten',
-'filemissing' => 'Bestaand ontbreekt',
+'filemissing' => 'Bestaand ontbrik',
'thumbnail_error' => 'Fout bie t laojen van de miniatuuraofbeelding: $1',
-'djvu_page_error' => 'DjVu-pagina buten bereik',
+'djvu_page_error' => 'DjVu-zied buten bereik',
'djvu_no_xml' => 'Kon de XML-gegevens veur t DjVu-bestaand niet oproepen',
'thumbnail-temp-create' => 'Kon gien tiedelik miniatuurbestaand anmaken.',
'thumbnail-dest-create' => 'Kon gien miniatuurbestaand op de doellokasie opslaon.',
'thumbnail_invalid_params' => 'Ongeldige parameters veur de miniatuuraofbeelding',
'thumbnail_dest_directory' => 'De bestemmingsmap kon niet an-emaakt wörden.',
'thumbnail_image-type' => 'Dit bestaandstype wörden niet ondersteund',
-'thumbnail_gd-library' => 'De instellingen veur de GD-biebeltheek bin niet compleet. De funksie $1 ontbreekt',
+'thumbnail_gd-library' => 'De instellingen veur de GD-biebeltheek bin niet compleet. De funksie $1 ontbrik',
'thumbnail_image-missing' => 't Lik derop dat t bestaand vort is: $1',
# Special:Import
-'import' => "Pagina's invoeren",
+'import' => 'Ziejen invoeren',
'importinterwiki' => 'Transwiki-invoer',
-'import-interwiki-text' => 'Kies n wiki en paginanaam um in te voeren.
+'import-interwiki-text' => 'Kies n wiki en ziednaam um in te voeren.
Versie- en auteursgegevens blieven hierbie beweerd.
Alle transwiki-invoerhaandelingen wörden op-esleugen in t [[Special:Log/import|invoerlogboek]].',
-'import-interwiki-source' => 'Bronwiki/pagina:',
-'import-interwiki-history' => 'Kopieer de hele geschiedenisse veur disse pagina',
+'import-interwiki-source' => 'Bronwiki/zied:',
+'import-interwiki-history' => 'Kopieer de hele geschiedenisse veur disse zied',
'import-interwiki-templates' => 'Alle mallen opnemen',
'import-interwiki-submit' => 'Invoeren',
'import-interwiki-namespace' => 'Doelnaamruumte:',
'import-upload-filename' => 'Bestaandsnaam:',
'import-comment' => 'Opmarkingen:',
-'importtext' => 'Gebruuk de [[Special:Export|uutvoerfunksie]] in de wiki waor de informasie vandaon kump.
+'importtext' => 'Gebruuk de [[Special:Export|uutvoerfunksie]] in de wiki waor de informasie vandaon kömp.
Slao t op joew eigen systeem op, en stuur t daornao hier op.',
-'importstart' => "Pagina's an t invoeren...",
+'importstart' => 'Ziejen an t invoeren...',
'import-revision-count' => '$1 {{PLURAL:$1|versie|versies}}',
-'importnopages' => "Der bin gien pagina's um in te voeren.",
+'importnopages' => 'Der bin gien ziejen um in te voeren.',
'imported-log-entries' => '$1 {{PLURAL:$1|logboekregel|logboekregels}} in-evoerd.',
'importfailed' => 'Invoeren is mislokt: $1',
'importunknownsource' => 'Onbekend invoerbrontype',
'importbadinterwiki' => 'Foute interwikiverwiezing',
'importnotext' => 'Leeg of gien tekste',
'importsuccess' => 'Invoeren suksesvol!',
-'importhistoryconflict' => 'Der bin konflikten in de geschiedenisse van de pagina (is misschien eerder al in-evoerd)',
+'importhistoryconflict' => 'Der bin konflikten in de geschiedenisse van de zied (is misschien eerder al in-evoerd)',
'importnosources' => 'Gien transwiki-invoerbronnen vastesteld en t drek inlaojen van versies is eblokkeerd.',
'importnofile' => 'Der is gien invoerbestaand op-estuurd.',
'importuploaderrorsize' => 't Opsturen van t invoerbestaand is mislokt.
'importuploaderrortemp' => 't Opsturen van t invoerbestaand is mislokt.
De tiedelike map is niet anwezig.',
'import-parse-failure' => 'Fout bie t verwarken van de XML-invoer',
-'import-noarticle' => "Der bin gien pagina's um in te voeren!",
+'import-noarticle' => 'Der bin gien ziejen um in te voeren!',
'import-nonewrevisions' => 'Alle versies bin al eerder in-evoerd.',
'xml-error-string' => '$1 op regel $2, kolom $3 (byte $4): $5',
'import-upload' => 'XML-gegevens derbie doon',
'import-token-mismatch' => 'De sessiegegevens bin verleuren egaon. Probeer t opniej.',
'import-invalid-interwiki' => 't Is niet meugelik um van de an-egeven wiki in te voeren.',
-'import-error-edit' => 'De pagina "$1" is niet in-evoerd umda\'j de rechten niet hebben um t te bewarken.',
-'import-error-create' => 'De pagina "$1" is niet in-evoerd umda\'j de rechten niet hebben um t an te maken.',
-'import-error-interwiki' => 'De pagina "$1" is niet in-evoerd umdat disse naam ereserveerd is veur externe verwiezingen (interwiki).',
-'import-error-special' => 'Pagina "$1" is niet in-evoerd umdat t eplaotst is in n spesiale naamruumte waor gien pagina\'s in eplaotst kunnen wörden.',
-'import-error-invalid' => 'De pagina" "$1" is niet in-evoerd umdat de naam ongeldig is.',
+'import-error-edit' => 'De zied "$1" is niet in-evoerd umda\'j de rechten niet hebben um t te bewarken.',
+'import-error-create' => 'De zied "$1" is niet in-evoerd umda\'j de rechten niet hebben um t an te maken.',
+'import-error-interwiki' => 'De zied "$1" is niet in-evoerd umdat disse naam ereserveerd is veur externe verwiezingen (interwiki).',
+'import-error-special' => 'Zied "$1" is niet in-evoerd umdat t eplaotst is in n spesiale naamruumte waor gien ziejen in eplaotst kunnen wörden.',
+'import-error-invalid' => 'De zied" "$1" is niet in-evoerd umdat de naam ongeldig is.',
# Import log
'importlogpage' => 'Invoerlogboek',
-'importlogpagetext' => "Administratieve invoer van pagina's mit geschiedenisse van aandere wiki's.",
+'importlogpagetext' => "Administratieve invoer van ziejen mit geschiedenisse van aandere wiki's.",
'import-logentry-upload' => 'hef [[$1]] in-evoerd',
'import-logentry-upload-detail' => '$1 {{PLURAL:$1|versie|versies}}',
'import-logentry-interwiki' => 'transwiki $1',
'javascripttest' => 'JavaScript testen',
'javascripttest-disabled' => 'Disse funksie steet niet an op disse wiki.',
'javascripttest-title' => 'Tests uutvoeren veur $1',
-'javascripttest-pagetext-noframework' => 'Disse pagina is ereserveerd veur t uutvoeren van JavaScript-testen.',
+'javascripttest-pagetext-noframework' => 'Disse zied is ereserveerd veur t uutvoeren van JavaScript-testen.',
'javascripttest-pagetext-unknownframework' => 'Onbekend testraamwark "$1".',
'javascripttest-pagetext-frameworks' => 'Kies een van de volgende testraamwarken: $1',
'javascripttest-pagetext-skins' => 'Kies n vormgeving um de tests mee uut te voeren:',
'tooltip-ca-unwatch' => 'Smiet disse bladziede van oewe voalglieste',
'tooltip-search' => '{{SITENAME}} duurzeukn',
'tooltip-search-go' => 'Noar n bladziede mit disse naam goan as t besteet',
-'tooltip-search-fulltext' => "De pagina's vuur disse tekst zeuken",
+'tooltip-search-fulltext' => 'Zeuk noar ziedn woar disse tekst in steet',
'tooltip-p-logo' => 'Goa noar t vuurblad',
'tooltip-n-mainpage' => 'Goa noar t vuurblad',
'tooltip-n-mainpage-description' => 'Goa noar t vuurblad',
'tooltip-diff' => 'Bekiek oew aegen wiezigingen',
'tooltip-compareselectedversions' => 'Bekiek de verschillen tussen de ekeuzen versies.',
'tooltip-watch' => 'Voog disse bladziede to an oew volglieste',
-'tooltip-watchlistedit-normal-submit' => "Pagina's vortdoon",
+'tooltip-watchlistedit-normal-submit' => 'Ziejen vortdoon',
'tooltip-watchlistedit-raw-submit' => 'Volglieste biewarken',
'tooltip-recreate' => 'Disse bladziede opniej anmaken, ondanks t feit dat t vortdoan is.',
'tooltip-upload' => 'Bestaanden tovogen',
'anonymous' => 'Anonieme {{PLURAL:$1|gebruker|gebrukers}} van {{SITENAME}}',
'siteuser' => '{{SITENAME}}-gebruker $1',
'anonuser' => 'Anonieme {{SITENAME}}-gebruker $1',
-'lastmodifiedatby' => 'Disse pagina is t lest ewiezigd op $2, $1 deur $3.',
+'lastmodifiedatby' => 'Disse zied is t lest ewiezigd op $2, $1 deur $3.',
'othercontribs' => 'Ebaseerd op wark van $1.',
'others' => 'aandere',
'siteusers' => '{{SITENAME}}-{{PLURAL:$2|gebruker|gebrukers}} $1',
'anonusers' => 'Anonieme {{SITENAME}}-{{PLURAL:$2|gebruker|gebrukers}} $1',
-'creditspage' => 'Pagina-auteurs',
-'nocredits' => 'Der is gien auteursinformasie beschikbaor veur disse pagina.',
+'creditspage' => 'Auteursinformasie',
+'nocredits' => 'Der is gien auteursinformasie beschikbaor veur disse zied.',
# Spam protection
'spamprotectiontitle' => 'Moekfilter',
-'spamprotectiontext' => "De pagina die'j opslaon wollen is eblokkeerd deur de moekfilter.
-Meestentieds kump dit deur n uutgaonde verwiezing die op de zwarte lieste steet.",
+'spamprotectiontext' => "De zied die'j opslaon wollen is eblokkeerd deur de moekfilter.
+Meestentieds kömp dit deur n uutgaonde verwiezing die op de zwarte lieste steet.",
'spamprotectionmatch' => 'Disse tekste zörgen derveur dat onze moekfilter alarmsleug: $1',
'spambot_username' => 'MediaWiki ongewunste zooi oprumen',
'spam_reverting' => 'Bezig mit t weerummezetten naor de leste versie die gien verwiezing hef naor $1',
'spam_blanking' => 'Alle wiezigingen mit n verwiezing naor $1 wörden vortehaold',
-'spam_deleting' => 'In alle versies staon verwiezingen naor $1. Pagina vortedaon',
+'spam_deleting' => 'In alle versies staon verwiezingen naor $1. Zied vortedaon',
# Info page
'pageinfo-title' => 'Informasie over "$1"',
'pageinfo-header-edits' => 'Bewarkingen',
'pageinfo-header-watchlist' => 'Volglieste',
'pageinfo-header-views' => 'Bekeken',
-'pageinfo-subjectpage' => 'Pagina:',
-'pageinfo-talkpage' => 'Overlegpagina',
+'pageinfo-subjectpage' => 'Zied:',
+'pageinfo-talkpage' => 'Overlegzied',
'pageinfo-watchers' => 'Antal volgers',
'pageinfo-edits' => 'Antal bewarkingen',
'pageinfo-authors' => 'Antal verschillende auteurs',
'skinname-modern' => 'Niejmoeds',
# Patrolling
-'markaspatrolleddiff' => 'Markeer as ekontroleerd',
-'markaspatrolledtext' => 'Disse pagina is emarkeerd as ekontroleerd',
-'markedaspatrolled' => 'Emarkeerd as ekontroleerd',
-'markedaspatrolledtext' => 'De ekeuzen versie van [[:$1]] is emarkeerd as ekontroleerd.',
-'rcpatroldisabled' => 'De kontrolemeugelikheid op leste wiezigingen is uuteschakeld.',
-'rcpatroldisabledtext' => 'De meugelikheid um de leste wiezigingen as ekontroleerd te markeren is noen uuteschakeld.',
+'markaspatrolleddiff' => 'Zet op nao-ekeken',
+'markaspatrolledtext' => 'Disse zied is op nao-ekeken ezet',
+'markedaspatrolled' => 'Op nao-ekeken ezet',
+'markedaspatrolledtext' => 'De ekeuzen versie van [[:$1]] is op nao-ekeken ezet.',
+'rcpatroldisabled' => 'De naokiekmeugelikheid op "Leste wiezigingen" is uutezet.',
+'rcpatroldisabledtext' => 'De meugelikheid um de leste wiezigingen op nao-ekeken te zetten is noen uutezet.',
'markedaspatrollederror' => 'De bewarking kon niet aofevinkt wörden.',
'markedaspatrollederrortext' => 'Je mutten n wieziging selekteren um t as nao-ekeken te markeren.',
-'markedaspatrollederror-noautopatrol' => 'Je maggen joew eigen bewarkingen niet as ekontroleerd markeren.',
+'markedaspatrollederror-noautopatrol' => 'Je maggen joew eigen bewarkingen niet op nao-ekeken zetten.',
# Patrol log
'patrol-log-page' => 'Markeerlogboek',
-'patrol-log-header' => 'In dit logboek staon de versies die emarkeerd bin as ekontroleerd.',
+'patrol-log-header' => 'In dit logboek staon de versies die op nao-ekeken ezet bin.',
'log-show-hide-patrol' => 'Markeerlogboek $1',
# Image deletion
# Media information
'mediawarning' => "'''Waorschuwing:''' in dit bestaand zit misschien kodering die slicht is veur t systeem.",
'imagemaxsize' => "Maximale aofmetingen van aofbeeldingen:<br />
-''(veur op de beschrievingspagina)''",
+''(veur op de beschrievingszied)''",
'thumbsize' => 'Grootte van de miniatuuraofbeelding:',
-'widthheightpage' => "$1 × $2, $3 {{PLURAL:$3|pagina|pagina's}}",
+'widthheightpage' => '$1 × $2, $3 {{PLURAL:$3|zied|ziejen}}',
'file-info' => 'Bestaandsgrootte: $1, MIME-type: $2',
'file-info-size' => '$1 × $2 beeldpunten, bestaandsgrootte: $3, MIME-type: $4',
-'file-info-size-pages' => "$1 × $2 beeldpunten, bestaandsgrootte: $3, MIME-type: $4, $5 {{PLURAL:$5|pagina|pagina's}}",
+'file-info-size-pages' => '$1 × $2 beeldpunten, bestaandsgrootte: $3, MIME-type: $4, $5 {{PLURAL:$5|zied|ziejen}}',
'file-nohires' => 'Gien hogere resolusie beschikbaor.',
'svg-long-desc' => 'SVG-bestaand, uutgangsgrootte $1 × $2 beeldpunten, bestaandsgrootte: $3',
'show-big-image' => 'Volle resolusie',
# Special:NewFiles
'newimages' => 'Nieje bestaanden',
'imagelisttext' => "Hier volgt n lieste mit '''$1''' {{PLURAL:$1|bestaand|bestaanden}} esorteerd $2.",
-'newimages-summary' => 'Op disse spesiale pagina staon de bestaanden die der as lest bie-ekeumen bin.',
+'newimages-summary' => 'Op disse spesiale zied staon de bestaanden die der as lest bie-ekeumen bin.',
'newimages-legend' => 'Bestaandsnaam',
'newimages-label' => 'Bestaandsnaam (of deel dervan):',
'showhidebots' => '(Bots $1)',
'ago' => '$1 eleen',
# Bad image list
-'bad_image_list' => "De opmaak is as volgt:
+'bad_image_list' => 'De opmaak is as volgt:
Allinnig regels in n lieste (regels die beginnen mit *) wörden verwarkt.
De eerste verwiezing op n regel mut n verwiezing ween naor n ongewunst bestaand.
-Alle volgende verwiezingen die op de zelfde regel staon, wörden behaandeld as uutzundering, zo as pagina's waorop t bestaand in te tekste op-eneumen is.",
+Alle volgende verwiezingen die op de zelfde regel staon, wörden behaandeld as uutzundering, zo as ziejen waorop t bestaand in te tekste op-eneumen is.',
# Metadata
'metadata' => 'Metadata',
'metadata-help' => 'In dit bestaand zit metadata mit EXIF-informasie, die deur n fotokamera, inleesapparaot of fotobewarkingsprogramma op-estuurd kan ween.',
'metadata-expand' => 'Bekiek uutebreiden gegevens',
'metadata-collapse' => 'Verbarg uutebreiden gegevens',
-'metadata-fields' => 'De aofbeeldingsmetadatavelden in dit bericht staon oek op n aofbeeldingspagina as de metadatatabel in-eklapt is.
+'metadata-fields' => 'De aofbeeldingsmetadatavelden in dit bericht staon oek op n aofbeeldingszied as de metadatatabel in-eklapt is.
Aandere velden wörden verbörgen.
* make
* model
'exif-jpeginterchangeformat' => 'Aofstaand tot JPEG SOI',
'exif-jpeginterchangeformatlength' => 'Bytes van JPEG-gegevens',
'exif-whitepoint' => 'Witpuntchromaticiteit',
-'exif-primarychromaticities' => 'Chromaciteit van primaire kleuren',
+'exif-primarychromaticities' => 'Chromasiteit van primaere kleuren',
'exif-ycbcrcoefficients' => 'Transformasiematrixkoëfficiënten veur de kleurruumte',
'exif-referenceblackwhite' => 'Referensieweerden veur zwart/wit',
'exif-datetime' => 'Tiedstip van digitalisasie',
# E-mail address confirmation
'confirmemail' => 'Bevestig netpostadres',
'confirmemail_noemail' => 'Je hebben gien geldig netpostadres in-evoerd in joew [[Special:Preferences|veurkeuren]].',
-'confirmemail_text' => "Bie disse wiki mö'j je netpostadres bevestigen veurda'j de berichtopsies gebruken kunnen. Klik op de onderstaonde knoppe um n bevestigingsbericht te ontvangen. In dit bericht zit n kode mit n verwiezing; um je netpostadres te bevestigen mö'j disse verwiezing los doon.",
+'confirmemail_text' => "Bie disse wiki mu'j je netpostadres bevestigen veurda'j de berichtopsies gebruken kunnen. Klik op de onderstaonde knoppe um n bevestigingsbericht te ontvangen. In dit bericht zit n kode mit n verwiezing; um je netpostadres te bevestigen mu'j disse verwiezing openen.",
'confirmemail_pending' => "Der is al n bevestigingskode op-estuurd; a'j net n gebrukersnaam an-emaakt hebben, wacht dan eerst n paor minuten tot da'j dit bericht ontvöngen hebben veurda'j n nieje kode anvragen.",
'confirmemail_send' => 'Stuur n bevestigingskode',
'confirmemail_sent' => 'Bevestigingsbericht verstuurd.',
'confirmemail_oncreate' => "n Bevestigingskode is naor joew netpostadres verstuurd. Disse kode is niet neudig um an te melden, mer je mutten t wel bevestigen veurda'j de netpostmeugelikheen van disse wiki gebruken kunnen.",
'confirmemail_sendfailed' => '{{SITENAME}} kon joe gien bevestigingskode toesturen.
-Kontroleer joew netpostadres op ongeldige tekens.
+Kiek nao of der gien ongeldige tekens in t netpostadres zitten.
Fout bie t versturen: $1',
'confirmemail_invalid' => 'Ongeldige bevestigingskode. De kode kan verlopen ween.',
'invalidateemail' => 'Netpostbevestiging ofbreken',
# Scary transclusion
-'scarytranscludedisabled' => '[Interwiki-intergrasie is uuteschakeld]',
+'scarytranscludedisabled' => '[Interwiki-intergrasie is uutezet]',
'scarytranscludefailed' => '[De mal $1 kon niet op-ehaold wörden]',
'scarytranscludetoolong' => '[URL is te lang]',
# Delete conflict
-'deletedwhileediting' => "'''Waorschuwing''': disse pagina is vortedaon terwiel jie t an t bewarken waren!",
-'confirmrecreate' => "Gebruker [[User:$1|$1]] ([[User talk:$1|Overleg]]) hef disse pagina vortedaon naoda'j begunnen bin mit joew wieziging, mit opgave van de volgende reden: ''$2''. Bevestig da'j t artikel herschrieven willen.",
-'confirmrecreate-noreason' => "Gebruker [[User:$1|$1]] ([[User talk:$1|overleg]]) hef disse pagina vortedaon naoda'j begunnen bin mit joew wieziging. Bevestig da'j t artikel herschrieven willen.",
+'deletedwhileediting' => "'''Waorschuwing''': disse zied is vortedaon terwiel jie t an t bewarken waren!",
+'confirmrecreate' => "Gebruker [[User:$1|$1]] ([[User talk:$1|Overleg]]) hef disse zied vortedaon naoda'j begunnen bin mit joew wieziging, mit opgave van de volgende reden: ''$2''. Bevestig da'j t artikel herschrieven willen.",
+'confirmrecreate-noreason' => "Gebruker [[User:$1|$1]] ([[User talk:$1|overleg]]) hef disse zied vortedaon naoda'j begunnen bin mit joew wieziging. Bevestig da'j t artikel herschrieven willen.",
'recreate' => 'Herschrieven',
# action=purge
'confirm_purge_button' => 'Bevestig',
-'confirm-purge-top' => "Klik op 'bevestig' um t tussengeheugen van disse pagina te legen.",
-'confirm-purge-bottom' => "t leegmaken van t tussengeheugen zörgt derveur da'j de leste versie van n pagina zien.",
+'confirm-purge-top' => "Klik op 'bevestig' um t tussengeheugen van disse zied te legen.",
+'confirm-purge-bottom' => "t leegmaken van t tussengeheugen zörgt derveur da'j de leste versie van n zied zien.",
# action=watch/unwatch
'confirm-watch-button' => 'Oké',
-'confirm-watch-top' => 'Disse pagina op joew volglieste zetten?',
+'confirm-watch-top' => 'Disse zied op joew volglieste zetten?',
'confirm-unwatch-button' => 'Oké',
-'confirm-unwatch-top' => 'Disse pagina van joew volglieste aofhaolen?',
+'confirm-unwatch-top' => 'Disse zied van joew volglieste aofhaolen?',
# Multipage image navigation
'imgmultipageprev' => '← veurige',
'imgmultipagenext' => 'volgende →',
'imgmultigo' => 'Oké',
-'imgmultigoto' => 'Gao naor de pagina $1',
+'imgmultigoto' => 'Gao naor de zied $1',
# Table pager
'ascending_abbrev' => 'aofl.',
'descending_abbrev' => 'opl.',
'table_pager_next' => 'Volgende',
'table_pager_prev' => 'Veurige',
-'table_pager_first' => 'Eerste pagina',
-'table_pager_last' => 'Leste pagina',
-'table_pager_limit' => 'Laot $1 resultaoten per pagina zien',
-'table_pager_limit_label' => 'Zaken per pagina:',
+'table_pager_first' => 'Eerste zied',
+'table_pager_last' => 'Leste zied',
+'table_pager_limit' => 'Laot $1 resultaoten per zied zien',
+'table_pager_limit_label' => 'Zaken per zied:',
'table_pager_limit_submit' => 'Zeuk',
'table_pager_empty' => 'Gien resultaoten',
# Auto-summaries
-'autosumm-blank' => 'Pagina leegemaakt',
+'autosumm-blank' => 'Zied leegemaakt',
'autosumm-replace' => "Tekste vervöngen deur '$1'",
'autoredircomment' => 'deurverwiezing naor [[$1]]',
-'autosumm-new' => "Nieje pagina: '$1'",
+'autosumm-new' => "Nieje zied: '$1'",
# Size units
'size-kilobytes' => '$1 kB',
'livepreview-loading' => 'An t laojen…',
'livepreview-ready' => 'An t laojen… ree!',
'livepreview-failed' => 'Rechtstreeks naokieken is niet meugelik!
-Kiek de pagina op de normale maniere nao.',
+Kiek de zied op de normale maniere nao.',
'livepreview-error' => 'Verbiending niet meugelik: $1 "$2"
-Kiek de pagina op de normale maniere nao.',
+Kiek de zied op de normale maniere nao.',
# Friendlier slave lag warnings
'lag-warn-normal' => 'Wiezigingen die niejer bin as $1 {{PLURAL:$1|seconde|seconden}} staon misschien nog niet in de lieste.',
'lag-warn-high' => 'De databanke is aorig zwaor belast. Wiezigingen die niejer bin as $1 {{PLURAL:$1|sekonde|sekonden}} staon daorumme misschien nog niet in de lieste.',
# Watchlist editor
-'watchlistedit-numitems' => "Der {{PLURAL:$1|steet 1 pagina|staon $1 pagina's}} op joew volglieste, zonder overlegpagina's.",
+'watchlistedit-numitems' => 'Der {{PLURAL:$1|steet 1 zied|staon $1 ziejen}} op joew volglieste, zonder overlegziejen.',
'watchlistedit-noitems' => 'Joew volglieste is leeg.',
'watchlistedit-normal-title' => 'Volglieste bewarken',
-'watchlistedit-normal-legend' => "Disse pagina's van mien volglieste aofhaolen.",
-'watchlistedit-normal-explain' => "Pagina's die op joew volglieste staon, zie'j hieronder.
-Um n pagina van joew volglieste aof te haolen mö'j t vakjen dernaost anklikken, en klik dan op \"{{int:Watchlistedit-normal-submit}}\".
-Je kunnen oek [[Special:EditWatchlist/raw|de roewe lieste bewarken]].",
-'watchlistedit-normal-submit' => "Pagina's deraof haolen",
-'watchlistedit-normal-done' => "Der {{PLURAL:$1|is 1 pagina|bin $1 pagina's}} vortedaon uut joew volglieste:",
+'watchlistedit-normal-legend' => 'Disse ziejen van mien volglieste aofhaolen.',
+'watchlistedit-normal-explain' => 'Ziejen die op joew volglieste staon, zie\'j hieronder.
+Um n zied van joew volglieste aof te haolen mu\'j t vakjen dernaost anklikken, en klik dan op "{{int:Watchlistedit-normal-submit}}".
+Je kunnen oek [[Special:EditWatchlist/raw|de roewe lieste bewarken]].',
+'watchlistedit-normal-submit' => 'Ziejen deraof haolen',
+'watchlistedit-normal-done' => 'Der {{PLURAL:$1|is 1 zied|bin $1 ziejen}} vortedaon uut joew volglieste:',
'watchlistedit-raw-title' => 'Roewe volglieste bewarken',
'watchlistedit-raw-legend' => 'Roewe volglieste bewarken',
-'watchlistedit-raw-explain' => "Pagina's die op joew volglieste staon, zie'j hieronder. Je kunnen de lieste bewarken deur pagina's deruut vort te haolen en derbie te te zetten.
-Eén pagina per regel.
-A'j klaor bin, klik dan op \"{{int:Watchlistedit-raw-submit}}\".
-Je kunnen oek [[Special:EditWatchlist|t standardbewarkingsscharm gebruken]].",
+'watchlistedit-raw-explain' => 'Ziejen die op joew volglieste staon, zie\'j hieronder. Je kunnen de lieste bewarken deur ziejen deruut vort te haolen en derbie te te zetten.
+Eén zied per regel.
+A\'j klaor bin, klik dan op "{{int:Watchlistedit-raw-submit}}".
+Je kunnen oek [[Special:EditWatchlist|t standardbewarkingsscharm gebruken]].',
'watchlistedit-raw-titles' => 'Titels:',
'watchlistedit-raw-submit' => 'Volglieste biewarken',
'watchlistedit-raw-done' => 'Joew volglieste is bie-ewörken.',
-'watchlistedit-raw-added' => "Der {{PLURAL:$1|is 1 pagina|bin $1 pagina's}} bie edaon:",
-'watchlistedit-raw-removed' => "Der {{PLURAL:$1|is 1 pagina|bin $1 pagina's}} vortedaon:",
+'watchlistedit-raw-added' => 'Der {{PLURAL:$1|is 1 zied|bin $1 ziejen}} bie edaon:',
+'watchlistedit-raw-removed' => 'Der {{PLURAL:$1|is 1 zied|bin $1 ziejen}} vortedaon:',
# Watchlist editing tools
'watchlisttools-view' => 'Wiezigingen bekieken',
# Special:Version
'version' => 'Versie',
'version-extensions' => 'Uutbreidingen die installeerd bin',
-'version-specialpages' => "Spesiale pagina's",
+'version-specialpages' => 'Spesiale ziejen',
'version-parserhooks' => 'Parserhoeken',
'version-variables' => 'Variabels',
'version-antispam' => 'Veurkoemen van ongewunste bewarkingen',
'filepath' => 'Bestaandslokasie',
'filepath-page' => 'Bestaand:',
'filepath-submit' => 'Zeuken',
-'filepath-summary' => 'Disse spesiale pagina geef t hele pad veur n bestaand.
+'filepath-summary' => 'Disse spesiale zied gif t hele pad veur n bestaand.
Aofbeeldingen wörden in resolusie helemaole weeregeven.
Aandere bestaandstypen wörden gelieke in t mit t MIME-type verbunnen programma opend.',
'fileduplicatesearch-noresults' => 'Der is gien bestaand mit de naam "$1" evunnen.',
# Special:SpecialPages
-'specialpages' => "Spesiale pagina's",
+'specialpages' => 'Spesiale ziejen',
'specialpages-note' => '----
-* Normale spesiale pagina\'s.
-* <strong class="mw-specialpagerestricted">Beparkt toegankelike spesiale pagina\'s.</strong>
-* <span class="mw-specialpagecached">Spesiale pagina\'s mit allinnig gegevens uut t tussengeheugen (kunnen verouwerd ween).</span>',
+* Normale spesiale ziejen.
+* <strong class="mw-specialpagerestricted">Beparkt toegankelike spesiale ziejen.</strong>
+* <span class="mw-specialpagecached">Spesiale ziejen mit allinnig gegevens uut t tussengeheugen (kunnen verouwerd ween).</span>',
'specialpages-group-maintenance' => 'Onderhoudsliesten',
-'specialpages-group-other' => "Overige spesiale pagina's",
+'specialpages-group-other' => 'Aandere spesiale ziejen',
'specialpages-group-login' => 'Anmelden / inschrieven',
'specialpages-group-changes' => 'Leste wiezigingen en logboeken',
'specialpages-group-media' => 'Media-overzichten en nieje bestaanden',
'specialpages-group-users' => 'Gebrukers en rechten',
-'specialpages-group-highuse' => "Veulgebruukten pagina's",
-'specialpages-group-pages' => 'Paginaliesten',
-'specialpages-group-pagetools' => 'Paginahulpmiddels',
+'specialpages-group-highuse' => 'Veulgebruukten ziejen',
+'specialpages-group-pages' => 'Liesten mit ziejen',
+'specialpages-group-pagetools' => 'Ziedhulpmiddels',
'specialpages-group-wiki' => 'Wikigegevens en -hulpmiddels',
-'specialpages-group-redirects' => "Deurverwiezende spesiale pagina's",
+'specialpages-group-redirects' => 'Deurverwiezende spesiale ziejen',
'specialpages-group-spam' => 'Hulpmiddels tegen ongewunste bewarkingen',
# Special:BlankPage
-'blankpage' => 'Lege pagina',
-'intentionallyblankpage' => 'Disse pagina is bewust leeg eleuten.',
+'blankpage' => 'Lege zied',
+'intentionallyblankpage' => 'Disse zied is bewust leeg eleuten.',
# External image whitelist
'external_image_whitelist' => ' #Laot disse regel onveraanderd<pre>
'tag-filter' => '[[Special:Tags|Etiketfilter]]:',
'tag-filter-submit' => 'Filtreren',
'tags-title' => 'Etiket',
-'tags-intro' => 'Op disse pagina staon de etiketten waormee de programmatuur elke bewarking kan markeren, en de betekenisse dervan.',
+'tags-intro' => 'Op disse zied staon de etiketten waormee de programmatuur elke bewarking kan markeren, en de betekenisse dervan.',
'tags-tag' => 'Etiketnaam',
'tags-display-header' => 'Weergave in wiezigingsliesten',
'tags-description-header' => 'Beschrieving van de betekenisse',
'tags-hitcount' => '$1 {{PLURAL:$1|wieziging|wiezigingen}}',
# Special:ComparePages
-'comparepages' => "Pagina's vergelieken",
-'compare-selector' => 'Paginaversies vergelieken',
-'compare-page1' => 'Pagina 1',
-'compare-page2' => 'Pagina 2',
+'comparepages' => 'Ziejen vergelieken',
+'compare-selector' => 'Ziedversies vergelieken',
+'compare-page1' => 'Zied 1',
+'compare-page2' => 'Zied 2',
'compare-rev1' => 'Versie 1',
'compare-rev2' => 'Versie 2',
'compare-submit' => 'Vergelieken',
'compare-revision-not-exists' => "De versie die'j op-egeven hebben, besteet niet.",
# Database error messages
-'dberr-header' => 'Disse wiki hef n probleem',
+'dberr-header' => 'Disse wiki hef wat kuren',
'dberr-problems' => 't Spiet ons, mer disse webstee hef op t moment wat techniese problemen.',
'dberr-again' => 'Wach n paor minuten en probeer t daornao opniej.',
'dberr-info' => '(Kan gien verbiending maken mit de databankeserver: $1)',
'dberr-usegoogle' => "Misschien ku'j ondertussen zeuken via Google.",
-'dberr-outofdate' => "Let op: indexen die zee hebben van onze pagina's bin misschien niet aktueel.",
-'dberr-cachederror' => 'Disse pagina is n kopie uut t tussengeheugen en is misschien niet aktueel.',
+'dberr-outofdate' => 'Let op: indexen die zee hebben van onze ziejen bin misschien niet aktueel.',
+'dberr-cachederror' => 'Disse zied is n kopie uut t tussengeheugen en is misschien niet aktueel.',
# HTML forms
'htmlform-invalid-input' => 'Der bin problemen mit n paor in-egeven weerden',
'sqlite-no-fts' => 'Versie $1 zonder ondersteuning veur "full-text" zeuken',
# New logging system
-'logentry-delete-delete' => '$1 hef de pagina $3 vortedaon',
-'logentry-delete-restore' => '$1 hef de pagina $3 weerummezet',
+'logentry-delete-delete' => '$1 hef de zied $3 vortedaon',
+'logentry-delete-restore' => '$1 hef de zied $3 weerummezet',
'logentry-delete-event' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een logboekregel|$5 logboekregels}} van $3 ewiezigd: $4',
-'logentry-delete-revision' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een versie|$5 versies}} van de pagina $3 ewiezigd: $4',
+'logentry-delete-revision' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een versie|$5 versies}} van de zied $3 ewiezigd: $4',
'logentry-delete-event-legacy' => '$1 hef de zichtbaorheid van logboekregels van $3 ewiezigd',
-'logentry-delete-revision-legacy' => '$1 hef de zichtbaorheid van versies van de pagina $3 ewiezigd.',
-'logentry-suppress-delete' => '$1 hef de pagina $3 onderdrokt',
+'logentry-delete-revision-legacy' => '$1 hef de zichtbaorheid van versies van de zied $3 ewiezigd.',
+'logentry-suppress-delete' => '$1 hef de zied $3 onderdrokt',
'logentry-suppress-event' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een logboekregel|$5 logboekregels}} van $3 sluuksem ewiezigd: $4',
-'logentry-suppress-revision' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een versie|$5 versies}} van de pagina $3 sluuksem ewiezigd: $4',
+'logentry-suppress-revision' => '$1 hef de zichtbaorheid van {{PLURAL:$5|een versie|$5 versies}} van de zied $3 sluuksem ewiezigd: $4',
'logentry-suppress-event-legacy' => '$1 hef de zichtbaorheid van logboekregels van $3 sluuksem ewiezigd',
-'logentry-suppress-revision-legacy' => '$1 hef de zichtbaorheid van versies van de pagina $3 sluuksem ewiezigd.',
+'logentry-suppress-revision-legacy' => '$1 hef de zichtbaorheid van versies van de zied $3 sluuksem ewiezigd.',
'revdelete-content-hid' => 'inhoud verbörgen',
'revdelete-summary-hid' => 'bewarkingssamenvatting verbörgen',
'revdelete-uname-hid' => 'gebrukersnaam verbörgen',
'revdelete-uname-unhid' => 'gebrukersnaam zichtbaor emaakt',
'revdelete-restricted' => 'hef beparkingen an beheerders op-eleg',
'revdelete-unrestricted' => 'hef beparkingen veur beheerders deraof ehaold',
-'logentry-move-move' => '$1 herneumden de pagina $3 naor $4',
-'logentry-move-move-noredirect' => '$1 herneumden de pagina $3 naor $4 zonder n deurverwiezing achter te laoten',
-'logentry-move-move_redir' => '$1 herneumden de pagina $3 naor $4 over n deurverwiezing heer',
-'logentry-move-move_redir-noredirect' => '$1 herneumden de pagina $3 naor $4 over n deurverwiezing heer zonder n deurverwiezing achter te laoten',
-'logentry-patrol-patrol' => '$1 hef versie $4 van pagina $3 as ekontroleerd emarkeerd',
-'logentry-patrol-patrol-auto' => '$1 hef versie $4 van pagina $3 automaties as ekontroleerd emarkeerd',
+'logentry-move-move' => '$1 herneumden de zied $3 naor $4',
+'logentry-move-move-noredirect' => '$1 herneumden de zied $3 naor $4 zonder n deurverwiezing achter te laoten',
+'logentry-move-move_redir' => '$1 herneumden de zied $3 naor $4 over n deurverwiezing heer',
+'logentry-move-move_redir-noredirect' => '$1 herneumden de zied $3 naor $4 over n deurverwiezing heer zonder n deurverwiezing achter te laoten',
+'logentry-patrol-patrol' => '$1 hef versie $4 van de zied $3 op nao-ekeken ezet',
+'logentry-patrol-patrol-auto' => '$1 hef versie $4 van de zied $3 automaties op nao-ekeken ezet',
'logentry-newusers-newusers' => '$1 hef n gebruker an-emaakt',
'logentry-newusers-create' => '$1 hef n gebruker an-emaakt',
'logentry-newusers-create2' => '$1 hef n gebruker $3 an-emaakt',
# Feedback
'feedback-bugornote' => 'A\'j zovere bin um n technies probleem nauwkeurig te beschrieven, [$1 meld dan n programmafout].
-Aanders ku\'j oek t eenvoudige formulier hieronder gebruken. Joew opmarkingen zullen op de pagina "[$3 $2]" ezet wörden, samen mit joew gebrukersnaam en de webkieker die\'j gebruken.',
+Aanders ku\'j oek t eenvoudige formulier hieronder gebruken. Joew opmarkingen zullen op de zied "[$3 $2]" ezet wörden, samen mit joew gebrukersnaam en de webkieker die\'j gebruken.',
'feedback-subject' => 'Onderwarp:',
'feedback-message' => 'Bericht:',
'feedback-cancel' => 'Aofbreken',
'feedback-submit' => 'Troegkoppeling opslaon',
-'feedback-adding' => 'Joew troegkoppeling wörden op de pagina ezet...',
+'feedback-adding' => 'Joew troegkoppeling wörden op de zied ezet...',
'feedback-error1' => 'Fout: onbekend resultaot uut de API',
'feedback-error2' => 'Fout: de bewarking is mislokt',
'feedback-error3' => 'Fout: gien reaksie van de API',
-'feedback-thanks' => 'Bedankt! Joew troegkoppeling is op de pagina "[$2 $1]" ezet.',
+'feedback-thanks' => 'Bedankt! Joew troegkoppeling is op de zied "[$2 $1]" ezet.',
'feedback-close' => 'Ree',
'feedback-bugcheck' => 'Mooi! Kiek nao of t niet al één van de [$1 bekende problemen] is.',
'feedback-bugnew' => 'Ik heb t nao-ekeken. Meld n nieje programmafout',
'api-error-duplicate-archive-popup-title' => '{{PLURAL:$1|Duplikaotbestaand dat al vortedaon is|Duplikaotbestaanden die al vortedaon bin}}',
'api-error-duplicate-popup-title' => 'Zelfde {{PLURAL:$1|bestaand|bestaanden}}',
'api-error-empty-file' => "t Bestaand da'j op-estuurd hebben is leeg.",
-'api-error-emptypage' => "Je maggen gien lege nieje pagina's anmaken.",
+'api-error-emptypage' => 'Je maggen gien lege nieje ziejen anmaken.',
'api-error-fetchfileerror' => 'Interne fout: der is iets verkeerd egaon mit t ophaolen van t bestaand.',
'api-error-fileexists-forbidden' => 'Der besteet al n bestaand mit de naam "$1" die niet overschreven kan wörden.',
'api-error-fileexists-shared-forbidden' => 'Der besteet al n bestaand mit de naam "$1" in de edeelden bestaandsarchief dat niet overschreven kan wörden.',
'api-error-mustbeloggedin' => 'Je mutten an-emeld ween um bestaanden te kunnen opsturen.',
'api-error-mustbeposted' => 'Der zit n fout in de programmatuur. Der wörden gien gebruukemaakt van de juuste HTTP-methode.',
'api-error-noimageinfo' => 't Opsturen van t bestaand is aoferond, mer de server hef gien gegevens over t bestaand egeven.',
-'api-error-nomodule' => 'Interne fout: der is gien inlaodmodule in-esteld.',
+'api-error-nomodule' => 'Interne fout: der is gien inlaojmodule in-esteld.',
'api-error-ok-but-empty' => 'Interne fout: de server hef gien gegevens weerestuurd.',
'api-error-overwrite' => 'Je maggen gien bestaond bestaand overschrieven.',
'api-error-stashfailed' => 'Interne fout: de server kon t tiedelike bestaand niet opslaon.',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Kon gien vergrendeling op server $1 zetten.',
+'api-error-filetype-banned-type' => '{{PLURAL:$4|t Bestaandstype $1 wordt|De bestandstypes $1 worden}} niet toegelaten. {{PLURAL:$3|t Toe-estaone bestaandstype is|De toe-estaone bestaandstypen bin}} $2.',
);
'tog-editsectiononrightclick' => 'Bewerken van deelpagina’s mogelijk maken met een rechtermuisklik op een tussenkop (vereist JavaScript)',
'tog-showtoc' => 'Inhoudsopgave weergeven (voor pagina’s met minstens 3 tussenkoppen)',
'tog-rememberpassword' => 'Aanmeldgegevens onthouden (maximaal $1 {{PLURAL:$1|dag|dagen}})',
-'tog-watchcreations' => 'Pagina’s die ik aanmaak automatisch volgen',
-'tog-watchdefault' => 'Pagina’s die ik bewerk automatisch volgen',
-'tog-watchmoves' => 'Pagina’s die ik hernoem automatisch volgen',
-'tog-watchdeletion' => 'Pagina’s die ik verwijder automatisch volgen',
+'tog-watchcreations' => "Pagina's die ik aanmaak en bestanden die ik upload automatisch volgen",
+'tog-watchdefault' => 'Pagina’s en bestanden die ik bewerk automatisch volgen',
+'tog-watchmoves' => 'Pagina’s en bestanden die ik hernoem automatisch volgen',
+'tog-watchdeletion' => 'Pagina’s en bestanden die ik verwijder automatisch volgen',
'tog-minordefault' => 'Mijn bewerkingen als ‘klein’ markeren',
'tog-previewontop' => 'Voorvertoning boven bewerkingsveld weergeven',
'tog-previewonfirst' => 'Voorvertoning bij eerste bewerking weergeven',
'tog-nocache' => "Cachen van pagina's door de browser uitschakelen",
-'tog-enotifwatchlistpages' => 'Mij e-mailen bij bewerkingen van pagina’s op mijn volglijst',
+'tog-enotifwatchlistpages' => 'Mij e-mailen bij bewerkingen van pagina’s of bestanden op mijn volglijst',
'tog-enotifusertalkpages' => 'Mij e-mailen als iemand mijn overlegpagina wijzigt',
-'tog-enotifminoredits' => 'Mij e-mailen bij kleine bewerkingen van pagina’s op mijn volglijst',
+'tog-enotifminoredits' => 'Mij e-mailen bij kleine bewerkingen van pagina’s en bestanden op mijn volglijst',
'tog-enotifrevealaddr' => 'Mijn e-mailadres weergeven in e-mailberichten',
'tog-shownumberswatching' => 'Het aantal gebruikers weergeven dat deze pagina volgt',
'tog-oldsig' => 'Bestaande ondertekening:',
'vector-simplesearch-preference' => 'Verbeterde zoeksuggesties inschakelen (alleen voor het uiterlijk Vector)',
'vector-view-create' => 'Aanmaken',
'vector-view-edit' => 'Bewerken',
-'vector-view-history' => 'Geschiedenis bekijken',
+'vector-view-history' => 'Geschiedenis weergeven',
'vector-view-view' => 'Lezen',
'vector-view-viewsource' => 'Brontekst bekijken',
'actions' => 'Handelingen',
'youhavenewmessages' => 'U hebt $1 ($2).',
'newmessageslink' => 'nieuwe berichten',
'newmessagesdifflink' => 'laatste wijziging',
+'youhavenewmessagesfromusers' => 'U heeft $1 van {{PLURAL:$3|een andere gebruiker|$3 gebruikers}} ($2).',
+'youhavenewmessagesmanyusers' => 'U heeft $1 van een groot aantal gebruikers ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|een nieuw bericht|nieuwe berichten}}',
+'newmessagesdifflinkplural' => 'laatste {{PLURAL:$1|wijziging|wijzigingen}}',
'youhavenewmessagesmulti' => 'U hebt nieuwe berichten op $1',
'editsection' => 'bewerken',
'editold' => 'bewerken',
'cannotdelete' => 'De pagina of het bestand "$1" kon niet verwijderd worden.
Mogelijk is deze al door iemand anders verwijderd.',
'cannotdelete-title' => 'Pagina "$1" kan niet verwijderd worden',
-'delete-hook-aborted' => 't Vortdoon is aofebreuken deur n haak.
-Der is gien informasie over beschikbaor.',
+'delete-hook-aborted' => 'Het verwijderen is afgebroken door een hook.
+Er is geen toelichting beschikbaar.',
'badtitle' => 'Ongeldige paginanaam',
'badtitletext' => 'De naam van de opgevraagde pagina was ongeldig, leeg of bevatte een verkeerde intertaal- of interwikinaamverwijzing.
Wellicht bevat de paginanaam niet toegestane tekens.',
'remembermypassword' => 'Aanmeldgegevens onthouden (maximaal $1 {{PLURAL:$1|dag|dagen}})',
'securelogin-stick-https' => 'Verbonden blijven via HTTPS na aanmelden',
'yourdomainname' => 'Uw domein:',
+'password-change-forbidden' => 'U kunt uw wachtwoord niet wijzigen in deze wiki.',
'externaldberror' => 'Er is een fout opgetreden bij het aanmelden bij de database of u hebt geen toestemming uw externe gebruiker bij te werken.',
'login' => 'Aanmelden',
'nav-login-createaccount' => 'Aanmelden / registreren',
'noarticletext-nopermission' => 'Deze pagina bevat geen tekst.
U kunt [[Special:Search/{{PAGENAME}}|naar deze term zoeken]] in andere pagina\'s of
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboeken doorzoeken]</span>.',
+'missing-revision' => 'De versie #$1 van de pagina "{{PAGENAME}} bestaat niet.
+
+Dit wordt meestal veroorzaakt door het volgen van een verouderde verwijzing naar een pagina die is verwijderd.
+Meer gegevens zijn mogelijk te vinden in het [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwijderlogboek].',
'userpage-userdoesnotexist' => 'U bewerkt een gebruikerspagina van een gebruiker die niet bestaat (gebruiker "<nowiki>$1</nowiki>").
Controleer of u deze pagina wel wilt aanmaken of bewerken.',
'userpage-userdoesnotexist-view' => 'De gebruiker "$1" is niet geregistreerd.',
'expansion-depth-exceeded-warning' => 'De pagina bevat te veel sjablonen',
'parser-unstrip-loop-warning' => 'Er is een "unstrip"-lus gedetecteerd',
'parser-unstrip-recursion-limit' => 'De recursielimiet ($1) voor "unstrip" is overschreden',
+'converter-manual-rule-error' => 'Er is een fout gedetecteerd in een handmatig toegevoegde taalconversieregel.',
# "Undo" feature
'undo-success' => 'Deze bewerking kan ongedaan gemaakt worden.
'editundo' => 'ongedaan maken',
'diff-multi' => '({{PLURAL:$1|Eén tussenliggende versie|$1 tussenliggende versies}} door {{PLURAL:$2|één gebruiker|$2 gebruikers}} {{PLURAL:$1|wordt|worden}} niet weergegeven)',
'diff-multi-manyusers' => '($1 tussenliggende {{PLURAL:$1|versie|versies}} door meer dan $2 {{PLURAL:$2|gebruiker|gebruikers}} worden niet weergegeven)',
+'difference-missing-revision' => '{{PLURAL:$2|Eén versie|$2 versies}} van deze verschillen ($1) {{PLURAL:$2|is|zijn}} niet aangetroffen.
+
+Dit wordt meestal veroorzaakt door het volgen van een verouderde verwijzing verschillen voor een pagina die is verwijderd.
+Meer gegevens zijn mogelijk te vinden in het [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwijderlogboek].',
# Search results
'searchresults' => 'Zoekresultaten',
'right-writeapi' => 'Bewerken via de API',
'right-delete' => "Pagina's verwijderen",
'right-bigdelete' => "Pagina's met een grote geschiedenis verwijderen",
+'right-deletelogentry' => 'Specifieke logboekregels verwijderen en terugplaatsen',
'right-deleterevision' => "Versies van pagina's verbergen",
'right-deletedhistory' => 'Verwijderde versies bekijken, zonder te kunnen zien wat verwijderd is',
'right-deletedtext' => 'Verwijderde tekst en wijzigingen tussen verwijderde versies bekijken',
'lockmanager-fail-releaselock' => 'Het was niet mogelijk vergrendeling van "$1" op te heffen.',
'lockmanager-fail-db-bucket' => 'Het was niet mogelijk om in contact te komen met voldoende vergrendelingsdatabases in de bucket $1.',
'lockmanager-fail-db-release' => 'Het was niet mogelijk om de vergrendeling voor de database $1 op te heffen.',
+'lockmanager-fail-svr-acquire' => 'Het was niet mogelijk een vergrendeling te krijgen op server $1.',
'lockmanager-fail-svr-release' => 'Het was niet mogelijk om de vergrendeling voor de server $1 op te heffen.',
# ZipDirectoryReader
'disambiguations' => "Pagina's die verwijzen naar doorverwijspagina's",
'disambiguationspage' => 'Template:Doorverwijspagina',
-'disambiguations-text' => "Hieronder staan pagina's die verwijzen naar een '''doorverwijspagina'''.
-Deze horen waarschijnlijk direct naar het juiste onderwerp te verwijzen.<br />
+'disambiguations-text' => "Hieronder staan pagina's met tenminste één verwijzing naar een '''doorverwijspagina'''.
+Deze horen waarschijnlijk direct naar een meer toepasselijke pagina te verwijzen.<br />
Een pagina wordt gezien als doorverwijspagina als er een sjabloon op staat dat opgenomen is op [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dubbele doorverwijzingen',
'rollback' => 'Wijzigingen ongedaan maken',
'rollback_short' => 'Terugdraaien',
'rollbacklink' => 'terugdraaien',
+'rollbacklinkcount' => '{{PLURAL:$1|één bewerking|$1 bewerkingen}} terugdraaien',
+'rollbacklinkcount-morethan' => 'Meer dan {{PLURAL:$1|één bewerking|$1 bewerkingen}} terugdraaien',
'rollbackfailed' => 'Ongedaan maken van wijzigingen mislukt.',
'cantrollback' => 'Ongedaan maken van wijzigingen onmogelijk: deze pagina heeft slechts 1 auteur.',
'alreadyrolled' => 'Het is niet mogelijk om de bewerking van de pagina [[:$1]] door [[User:$2|$2]] ([[User talk:$2|overleg]]{{int:pipe-separator}}[[Special:Contributions/$2|bijdragen]]) ongedaan te maken.
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Het was niet mogelijk een vergrendeling te krijgen op server $1.',
+'api-error-filetype-banned-type' => '{{PLURAL:$4|Het bestandstype $1 wordt|De bestandstypes $1 worden}} niet toegelaten. {{PLURAL:$3|Het toegelaten bestandstype is|De toegelaten bestandstypes zijn}} $2.',
);
* @author Eirik
* @author Finnrind
* @author Frokor
+ * @author Geitost
* @author Gunnernett
* @author Guttorm Flatabø
* @author H92
'difference-multipage' => '(Skilnad mellom sider)',
'lineno' => 'Line $1:',
'compareselectedversions' => 'Samanlikn valde versjonar',
-'showhideselectedversions' => 'Syn/skjul valde versjonar',
+'showhideselectedversions' => 'Vis/løyn valde versjonar',
'editundo' => 'angre',
'diff-multi' => '({{PLURAL:$1|Éin mellomversjon|$1 mellomversjonar}} frå {{PLURAL:$2|éin brukar|$2 brukarar}} er ikkje {{PLURAL:$1|vist|viste}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Ein mellomversjon|$1 mellomversjonar}} av meir enn $2 {{PLURAL:$2|brukar|brukarar}} er ikkje {{PLURAL:$1|vist|viste}})',
'prefs-custom-js' => 'Eigendefinert JavaScript',
'prefs-common-css-js' => 'Delt CSS/JavaScript for alle draktene:',
'prefs-reset-intro' => 'Du kan nytta denne sida til å tilbakestilla innstillingane dine til standardinnstillingane.
-Dette kan ikke tilbakestillast.',
+Dette kan ikkje tilbakestillast.',
'prefs-emailconfirm-label' => 'Stadfesting av e-post:',
'prefs-textboxsize' => 'Storleiken til redigeringsvindauga',
'youremail' => 'E-post:',
'right-ipblock-exempt' => 'Kan gjere endringar frå blokkerte IP-adresser',
'right-proxyunbannable' => 'Kan gjere endringar frå blokkerte proxyar',
'right-unblockself' => 'Avblokkera seg sjølve',
-'right-protect' => 'Endre vernenivå',
+'right-protect' => 'Endre vernenivå<!-- og redigera beskyttete sider-->',
'right-editprotected' => 'Endre verna sider',
'right-editinterface' => 'Redigere brukargrensesnittet',
'right-editusercssjs' => 'Endre andre brukarar sine CSS- og JS-filer',
'alllogstext' => 'Kombinert vising av alle loggane på {{SITENAME}}. Du kan avgrense resultatet ved å velje loggtype, brukarnamn eller den sida som er påverka (hugs å skilje mellom store og små bokstavar)',
'logempty' => 'Ingen treff i loggane.',
'log-title-wildcard' => 'Søk i titlar som byrjar med denne teksten',
+'showhideselectedlogentries' => 'Vis/gøym valde loggoppføringar',
# Special:AllPages
'allpages' => 'Alle sider',
'move-subpages' => 'Flytt undersider (opp til $1)',
'move-talk-subpages' => 'Flytt undersider av diskusjonssida (opp til $1)',
'movepage-page-exists' => 'Sida $1 finst alt og kan ikkje skrivast over automatisk.',
-'movepage-page-moved' => 'Sida $1 har blitt flytta til $2.',
+'movepage-page-moved' => 'Sida $1 er flytt til $2.',
'movepage-page-unmoved' => 'Sida $1 kunne ikkje flyttast til $2.',
'movepage-max-pages' => 'Grensa på {{PLURAL:$1|éi side|$1 sider}} er nådd; ingen fleire sider kjem til å verte flytta automatisk.',
'movelogpage' => 'Flyttelogg',
'duration-centuries' => '$1 {{PLURAL:$1|hundreår|hundreår}}',
'duration-millennia' => '$1 {{PLURAL:$1|tusenår|tusenår}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 er ikkje ein tillaten filtype. {{PLURAL:$3|Tillaten filtype er|Tillatne filtypar er}} $2.',
);
'tog-hidepatrolled' => 'ନଗଦ ବଦଳରେ ଥିବା ଜଗାହୋଇଥିବା ବଦଳ ସବୁକୁ ଲୁଚାଇବେ',
'tog-newpageshidepatrolled' => 'ନୂଆ ପୃଷ୍ଠାତାଲିକାରୁ ଜଗାହୋଇଥିବା ବଦଳସବୁକୁ ଲୁଚାଇବେ',
'tog-extendwatchlist' => 'କେବଳ ନଗଦ ନୁହେଁ, ସବୁଯାକ ବଦଳକୁ ଦେଖାଇବା ନିମନ୍ତେ ଦେଖଣାତାଲିକାକୁ ବଢ଼ାଇବେ',
-'tog-usenewrc' => 'à¬\89ନà\8dନତ ନà¬\97ଦ ବଦଳ ବà\8dà\9fବହାର à¬\95ରନà\8dତà\81 (ଜାଭାସ୍କ୍ରିପ୍ଟ ଲୋଡ଼ା)',
+'tog-usenewrc' => 'ନà¬\97ଦ ବଦଳରà\87 ପà\83ଷà\8dଠା à¬\85ନà\81ଯାà\9fà\80 à¬\97à\8bଷà\8dଠà\80 ବଦଳ à¬\8fବà¬\82 ଦà\87à¬\96ଣା (ଜାଭାସ୍କ୍ରିପ୍ଟ ଲୋଡ଼ା)',
'tog-numberheadings' => 'ଆପେଆପେ-ସଂଖ୍ୟାର ନାମଗୁଡ଼ିକ',
'tog-showtoolbar' => 'ସମ୍ପାଦନା ଟୁଲବାର ଦେଖାଇବେ (ଜାଭାସ୍କ୍ରିପ୍ଟ ସଚଳ କରିବେ)',
'tog-editondblclick' => 'ଦୁଇଥର କ୍ଲିକରେ ପୃଷ୍ଠା ବଦଳାଇବେ (ଜାଭାସ୍କ୍ରିପ୍ଟ ଲୋଡ଼ା)',
'tog-editsectiononrightclick' => 'ବିଭାଗ ନାମରେ ଡାହାଣ କ୍ଲିକ କରି ବିଭାଗ ସମ୍ପାଦନାକୁ ସଚଳ କରିବେ (ଜାଭାସ୍କ୍ରିପ୍ଟ ଲୋଡ଼ା)',
'tog-showtoc' => 'ସୂଚୀପତ୍ର ଦେଖାଇବେ (୩ରୁ ଅଧିକ ମୁଖ୍ୟ ନାମ ଥିଲେ)',
'tog-rememberpassword' => 'ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ',
-'tog-watchcreations' => 'ମà\8b ତିà¬\86ରି ପà\83ଷà\8dଠାସବà\81à¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ା à¬à¬¿à¬¤à¬°à\87 ରà¬\96ିବà\87',
-'tog-watchdefault' => 'ମà\8b ଦà\87à¬\87 ସମà\8dପାଦିତ ପà\83ଷà\8dଠାସବà\81à¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ା à¬à¬¿à¬¤à¬°à\87 ରà¬\96ିବà\87',
-'tog-watchmoves' => 'ମà\8b ଦà\87à¬\87 à¬\98à\81à¬\9eà\8dà¬\9aାଯାà¬\87ଥିବା ପà\83ଷà\8dଠାସବà\81à¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ା à¬à¬¿à¬¤à¬°à\87 ରà¬\96ିବà\87',
-'tog-watchdeletion' => 'ମà\8b ଦà\87à¬\87 ଲିà¬à¬¾à¬¯à¬¾à¬\87ଥିବା ପà\83ଷà\8dଠାସବà\81à¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ା à¬à¬¿à¬¤à¬°à\87 ରà¬\96ିବà\87',
+'tog-watchcreations' => 'ମà\8b ତିà¬\86ରି ପà\83ଷà\8dଠାସବà\81à¬\95à\81 à¬\8fବà¬\82 ମà\8b à¬\85ପଲà\8bଡà¬\97à\81ଡିà¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ାରà\87 ଯà\8bଡନà\8dତà\81',
+'tog-watchdefault' => 'ମà\81à¬\81 ବଦଳà\87à¬\87ଥିବା ପà\83ଷà\8dଠା à¬\8fବà¬\82 ଫାà¬\87ଲà¬\97à\81ଡିà¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ାରà\87 ଯà\8bଡନà\8dତà\81',
+'tog-watchmoves' => 'ମà\81à¬\81 à¬\98à\81à¬\9eà\8dà¬\9aାà¬\87ଥିବା ପà\83ଷà\8dଠା à¬\8fବà¬\82 ଫାà¬\87ଲà¬\97à\81ଡିà¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ାରà\87 ଯà\8bଡନà\8dତà\81',
+'tog-watchdeletion' => 'ମà\81à¬\81 ଲିà¬à¬¾à¬\87ଥିବା ପà\83ଷà\8dଠା à¬\8fବà¬\82 ଫାà¬\87ଲà¬\97à\81ଡିà¬\95à\81 ମà\8bର ଦà\87à¬\96ଣାତାଲିà¬\95ାରà\87 ଯà\8bଡନà\8dତà\81',
'tog-minordefault' => 'ସବୁଯାକ ସମ୍ପାଦନାକୁ ଛାଏଁ ଟିକେ ବଦଳ ଭାବରେ ସୂଚିତ କରିବେ',
'tog-previewontop' => 'ଏଡ଼ିଟ ବାକ୍ସ ଆଗରୁ ଦେଖଣା ଦେଖାଇବେ',
'tog-previewonfirst' => 'ପ୍ରଥମ ବଦଳର ଦେଖଣା ଦେଖାଇବେ',
'tog-nocache' => 'ବ୍ରାଉଜର ପୃଷ୍ଠା ସଂରକ୍ଷଣକୁ ଅଚଳ କରିବେ',
'tog-enotifwatchlistpages' => 'ମୋ ଦେଖଣାତାଲିକାରେ ଥିବା ପୃଷ୍ଠା ବା ଫାଇଲରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇ-ମେଲ କରିବେ',
'tog-enotifusertalkpages' => 'ମୋର ଆଲୋଚନା ପୃଷ୍ଠାରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇ-ମେଲ କରିବେ',
-'tog-enotifminoredits' => 'ପୃଷ୍ଠାରେ ଛୋଟ ଛୋଟ ବଦଳ ହେଲେ ବି ମୋତେ ଇ-ମେଲ କରିବେ',
+'tog-enotifminoredits' => 'ପà\83ଷà\8dଠାରà\87 à¬\8fବà¬\82 ଫାà¬\87ଲà¬\97à\81ଡିà¬\95ରà\87 à¬\9bà\8bà¬\9f à¬\9bà\8bà¬\9f ବଦଳ ହà\87ଲà\87 ବି ମà\8bତà\87 à¬\87-ମà\87ଲ à¬\95ରିବà\87',
'tog-enotifrevealaddr' => 'ସୂଚନା ଇ-ମେଲ ରେ ମୋର ଇ-ମେଲ ଠିକଣା ଦେଖାଇବେ',
'tog-shownumberswatching' => 'ଦେଖୁଥିବା ବ୍ୟବହାରକାରୀଙ୍କ ସଂଖ୍ୟା ଦେଖାଇବେ',
'tog-oldsig' => 'ଏବେ ଥିବା ନାମ:',
'youhavenewmessages' => 'ଆପଣଙ୍କର $1 ($2).',
'newmessageslink' => 'ନୂଆ ମେସେଜ',
'newmessagesdifflink' => 'ଶେଷ ବଦଳ',
+'youhavenewmessagesmanyusers' => 'ଆପଣଙ୍କର ବହୁତ ବ୍ୟବହାରକାରୀ($2)ମାନଙ୍କଠାରୁ $1 ଅଛି ।',
+'newmessagesdifflinkplural' => 'ଶେଷ{{PLURAL:$1|change|changes}}',
'youhavenewmessagesmulti' => '$1 ତାରିଖରେ ନୂଆ ଚିଠିଟିଏ ଆସିଛି',
'editsection' => 'ସମ୍ପାଦନା',
'editold' => 'ଏହାକୁ ବଦଳାନ୍ତୁ',
'badarticleerror' => 'ଏହି ପୃଷ୍ଠାରେ ଏହି କାମଟି ହୋଇପାରିବ ନାହିଁ ।',
'cannotdelete' => '"$1" ପୃଷ୍ଠା ବା ଫାଇଲଟି ଲିଭାଯାଇପାରିବ ନାହିଁ । ଏହା ଆଗରୁ କାହା ଦେଇ ବୋଧେ ଲିଭାଇ ଦିଆଯାଇଛି ।',
'cannotdelete-title' => '"$1" ପୃଷ୍ଠାଟି ଲିଭଯାଇପାରିବ ନାହିଁ',
+'delete-hook-aborted' => 'ସମ୍ପାଦନା ଏକ ହୁକ (hook) ଦେଇ ବାରଣ କରାଗଲା ।
+ଏହା କିଛି ବି କାରଣ ଦେଇନାହିଁ ।',
'badtitle' => 'ଖରାପ ନାଆଁ',
'badtitletext' => 'ଆପଣ ଅନୁରୋଧ କରିଥିବା ପୃଷ୍ଠାଟି ଭୁଲ, ଖାଲି ଅଛି ବା ବାକି ଭାଷା ସାଙ୍ଗରେ ଭୁଲରେ ଯୋଡ଼ା ଯାଇଛି ବା ଭୁଲ ଇଣ୍ଟର ଉଇକି ନାମ ଦିଆଯାଇଛି ।
ଏଥିରେ ଥିବା ଗୋଟିଏ ବା ଦୁଇଟି ଅକ୍ଷର ଶିରୋନାମା ଭାବରେ ବ୍ୟବହାର କରାଯାଇ ପାରିବ ନାହିଁ ।',
-'perfcached' => 'ତଳଲିà¬\96ିତ ତଥà\8dà\9fà¬\9fି à¬\86à¬\97ରà\81 ରହିଥିବା ତଥà\8dà\9f, ତà\87ଣà\81 ନà¬\97ଦ ହà\8bà¬\87ନପାରà\87 । A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts' => 'ତଳଲିଖିତ ତଥ୍ୟ ଆଗରୁ ଥିବା ତଥ୍ୟ ଓ $1ରେ ଶେଷଥର ଅପଡେଟ ହୋଇଥିଲା । A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
+'perfcached' => 'ତଳଲିà¬\96ିତ ତଥà\8dà\9fà¬\97à\81ଡିà¬\95à\81 à¬\85ସà\8dଥାà\9fà\80 à¬à¬¾à¬¬à\87 ରà¬\96ାà¬\97ଲା à¬\8fବà¬\82 à¬\8fହା à¬\85ପଡà\87à¬\9f ନ ହà\8bà¬\87ପାରà\87 । à¬\85ତିବà\87ଶିରà\87 {{PLURAL:$1|ଫଳ|$1ଫଳà¬\97à\81ଡିà¬\95 }} à¬\85ସà\8dଥାà\9fà\80 ରà\82ପà\87 ରହି ପାରିବ ।',
+'perfcachedts' => 'ତଳଲିଖିତ ତଥ୍ୟଗୁଡିକୁ ଅସ୍ଥାୟୀ ଭାବେ ରଖାଗଲା ଏବଂ $1ରେ ଶେଷଥର ଅପଡେଟ ହୋଇଥିଲା । ଅତିବେଶିରେ {{PLURAL:$1|ଫଳ|$1ଫଳଗୁଡିକ }} ଅସ୍ଥାୟୀ ରୂପେ ରହି ପାରିବ ।',
'querypage-no-updates' => 'ଏହି ପୃଷ୍ଠାଟି ପାଇଁ ଅପଡେଟସବୁ ଏବେ ଅଚଳ କରାଯାଇଅଛି ।
ଏଠାରେ ଥିବା ତଥ୍ୟ ସବୁ ଏବେ ସତେଜ ହୋଇପାରିବ ନାହିଁ ।',
'wrong_wfQuery_params' => 'wfQuery() ପାଇଁ ଭୁଲ ପାରାମିଟର<br />
'ns-specialprotected' => 'ବିଶେଷ ପୃଷ୍ଠାସବୁକୁ ବଦଳାଯାଇପାରିବ ନାହିଁ ।',
'titleprotected' => 'ଏହି ନାମଟି [[User:$1|$1]]ଙ୍କ ଦେଇ ନୂଆ ତିଆରିହେବାରୁ କିଳାଯାଇଅଛି ।
ଏହାର କାରଣ ହେଲା "\'\'$2\'\'" ।',
+'exception-nologin' => 'ଲଗ ଇନ କରିନାହାନ୍ତି',
+'exception-nologin-text' => 'ଏହା କରିବାକୁ ହେଲେ ଆପଣଙ୍କୁ ଏହି ଉଇକିରେ ଲଗଇନ କରିବାକୁ ପଡିବ ।',
# Virus scanner
'virus-badscanner' => "ମନ୍ଦ ସଂରଚନା: ଅଜଣା ଭାଇରସ ସ୍କାନର: ''$1''",
'remembermypassword' => 'ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ',
'securelogin-stick-https' => 'ଲଗ ଇନ କଲାପରେ HTTPS ସହ ଯୋଡ଼ି ହୋଇ ରହନ୍ତୁ',
'yourdomainname' => 'ଆପଣଙ୍କ ଡୋମେନ:',
+'password-change-forbidden' => 'ଆପଣ ଏହି ଉଇକିରେ ପାସୱାର୍ଡ ବଦଳାଇ ପାରିବେ ନାହିଁ ।',
'externaldberror' => 'ବୋଧ ହୁଏ ଚିହ୍ନଟ ଡାଟାବେସ ଭୁଲଟିଏ ହୋଇଥିଲା ବା ଆପଣଙ୍କୁ ନିଜର ବାହାର ଖାତା ଅପଡେଟ କରିବା ନିମନ୍ତେ ଅନୁମତି ମିଳିନାହିଁ ।',
'login' => 'ଲଗଇନ',
'nav-login-createaccount' => 'ଲଗ ଇନ /ନୂଆ ଖାତା ଖୋଲନ୍ତୁ',
'login-userblocked' => 'ଏହି ସଭ୍ୟଙ୍କୁ ଅଟକାଯାଇଛି । ଲଗ ଇନ କରିବାକୁ ଅନୁମତି ନାହିଁ ।',
'wrongpassword' => 'ଦିଆଯାଇଥିବା ପାସବାର୍ଡ଼ଟି ଭୁଲ ଅଟେ ।
ଦୟାକରି ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।',
-'wrongpasswordempty' => 'ଦିଆଯାଇଥିବା ପାସବାର୍ଡ଼ଟି ଖାଲି ଛଡ଼ାଯାଇଛି ।</br>
+'wrongpasswordempty' => 'ଦିଆଯାଇଥିବା ପାସବାର୍ଡ଼ଟି ଖାଲି ଛଡ଼ାଯାଇଛି ।
ଦୟାକରି ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।',
'passwordtooshort' => 'ପାସବାର୍ଡ଼ଟି ଅତି କମରେ {{PLURAL:$1|ଗୋଟିଏ ଅକ୍ଷର|$1ଟି ଅକ୍ଷର}}ର ହୋଇଥିବା ଲୋଡ଼ା ।',
'password-name-match' => 'ଆପଣଙ୍କ ପାସବାର୍ଡ଼ଟି ଆପଣଙ୍କ ଇଉଜର ନାମ ଠାରୁ ଅଲଗା ହେବା ଉଚିତ ।',
'invalidemailaddress' => 'ଏହି ଇ-ମେଲ ଠିକଣାଟି ସଠିକ ସଜାଣିରେ ନଥିବାରୁ ଏହାକୁ ଗ୍ରହଣ କରାଯାଇପାରିବ ନାହିଁ ।
ଦୟାକରି ଏକ ସଚଳ ଓ ଠିକ ସଜାଣିରେ ଥିବା ଇ-ମେଲ ଠିକଣା ଦିଅନ୍ତୁ ।',
'cannotchangeemail' => 'ଖାତାରେ ଥିବା ଇମେଲ ଏହି ଉଇକିରେ ବଦଳାଯାଇପାରିବ ନାହିଁ ।',
+'emaildisabled' => 'ଏହି ସାଇଟ ଇ-ମେଲ ପଠାଇ ପାରିବ ନାହିଁ ।',
'accountcreated' => 'ଖାତାଟି ଖୋଲାହୋଇଗଲା',
'accountcreatedtext' => '$1 ପାଇଁ ନୂଆ ଖାତାଟିଏ ତିଆରି ହୋଇଗଲା ।',
'createaccount-title' => '{{SITENAME}} ପାଇଁ ଖାତା ଖୋଲା',
'note' => "'''ଟୀକା:'''",
'previewnote' => "'''ଜାଣିରଖନ୍ତୁ ଯେ, ଏହା କେବଳ ଏକ ଦେଖଣା ।'''
ଆପଣ କରିଥିବା ବଦଳସବୁ ଏଯାଏଁ ସାଇତା ଯାଇନାହିଁ!",
+'continue-editing' => 'ବଦଳାଇବା ଜାରି ରଖନ୍ତୁ',
'previewconflict' => 'ଉପରେ ଦିଶୁଥିବା ଏହି ଦେଖଣାକୁ ସାଇତିଲେ ଏହା ଏକାପରି ଦେଖାଯିବ ।',
'session_fail_preview' => "'''କ୍ଷମା କରିବେ! ଅବଧି ତଥ୍ୟ ନଷ୍ଟ ହୋଇଯାଇଥିବାରୁ ଆମେ ଆପଣଙ୍କ ବଦଳସବୁକୁ ଗ୍ରହଣ କରିପାରିଲୁ ନାହିଁ ।'''
ଦୟାକରି ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।
ଆପଣ ଏକ ୱେବ-ରେ ଥିବା ଅଜଣା ପ୍ରକ୍ସି ସାଇଟ କରି ବ୍ୟବହାର କରୁଥିଲେ ଏପରି ହୋଇଥାଏ ।",
'edit_form_incomplete' => "'''ସମ୍ପାଦନାର କେତେକ ଭାଗ ସର୍ଭର ଠେଇଁ ପହଞ୍ଚିଲା ନାହିଁ; ଭଲକରି ପରଖିନିଅନ୍ତୁ ଯେ ନିଜ ସମ୍ପାଦନା ସବୁ ଅକ୍ଷତ କି ନାହିଁ ଓ ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।'''",
'editing' => '$1 କୁ ବଦଳାଉଛି',
+'creating' => '$1କୁ ତିଆରି କରୁଛି',
'editingsection' => '$1 (ଭାଗ)କୁ ବଦଳାଇବେ',
'editingcomment' => '$1 (ନୂଆ ଭାଗ)କୁ ବଦଳାଉଛୁ',
'editconflict' => 'ବଦଳାଇବା ଦ୍ଵନ୍ଦ: $1',
-'explainconflict' => "ଆପଣ ବଦଳାଇବା ଆରମ୍ଭ କରିବା ଭିତରେ କେହିଜଣେ ଏହି ପୃଷ୍ଠାକୁ ବଦଳାଇଛନ୍ତି ।</br>
-ଉପର ଲେଖା ଜାଗାଟି ଏହା ଯେମିତି ଅଛି ସେମିତି ଥିବା ଲେଖାଟି ଦେଖାଉଛି ।</br>
-ତଳ ଜାଗାଟିରେ ଆପଣ କରିଥିବା ବଦଳ ଦେଖାଉଛି ।</br>
-ଏବେ ଥିବା ଲେଖାରେ ଆପଣଙ୍କୁ ନିଜ ବଦଳକୁ ମିଶାଇବାକୁ ହେବ ।</br>
+'explainconflict' => "ଆପଣ ବଦଳାଇବା ଆରମ୍ଭ କରିବା ଭିତରେ କେହିଜଣେ ଏହି ପୃଷ୍ଠାକୁ ବଦଳାଇଛନ୍ତି ।
+ଉପର ଲେଖା ଜାଗାଟି ଏହା ଯେମିତି ଅଛି ସେମିତି ଥିବା ଲେଖାଟି ଦେଖାଉଛି ।
+ତଳ ଜାଗାଟିରେ ଆପଣ କରିଥିବା ବଦଳ ଦେଖାଉଛି ।
+ଏବେ ଥିବା ଲେଖାରେ ଆପଣଙ୍କୁ ନିଜ ବଦଳକୁ ମିଶାଇବାକୁ ହେବ ।
ଯଦି ଆପଣ \"{{int:savearticle}}\" ଦବାନ୍ତି ତେବେ '''କେବଳ''' ଉପର ଲେଖାଟି ସାଇତା ହୋଇଯିବ ।",
'yourtext' => 'ଆପଣଙ୍କ ଲେଖା',
'storedversion' => 'ସାଇତା ସଙ୍କଳନ',
ଯଦି ଆପଣ ନିଜର ଲେଖା ନିର୍ଦୟ ଭାବେ ସମ୍ପାଦିତ ହେଉ ବୋଲି ଚାହୁଁନାହାନ୍ତି ବା ବଣ୍ଟନ କରାଯାଉ ବୋଲି ଚାହୁଁ ନାହାନ୍ତି ତେବେ ତାହା ଏଠାରେ ଦିଅନ୍ତୁ ନାହିଁ ।<br />
ଆପଣ ଆମପକ୍ଷେ ମଧ୍ୟ ପ୍ରତିଜ୍ଞା କରୁଛନ୍ତି ଯେ ଏହା ଆପଣ ନିଜେ ଲେଖିଛନ୍ତି, କିମ୍ବା ଏକ ପବ୍ଲିକ ଡୋମେନରୁ ବା ମାଗଣା ଓ ଖୋଲା ଲାଇସେନ୍ସ ଥିବା ସାଇଟରୁ ନକଲ କରି ଆଣିଛନ୍ତି । (ଦୟାକରି ସବିଶେଷ ପାଇଁ $1 ଦେଖନ୍ତୁ) ।
'''ଅନୁମତି ବିନା ସତ୍ଵାଧିକାର ଥିବା କାମ ଏଠାରେ ଦିଅନ୍ତୁ ନାହିଁ !'''",
-'longpageerror' => "'''ଭୁଲ: ଆପଣ ଦେଇଥିବା ଲେଖାଟି $1 କିଲୋବାଇଟ ଲମ୍ବା, ଯାହାକି ସବୁଠାରୁ ଅଧିକ $2 ଠାରୁ ବି ଅଧିକ ।'''
+'longpageerror' => "'''ଭୁଲ: ଆପଣ ଦେଇଥିବା ଲେଖାଟି {{PLURAL:$1|କିଲୋବାଇଟ|$1 କିଲୋବାଇଟ}} ଲମ୍ବା, ଯାହାକି ସବୁଠାରୁ ଅଧିକ {{PLURAL:$2|କିଲୋବାଇଟ|$2 କିଲୋବାଇଟ}} ଠାରୁ ବି ଅଧିକ ।'''
ଏହା ସାଇତାଯାଇପାରିବ ନାହିଁ ।",
'readonlywarning' => "'''ସୂଚନା: ଏହି ଡାଟାବେସଟି ରକ୍ଷଣାବେକ୍ଷଣା ପାଇଁ କିଳାଯାଇଛି । ତେଣୁ ଆପଣ ଆପଣା ସମ୍ପାଦନା ଏବେ ସାଇତି ପାରିବେ ନାହିଁ ।'''
ଆପଣ ଲେଖାସବୁ ଏକ ଟେକ୍ସଟ ଫାଇଲରେ ନକଲ କରି ପେଷ୍ଟ କରି ଆଗକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ସାଇତି ପାରିବେ ।
'edit-no-change' => 'ଆପଣଙ୍କ ସମ୍ପାଦନାକୁ ଅଣଦେଖା କରାଗଲା, କାରଣ ଲେଖାରେ କିଛି ବି ବଦଳ କରାଯାଇନଥିଲା ।',
'edit-already-exists' => 'ନୂଆ ପୃଷ୍ଠାଟିଏ ତିଆରି କରିପାରିଲୁଁ ନାହିଁ ।
ଏହା ଅଗରୁ ଅଛି ।',
+'defaultmessagetext' => 'ଡିଫଲ୍ଟ ମେସେଜ ଲେଖାଗୁଡିକ',
# Parser/template warnings
'expensive-parserfunction-warning' => "'''ଚେତାବନୀ:''' ଏହି ପୃଷ୍ଠାରେ ଅନେକ ଗୁଡ଼ିଏ ମୂଲ୍ୟବାନ ପାର୍ସର ଫଙ୍କସନ କଲ ଅଛି ।
# Suppression log
'suppressionlog' => 'ଦବାଇବା ଲଗ',
-'suppressionlogtext' => 'ଲିଭାଯାଇଥିବା ଓ ଅଟକାଯାଇଥିବା ବସ୍ତୁର ଏକ ତାଲିକା ତଳେ ଦିଆଯାଇଛି ଯେଉଁଥିରେ ପରିଛାମାନଙ୍କଠାରୁ ଲୁଚାଯାଇଥିବା ବସ୍ତୁ ମଧ୍ୟ ଅଛି ।
-ଏବେ କରାଯାଇଥିବା ବାସନ୍ଦ ଓ ବାରଣ ପାଇଁ [[Special:BlockList|IP ଅଟକ ତାଲିକା]] ଦେଖନ୍ତୁ ।',
+'suppressionlogtext' => 'ଲିଭାଯାଇଥିବା ଓ ଅଟକାଯାଇଥିବା, ଏହା ସହ ପରିଛାମାନଙ୍କଠାରୁ ଲୁଚାଯାଇଥିବା ଲେଖାଗୁଡ଼ିକର ଏକ ତାଲିକା ତଳେ ଦିଆଯାଇଛି ।
+ଏବେ କରାଯାଇଥିବା ବାସନ୍ଦ ଓ ବାରଣ ପାଇଁ [[Special:BlockList|block list]] ଦେଖନ୍ତୁ ।',
# History merging
'mergehistory' => 'ପୃଷ୍ଠାର ଇତିହାସ ସବୁ ଯୋଡ଼ିଦେବେ',
'mergelogpagetext' => 'ତଳେ ସବୁଠାରୁ ନଗଦ ଯୋଡ଼ାଯାଇଥିବା ପୃଷ୍ଠାର ଇତିହାସ ଆଉ ଗୋଟିଏ ସହ ଦିଆଯାଇଅଛି ।',
# Diffs
-'history-title' => '"$1" ପାà¬\87à¬\81 ସà¬\99à\8dà¬\95ଳନ ଇତିହାସ',
+'history-title' => '"$1" ର ପà\81ନରାବà\83ତି ଇତିହାସ',
'difference-multipage' => '(ପୃଷ୍ଠା ଭିତରେ ଥିବା ତଫାତ)',
'lineno' => '$1 କ ଧାଡ଼ି:',
'compareselectedversions' => 'ବଛାହୋଇଥିବା ସଙ୍କଳନ ଗୁଡ଼ିକୁ ତଉଲିବେ',
'viewprevnext' => '($1 {{int:pipe-separator}} $2) ($3) ଟି ଦେଖିବେ',
'searchmenu-legend' => 'ଖୋଜିବା ବିକଳ୍ପ',
'searchmenu-exists' => "'''ଏହି ଉଇକିରେ \"[[:\$1]]\" ନାଆଁରେ ପୃଷ୍ଠାଟିଏ ଅଛି ।'''",
-'searchmenu-new' => "<big><big><big>'''ଏହି ପ୍ରସଙ୍ଗଟି ଆଗରୁ ନାହିଁ, ତେଣୁ <big>''[[:$1]]''</big> ନାମରେ ପ୍ରସଙ୍ଗଟିଏ ଏଠାରେ ଗଢ଼ନ୍ତୁ!'''</big></big></big>",
+'searchmenu-new' => "'''ଏହି ପ୍ରସଙ୍ଗଟି ଆଗରୁ ନାହିଁ, ତେଣୁ ''[[:$1]]'' ନାମରେ ପ୍ରସଙ୍ଗଟିଏ ଏଠାରେ ଗଢ଼ନ୍ତୁ!'''",
'searchhelp-url' => 'Help:ସୂଚୀ',
'searchmenu-prefix' => '[[Special:PrefixIndex/$1|ଏହି ନାମ ଆଗରୁ ଥିବା ପୃଷ୍ଠାସବୁ ଖୋଜିବେ]]',
'searchprofile-articles' => 'ସୂଚୀ ପୃଷ୍ଠା',
'backend-fail-writetemp' => 'ଅସ୍ଥାୟୀ ଫାଇଲ ତିଆରି କରିପାରିଲୁ ନାହିଁ ।',
'backend-fail-closetemp' => 'ଅସ୍ଥାୟୀ ଫାଇଲ ବନ୍ଦ କରିହେଲା ନାହିଁ ।',
'backend-fail-read' => '$1 ଫାଇଲଟି ପଢ଼ିପାରିଲୁ ନାହିଁ ।',
-'backend-fail-create' => '$1 ଫାà¬\87ଲà¬\9fି ତିà¬\86ରି à¬\95ରିପାରିଲà\81 ନାହିଁ ।',
+'backend-fail-create' => '$1 ଫାà¬\87ଲରà\87 à¬\95ିà¬\9bି ଲà\87à¬\96ି ହà\87ଲା ନାହିଁ ।',
# Lock manager
'lockmanager-notlocked' => 'କିଳାଯାଇଥିବା "$1"କୁ ଖୋଲିପାରିଲୁ ନାହିଁ; ଏହା ପ୍ରକୃତରେ କିଳାଯାଇନାହିଁ ।',
'brokenredirects' => 'ଭଙ୍ଗା ପୁନପ୍ରେରଣ',
'brokenredirectstext' => 'ତଳଲିଖିତ ପୁନପ୍ରେରଣ ସବୁ ସ୍ଥିତିହିନ ପୃଷ୍ଠାମାନଙ୍କୁ ପୁନପ୍ରେରିତ ହୋଇଥାଏ :',
-'brokenredirects-edit' => 'ସମà\8dପାଦନା',
+'brokenredirects-edit' => 'à¬\8fହାà¬\95à\81 ବଦଳାନà\8dତà\81',
'brokenredirects-delete' => 'ଲିଭାଇବେ',
'withoutinterwiki' => 'ଭାଷାର ଲିଙ୍କ ନଥିବା ପୃଷ୍ଠାମାନ',
'api-error-uploaddisabled' => 'ଉଇକିରେ ଅପଲୋଡ଼ କରିବା ଅଚଳ କରାଯାଇଅଛି ।',
'api-error-verification-error' => 'ଏହି ଫାଇଲଟି ବୋଧ ହୁଏ ନଷ୍ଟ ହୋଇଯାଇଅଛି କିମ୍ବା ଭୁଲ ଏକ୍ସଟେନସନ ଦିଆଯାଇଅଛି ।',
+# Durations
+'duration-years' => '$1 {{PLURAL:$1|year|years}}',
+'duration-decades' => '$1 {{PLURAL:$1|decade|decades}',
+'duration-centuries' => '$1 {{PLURAL:$1|century|centuries}}',
+'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ଏକ ଅନୁମୋଦିତ ଫାଇଲ ପ୍ରକାର ନୁହେଁ|ମାନ ଅନୁମୋଦିତ ଫାଇଲ ପ୍ରକାର ନୁହଁନ୍ତି}} । ଅନୁମୋଦିତ {{PLURAL:$3|ଫାଇଲ ପ୍ରକାର ହେଲା|ଫାଇଲ ପ୍ରକାର ହେଲା}} $2 ।',
);
'feedback-message' => 'Фыстæг:',
'feedback-close' => 'Æххæст',
+# API errors
+'api-error-noimageinfo' => 'Æвгæд æххæст у, фæлæ нын сервер ницыуал рабæрæг кодта файлы тыххæй.',
+'api-error-nomodule' => 'Мидæггаг рæдыд: Бавгæнæн модуль нæу æвæрд.',
+'api-error-ok-but-empty' => 'Мидæггаг рæдыд: Серверæй дзуапп нæй.',
+'api-error-overwrite' => 'Уæвгæ файл ногæй фыссын нæй гæнæн.',
+'api-error-stashfailed' => 'Мидæггаг рæдыд: Серверæн нæ рауадис рæстæгмæ файл фæдарын.',
+'api-error-timeout' => 'Сервер нæ радта дзуапп бадзырд рæстæгмæ.',
+'api-error-unclassified' => 'Нæзонгæ рæдыд æрцыд.',
+'api-error-unknown-code' => 'Нæзонгæ рæдыд: "$1".',
+'api-error-unknown-error' => 'Мидæггаг рæдыд: Цыдæр раст нæ ацыдис, файл куы æвгæдтай, уæд.',
+'api-error-unknown-warning' => 'Нæзонгæ фæдзæхст: "$1".',
+'api-error-unknownerror' => 'Нæзонгæ рæдыд: "$1".',
+'api-error-uploaddisabled' => 'Ацы викийы бавгæныны фадат хицæн у.',
+'api-error-verification-error' => 'Ацы файл гæнæн ис хæлд у, кæнæ йæ номы фæстаг хай раст нæу.',
+
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|секунд|секунды}}',
+'duration-minutes' => '$1 {{PLURAL:$1|минут|минуты}}',
+'duration-hours' => '$1 {{PLURAL:$1|сахат|сахаты}}',
+'duration-days' => '$1 {{PLURAL:$1|бон|боны}}',
+'duration-weeks' => '$1 {{PLURAL:$1|къуыри|къуырийы}}',
+'duration-years' => '$1 {{PLURAL:$1|аз|азы}}',
+'duration-decades' => '$1 {{PLURAL:$1|дæсадз|дæсадзы}}',
+'duration-centuries' => '$1 {{PLURAL:$1|æнус|æнусы}}',
+'duration-millennia' => '$1 {{PLURAL:$1|мин аз|мин азы}}',
+
);
* @author Kaganer
* @author Sukh
* @author Surinder.wadhawan
+ * @author TariButtar
* @author Ævar Arnfjörð Bjarmason
* @author לערי ריינהארט
*/
'tog-hidepatrolled' => 'ਮੌਨਜੁਦਾ ਬਦਲਾਬ ਮੈ ਸੈ ਸਹੀਤਕ ਬਦਲਾਬ ਕੌ ਛੁਪਾ ਕਰ ਰਖੇ.',
'tog-newpageshidepatrolled' => 'ਨਵੀ ਸੁਚੀ ਮੈ ਸੈ ਗਸ਼ਤ ਪਰਚੇ ਕੌ ਛੁਪਾਏ.',
'tog-extendwatchlist' => 'ਸਾਰੀ ਨਵੀ ਤਬਦੀਲੀਆ ਹੀ ਨਹੀ ,ਪੂਰਾਣੀ ਤਬਦੀਲੀਆ ਨੂੰ ਵੀ ਨਵੀ ਸੂਚੀ ਵਿਚ ਵਧਾ ਕੈ ਸ਼ਾਮੀਲ ਕਰੌ.',
-'tog-usenewrc' => 'ਸà©\81ਦਾਰ à¨\95à©\80ਤà©\87 ਹà©\8cà¨\82à¨\82à¨\9eà©\87 ਰà©\82à¨\9aà©\80 ਦà©\80 ਵਰਤà©\8c à¨\95ਰà©\8c (à¨\9cਰà©\82ਰਤ ਹà©\88 à¨\9cਾਵਾ ਸà¨\95à©\8dਰਿਪà©\8dà¨\9f à¨\95à©\80)',
+'tog-usenewrc' => 'ਤਾà¨\9c਼ਾ ਤਬਦà©\80ਲà©\80à¨\86à¨\82 à¨\85ਤà©\87 ਨਿà¨\97ਰਾਨà©\80 ਲਿਸà¨\9f ਵਿà¨\9a ਤਬਦà©\80ਲà©\80à¨\86à¨\82 ਸਫ਼à©\87 ਮà©\81ਤਾਬà¨\95 (à¨\9cਾਵਾ ਸà¨\95à©\8dਰਿਪà¨\9f ਲà©\8bà©\9cà©\80à¨\82ਦà©\80 ਹà©\88)',
'tog-numberheadings' => 'ਆਟੋ-ਨੰਬਰ ਹੈਡਿੰਗ',
'tog-showtoolbar' => 'ਐਡਿਟ ਟੂਲਬਾਰ ਵੇਖੋ (JavaScript)',
+'tog-editondblclick' => 'ਦੋਹਰੇ ਕਲਿੱਕ ਨਾਲ਼ ਸਫ਼ਾ ਸੋਧੋ (ਜਾਵਾ ਸਕ੍ਰਿਪਟ ਲੋੜੀਂਦੀ ਹੈ)',
+'tog-editsection' => '[ਸੋਧੋ] ਲਿੰਕਾਂ ਜ਼ਰੀਏ ਸੈਕਸ਼ਨ ਸੋਧ ਯੋਗ ਕਰੋ',
+'tog-editsectiononrightclick' => 'ਸੈਕਸ਼ਨ ਸਿਰਲੇਖਾਂ ਤੇ ਸੱਜੀ ਕਲਿੱਕ ਦੁਆਰਾ ਸੋਧ ਯੋਗ ਕਰੋ (ਜਾਵਾ ਸਕ੍ਰਿਪਟ ਲੋੜੀਂਦੀ ਹੈ)',
'tog-showtoc' => 'ਟੇਬਲ ਆਫ਼ ਕੰਨਟੈੱਟ ਵੇਖਾਓ (for pages with more than 3 headings)',
'tog-rememberpassword' => 'ਇਸ ਬਰਾਊਜ਼ਰ ਉੱਤੇ ਮੇਰਾ ਲਾਗਇਨ ਯਾਦ ਰੱਖੋ ($1 {{PLURAL:$1|ਦਿਨ|ਦਿਨਾਂ}} ਲਈ ਵੱਧ ਤੋਂ ਵੱਧ)',
-'tog-watchcreations' => 'ਮà©\87ਰà©\87 ਵਲà©\8bà¨\82 ਬਣਾà¨\8f à¨\97à¨\8f ਨਵà©\87à¨\82 ਸਫ਼à©\87 ਮà©\87ਰà©\80 ਵਾà¨\9a-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
-'tog-watchdefault' => 'à¨\9cà©\8b ਸਫ਼à©\87 ਮà©\88à¨\82 ਸà©\8bਧਦਾ ਹਾà¨\82, à¨\93ਹ ਪà©\87à¨\9c ਮà©\87ਰà©\80 ਵਾà¨\9a-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
-'tog-watchmoves' => 'ਮà©\87ਰà©\87 ਵਲà©\8bà¨\82 à¨à©\87à¨\9cà©\87 à¨\95ਿਤà©\87 ਸਫ਼à©\87 ਨà©\82à©° ਮà©\87ਰà©\80 ਵਾà¨\9a-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
+'tog-watchcreations' => 'ਮà©\87ਰà©\87 ਵਲà©\8bà¨\82 ਬਣਾà¨\8f à¨\97à¨\8f ਨਵà©\87à¨\82 ਸਫ਼à©\87 à¨\85ਤà©\87 à¨\85ੱਪਲà©\8bਡ à¨\95à©\80ਤà©\80à¨\86à¨\82 ਫ਼ਾà¨\88ਲਾà¨\82 ਮà©\87ਰà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
+'tog-watchdefault' => 'ਮà©\87ਰà©\87 ਵੱਲà©\8bà¨\82 ਸà©\8bਧà©\87 ਸਫ਼à©\87 à¨\85ਤà©\87 ਫ਼ਾà¨\88ਲਾà¨\82 ਮà©\87ਰà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
+'tog-watchmoves' => 'ਮà©\87ਰà©\87 ਵੱਲà©\8bà¨\82 ਬਦਲà©\87 ਸਿਰਲà©\87à¨\96ਾà¨\82 ਵਾਲ਼à©\87 ਸਫ਼à©\87 à¨\85ਤà©\87 ਫ਼ਾà¨\88ਲਾà¨\82 ਮà©\87ਰà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
'tog-watchdeletion' => 'ਮੇਰੇ ਵਲੋਂ ਹਟਾਏ ਗਏ ਸਫ਼ੇ ਮੇਰੀ ਵਾਚ-ਲਿਸਟ ਵਿੱਚ ਪਾਓ',
+'tog-minordefault' => 'ਸਾਰੀਆਂ ਸੋਧਾਂ ਤੇ ਮੂਲ ਰੂਪ ਵਿਚ ਛੋਟੀਆਂ ਹੋਣ ਦਾ ਨਿਸ਼ਾਨ ਲਾਓ',
'tog-previewontop' => 'ਐਡਿਟ ਬਕਸੇ ਤੋਂ ਪਹਿਲਾਂ ਝਲਕ ਵੇਖਾਓ',
'tog-previewonfirst' => 'ਪਹਿਲੇ ਐਡਿਟ ਉੱਤੇ ਝਲਕ ਵੇਖਾਓ',
'tog-nocache' => 'ਬਰਾਊਜ਼ਰ ਸਫ਼ਾ ਕੈਸ਼ ਕਰਨਾ ਬੰਦ ਕਰੋ',
-'tog-enotifwatchlistpages' => 'ਜਦੋਂ ਮੇਰੀ ਵਾਚ-ਲਿਸਟ ਵਿੱਚੋਂ ਸਫ਼ਾ ਬਦਲਿਆ ਜਾਵੇ ਤਾਂ ਮੈਨੂੰ ਈਮੇਲ ਭੇਜੋ',
+'tog-enotifwatchlistpages' => 'ਜਦੋਂ ਮੇਰੀ ਵਾਚ-ਲਿਸਟ ਵਿਚ ਦਰਜ ਕੋਈ ਸਫ਼ਾ ਬਦਲਿਆ ਜਾਵੇ ਯਾ ਮਿਸਲ ਬਦਲੀ ਜਾਵੇ ਤਾਂ ਮੈਨੂੰ ਈਮੇਲ ਭੇਜੋ',
+'tog-enotifusertalkpages' => 'ਜਦੋਂ ਮੇਰਾ ਗੱਲ-ਬਾਤ ਸਫ਼ਾ ਬਦਲਿਆ ਜਾਵੇ ਤਾਂ ਮੈਨੂੰ ਈ-ਮੇਲ ਭੇਜੋ',
+'tog-enotifminoredits' => 'ਸਫ਼ਿਆਂ ਅਤੇ ਫ਼ਾਈਲਾਂ ਦੀਆਂ ਛੋਟੀਆਂ ਤਬਦੀਲੀਆਂ ਲਈ ਵੀ ਮੈਨੂੰ ਈ-ਮੇਲ ਭੇਜੋ',
+'tog-enotifrevealaddr' => 'ਜਾਣੂ ਕਰਵਾਉਣ ਵਾਲ਼ੀਆਂ ਈ-ਮੇਲਾਂ ਵਿਚ ਮੇਰਾ ਈ-ਮੇਲ ਪਤਾ ਜ਼ਾਹਰ ਕਰੋ',
+'tog-shownumberswatching' => 'ਨਜ਼ਰ ਰੱਖ ਰਹੇ ਮੈਂਬਰਾਂ ਦੀ ਗਿਣਤੀ ਵਖਾਓ',
'tog-oldsig' => 'ਮੌਜੂਦਾ ਦਸਤਖਤ:',
+'tog-fancysig' => 'ਦਸਤਖ਼ਤ ਨੂੰ ਵਿਕੀਲਿਖਤ ਦੇ ਤੌਰ ਤੇ ਵਰਤੋ (ਬਿਨਾਂ ਆਟੋਮੈਟਿਕ ਲਿੰਕ)',
+'tog-externaleditor' => 'ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਬਾਹਰੀ ਸੋਧਕ ਵਰਤੋ (ਸਿਰਫ਼ ਮਾਹਿਰਾਂ ਲਈ ਹੈ, ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਤੇ ਖ਼ਾਸ ਸੈਟਿੰਗਾਂ ਲੋੜੀਂਦੀਆਂ ਹਨ। [//
+www.mediawiki.org/wiki/
+Manual:External_editors More
+information.])',
+'tog-externaldiff' => 'ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਬਾਹਰੀ ਫ਼ਰਕ ਵਰਤੋ (ਸਿਰਫ਼
+ਮਾਹਿਰਾਂ ਲਈ ਹੈ, ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਤੇ ਖ਼ਾਸ
+ਸੈਟਿੰਗਾਂ ਲੋੜੀਂਦੀਆਂ ਹਨ। [//
+www.mediawiki.org/wiki/
+Manual:External_editors More
+information.])',
+'tog-showjumplinks' => '"ਇਸ ਤੇ ਜਾਓ" ਦੇ ਲਿੰਕ ਦਿਖਾਣਾ ਸਮਰੱਥ ਕਰੋ',
+'tog-uselivepreview' => 'ਸਿੱਧੀ ਝਲਕ ਵਰਤੋ (ਜਾਵਾਸਕ੍ਰਿਪਟ ਲੋੜੀਂਦੀ ਹੈ) (ਤਜਰਬੇਕਾਰੀ)',
+'tog-forceeditsummary' => 'ਜਦੋਂ ਮੈਂ ਖ਼ਾਲੀ ਸੋਧ ਸਾਰ ਦਾਖ਼ਲ ਕਰਾਂ ਤਾਂ ਮੈਨੂੰ ਖ਼ਬਰਦਾਰ ਕਰੋ',
'tog-watchlisthideown' => 'ਮੇਰੀ ਵਾਚ-ਲਿਸਟ ਵਿੱਚੋਂ ਮੇਰੀਆਂ ਸੋਧਾਂ ਹਟਾਓ',
'tog-watchlisthidebots' => 'ਮੇਰੀ ਵਾਚ-ਲਿਸਟ ਵਿੱਚੋਂ ਰੋਬਾਟ ਦਿਆਂ ਸੋਧਾਂ ਹਟਾਓ',
'tog-watchlisthideminor' => 'ਛੋਟੇ ਸੋਧ ਵਾਚ-ਲਿਸਟ ਤੋਂ ਓਹਲੇ ਰੱਖੋ',
+'tog-watchlisthideliu' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚੋਂ ਲਾਗ ਇਨ ਕੀਤੇ ਮੈਂਬਰਾਂ ਦੀਆਂ ਸੋਧਾਂ ਲੁਕਾਓ',
+'tog-watchlisthideanons' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਗੁਮਨਾਮ ਮੈਂਬਰਾਂ ਦੀਆਂ ਕੀਤੀਆਂ ਸੋਧਾਂ ਲੁਕਾਓ',
+'tog-watchlisthidepatrolled' => 'ਵੇਖੀਆਂ ਜਾ ਚੁੱਕੀਆਂ ਸੋਧਾਂ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚੋਂ ਲੁਕਾਓ',
+'tog-ccmeonemails' => 'ਜੋ ਈ-ਮੇਲਾਂ ਮੈਂ ਦੂਜੇ ਮੈਂਬਰਾਂ ਨੂੰ ਭੇਜਦਾ ਹਾਂ ਓਹਨਾਂ ਦੀਆਂ ਨਕਲਾਂ ਮੈਨੂੰ ਭੇਜੋ',
+'tog-diffonly' => 'ਫ਼ਰਕਾਂ ਤੋਂ ਹੇਠ ਸਫ਼ੇ ਦੀ ਸਮੱਗਰੀ ਨਾ ਵਖਾਓ',
+'tog-showhiddencats' => 'ਲੁਕੀਆਂ ਸ਼੍ਰੇਣੀਆਂ ਵਖਾਓ',
+'tog-norollbackdiff' => '"ਵਾਪਸ ਮੋੜੌ"ਅਮਲ ਵਿਚ ਲਿਆਣ ਬਾਦ ਫ਼ਰਕ ਨਾ ਦਿਖਾਓ',
'underline-always' => 'ਹਮੇਸ਼ਾਂ',
'underline-never' => 'ਕਦੇ ਨਹੀਂ',
'underline-default' => 'ਬਰਾਊਜ਼ਰ ਡਿਫਾਲਟ',
# Font style option in Special:Preferences
+'editfont-style' => 'ਸੰਪਾਦਨ ਖੇਤਰ ਦੇ ਅੱਖਰਾਂ ਦੀ ਫ਼ੌਂਟ ਰੀਤੀ',
'editfont-default' => 'ਬਰਾਊਜ਼ਰ ਡਿਫਾਲਟ',
+'editfont-monospace' => 'ਇੱਕੋ ਜਿਹੀ ਖ਼ਾਲੀ ਥਾਂ ਵਾਲ਼ਾ ਅੱਖਰ',
+'editfont-sansserif' => 'Sans-serif ਨਾਂ ਦਾ ਫ਼ੌਂਟ',
+'editfont-serif' => 'ਨੋਕਦਾਰ ਅੱਖਰ',
# Dates
'sunday' => 'ਐਤਵਾਰ',
'wednesday' => 'ਬੁੱਧਵਾਰ',
'thursday' => 'ਵੀਰਵਾਰ',
'friday' => 'ਸ਼ੁੱਕਰਵਾਰ',
-'saturday' => 'ਸ਼ਨਿੱà¨\9aਰਵਾਰ',
+'saturday' => 'ਸ਼ਨà©\80ਵਾਰ',
'sun' => 'ਐਤ',
'mon' => 'ਸੋਮ',
'tue' => 'ਮੰਗਲ',
'wed' => 'ਬੁੱਧ',
'thu' => 'ਵੀਰ',
'fri' => 'ਸ਼ੁੱਕਰ',
-'sat' => 'ਸ਼ਨਿੱà¨\9aਰ',
+'sat' => 'ਸ਼ਨà©\80',
'january' => 'ਜਨਵਰੀ',
'february' => 'ਫ਼ਰਵਰੀ',
'march' => 'ਮਾਰਚ',
'june' => 'ਜੂਨ',
'july' => 'ਜੁਲਾਈ',
'august' => 'ਅਗਸਤ',
-'september' => 'ਸਤੰਬਰ',
+'september' => 'ਸਿਤੰਬਰ',
'october' => 'ਅਕਤੂਬਰ',
'november' => 'ਨਵੰਬਰ',
-'december' => 'ਦਸੰਬਰ',
+'december' => 'ਦਿਸੰਬਰ',
'january-gen' => 'ਜਨਵਰੀ',
'february-gen' => 'ਫ਼ਰਵਰੀ',
'march-gen' => 'ਮਾਰਚ',
'june-gen' => 'ਜੂਨ',
'july-gen' => 'ਜੁਲਾਈ',
'august-gen' => 'ਅਗਸਤ',
-'september-gen' => 'ਸਤੰਬਰ',
+'september-gen' => 'ਸਿਤੰਬਰ',
'october-gen' => 'ਅਕਤੂਬਰ',
'november-gen' => 'ਨਵੰਬਰ',
-'december-gen' => 'ਦਸੰਬਰ',
-'jan' => 'ਜਨ',
-'feb' => 'ਫ਼ਰ',
-'mar' => 'ਮਾਰ',
-'apr' => 'ਅਪ',
+'december-gen' => 'ਦਿਸੰਬਰ',
+'jan' => 'ਜਨਵਰੀ',
+'feb' => 'ਫ਼ਰਵਰੀ',
+'mar' => 'ਮਾਰਚ',
+'apr' => 'ਅਪਰੈਲ',
'may' => 'ਮਈ',
'jun' => 'ਜੂਨ',
'jul' => 'ਜੁਲਾਈ',
-'aug' => 'ਅਗ',
-'sep' => 'ਸਤੰ',
-'oct' => 'ਅਕ',
-'nov' => 'ਨਵੰ',
-'dec' => 'ਦਸੰ',
+'aug' => 'ਅਗਸਤ',
+'sep' => 'ਸਿਤੰਬਰ',
+'oct' => 'ਅਕਤੂਬਰ',
+'nov' => 'ਨਵੰਬਰ',
+'dec' => 'ਦਿਸੰਬਰ',
# Categories related messages
'pagecategories' => '{{PLURAL:$1|ਕੈਟਾਗਰੀ|ਕੈਟਾਗਰੀਆਂ}}',
'category_header' => 'ਕੈਟਾਗਰੀ "$1" ਵਿੱਚ ਲੇਖ',
'subcategories' => 'ਸਬ-ਕੈਟਾਗਰੀਆਂ',
-'category-media-header' => 'ਕੈਟਾਗਰੀ "$1" ਵਿੱਚ ਮੀਡਿਆ',
-'category-empty' => "''ਇਹ ਕੈਟਾਗਰੀ ਵਿੱਚ ਇਸ ਵੇਲੇ ਕੋਈ ਲੇਖ (ਆਰਟੀਕਲ) ਜਾਂ ਮੀਡਿਆ ਨਹੀਂ ਹੈ।''",
-'hidden-categories' => '{{PLURAL:$1|ਅਲੋਪ ਸ਼੍ਰੇਣੀ|ਅਲੋਪ ਸ਼੍ਰੇਣੀਆ}}',
-'category-subcat-count' => '{{ਕੁਲ $2 ਸ਼੍ਰੇਣੀਆਂ ਵਿਚੌਂ , PLURAL:$2|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਸਿਰਫ਼ ਹੇਠ ਲਿਖੀ ਸ਼੍ਰੇਣੀ ਹੈ|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਨਿਮਿਨਲਿਖਿਤ {{PLURAL:$1|ਉਪ ਸ਼੍ਰੇਣੀ ਹੈ|$1ਉਪਸ਼੍ਰੇਣੀਆਂ ਹਨ}} }}',
-'category-article-count' => '{{ ਕੁਲ $2 ਲੇਖਾਂ ਵਿਚੌਂ , PLURAL:$2| ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਸਿਰਫ਼ ਨਿਮਨਲਿਖਿਤ {{PLURAL:$1|ਲੇਖ ਹੈ|$1 ਲੇਖ ਹਨ}}, }}',
-'listingcontinuesabbrev' => 'ਅਗੇ.',
+'category-media-header' => 'ਕੈਟਾਗਰੀ "$1" ਵਿੱਚ ਮੀਡੀਆ',
+'category-empty' => "''ਇਸ ਕੈਟਾਗਰੀ ਵਿੱਚ ਇਸ ਵੇਲ਼ੇ ਕੋਈ ਵੀ ਲੇਖ ਜਾਂ ਮੀਡੀਆ ਨਹੀਂ ਹੈ।''",
+'hidden-categories' => '{{PLURAL:$1|ਲੁਕੀਵੀਂ ਸ਼੍ਰੇਣੀ|ਲੁਕਵੀਂਆਂ ਸ਼੍ਰੇਣੀਆਂ}}',
+'hidden-category-category' => 'ਲੁਕੀਆਂ ਸ਼੍ਰੇਣੀਆਂ',
+'category-subcat-count' => '{{ਕੁੱਲ $2 ਸ਼੍ਰੇਣੀਆਂ ਵਿਚੋਂ, PLURAL:$2|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਸਿਰਫ਼ ਹੇਠ ਲਿਖੀ ਸ਼੍ਰੇਣੀ ਹੈ| ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ {{PLURAL:$1|ਉਪ ਸ਼੍ਰੇਣੀ ਹੈ|$1 ਉਪ-ਸ਼੍ਰੇਣੀਆਂ ਹਨ}}}}',
+'category-subcat-count-limited' => 'ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ {{PLURAL:$1|ਉਪ-ਸ਼੍ਰੇਣੀ ਹੈ।|$1 ਉਪ-ਸ਼੍ਰੇਣੀਆਂ ਹਨ।}}',
+'category-article-count' => '{{PLURAL:$2|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਸਿਰਫ਼ ਇਹ ਸਫ਼ਾ ਹੈ|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ, ਕੁੱਲ $2 ਵਿਚੋਂ, ਇਹ {{PLURAL:$1|ਸਫ਼ਾ ਹੈ|$1 ਸਫ਼ੇ}} ਹਨ}}',
+'category-article-count-limited' => 'ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਇਹ {{PLURAL:$1|ਸਫ਼ਾ ਹੈ|$1 ਸਫ਼ੇ ਹਨ।}}',
+'category-file-count' => '{{PLURAL:$2|ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਸਿਰਫ਼ ਇਹ ਫ਼ਾਈਲ ਹੈ।| ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ {{PLURAL:$1|ਫ਼ਾਈਲ ਹੈ|$1 ਫ਼ਾਈਲਾਂ ਹਨ।}}}}',
+'category-file-count-limited' => 'ਇਸ ਸ਼੍ਰੇਣੀ ਵਿਚ ਇਹ {{PLURAL:$1|ਫ਼ਾਈਲ ਹੈ|$1 ਫ਼ਾਈਲਾਂ ਹਨ।}}',
+'listingcontinuesabbrev' => 'ਜਾਰੀ',
+'index-category' => 'ਤਤਕਰੇ ਵਾਲ਼ੇ ਸਫ਼ੇ',
'noindex-category' => 'ਕ੍ਰਮਸੂਚੀ ਰਹਿਤ ਸਫ਼ੇ',
+'broken-file-category' => 'ਟੁੱਟੇ ਹੋਏ ਫ਼ਾਈਲ ਜੋੜਾਂ ਵਾਲ਼ੇ ਸਫ਼ੇ',
'about' => 'ਇਸ ਬਾਰੇ',
'article' => 'ਸਮੱਗਰੀ ਪੇਜ',
-'newwindow' => '(ਨਵà©\80à¨\82 ਵਿੰਡà©\8b ਵਿੱà¨\9a à¨\96à©\8bਲà©\8dਹà©\8b)',
+'newwindow' => '(ਨਵà©\80à¨\82 ਵਿੰਡà©\8b ਵਿੱà¨\9a à¨\96à©\81ੱਲà©\8dਹਦà©\80 ਹà©\88)',
'cancel' => 'ਰੱਦ ਕਰੋ',
'moredotdotdot' => 'ਹੋਰ...',
'mypage' => 'ਮੇਰਾ ਪੇਜ',
'mytalk' => 'ਮੇਰੀ ਗੱਲਬਾਤ',
'anontalk' => 'ਇਹ IP ਲਈ ਗੱਲਬਾਤ',
-'navigation' => 'ਨà©\87ਵà©\80à¨\97à©\87ਸ਼ਨ',
+'navigation' => 'ਰਹਿਨà©\81ਮਾà¨\88',
'and' => ' ਅਤੇ',
# Cologne Blue skin
'qbpageinfo' => 'ਭਾਗ',
'qbmyoptions' => 'ਮੇਰੇ ਪੇਜ',
'qbspecialpages' => 'ਖਾਸ ਪੇਜ',
-'faq' => 'ਸਵਾਲ-à¨\9cਵਾਬ',
+'faq' => 'à¨\85à¨\95ਸਰ ਪà©\81ੱà¨\9bà©\87 à¨\9cਾਣ ਵਾਲ਼à©\87 ਸਵਾਲ',
'faqpage' => 'Project:ਸਵਾਲ-ਜਵਾਬ',
# Vector skin
'vector-action-addsection' => 'ਮਜ਼ਮੂਨ ਜੋੜੋ',
-'vector-action-delete' => 'ਹਟਾਓ',
+'vector-action-delete' => 'ਮਿਟਾਓ',
'vector-action-move' => 'ਭੇਜੋ',
-'vector-action-protect' => 'ਸੁਰੱਖਿਅਤ',
+'vector-action-protect' => 'ਸੁਰੱਖਿਅਤ ਬਣਾਓ',
'vector-action-undelete' => 'ਹਟਾਉਣਾ ਵਾਪਸ',
'vector-action-unprotect' => 'ਸੁਰੱਖਿਆ ਬਦਲੋ',
+'vector-simplesearch-preference' => 'ਵਾਧੂ ਖੋਜ ਸਲਾਹਾਂ ਯੋਗ ਕਰੋ (ਸਿਰਫ਼ ਵਿਕਟਰ ਸਕਿੰਨ ਵਿਚ)',
'vector-view-create' => 'ਬਣਾਓ',
'vector-view-edit' => 'ਸੋਧ',
'vector-view-history' => 'ਅਤੀਤ ਵੇਖੋ',
'vector-view-view' => 'ਪੜ੍ਹੋ',
'vector-view-viewsource' => 'ਸਰੋਤ ਵੇਖੋ',
-'actions' => 'ਕਾਰਵਾਈ',
-'namespaces' => 'ਨਾà¨\82-ਥਾà¨\82:',
+'actions' => 'ਕਾਰਵਾਈਆਂ',
+'namespaces' => 'ਨਾਮ-ਥਾà¨\82ਵਾà¨\82',
'variants' => 'ਬਦਲ',
-'errorpagetitle' => 'ਗਲਤੀ',
-'returnto' => '$1 ਤੇ ਵਾਪਸ ਜਾਓ',
+'errorpagetitle' => 'à¨\97਼ਲਤà©\80',
+'returnto' => '$1 ’ਤੇ ਵਾਪਸ ਜਾਓ।',
'tagline' => '{{SITENAME}} ਤੋਂ',
-'help' => 'ਮੱਦਦ',
-'search' => 'ਖੋਜ',
-'searchbutton' => 'ਖੋਜ',
+'help' => 'ਮਦਦ',
+'search' => 'ਖੋਜੋ',
+'searchbutton' => 'ਖੋਜੋ',
'go' => 'ਜਾਓ',
'searcharticle' => 'ਜਾਓ',
-'history' => 'ਸਫ਼ਾ ਅਤੀਤ',
+'history' => 'ਸਫ਼à©\87 ਦਾ à¨\85ਤà©\80ਤ',
'history_short' => 'ਅਤੀਤ',
'updatedmarker' => 'ਮੇਰੇ ਆਖਰੀ ਖੋਲ੍ਹਣ ਬਾਦ ਅੱਪਡੇਟ',
-'printableversion' => 'ਪਰਿੰà¨\9fਯੋਗ ਵਰਜਨ',
+'printableversion' => 'à¨\9bਪਣਯੋਗ ਵਰਜਨ',
'permalink' => 'ਪੱਕਾ ਲਿੰਕ',
'print' => 'ਪਰਿੰਟ ਕਰੋ',
'view' => 'ਵੇਖੋ',
'create' => 'ਬਣਾਓ',
'editthispage' => 'ਇਹ ਪੇਜ ਸੋਧੋ',
'create-this-page' => 'ਇਹ ਸਫ਼ਾ ਬਣਾਓ',
-'delete' => 'ਹਟਾਓ',
+'delete' => 'ਮਿਟਾਓ',
'deletethispage' => 'ਇਹ ਪੇਜ ਹਟਾਓ',
'undelete_short' => 'ਅਣ-ਹਟਾਓ {{PLURAL:$1|one edit|$1 edits}}',
+'viewdeleted_short' => '{{PLURAL:$1|ਇਕ ਮਿਟਾਈ ਸੋਧ|$1 ਮਿਟਾਈਆਂ ਸੋਧਾਂ}} ਵੇਖੋ',
'protect' => 'ਸੁਰੱਖਿਆ',
-'protect_change' => 'ਬਦਲà©\8b',
+'protect_change' => 'ਤਬਦà©\80ਲà©\80',
'protectthispage' => 'ਇਹ ਪੇਜ ਸੁਰੱਖਿਅਤ ਕਰੋ',
'unprotect' => 'ਸੁਰੱਖਿਆ ਬਦਲੋ',
'unprotectthispage' => 'ਇਹ ਪੇਜ਼ ਦੀ ਸੁਰੱਖਿਆ ਬਦਲੋ',
-'newpage' => 'ਨਵਾà¨\82 ਪà©\87à¨\9c',
+'newpage' => 'ਨਵਾà¨\82 ਸਫ਼ਾ',
'talkpage' => 'ਇਸ ਪੇਜ ਬਾਰੇ ਚਰਚਾ',
'talkpagelinktext' => 'ਗੱਲਬਾਤ',
'specialpage' => 'ਖਾਸ ਪੇਜ',
-'personaltools' => 'ਨਿੱà¨\9cà©\80 à¨\9fà©\82ਲ',
+'personaltools' => 'ਨਿੱà¨\9cà©\80 ਸੰਦ',
'postcomment' => 'ਨਵਾਂ ਭਾਗ',
'articlepage' => 'ਸਮੱਗਰੀ ਪੇਜ ਵੇਖੋ',
'talk' => 'ਚਰਚਾ',
'views' => 'ਵੇਖੋ',
-'toolbox' => 'à¨\9fà©\82ਲਬਾà¨\95ਸ',
+'toolbox' => 'ਸੰਦ ਬà¨\95ਸਾ',
'userpage' => 'ਯੂਜ਼ਰ ਪੇਜ ਵੇਖੋ',
'projectpage' => 'ਪਰੋਜੈਕਟ ਪੇਜ ਵੇਖੋ',
'imagepage' => 'ਫਾਇਲ ਪੇਜ ਵੇਖੋ',
'viewhelppage' => 'ਮੱਦਦ ਪੇਜ ਵੇਖੋ',
'categorypage' => 'ਕੈਟਾਗਰੀ ਪੇਜ ਵੇਖੋ',
'viewtalkpage' => 'ਚਰਚਾ ਵੇਖੋ',
-'otherlanguages' => 'ਹà©\8bਰ à¨à¨¾à¨¸à¨¼à¨¾à¨µà¨¾à¨\82 ਵਿੱਚ',
+'otherlanguages' => 'ਹà©\8bਰ à¨\9c਼ਬਾਨਾà¨\82 ਵਿਚ',
'redirectedfrom' => '($1 ਤੋਂ ਰੀ-ਡਿਰੈਕਟ)',
'redirectpagesub' => 'ਰੀ-ਡਿਰੈਕਟ ਪੇਜ',
-'lastmodifiedat' => 'à¨\87ਹ ਪà©\87à¨\9c à¨\86à¨\96ਰà©\80 ਵਾਰ $2, $1 ਨà©\82à©° ਸੋਧਿਆ ਗਿਆ ਸੀ।',
+'lastmodifiedat' => 'à¨\87ਹ ਸਫ਼ਾ à¨\86à¨\96਼ਰà©\80 ਵਾਰ $1 ਨà©\82à©° $2 â\80\99ਤà©\87 ਸੋਧਿਆ ਗਿਆ ਸੀ।',
'viewcount' => 'ਇਹ ਪੇਜ ਅਸੈੱਸ ਕੀਤਾ ਗਿਆ {{PLURAL:$1|ਇੱਕਵਾਰ|$1 ਵਾਰ}}.',
'protectedpage' => 'ਸੁਰੱਖਿਅਤ ਪੇਜ',
-'jumpto' => 'ਜੰਪ ਕਰੋ:',
-'jumptonavigation' => 'ਨੇਵੀਗੇਸ਼ਨ',
-'jumptosearch' => 'ਖੋਜ',
+'jumpto' => 'ਇਸ ’ਤੇ ਜਾਓ:',
+'jumptonavigation' => 'ਰਹਿਨੁਮਾਈ',
+'jumptosearch' => 'ਖੋਜੋ',
+'view-pool-error' => 'ਆਫ਼ਸੋਸ, ਸਰਵਰ ਇਸ ਵੇਲ਼ੇ ਓਵਰਲੋਡ ਹੈ।
+ਬਹੁਤ ਸਾਰੇ ਮੈਂਬਰ ਇਸ ਸਫ਼ੇ ਨੂੰ ਵੇਖਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੇ ਹਨ।
+ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਥੋੜੀ ਉਡੀਕ ਕਰੋ।
+$1',
+'pool-timeout' => 'ਲੌਕ ਲਈ ਉਡੀਕ ਦਾ ਵਕਤ ਖ਼ਤਮ ਹੋ ਗਿਆ ਹੈ',
+'pool-queuefull' => 'ਪੂਲ ਕਤਾਰ ਪੂਰੀ ਲੱਦੀ ਹੈ',
'pool-errorunknown' => 'ਅਣਜਾਣ ਗਲਤੀ',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
'copyrightpage' => '{{ns:project}}:ਕਾਪੀਰਾਈਟ',
'currentevents' => 'ਮੌਜੂਦਾ ਇਵੈਂਟ',
'currentevents-url' => 'Project:ਮੌਜੂਦਾ ਈਵੈਂਟ',
-'disclaimers' => 'ਦਾà¨\85ਵà©\87',
-'disclaimerpage' => 'Project:à¨\86ਮ ਦਾà¨\85ਵਾ',
+'disclaimers' => 'à¨\87ਨà¨\95ਾਰà©\80 à¨\90ਲਾਨ',
+'disclaimerpage' => 'Project:à¨\86ਮ à¨\87ਨà¨\95ਾਰ',
'edithelp' => 'ਮੱਦਦ ਐਡੀਟਿੰਗ',
'edithelppage' => 'Help:ਐਡਟਿੰਗ',
-'helppage' => 'Help:ਸਮੱà¨\97ਰà©\80',
-'mainpage' => 'ਮà©\81ੱà¨\96 ਪà©\87à¨\9c',
-'mainpage-description' => 'ਮà©\81ੱà¨\96 ਪà©\87à¨\9c',
+'helppage' => 'Help:à¨\9aà©\80à¨\9c਼ਾà¨\82',
+'mainpage' => 'ਮà©\81ੱà¨\96 ਸਫ਼ਾ',
+'mainpage-description' => 'ਮà©\81ੱà¨\96 ਸਫ਼ਾ',
'policy-url' => 'Project:ਪਾਲਸੀ',
'portal' => 'ਕਮਿਊਨਟੀ ਪੋਰਟਲ',
'portal-url' => 'Project:ਕਮਿਊਨਟੀ ਪੋਰਟਲ',
'badaccess' => 'ਅਧਿਕਾਰ ਗਲਤੀ',
'badaccess-group0' => 'ਤੁਹਾਨੂੰ ਉਹ ਐਕਸ਼ਨ ਕਰਨ ਦੀ ਮਨਜ਼ੂਰੀ ਨਹੀਂ, ਜਿਸ ਦੀ ਤੁਸੀਂ ਮੰਗ ਕੀਤੀ ਹੈ।',
+'badaccess-groups' => 'ਜੋ ਕੰਮ ਤੁਸੀਂ ਕਰਨਾ ਚਾਹਿਆ ਹੈ ਓਹ {{PLURAL:$2|ਇਸ ਗਰੁੱਪ|ਇਹਨਾਂ ਗਰੁੱਪਾਂ}} ਦੇ ਮੈਂਬਰ ਹੀ ਕਰ ਸਕਦੇ ਹਨ: $1',
+
+'versionrequired' => 'ਮੀਡੀਆਵਿਕੀ ਦੇ $1 ਵਰਜਨ ਦੀ ਲੋੜ ਹੈ',
+'versionrequiredtext' => 'ਇਸ ਸਫ਼ੇ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਮੀਡੀਆਵਿਕੀ ਦੇ $1 ਵਰਜਨ ਦੀ ਲੋੜ ਹੈ।
+ਵੇਖੋ [[Special:Version|ਵਰਜਨ ਸਫ਼ੇ]]।',
'ok' => 'ਠੀਕ ਹੈ',
'retrievedfrom' => '"$1" ਤੋਂ ਲਿਆ',
-'youhavenewmessages' => 'ਤà©\81ਹਾਨà©\82à©° $1 ($2).',
+'youhavenewmessages' => 'ਤà©\81ਹਾਡà©\87 ਲà¨\88 $1। ($2)',
'newmessageslink' => 'ਨਵੇਂ ਸੁਨੇਹੇ',
-'newmessagesdifflink' => 'ਆਖਰੀ ਬਦਲਾਅ',
+'newmessagesdifflink' => 'ਆਖ਼ਰੀ ਤਬਦੀਲੀ',
+'youhavenewmessagesfromusers' => '{{PLURAL:$3|ਇਕ ਮੈਂਬਰ|$3 ਮੈਂਬਰਾਂ}} ਵੱਲੋਂ ਤੁਹਾਨੂੰ $1 ($2)',
+'youhavenewmessagesmanyusers' => 'ਕਈ ਮੈਂਬਰਾਂ ਵੱਲੋਂ ਤੁਹਾਨੂੰ $1 ($2)।',
+'newmessageslinkplural' => '{{PLURAL:$1|ਇੱਕ ਨਵਾਂ ਸੁਨੇਹਾ|ਨਵੇਂ ਸੁਨੇਹੇ}}',
+'newmessagesdifflinkplural' => 'ਆਖ਼ਰੀ {{PLURAL:$1|ਤਬਦੀਲੀ|ਤਬਦੀਲੀਆਂ}}',
'youhavenewmessagesmulti' => 'ਤੁਹਾਨੂੰ ਨਵੇਂ ਸੁਨੇਹੇ $1 ਉੱਤੇ ਹਨ',
'editsection' => 'ਸੋਧ',
-'editold' => 'ਸੋਧ',
+'editold' => 'ਸੋਧੋ',
'viewsourceold' => 'ਸਰੋਤ ਵੇਖੋ',
'editlink' => 'ਸੋਧ',
'viewsourcelink' => 'ਸਰੋਤ ਵੇਖੋ',
'editsectionhint' => 'ਸ਼ੈਕਸ਼ਨ ਸੋਧ: $1',
-'toc' => 'ਸਮà¨\97ੱਰà©\80',
+'toc' => 'ਲਿਸà¨\9f',
'showtoc' => 'ਵੇਖੋ',
'hidetoc' => 'ਓਹਲੇ',
'collapsible-collapse' => 'ਸਮੇਟੋ',
'collapsible-expand' => 'ਫੈਲਾਓ',
'thisisdeleted' => 'ਵੇਖੋ ਜਾਂ $1 ਰੀਸਟੋਰ?',
'viewdeleted' => '$1 ਵੇਖਣਾ?',
+'restorelink' => '{{PLURAL:$1|ਇਕ ਮਿਟਾਈ ਹੋਈ ਸੋਧ|$1
+ਮਿਟਾਈਆਂ ਹੋਈਆਂ ਸੋਧਾਂ}}',
'feedlinks' => 'ਫੀਡ:',
+'feed-invalid' => 'ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਫ਼ੀਡ ਦੀ ਅਵੈਧ ਕਿਸਮ',
+'feed-unavailable' => 'ਸੰਸਥਾਵਾਂ ਸਮੱਗਰੀ ਦਾ ਆਧੁਨਕੀਕਰਣ ਉਪਲਬਧ ਨਹੀਂ',
'site-rss-feed' => '$1 RSS ਫੀਡ',
'site-atom-feed' => '$1 ਐਟਮ ਫੀਡ',
'page-rss-feed' => '"$1" RSS ਫੀਡ',
'page-atom-feed' => '"$1" ਐਟਮ ਫੀਡ',
-'red-link-title' => '$1 (à¨\87ਸ ਨਾà¨\82 ਦਾ ਪà©\87à¨\9c ਨਹੀਂ ਹੈ)',
+'red-link-title' => '$1 (ਸਫ਼ਾ ਮà©\8cà¨\9cà©\82ਦ ਨਹੀਂ ਹੈ)',
'sort-descending' => 'ਘੱਟਦਾ ਕ੍ਰਮ',
'sort-ascending' => 'ਵੱਧਦਾ ਕ੍ਰਮ',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'ਲੇਖ',
-'nstab-user' => 'ਯà©\82à¨\9c਼ਰ ਪà©\87à¨\9c',
+'nstab-user' => 'ਮà©\88à¨\82ਬਰ ਸਫ਼ਾ',
'nstab-media' => 'ਮੀਡਿਆ ਪੇਜ',
-'nstab-special' => 'ਖਾਸ ਸਫ਼ਾ',
-'nstab-project' => 'ਪਰà©\8bà¨\9cà©\88à¨\95à¨\9f ਪà©\87à¨\9c',
-'nstab-image' => 'ਫਾà¨\87ਲ',
+'nstab-special' => 'à¨\96਼ਾਸ ਸਫ਼ਾ',
+'nstab-project' => 'ਪà©\8dਰà©\8bà¨\9cà©\88à¨\95à¨\9f ਸਫ਼ਾ',
+'nstab-image' => 'ਫ਼ਾà¨\88ਲ',
'nstab-mediawiki' => 'ਸੁਨੇਹਾ',
-'nstab-template' => 'à¨\9fà©\88ਪਲà©\87à¨\9f',
+'nstab-template' => 'ਸਾà¨\82à¨\9aਾ',
'nstab-help' => 'ਮੱਦਦ ਪੇਜ',
'nstab-category' => 'ਕੈਟਾਗਰੀ',
# Main script and global functions
'nosuchaction' => 'ਕੋਈ ਇੰਝ ਦਾ ਐਕਸ਼ਨ ਨਹੀਂ',
+'nosuchactiontext' => 'URL ਦੁਆਰਾ ਦੱਸਿਆ ਕੰਮ ਗ਼ਲਤ ਹੈ।
+ਸ਼ਾਇਦ ਤੁਸੀਂ URL ਸਹੀ ਨਹੀਂ ਲਿਖਿਆ ਜਾਂ ਕਿਸੇ ਗ਼ਲਤ ਲਿੰਕ ਤੇ ਆਏ ਹੋ।
+ਇਹ ਵੀ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ {{SITENAME}} ਦੁਆਰੇ ਵਰਤੇ ਜਾਂਦੇ ਸਾਫ਼ਟਵੇਅਰ ਵਿਚਲੀ ਗ਼ਲਤੀ ਵੱਲ ਇਸ਼ਾਰਾ ਹੋਵੇ।',
'nosuchspecialpage' => 'ਕੋਈ ਇੰਝ ਦਾ ਖਾਸ ਪੇਜ ਨਹੀਂ',
'nospecialpagetext' => '<strong>ਤੁਸੀਂ ਇੱਕ ਅਵੈਧ ਖਾਸ ਪੇਜ ਦੀ ਮੰਗ ਕੀਤੀ ਹੈ।</strong>
# General errors
'error' => 'ਗਲਤੀ',
'databaseerror' => 'ਡਾਟਾਬੇਸ ਗਲਤੀ',
+'dberrortext' => 'ਡੈਟਾਬੇਸ ਪੁ੍ਛ ਗਿੱਛ ਵਿਚ ਹਿਦਾਇਤਾਂ ਦੀ ਤਰੁੱਟੀ ਮਿਲੀ ਹੈ।
+ਹੋ ਸਕਦਾ ਹੈ ਇਹ ਤਰੁ੍ੱਟੀ ਸਾਫ਼ਟਵੇਅਰ ਦੀ ਹੋਵੇ।
+ਇਸ ਗਣਿਤਫ਼ਲ "<tt>$2</tt>" ਵਿਚੌਂ ਪਿਛਲੀ ਡੈਟਬਾਸ ਪੁੱਛ ਗਿੱਛ ਸੀ: <blockquote><tt>$1</tt></blockquote.
+ਡੈਟਾਬੇਸ ਨੇ ਇਹ ਤਰੁੱਟੀ "<tt>$3: $4</tt>"ਜਵਾਬ ਵਿਚ ਦਿੱਤੀ।',
+'dberrortextcl' => 'ਡੈਟਾਬੇਸ ਪੁ੍ਛ ਗਿੱਛ ਵਿਚ ਹਿਦਾਇਤਾਂ ਦੀ ਤਰੁੱਟੀ ਮਿਲੀ ਹੈ।
+ਹੋ ਸਕਦਾ ਹੈ ਇਹ ਤਰੁੱਟੀ ਸਾਫ਼ਟਵੇਅਰ ਦੀ ਹੋਵੇ।
+ਇਸ ਗਣਿਤਫ਼ਲ "$2" ਵਿਚੌਂ ਪਿਛਲੀ ਡੈਟਬਾਸ ਪੁੱਛ ਗਿੱਛ ਸੀ: "$1".
+ਡੈਟਾਬੇਸ ਨੇ ਇਹ ਤਰੁੱਟੀ "$1"ਜਵਾਬ ਵਿਚ ਦਿੱਤੀ।',
+'laggedslavemode' => "'''ਖ਼ਬਰਦਾਰ:''' ਹੋ ਸਕਦਾ ਹੈ ਸਫ਼ੇ ਵਿਚ ਤਾਜ਼ਾ ਤਬਦੀਲੀਆਂ ਸ਼ਾਮਲ ਨਾ ਹੋਣ।",
'readonly' => 'ਡਾਟਾਬੇਸ ਲਾਕ ਹੈ',
-'missing-article' => "ਡੈਟਾਬੇਸ ਨੂੰ ਕਿਸੇ ਪੰਨੇ ਦਾ ਪਾਠ ''$1''ਜੋ ਇਸ ਨੂੰ $2 ਵਿਚ ਢੂੰਡਣਾ ਸੀ ,ਨਹੀਂ ਮਿਲਿਆ।
-ਆਮ ਤੌਰ ਤੇ ਮਿਟਾਏ ਜਾ ਚੁਕੇ ਪੰਨੇ ਦੀ ਇਤਿਹਾਸ ਕੜੀ ਦੀ ਵਰਤੌਂ ਕਰਣ ਨਾਲ ਇਸ ਤਰਾਂ ਹੁੰਦਾ ਹੈ।
-ਜੇ ਇਸ ਤਰਾਂ ਦਿ ਗੱਲ ਨਹੀਂ ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਤੁਹਾਨੂੰ ਸਾਫ਼ਟਵੇਅਰ ਵਿਚ ਤ੍ਰੁਟੀ ਮਿਲ ਗਈ ਹੈ।ਕਿਰਪਾ ਕਰਕੇ ਪਤੇ ਸਮੇਤ [[Special:ListUsers/sysop|administrator]] ਨੂੰ ਇਸ ਦੀ ਸੂਚਨਾ ਦਿਓ।",
+'enterlockreason' => 'ਤਾਲਾ-ਬੰਦੀ ਲਈ ਕਾਰਨ ਦਾਖ਼ਲ ਕਰੋ, ਨਾਲ਼ ਹੀ ਤਾਲਾ-ਬੰਦੀ ਦੇ ਰਿਲੀਜ਼ ਹੋਣ ਦਾ ਅੰਦਾਜ਼ਨ ਵਕਤ',
+'readonlytext' => 'ਡੈਟਾਬੇਸ ਨੂੰ ਇਸ ਵੇਲ਼ੇ ਤਾਲਾ ਲੱਗਾ ਹੋਇਆ ਹੈ, ਸ਼ਾਇਦ ਆਮ ਰੱਖ-ਰਖਾਵ ਲਈ, ਇਸਤੋਂ ਬਾਅਦ ਇਹ ਆਮ ਵਾਂਗ ਉਪਲੱਬਧ ਹੋਵੇਗਾ।
+ਜਿਸ ਪ੍ਰਬੰਧਕ ਨੇ ਇਸਨੂੰ ਤਾਲਾ ਲਾਇਆ ਹੈ ਉਸਦਾ ਕਹਿਣਾ ਹੈ ਕਿ: $1',
+'missing-article' => "ਡਾਟਾਬੇਸ ਨੂੰ ''$1'' $2 ਨਾਮ ਦਾ ਕੋਈ ਸਫ਼ਾ ਨਹੀਂ ਮਿਲਿਆ।
+ਆਮ ਤੌਰ ਤੇ ਮਿਟਾਏ ਜਾ ਚੁੱਕੇ ਸਫ਼ੇ ਦੀ ਅਤੀਤ ਕੜੀ ਦੀ ਵਰਤੋਂ ਕਰਨ ਨਾਲ਼ ਇੰਝ ਹੁੰਦਾ ਹੈ।
+ਜੇ ਇਹ ਗੱਲ ਨਹੀਂ ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਤੁਹਾਨੂੰ ਸਾਫ਼ਟਵੇਅਰ ਵਿਚ ਖ਼ਾਮੀ ਮਿਲ ਗਈ ਹੈ। ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਸਫ਼ੇ ਦੇ ਪਤੇ ਸਮੇਤ [[Special:ListUsers/sysop|administrator]] ਨੂੰ ਇਤਲਾਹ ਦਿਓ।",
+'missingarticle-rev' => '(ਬਦਲਾਅ#: $1)',
+'missingarticle-diff' => '(ਫ਼ਰਕ: $1, $2)',
+'readonly_lag' => 'ਜਦੌਂ ਤਕ ਅਧੀਨ ਡੇਟਾਬੇਸ ਸਰਵਰ ਸੁਤੰਤਰ ਡੈਟਾਬੇਸ ਸਰਵਰ ਦੀ ਪਕੜ ਵਿਚ ਨਹੀਂ ਆ ਜਾਂਦੇ ਡੈਟਾਬੇਸ ਸਵੈ ਜਕੜਿਆ ਗਿਆ ਹੈ।',
'internalerror' => 'ਅੰਦਰੂਨੀ ਗਲਤੀ',
'internalerror_info' => 'ਅੰਦਰੂਨੀ ਗਲਤੀ: $1',
-'badtitle' => 'ਗਲਤ ਟਾਇਟਲ',
-'badtitletext' => 'ਤੁਹਾਡਾ ਅਰਜ਼ਿਤ ਸਿਰਲੇਖ ਅਪ੍ਰਮਾਣਿਕ,ਖਾਲੀ ਯਾ ਗਲਤ ਜੁੜਿਆ ਹੋਇਆ ਅੰਤਰ-ਭਾਸ਼ਾ ਯਾ ਅੰਤਰ-ਵਿਕਿ ਸਿਰਲੇਖ ਹੈ।ਇਹ ਵੀ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਸ ਵਿਚ ਇਕ ਦੋ ਅੱਖਰ ਐਸੇ ਹੋਣ ਜੋ ਸਿਰਲੇਖ ਵਿਚ ਵਰਤੇ ਨਹੀਂ ਜਾ ਸਕਦੇ।',
+'fileappenderrorread' => 'ਅੰਤਕਾ ਜੋੜਨ ਲਗਿਆਂ "$1"ਪੜ੍ਹਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।',
+'fileappenderror' => "''$1'' ''$2'' ਨਾਲ਼ ਜੋੜਿਆ ਨਹੀ ਜਾ ਸਕਦਾ",
+'filecopyerror' => "'''$1''' ਫ਼ਾਈਲ '''$2''' ਵਿਚ ਨਕਲ ਨਹੀਂ ਹੋ ਸਕੀ।",
+'filerenameerror' => "ਫ਼ਾਈਲ ''$1'' ਦਾ ਨਾਮ ਬਦਲ ਕੇ ''$2'' ਨਹੀਂ ਰੱਖਿਆ ਜਾ ਸਕਿਆ।",
+'filedeleteerror' => "ਫ਼ਾਈਲ ''$1'' ਮਿਟਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।",
+'directorycreateerror' => "''$1'' ਬਣਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।",
+'filenotfound' => "ਫ਼ਾਈਲ ''$1'' ਲੱਭੀ ਨਹੀਂ ਜਾ ਸਕੀ।",
+'fileexistserror' => 'ਮਿਸਲ "$1" ਤੇ ਲਿਖ ਨਹੀਂ ਸਕਦੇ: ਮਿਸਲ ਹੌਂਦ ਵਿਚ ਹੈ।',
+'unexpected' => 'ਨਾ-ਸੰਭਾਵਿਤ ਗਣਿਤਫ਼ਲ',
+'formerror' => 'ਦੋਸ਼:ਫ਼ਾਰਮ ਪੇਸ਼ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ',
+'badarticleerror' => 'ਇਹ ਕਾਰਵਾਈ ਇਸ ਸਫ਼ੇ ਤੇ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।',
+'cannotdelete' => "ਫ਼ਾਈਲ ਜਾਂ ਸਫ਼ਾ ''$1'' ਨੂੰ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।
+ਸ਼ਾਇਦ ਕੋਈ ਪਹਿਲਾਂ ਹੀ ਇਸਨੂੰ ਮਿਟਾ ਚੁੱਕਾ ਹੈ।",
+'cannotdelete-title' => "ਸਫ਼ਾ ''$1'' ਨੂੰ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ",
+'delete-hook-aborted' => 'ਹੁੱਕ ਨੇ ਮਿਟਾਉਣਾ ਨਾਕਾਮ ਕੀਤਾ।
+ਇਸਨੇ ਕੋਈ ਕਾਰਨ ਨਹੀਂ ਦੱਸਿਆ।',
+'badtitle' => 'ਗ਼ਲਤ ਸਿਰਲੇਖ',
+'badtitletext' => 'ਤੁਹਾਡਾ ਦਰਖ਼ਾਸਤਸ਼ੁਦਾ ਸਿਰਲੇਖ ਨਾਕਾਬਿਲ, ਖ਼ਾਲੀ ਜਾਂ ਗ਼ਲਤ ਜੁੜਿਆ ਹੋਇਆ inter-languagd ਜਾਂ inter-wiki ਸਿਰਲੇਖ ਹੈ। ਇਹ ਵੀ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਸ ਵਿਚ ਇਕ-ਦੋ ਅੱਖਰ ਐਸੇ ਹੋਣ ਜੋ ਸਿਰਲੇਖ ਵਿਚ ਵਰਤੇ ਨਹੀਂ ਜਾ ਸਕਦੇ।',
'viewsource' => 'ਸਰੋਤ ਵੇਖੋ',
'protectedpagetext' => 'ਇਸ ਪੰਨੇ ਨੂੰ ਐਡਿਟ ਕਰਨ ਦੀ ਮਨਾਹੀ ਹੈ।',
'viewsourcetext' => 'ਤੁਸੀਂ ਇਸ ਪੰਨੇ ਦਾ ਸੋਮਾ ਦੇਖ ਸਕਦੇ ਹੋ ਤੇ ਉਸ ਦਾ ਉਤਾਰਾ ਵੀ ਲੈ ਸਕਦੇ ਹੋ।',
'protectedinterface' => 'ਇਹ ਪੰਨਾ ਸਾਫ਼ਟਵੇਅਰ ਇੰਟਰਫ਼ੇਸ ਦਾ ਮੂਲ ਪਾਠ ਹੈ ,ਅਤੇ ਦੁਰਵਰਤੌਂ ਤੌਂ ਬਚਾਅ ਲਈ ਰਾਖਵਾਂ ਕੀਤਾ ਗਿਆ ਹੈ।',
'editinginterface' => "'''ਚਿਤਾਵਨੀ''' ਤੁਸੀਂ ਐਸੇ ਪੰਨੇ ਨੂੰ ਬਦਲ ਰਹੇ ਹੋ ਜੋ ਸਾਫ਼ਟਵੇਅਰ ਇੰਟਰਫ਼ੇਸ ਦੇ ਮੂਲ ਪਾਠ ਲਈ ਵਰਤਿਆ ਗਿਆ ਹੈ।
ਇਸ ਪੰਨੇ ਦੇ ਬਦਲਾਅ ਦੁਸਰੇ ਵਰਤੋਂ ਕਰਣ ਵਾਲਿਆਂ ਲਈ ਵਰਤੇ ਜਾਣ ਵਾਲੇ ਇੰਟਰਫਲੇਸ ਦੀ ਸ਼ਕਲ ਤੇ ਅਸਰ ਪਾ ਦੇਣਗੇ।ਅਨੁਵਾਦ ਕਰਣ ਲਈ ,ਕਿਰਪਾ ਕਰਕੇ [//translatewiki.net/wiki/Main_Page?setlang=pa ਟ੍ਰਾਂਸਲੇਟਵਿਕੀ.ਨੈਟ] ਦੀ ਵਰਤੌਂ ਕਰੋ,ਇਹ ਮੀਡੀਆਵਿਕੀ ਦੀ ਸਥਾਨਕੀਕਰਣ ਯੋਜਨਾ ਹੈ।",
+'namespaceprotected' => "ਤੁਹਾਨੂੰ '''$1''' ਥਾਂ-ਨਾਮ ਵਾਲ਼ੇ ਸਫ਼ਿਆਂ ਵਿਚ ਸੋਧ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।",
+'customcssprotected' => 'ਤੁਹਾਨੂੰ ਇਸ CSS ਸਫ਼ੇ ਵਿਚ ਸੋਧ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਇਸ ਵਿਚ ਕਿਸੇ ਹੋਰ ਮੈਂਬਰ ਦੀਆਂ ਨਿੱਜੀ ਸੈਟਿੰਗਾਂ ਹਨ।',
+'customjsprotected' => 'ਤੁਹਾਨੂੰ ਇਸ CSS ਸਫ਼ੇ ਵਿਚ ਸੋਧ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਇਸ ਵਿਚ ਕਿਸੇ ਹੋਰ ਮੈਂਬਰ ਦੀਆਂ ਨਿੱਜੀ ਸੈਟਿੰਗਾਂ ਹਨ।',
+'ns-specialprotected' => 'ਖ਼ਾਸ ਸਫ਼ੇ ਸੋਧੇ ਨਹੀਂ ਜਾ ਸਕਦੇ।',
+'titleprotected' => 'ਇਹ ਸਿਰਲੇਖ [[User:$1|$1]] ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ ਹੈ ਅਤੇ ਵਰਤਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ। ਦਿੱਤਾ ਹੋਇਆ ਕਾਰਨ ਹੈ, "\'\'$2\'\'"।',
+'invalidtitle-knownnamespace' => 'ਥਾਂ-ਨਾਮ "$2" ਅਤੇ ਲਿਖਤ "$3" ਵਾਲ਼ਾ ਗ਼ਲਤ ਸਿਰਲੇਖ',
+'exception-nologin' => 'ਲਾਗਇਨ ਨਹੀਂ ਕੀਤਾ',
+'exception-nologin-text' => 'ਇਹ ਸਫ਼ਾ ਜਾਂ ਕਾਰਵਾਈ ਤੁਹਾਡਾ ਇਸ ਵਿਕੀ ਤੇ ਲਾਗਇਨ ਹੋਣਾ ਲੋੜਦੀ ਹੈ।',
+
+# Virus scanner
+'virus-unknownscanner' => 'ਅਣਪਛਾਤਾ ਐਂਟੀਵਾਇਰਸ:',
# Login and logout pages
'logouttext' => "'''ਹੁਣ ਤੁਸੀਂ ਲਾਗਆਉਟ ਹੋ ਗਏ ਹੋ।'''
You can continue to use {{SITENAME}} anonymously, or you can log in again as the same or as a different user.
Note that some pages may continue to be displayed as if you were still logged in, until you clear your browser cache.",
-'welcomecreation' => '== $1 ਜੀ ਆਇਆਂ ਨੂੰ! ==
+'welcomecreation' => '== ਜੀ ਆਇਆਂ ਨੂੰ, $1! ==
-ਤà©\81ਹਾਡਾ à¨\85à¨\95ਾà¨\8aà¨\82à¨\9f ਬਣਾà¨\87à¨\86 à¨\97ਿà¨\86 ਹà©\88। à¨\86ਪਣà©\80 [[Special:ਪਸੰਦ|{{SITENAME}} ਪਸੰਦ]] ਬਦਲਣà©\80 ਨਾ ਭੁੱਲੋ।',
-'yourname' => 'ਯà©\82à¨\9c਼ਰ ਨਾਂ:',
+ਤà©\81ਹਾਡਾ à¨\96ਾਤਾ ਬਣ à¨\9aà©\81ੱà¨\95ਾ ਹà©\88। à¨\86ਪਣà©\80à¨\86à¨\82 [[Special:Preferences|{{SITENAME}} ਪਸੰਦਾà¨\82]] ਬਦਲਣà©\80à¨\86à¨\82 ਨਾ ਭੁੱਲੋ।',
+'yourname' => 'ਮà©\88à¨\82ਬਰ ਨਾਂ:',
'yourpassword' => 'ਪਾਸਵਰਡ:',
-'yourpasswordagain' => 'ਪਾਸਵਰਡ ਮà©\81à©\9c-ਲਿਖੋ:',
-'remembermypassword' => 'ਇਸ ਕੰਪਿਊਟਰ ਉੱਤੇ ਮੇਰਾ ਲਾਗਇਨ ਯਾਦ ਰੱਖੋ ($1 {{PLURAL:$1|ਦਿਨ|ਦਿਨਾਂ}} ਲਈ ਵੱਧ ਤੋਂ ਵੱਧ)',
+'yourpasswordagain' => 'ਪਾਸਵਰਡ ਦà©\81ਬਾਰਾ ਲਿਖੋ:',
+'remembermypassword' => 'ਇਸ ਕੰਪਿਊਟਰ ’ਤੇ ਮੇਰਾ ਲਾਗਇਨ ਯਾਦ ਰੱਖੋ (ਵੱਧ ਤੋਂ ਵੱਧ $1 {{PLURAL:$1|ਦਿਨ|ਦਿਨਾਂ}} ਲਈ)',
'yourdomainname' => 'ਤੁਹਾਡੀ ਡੋਮੇਨ:',
+'password-change-forbidden' => 'ਇਸ ਵਿਕੀ ਤੇ ਤੁਸੀਂ ਪਾਸਵਰਡ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।',
'login' => 'ਲਾਗ ਇਨ',
-'nav-login-createaccount' => 'ਲਾਗ ਇਨ / ਅਕਾਊਂਟ ਬਣਾਓ',
-'loginprompt' => 'ਤੁਹਾਨੂੰ {{SITENAME}} ਉੱਤੇ ਲਾਗਇਨ ਕਰਨ ਲਈ ਕੂਕੀਜ਼ ਯੋਗ ਕਰਨੇ ਜ਼ਰੂਰੀ ਹਨ।',
-'userlogin' => 'ਲਾਗ ਇਨ / ਅਕਾਊਂਟ ਬਣਾਓ',
+'nav-login-createaccount' => 'ਲਾਗ ਇਨ/ਖਾਤਾ ਬਣਾਓ',
+'loginprompt' => 'ਤੁਹਾਨੂੰ {{SITENAME}} ’ਤੇ ਲਾਗਇਨ ਕਰਨ ਲਈ ਕੂਕੀਜ਼ ਯੋਗ ਕਰਨੇ ਜ਼ਰੂਰੀ ਹਨ।',
+'userlogin' => 'ਲਾਗ ਇਨ/ਖਾਤਾ ਖੋਲ੍ਹੋ',
'userloginnocreate' => 'ਲਾਗ ਇਨ',
'logout' => 'ਲਾਗ ਆਉਟ',
'userlogout' => 'ਲਾਗ ਆਉਟ',
'notloggedin' => 'ਲਾਗਇਨ ਨਹੀਂ',
-'nologin' => 'ਅਕਾਊਂਟ ਨਹੀਂ ਹੈ? $1',
-'nologinlink' => 'ਇੱਕ ਅਕਾਊਂਟ ਬਣਾਓ',
-'createaccount' => 'ਅਕਾਊਂਟ ਬਣਾਓ',
-'gotaccount' => "ਪਹਿਲਾਂ ਹੀ ਇੱਕ ਅਕਾਊਂਟ ਹੈ? '''$1'''.",
-'gotaccountlink' => 'ਲਾਗਇਨ',
+'nologin' => 'ਖਾਤਾ ਨਹੀਂ ਹੈ? $1।',
+'nologinlink' => 'ਖਾਤਾ ਬਣਾਓ',
+'createaccount' => 'ਖਾਤਾ ਬਣਾਓ',
+'gotaccount' => "ਖਾਤਾ ਹੈ? '''$1'''।",
+'gotaccountlink' => 'ਲਾਗ ਇਨ',
+'userlogin-resetlink' => 'ਆਪਣੀ ਲਾਗਇਨ ਜਾਣਕਾਰੀ ਭੁੱਲ ਗਏ ਹੋ?',
'createaccountmail' => 'ਈਮੇਲ ਨਾਲ',
'createaccountreason' => 'ਕਾਰਨ:',
'badretype' => 'ਤੁਹਾਡੇ ਵਲੋਂ ਦਿੱਤੇ ਪਾਸਵਰਡ ਮਿਲਦੇ ਨਹੀਂ ਹਨ।',
-'userexists' => 'ਯà©\82à¨\9c਼ਰ ਨਾà¨\82 ਪਹਿਲਾà¨\82 ਹà©\80 ਵਰਤà©\8bà¨\82 à¨\85ਧà©\80ਨ ਹੈ।
-ਵੱà¨\96ਰਾ ਯà©\82à¨\9c਼ਰ ਨਾà¨\82 ਵਰਤà©\8bà¨\82 à¨\9cà©\80।',
+'userexists' => 'à¨\87ਹ ਮà©\88à¨\82ਬਰ-ਨਾਮ ਪਹਿਲਾà¨\82 ਹà©\80 ਵਰਤà©\8bà¨\82 â\80\99à¨\9a ਹੈ।
+ਮਿਹਰਬਾਨà©\80 à¨\95ਰà¨\95à©\87 ਵੱà¨\96ਰਾ ਮà©\88à¨\82ਬਰ-ਨਾਮ ਵਰਤà©\8bà¨\82।',
'loginerror' => 'ਲਾਗਇਨ ਗਲਤੀ',
'createaccounterror' => 'ਅਕਾਊਂਟ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ: $1',
'nocookiesnew' => 'ਯੂਜ਼ਰ ਅਕਾਊਂਟ ਬਣਾਇਆ ਗਿਆ ਹੈ, ਪਰ ਤੁਸੀਂ ਲਾਗਇਨ ਨਹੀਂ ਕੀਤਾ ਹੈ।{{SITENAME}} uses cookies to log in users. You have cookies disabled. Please enable them, then log in with your new username and password.',
'noname' => 'ਤੁਸੀਂ ਇੱਕ ਵੈਧ ਯੂਜ਼ਰ ਨਾਂ ਨਹੀਂ ਦਿੱਤਾ ਹੈ।',
'loginsuccesstitle' => 'ਲਾਗਇਨ ਸਫ਼ਲ ਰਿਹਾ',
'loginsuccess' => "'''ਤੁਸੀਂ {{SITENAME}} ਉੱਤੇ \"\$1\" ਵਾਂਗ ਲਾਗਇਨ ਕਰ ਚੁੱਕੇ ਹੋ।'''",
-'nosuchuser' => '"$1" ਨਾਂ ਨਾਲ ਕੋਈ ਯੂਜ਼ਰ ਨਹੀਂ ਹੈ। ਆਪਣੇ ਸ਼ਬਦ ਧਿਆਨ ਨਾਲ ਚੈੱਕ ਕਰੋ ਜਾਂ ਨਵਾਂ ਅਕਾਊਂਟ ਬਣਾਓ।',
+'nosuchuser' => '!"$1" ਨਾਂ ਨਾਲ ਕੋਈ ਯੂਜ਼ਰ ਨਹੀਂ ਹੈ। ਆਪਣੇ ਸ਼ਬਦ ਜੋੜ ਧਿਆਨ ਨਾਲ ਚੈਕ ਕਰੋ ਉਪਰ ਹੇਠਾਂ ਦਾ ਕੇਸ ਵਰਤਣ ਨਾਲ ਫ਼ਰਕ ਪੈਂਦਾ ਹੈ ਜਾਂ [[Special:UserLogin/signup|ਨਵਾਂ ਖਾਤਾ ਬਣਾਓ]]',
'nosuchusershort' => '"$1" ਨਾਂ ਨਾਲ ਕੋਈ ਵੀ ਯੂਜ਼ਰ ਨਹੀਂ ਹੈ। ਆਪਣੇ ਸ਼ਬਦ ਧਿਆਨ ਨਾਲ ਚੈੱਕ ਕਰੋ।',
'nouserspecified' => 'ਤੁਹਾਨੂੰ ਇੱਕ ਯੂਜ਼ਰ-ਨਾਂ ਦੇਣਾ ਪਵੇਗਾ।',
+'login-userblocked' => 'ਇਹ ਮੈਂਬਰ ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੈ। ਲਾਗਇਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
'wrongpassword' => 'ਗਲਤ ਪਾਸਵਰਡ ਦਿੱਤਾ ਹੈ। ਮੁੜ-ਟਰਾਈ ਕਰੋ ਜੀ।',
'wrongpasswordempty' => 'ਖਾਲੀ ਪਾਸਵਰਡ ਦਿੱਤਾ ਹੈ। ਮੁੜ-ਟਰਾਈ ਕਰੋ ਜੀ।',
'passwordtooshort' => 'ਪਾਸਵਰਡ {{PLURAL:$1|1 ਅੱਖਰ|$1 ਅੱਖਰਾਂ}} ਦਾ ਹੋਣਾ ਲਾਜ਼ਮੀ ਹੈ।',
'password-name-match' => 'ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਤੁਹਾਡੇ ਯੂਜ਼ਰ ਨਾਂ ਤੋਂ ਵੱਖਰਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।',
-'mailmypassword' => 'ਨਵਾਂ ਪਾਸਵਰਡ ਈਮੇਲ ਕਰੋ',
+'password-login-forbidden' => 'ਇਹ ਮੈਂਬਰ-ਨਾਮ ਅਤੇ ਪਾਸਵਰਡ ਵਰਤਣ ਦੀ ਮਨਾਹੀ ਹੈ।',
+'mailmypassword' => 'ਨਵਾਂ ਪਾਸਵਰਡ ਈ-ਮੇਲ ਕਰੋ',
'passwordremindertitle' => '{{SITENAME}} ਲਈ ਪਾਸਵਰਡ ਯਾਦ ਰੱਖੋ',
-'passwordremindertext' => 'ਕਿਸੇ ਨੇ (ਸ਼ਾਇਦ ਤੁਸੀਂ, IP ਐਡਰੈੱਸ $1 ਤੋਂ)
-ਮੰਗ ਕੀਤੀ ਸੀ ਕਿ ਅਸੀਂ ਤੁਹਾਨੂੰ {{SITENAME}} ($4) ਲਈ ਪਾਸਵਰਡ ਭੇਜੀਏ।
-ਯੂਜ਼ਰ "$2" ਲਈ ਹੁਣ ਪਾਸਵਰਡ "$3" ਹੈ।
-ਤੁਹਾਨੂੰ ਹੁਣ ਲਾਗਇਨ ਕਰਕੇ ਆਪਣਾ ਪਾਸਵਰਡ ਹੁਣੇ ਬਦਲਣਾ ਚਾਹੀਦਾ ਹੈ।
-
-If someone else made this request or if you have remembered your password and
-you no longer wish to change it, you may ignore this message and continue using
-your old password.',
+'passwordremindertext' => 'ਕਿਸੇ ਨੇ (ਸ਼ਾਇਦ ਤੁਸੀਂ, IP ਪਤਾ $1 ਤੋਂ) {{SITENAME}} ਲਈ ਪਾਸਵਰਡ ਬਦਲਣ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ ($4)।
+ਮੈਂਬਰ "$2" ਲਈ ਆਰਜ਼ੀ ਪਾਸਵਰਡ ਬਣਾ ਕੇ "$3" ਤੇ ਭੇਜ ਦਿੱਤਾ ਗਿਆ ਹੈ।
+ਜੇ ਤੁਹਾਡਾ ਇਹੀ ਇਰਾਦਾ ਸੀ ਤਾਂ ਤੁਹਾਨੂੰ ਚਾਹੀਦਾ ਹੈ ਹੁਣੇ ਲਾਗਇਨ ਕਰਕੇ ਆਪਣਾ ਪਾਸਵਰਡ ਲਓ।
+ਤੁਹਾਡਾ ਆਰਜ਼ੀ ਪਾਸਵਰਡ {{PLURAL:$5|ਇਕ ਦਿਨ|$5 ਦਿਨਾਂ}} ਵਿਚ ਖ਼ਤਮ ਹੋ ਜਾਵੇਗਾ।
+
+ਜੇ ਕਿਸੇ ਹੋਰ ਨੇ ਇਹ ਬੇਨਤੀ ਕੀਤੀ ਸੀ ਜਾਂ ਜੇ ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਯਾਦ ਹੈ ਅਤੇ ਤੁਸੀਂ ਇਸਨੂੰ ਬਦਲਣਾ ਨਹੀਂ ਚਾਹੁੰਦੇ ਤਾਂ ਤੁਸੀਂ ਇਸ ਸੁਨੇਹੇ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਕੇ ਆਪਣਾ ਪੁਰਾਣਾ ਪਾਸਵਰਡ ਵਰਤਣਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ।',
'noemail' => 'ਯੂਜ਼ਰ "$1" ਲਈ ਰਿਕਾਰਡ ਵਿੱਚ ਕੋਈ ਈਮੇਲ ਐਡਰੈੱਸ ਨਹੀਂ ਹੈ।',
'noemailcreate' => 'ਤੁਹਾਨੂੰ ਠੀਕ ਈਮੇਲ ਐਡਰੈੱਸ ਦੇਣਾ ਪਵੇਗਾ',
'passwordsent' => '"$1" ਨਾਲ ਰਜਿਸਟਰ ਕੀਤੇ ਈਮੇਲ ਐਡਰੈੱਸ ਉੱਤੇ ਈਮੇਲ ਭੇਜੀ ਗਈ ਹੈ।
ਇਹ ਮਿਲ ਦੇ ਬਾਅਦ ਮੁੜ ਲਾਗਇਨ ਕਰੋ ਜੀ।',
-'throttled-mailpassword' => 'ਇੱਕ ਪਾਸਵਰਡ ਰੀਮਾਈਡਰ ਪਹਿਲਾਂ ਹੀ ਭੇਜਿਆ ਗਿਆ ਹੈ, ਆਖਰੀ
-$1 ਘੰਟੇ ਵਿੱਚ। ਨੁਕਸਾਨ ਤੋਂ ਬਚਣ ਲਈ, $1 ਘੰਟਿਆਂ ਵਿੱਚ ਇੱਕ ਹੀ ਪਾਸਵਰਡ ਰੀਮਾਈਡਰ ਭੇਜਿਆ ਜਾਂਦਾ ਹੈ।',
+'blocked-mailpassword' => 'ਤੁਹਾਡੇ IP ਪਤੇ ਤੇ ਸੋਧ ਕਰਨ ਤੇ ਪਾਬੰਦੀ ਹੈ ਅਤੇ ਇਸੇ ਕਰਕੇ, ਗ਼ਲਤ ਵਰਤੋਂ ਤੋਂ ਬਚਣ ਲਈ, ਪਾਸਵਰਡ ਹਾਸਲ ਕਰਨ ਵਾਲ਼ੀ ਸਹੂਲਤ ਦੀ ਵਰਤੋਂ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
+'eauthentsent' => 'ਇਕ ਤਸਦੀਕੀ ਈ-ਮੇਲ ਨਾਮਜ਼ਦ ਕੀਤੇ ਈ-ਮੇਲ ਪਤੇ ਤੇ ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤੁਹਾਡੇ ਪਤੇ ਤੇ ਕੋਈ ਹੋਰ ਈ-ਮੇਲ ਭੇਜਣ ਤੋਂ ਪਹਿਲਾਂ, ਇਹ ਤਸਦੀਕ ਕਰਨ ਲਈ ਕਿ ਖਾਤਾ ਅਸਲ ਵਿਚ ਤੁਹਾਡਾ ਹੀ ਹੈ, ਤੁਹਾਨੂੰ ਉਸ ਈ-ਮੇਲ ਵਿਚਲੀਆਂ ਹਦਾਇਤਾਂ ਤੇ ਅਮਲ ਕਰਨਾ ਹੋਵੇਗਾ।',
+'throttled-mailpassword' => 'ਆਖ਼ਰੀ {{PLURAL:$1|ਇੱਕ ਘੰਟੇ|$1 ਘੰਟਿਆਂ}} ਵਿਚ ਇੱਕ ਪਾਸਵਰਡ ਯਾਦ-ਦਹਾਨੀ ਪਹਿਲਾਂ ਹੀ ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।
+ਗ਼ਲਤ ਵਰਤੋਂ ਤੋਂ ਬਚਣ ਲਈ, {{PLURAL:$1|ਇੱਕ ਘੰਟੇ|$1 ਘੰਟਿਆਂ}} ਵਿੱਚ ਸਿਰਫ਼ ਇੱਕ ਹੀ ਪਾਸਵਰਡ ਯਾਦ-ਦਹਾਨੀ ਭੇਜੀ ਜਾਂਦੀ ਹੈ।',
'mailerror' => 'ਈਮੇਲ ਭੇਜਣ ਦੌਰਾਨ ਗਲਤੀ: $1',
'acct_creation_throttle_hit' => 'ਅਫਸੋਸ ਹੈ, ਪਰ ਤੁਸੀਂ ਪਹਿਲਾਂ ਹੀ $1 ਅਕਾਊਂਟ ਬਣਾ ਚੁੱਕੇ ਹੋ। ਤੁਸੀਂ ਹੋਰ ਨਹੀਂ ਬਣਾ ਸਕਦੇ।',
'emailauthenticated' => 'ਤੁਹਾਡਾ ਈਮੇਲ ਐਡਰੈੱਸ $1 ਉੱਤੇ ਪਰਮਾਣਿਤ ਕੀਤਾ ਗਿਆ ਹੈ।',
'noemailprefs' => 'ਇਹ ਫੀਚਰ ਵਰਤਣ ਲਈ ਇੱਕ ਈਮੇਲ ਐਡਰੈੱਸ ਦਿਓ।।',
'emailconfirmlink' => 'ਆਪਣਾ ਈ-ਮੇਲ ਐਡਰੈੱਸ ਕਨਫਰਮ ਕਰੋ।',
'invalidemailaddress' => 'ਈਮੇਲ ਐਡਰੈੱਸ ਮਨਜ਼ੂਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹ ਠੀਕ ਫਾਰਮੈਟ ਨਹੀਂ ਜਾਪਦਾ ਹੈ। ਇੱਕ ਠੀਕ ਫਾਰਮੈਟ ਵਿੱਚ ਦਿਓ ਜਾਂ ਇਹ ਖੇਤਰ ਖਾਲੀ ਛੱਡ ਦਿਓ।',
+'cannotchangeemail' => 'ਇਸ ਵਿਕੀ ਤੇ ਈ-ਮੇਲ ਪਤੇ ਬਦਲੇ ਨਹੀਂ ਜਾ ਸਕਦੇ।',
+'emaildisabled' => 'ਇਹ ਸਾਈਟ ਈ-ਮੇਲਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀ।',
'accountcreated' => 'ਅਕਾਊਂਟ ਬਣਾਇਆ',
'accountcreatedtext' => '$1 ਲਈ ਯੂਜ਼ਰ ਅਕਾਊਂਟ ਬਣਾਇਆ ਗਿਆ।',
'createaccount-title' => '{{SITENAME}} ਲਈ ਅਕਾਊਂਟ ਬਣਾਉਣਾ',
-'loginlanguagelabel' => 'ਭਾਸ਼ਾ: $1',
+'createaccount-text' => 'ਕਿਸੇ ਨੇ "$2" ਮੈਂਬਰ-ਨਾਮ ਅਤੇ "$3" ਪਾਸਵਰਡ ਨਾਲ਼ {{SITENAME}} ($4) ਤੇ, ਤੁਹਾਡਾ ਈ-ਮੇਲ ਪਤਾ ਵਰਤਦੇ ਹੋਏ, ਖਾਤਾ ਬਣਾਇਆ ਹੈ।
+ਤੁਹਾਨੂੰ ਹੁਣੇ ਲਾਗਇਨ ਕਰਕੇ ਆਪਣਾ ਪਾਸਵਰਡ ਬਦਲਣਾ ਚਾਹੀਦਾ ਹੈ।
+
+ਜੇ ਇਹ ਖਾਤਾ ਗ਼ਲਤੀ ਨਾਲ਼ ਬਣ ਗਿਆ ਹੈ ਤਾਂ ਤੁਸੀਂ ਇਸ ਸੁਨੇਹੇ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਸਕਦੇ ਹੋ।',
+'usernamehasherror' => 'ਮੈਂਬਰ-ਨਾਮ ਵਿਚ ਹੈਸ਼ ਅੱਖਰ ਨਹੀਂ ਹੋ ਸਕਦੇ',
+'login-throttled' => 'ਤੁਸੀਂ ਬਹੁਤ ਸਾਰੀਆਂ ਤਾਜ਼ਾ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਹਨ।
+ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਥੋੜੀ ਉਡੀਕ ਕਰੋ।',
+'login-abort-generic' => 'ਤੁਹਾਡੀ ਲਾਗਇਨ ਨਾਕਾਮ ਸੀ - ਰੱਦ',
+'loginlanguagelabel' => 'ਬੋਲੀ: $1',
+
+# E-mail sending
+'user-mail-no-addy' => 'ਬਿਨਾਂ ਈ-ਮੇਲ ਪਤਾ ਦਿੱਤੇ ਈ-ਮੇਲ ਭੇਜਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ।',
# Change password dialog
'resetpass' => 'ਪਾਸਵਰਡ ਬਦਲੋ',
'resetpass_submit' => 'ਪਾਸਵਰਡ ਸੈੱਟ ਕਰੋ ਅਤੇ ਲਾਗਇਨ ਕਰੋ',
'resetpass_success' => 'ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਠੀਕ ਤਰਾਂ ਬਦਲਿਆ ਗਿਆ ਹੈ! ਹੁਣ ਤੁਸੀਂ ਲਾਗਇਨ ਕਰ ਸਕਦੇ ਹੋ...',
'resetpass_forbidden' => 'ਪਾਸਵਰਡ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ',
+'resetpass-no-info' => 'ਇਸ ਸਫ਼ੇ ਨੂੰ ਸਿੱਧੇ ਹੀ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨਾ ਪਵੇਗਾ।',
'resetpass-submit-loggedin' => 'ਪਾਸਵਰਡ ਬਦਲੋ',
'resetpass-submit-cancel' => 'ਰੱਦ ਕਰੋ',
+'resetpass-wrong-oldpass' => 'ਗ਼ਲਤ ਆਰਜ਼ੀ ਜਾਂ ਚਾਲੂ ਪਾਸਵਰਡ।
+ਸ਼ਾਇਦ ਤੁਸੀਂ ਕਾਮਯਾਬੀ ਨਾਲ਼ ਆਪਣਾ ਪਾਸਵਰਡ ਬਦਲ ਚੁੱਕੇ ਹੋ ਜਾਂ ਆਰਜ਼ੀ ਪਾਸਵਰਡ ਲਈ ਬੇਨਤੀ ਕੀਤੀ ਸੀ।',
'resetpass-temp-password' => 'ਆਰਜ਼ੀ ਪਾਸਵਰਡ:',
+# Special:PasswordReset
+'passwordreset' => 'ਪਾਸਵਰਡ ਰੀਸੈੱਟ ਕਰੋ',
+'passwordreset-text' => 'ਆਪਣੇ ਖਾਤੇ ਦੀ ਤਫ਼ਸੀਲ ਦੀ ਈ-ਮੇਲ ਹਾਸਲ ਕਰਨ ਲਈ ਇਹ ਫ਼ਾਰਮ ਮੁਕੰਮਲ ਕਰੋ।',
+'passwordreset-legend' => 'ਪਾਸਵਰਡ ਰੀਸੈੱਟ ਕਰੋ',
+'passwordreset-disabled' => 'ਇਸ ਵਿਕੀ ਤੇ ਪਾਸਵਰਡ ਰੀਸੈੱਟ ਬੰਦ ਕੀਤੇ ਗਏ ਹਨ।',
+'passwordreset-username' => 'ਮੈਂਬਰ-ਨਾਂ:',
+'passwordreset-domain' => 'ਡੋਮੇਨ:',
+'passwordreset-email' => 'ਈ-ਮੇਲ ਪਤਾ:',
+'passwordreset-emailtitle' => '{{SITENAME}} ਤੇ ਖਾਤੇ ਦੀ ਜਾਣਕਾਰੀ',
+'passwordreset-emailtext-ip' => 'ਕਿਸੇ ਨੇ (ਸ਼ਾਇਦ ਤੁਸੀਂ, IP ਪਤਾ $1 ਤੋਂ) {{SITENAME}}
+($4) ਲਈ ਖਾਤਾ ਤਫ਼ਸੀਲ ਯਾਦ-ਦਹਾਨੀ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ। ਇਹ {{PLURAL:
+$3|ਖਾਤਾ ਇਸ ਈ-ਮੇਲ ਪਤੇ ਨਾਲ਼ ਜੁੜਿਆ ਹੈ|ਖਾਤੇ ਇਸ ਈ-ਮੇਲ ਪਤੇ ਨਾਲ਼ ਜੁੜੇ ਹਨ}}:
+$2
+
+ਇਹ ਆਰਜ਼ੀ ਪਾਸਵਰਡ
+{{PLURAL:$5|ਇੱਕ ਦਿਨ|$5 ਦਿਨਾਂ}} ਵਿਚ ਖ਼ਤਮ ਹੋ {{PLURAL:$3|ਜਾਵੇਗਾ|ਜਾਣਗੇ}}।
+ਤੁਹਾਨੂੰ ਹੁਣੇ ਲਾਗਇਨ ਕਰਕੇ ਨਵਾਂ ਪਾਸਵਰਡ ਬਣਾਉਣਾ ਚਾਹੀਦਾ ਹੈ। ਜੇ ਕਿਸੇ ਹੋਰ ਨੇ ਇਹ ਬੇਨਤੀ ਕੀਤੀ ਸੀ ਜਾਂ ਜੇ ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਯਾਦ ਹੈ ਅਤੇ ਤੁਸੀਂ ਇਸਨੂੰ ਬਦਲਣਾ ਨਹੀਂ ਚਾਹੁੰਦੇ ਤਾਂ ਤੁਸੀਂ ਇਸ ਸੁਨੇਹੇ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਕੇ ਆਪਣਾ ਪੁਰਾਣਾ ਪਾਸਵਰਡ ਵਰਤਣਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ।',
+'passwordreset-emailelement' => 'ਮੈਂਬਰ-ਨਾਂ: $1
+ਆਰਜ਼ੀ ਪਾਸਵਰਡ: $2',
+'passwordreset-emailsent' => 'ਇੱਕ ਯਾਦ-ਦਹਾਨੀ ਈ-ਮੇਲ ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।',
+'passwordreset-emailsent-capture' => 'ਇੱਕ ਯਾਦ-ਦਹਾਨੀ ਈ-ਮੇਲ, ਜਿਹੜੀ ਕਿ ਹੇਠਾਂ ਦਿੱਸ ਰਹੀ ਹੈ, ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।',
+
+# Special:ChangeEmail
+'changeemail' => 'ਈ-ਮੇਲ ਪਤਾ ਬਦਲੋ',
+'changeemail-header' => 'ਖਾਤੇ ਵਾਲ਼ਾ ਈ-ਮੇਲ ਪਤਾ ਬਦਲੋ',
+'changeemail-text' => 'ਆਪਣਾ ਈ-ਮੇਲ ਪਤਾ ਬਦਲਣ ਲਈ ਇਹ ਫ਼ਾਰਮ ਮੁਕੰਮਲ ਕਰੋ। ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਤਸਦੀਕ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖ਼ਲ ਕਰਨਾ ਪਵੇਗਾ।',
+'changeemail-no-info' => 'ਇਸ ਸਫ਼ੇ ਨੂੰ ਸਿੱਧੇ ਹੀ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨਾ ਪਵੇਗਾ।',
+'changeemail-oldemail' => 'ਚਾਲੂ ਈ-ਮੇਲ ਪਤਾ:',
+'changeemail-newemail' => 'ਨਵਾਂ ਈ-ਮੇਲ ਪਤਾ:',
+'changeemail-none' => '(ਕੋਈ ਨਹੀਂ)',
+'changeemail-submit' => 'ਈ-ਮੇਲ ਬਦਲੋ',
+'changeemail-cancel' => 'ਰੱਦ ਕਰੋ',
+
# Edit page toolbar
-'bold_sample' => 'à¨\97à©\82à©\9cਾ à¨\9fà©\88à¨\95ਸà¨\9f',
-'bold_tip' => 'ਬà©\8bਲਡ à¨\9fà©\88à¨\95ਸà¨\9f',
-'italic_sample' => 'ਤਿਰà¨\9bਾ à¨\9fà©\88à¨\95ਸà¨\9f',
-'italic_tip' => 'ਤਿਰà¨\9bਾ à¨\9fà©\88à¨\95ਸà¨\9f',
-'link_sample' => 'ਲਿੰà¨\95 à¨\9fਾà¨\87à¨\9fਲ',
+'bold_sample' => 'à¨\97à©\82à©\9cà©\8dਹà©\80 ਲਿà¨\96ਾà¨\88',
+'bold_tip' => 'à¨\97à©\81à©\9cà©\8dਹà©\80 ਲਿà¨\96ਾà¨\88',
+'italic_sample' => 'à¨\9fà©\87ਢà©\80 ਲਿà¨\96ਤ',
+'italic_tip' => 'à¨\9fà©\87ਢà©\80 ਲਿà¨\96ਾà¨\88',
+'link_sample' => 'ਲਿੰà¨\95 ਦਾ ਸਿਰਲà©\87à¨\96',
'link_tip' => 'ਅੰਦਰੂਨੀ ਲਿੰਕ',
-'extlink_sample' => 'http://www.example.com ਲਿੰà¨\95 à¨\9fਾà¨\88à¨\9fਲ',
-'extlink_tip' => 'ਬਾਹਰà©\80 à¨\95à©\9cà©\80( ਅਗੇਤਰ http:// ਯਾਦ ਰੱਖੋ)',
-'headline_sample' => 'ਹà©\88ੱਡਲਾà¨\88ਨ à¨\9fà©\88à¨\95ਸà¨\9f',
-'headline_tip' => 'ਦà©\82ਸਰੇ ਦਰਜੇ ਦਾ ਸਿਰਲੇਖ',
+'extlink_sample' => 'http://www.example.com ਲਿੰà¨\95 ਸਿਰਲà©\87à¨\96',
+'extlink_tip' => 'ਬਾਹਰà©\80 ਲਿੰà¨\95 (ਅਗੇਤਰ http:// ਯਾਦ ਰੱਖੋ)',
+'headline_sample' => 'ਸà©\81ਰà¨\96਼à©\80 ਦà©\80 ਲਿà¨\96ਤ',
+'headline_tip' => 'ਦà©\82à¨\9cੇ ਦਰਜੇ ਦਾ ਸਿਰਲੇਖ',
'nowiki_sample' => 'ਅਸੰਗਠਿਤ ਪਾਠ (NON -FORMATTED) ਇੱਥੇ ਰਖੋ।',
-'nowiki_tip' => 'ਵਿà¨\95ਿ ਸੰà¨\97ਠਨਾ (formatting) ਨà¨\9c਼ਰà¨\85ਦਾਜ਼ ਕਰੋ',
+'nowiki_tip' => 'ਵਿà¨\95à©\80 ਫ਼à©\8cਰਮà©\88à¨\9fਿੰà¨\97 ਨà¨\9c਼ਰà¨\85à©°ਦਾਜ਼ ਕਰੋ',
'image_tip' => 'ਇੰਬੈੱਡ ਚਿੱਤਰ',
'media_tip' => 'ਮੀਡਿਆ ਫਾਇਲ ਲਿੰਕ',
-'sig_tip' => 'à¨\9fਾà¨\88ਮ-ਸà¨\9fà©\88à¨\82ਪ ਨਾਲ ਤà©\81ਹਾਡà©\87 ਦਸਤà¨\96ਤ',
-'hr_tip' => 'ਹਰà©\80à¨\9cੱà¨\9fਲ ਲਾਈਨ (use sparingly)',
+'sig_tip' => 'ਤà©\81ਹਾਡà©\87 ਦਸਤà¨\96਼ਤ ਵà¨\95ਤ ਸਮà©\87ਤ',
+'hr_tip' => 'ਲà©\87à¨\9fਵà©\80à¨\82 ਲਾਈਨ (use sparingly)',
# Edit pages
-'summary' => 'ਸੰà¨\96à©\87ਪ:',
+'summary' => 'ਸਾਰ:',
'subject' => 'ਵਿਸ਼ਾ/ਹੈੱਡਲਾਈਨ:',
'minoredit' => 'ਇਹ ਛੋਟੀ ਸੋਧ ਹੈ',
-'watchthis' => 'à¨\87ਹ ਪà©\87à¨\9c ਵਾà¨\9a à¨\95ਰੋ',
-'savearticle' => 'ਪà©\87à¨\9c ਸੰà¨à¨¾à¨²ੋ',
+'watchthis' => 'à¨\87ਸ ਸਫ਼à©\87 â\80\99ਤà©\87 ਨà¨\9c਼ਰ ਰੱà¨\96ੋ',
+'savearticle' => 'ਸਫ਼ਾ ਸਾà¨\82à¨ੋ',
'preview' => 'ਝਲਕ',
'showpreview' => 'ਝਲਕ ਵੇਖੋ',
'showlivepreview' => 'ਲਾਈਵ ਝਲਕ',
-'showdiff' => 'ਬਦਲਾਅ ਵੇਖਾਓ',
-'anoneditwarning' => "'''ਚੇਤਾਵਨੀ:''' ਤੁਸੀਂ ਲਾਗਇਨ ਨਹੀਂ ਕੀਤਾ ਹੈ। ਤੁਹਾਡਾ IP ਐਡਰੈੱਸ ਇਸ ਪੇਜ ਦੇ ਐਡਿਟ ਅਤੀਤ ਵਿੱਚ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਵੇਗਾ।",
+'showdiff' => 'ਤਬਦੀਲੀ ਵੇਖੋ',
+'anoneditwarning' => "'''ਚੇਤਾਵਨੀ:''' ਤੁਸੀਂ ਲਾਗਇਨ ਨਹੀਂ ਕੀਤਾ ਹੈ। ਤੁਹਾਡਾ IP ਐਡਰੈੱਸ ਇਸ ਸਫ਼ੇ ਦੇ ਅਤੀਤ ਵਿੱਚ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਵੇਗਾ।",
+'anonpreviewwarning' => "''ਤੁਸੀਂ ਲਾਗਇਨ ਨਹੀਂ ਕੀਤਾ। ਤਬਦੀਲੀ ਸਾਂਭਣ ਨਾਲ਼ ਤੁਹਾਡਾ IP ਪਤਾ ਸਫ਼ੇ ਦੇ ਸੋਧ ਅਤੀਤ ਵਿਚ ਰਿਕਾਰਡ ਹੋ ਜਾਵੇਗਾ।''",
+'missingsummary' => "'''ਯਾਦ-ਦਹਾਨੀ:''' ਤੁਸੀਂ ਸੋਧ ਸਾਰ ਮੁਹੱਈਆ ਨਹੀਂ ਕਰਵਾਇਆ। ਜੇ ਤੁਸੀਂ \"{{int:savearticle}}\" ਤੇ ਦੁਬਾਰਾ ਕਲਿੱਕ ਕੀਤਾ ਤਾਂ ਤੁਹਾਡਾ ਸਫ਼ਾ ਇਸਦੇ ਬਿਨਾਂ ਹੀ ਸਾਂਭਿਆ ਜਾਵੇਗਾ।",
'missingcommenttext' => 'ਹੇਠਾਂ ਇੱਕ ਟਿੱਪਣੀ ਦਿਓ।',
'summary-preview' => 'ਸੰਖੇਪ ਝਲਕ:',
'subject-preview' => 'ਵਿਸ਼ਾ/ਹੈੱਡਲਾਈਨ ਝਲਕ:',
'blockedtitle' => 'ਯੂਜ਼ਰ ਬਲਾਕ ਕੀਤਾ ਗਿਆ',
+'blockedtext' => "'''ਤੁਹਾਡੇ ਮੈਂਬਰ-ਨਾਂ ਜਾਂ IP ਪਤੇ ਉੱਤੇ ਪਾਬੰਦੀ ਲੱਗ ਚੁੱਕੀ ਹੈ।'''
+
+ਪਾਬੰਦੀ $1 ਨੇ ਲਾਈ ਹੈ।
+ਦਿੱਤਾ ਗਿਆ ਕਾਰਨ ਇਹ ਹੈ, ''$2''।
+
+* ਪਾਬੰਦੀ ਸ਼ੁਰੂ: $8
+* ਪਾਬੰਦੀ ਖ਼ਤਮ: $6
+* ਪਾਬੰਦੀ ਲਾਉਣ ਵਾਲ਼ੇ ਦਾ ਇਰਾਦਾ: $7
+
+ਪਾਬੰਦੀ ਬਾਰੇ ਚਰਚਾ ਕਰਨ ਲਈ ਤੁਸੀਂ $1 ਜਾਂ ਕਿਸੇ ਹੋਰ
+[[{{MediaWiki:Grouppage-
+sysop}}|administrator]] ਨਾਲ਼ ਰਾਬਤਾ ਕਰ ਸਕਦੇ ਹੋ।
+ਤੁਸੀਂ 'ਇਸ ਮੈਂਬਰ ਨੂੰ ਈ-ਮੇਲ ਭੇਜੋ' ਸਹੂਲਤ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰ ਸਕਦੇ ਜੇ ਤੁਹਾਡੀਆਂ [[Special:Preferences|ਖਾਤਾ ਪਸੰਦਾਂ]] ਵਿਚ ਇੱਕ ਸਹੀ ਈ-ਮੇਲ ਪਤਾ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ ਅਤੇ ਜੇ ਤੁਹਾਡੇ ਇਸਨੂੰ ਵਰਤਣ ਤੇ ਪਾਬੰਦੀ ਹੈ।
+ਤੁਹਾਡਾ ਚਾਲੂ IP ਪਤਾ $3 ਹੈ,
+ਅਤੇ ਪਾਬੰਦੀ ਪਤਾ #$5 ਹੈ।
+ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੇ ਕਿਸੇ ਵੀ ਸਵਾਲ ਜਾਂ ਪੁੱਛ-ਗਿੱਛ ਵਿਚ ਇਹ ਉੱਪਰਲੀ ਤਫ਼ਸੀਲ ਜ਼ਰੂਰ ਸ਼ਾਮਲ ਕਰੋ।",
+'blockednoreason' => 'ਕੋਈ ਕਾਰਨ ਨਹੀਂ ਦੱਸਿਆ ਗਿਆ',
'whitelistedittext' => 'ਪੇਜ ਸੋਧਣ ਲਈ ਤੁਹਾਨੂੰ $1 ਕਰਨਾ ਪਵੇਗਾ।',
+'confirmedittext' => 'ਸਫ਼ਿਆਂ ਨੂੰ ਸੋਧਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਨੂੰ ਆਪਣਾ ਈ-ਮੇਲ ਪਤਾ ਤਸਦੀਕ ਕਰਨਾ ਪਵੇਗਾ।
+ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੀਆਂ [[Special:Preferences|ਖਾਤਾ ਪਸੰਦਾ]] ਜ਼ਰੀਏ ਸਹੀ ਈ-ਮੇਲ ਪਤਾ ਦਿਓ ਅਤੇ ਤਸਦੀਕ ਕਰੋ।',
'nosuchsectiontitle' => 'ਇੰਝ ਦਾ ਕੋਈ ਸ਼ੈਕਸ਼ਨ ਨਹੀਂ ਹੈ।',
+'nosuchsectiontext' => 'ਤੁਸੀਂ ਨਾ-ਮੌਜੂਦ ਸੈਕਸ਼ਨ ਨੂੰ ਸੋਧਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਹੈ।
+ਸ਼ਾਇਦ ਤੁਹਾਡੇ ਸਫ਼ੇ ਨੂੰ ਵੇਖਣ ਦੇ ਦੌਰਾਨ ਇਹ ਮਿਟਾਇਆ ਜਾਂ ਇਸਦਾ ਸਿਰਲੇਖ ਬਦਲਿਆ ਜਾ ਚੁੱਕਾ ਹੈ।',
'loginreqtitle' => 'ਲਾਗਇਨ ਚਾਹੀਦਾ ਹੈ',
'loginreqlink' => 'ਲਾਗਇਨ',
'loginreqpagetext' => 'ਹੋਰ ਪੇਜ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ $1 ਕਰਨਾ ਪਵੇਗਾ।',
'accmailtitle' => 'ਪਾਸਵਰਡ ਭੇਜਿਆ।',
'accmailtext' => '"$1" ਲਈ ਪਾਸਵਰਡ $2 ਨੂੰ ਭੇਜਿਆ ਗਿਆ।',
'newarticle' => '(ਨਵਾਂ)',
-'newarticletext' => "ਤੁਸੀਂ ਕਿਸੇ ਐਸੇ ਲਿੰਕ ਰਾਹੀਂ ਉਸ ਪੰਨੇ ਤੇ ਪੁੱਜੇ ਹੋ ਜੋ ਅਜੇ ਬਣਾਇਆ ਨਹੀਂ ਗਿਆ।
-ਪੰਨਾ ਬਨਾਉਣ ਲਈ ਹੇਠ ਦਿੱਤੇ ਖਾਨੇ ਵਿਚ ਪਾਠ ਲਿਖੋ।(ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ [[{{MediaWiki:Helppage}}|ਮੱਦਦ ਪੰਨਾ]] ਦੇਖੋ)
-ਜੇ ਤੁਸੀਂ ਇੱਥੇ ਗਲਤੀ ਨਾਲ ਆਏ ਹੋ ਤਾਂ ਆਪਣੇ ਬਰਾਊਜ਼ਰ ਦੇ ਬੈਕ ('''back''') ਬਟਨ ਪਰ ਕਲਿਕ ਕਰੋ।",
-'noarticletext' => 'ਫਿਲਹਾਲ ਇਸ ਪੰਨੇ ਤੇ ਐਸਾ ਕੋਈ ਪਾਠ ਨਹੀਂ ਹੈ।ਤੁਸੀਂ ਦੂਸਰੇ ਪੰਨਿਆਂ ਤੇ [[Special:Search/{{PAGENAME}}|ਇਸ ਪਾਠ ਦੀ ਖੋਜ]] ਕਰ ਸਕਦੇ ਹੋ।,<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ਸੰਭੰਧਿਤ ਖਾਤਿਆਂ ਵਿਚ ਖੋਜ ਸਕਦੇ ਹੋ],
-ਯਾ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ਇਸ ਪੰਨੇ ਨੂੰ ਸੰਪਾਦਨ ਕਰ ਸਕਦੇ ਹੋ]</span>।',
-'noarticletext-nopermission' => '↓
-ਫਿਲਹਾਲ ਇਸ ਪੰਨੇ ਤੇ ਐਸਾ ਕੋਈ ਪਾਠ ਨਹੀਂ ਹੈ।ਤੁਸੀਂ ਦੂਸਰੇ ਪੰਨਿਆਂ ਤੇ [[Special:Search/{{PAGENAME}}|ਇਸ ਪਾਠ ਦੀ ਖੋਜ]] ਕਰ ਸਕਦੇ ਹੋ।,<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ਸੰਬੰਧਿਤ ਖਾਤਿਆਂ ਵਿਚ ਖੋਜ ਸਕਦੇ ਹੋ],
-ਯਾ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ਇਸ ਪੰਨੇ ਨੂੰ ਸੰਪਾਦਨ ਕਰ ਸਕਦੇ ਹੋ]</span>।',
+'newarticletext' => "ਤੁਸੀਂ ਕਿਸੇ ਐਸੇ ਲਿੰਕ ਰਾਹੀਂ ਇਸ ਸਫ਼ੇ ’ਤੇ ਪੁੱਜੇ ਹੋ ਜੋ ਹਾਲੇ ਬਣਾਇਆ ਨਹੀਂ ਗਿਆ।
+ਸਫ਼ਾ ਬਣਾਉਣ ਲਈ ਹੇਠ ਦਿੱਤੇ ਖ਼ਾਨੇ ਵਿਚ ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ। (ਹੋਰ ਮਦਦ ਲਈ [[{{MediaWiki:Helppage}}|ਮਦਦ ਸਫ਼ਾ]] ਵੇਖੋ)
+ਜੇ ਤੁਸੀਂ ਗ਼ਲਤੀ ਨਾਲ਼ ਇੱਥੇ ਆਏ ਹੋ ਤਾਂ ਆਪਣੇ ਬਰਾਊਜ਼ਰ ਦੇ ''ਪਿੱਛੇ'' (back) ਬਟਨ ’ਤੇ ਕਲਿੱਕ ਕਰੋ।",
+'anontalkpagetext' => "----''ਇਹ ਇਕ ਗੁਮਨਾਮ ਮੈਂਬਰ ਲਈ ਇਕ ਚਰਚਾ ਸਫ਼ਾ ਹੈ ਜਿਸਨੇ ਹਾਲੇ ਖਾਤਾ ਨਹੀ ਬਣਾਇਆ ਜਾਂ ਉਸਨੂੰ ਵਰਤ ਨਹੀਂ ਰਿਹਾ।
+ਇਸ ਵਾਸਤੇ ਸਾਡੇ ਕੋਲ ਉਸਨੂੰ ਪਛਾਨਣ ਲਈ IP ਪਤਾ ਹੈ।
+ਇਕ IP ਪਤਾ ਕਈ ਵਰਤਣ ਵਾਲ਼ਿਆਂ ਦੁਆਰਾ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।
+ਜੇ ਤੁਸੀਂ ਇੱਕ ਗੁਮਨਾਮ ਮੈਂਬਰ ਹੋ ਅਤੇ ਸਮਝਦੇ ਹੋ ਕਿ ਇਹ ਟਿੱਪਣੀਆਂ ਤੁਹਾਡੇ ਲਈ ਹਨ ਤਾਂ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਹੋਰਾਂ ਗੁਮਨਾਮ ਮੈਂਬਰਾਂ ਨਾਲ਼ ਪੈਦਾ ਹੋਣ ਵਾਲ਼ੀ ਉਲਝਣ ਤੋਂ ਬਚਣ ਲਈ [[Special:UserLogin/signup|ਖਾਤਾ ਬਣਾਓ]] ਜਾਂ [[Special:UserLogin|ਲਾਗਇਨ ਕਰੋ]]।''",
+'noarticletext' => 'ਫ਼ਿਲਹਾਲ ਇਸ ਸਫ਼ੇ ’ਤੇ ਕੋਈ ਲਿਖਤ ਨਹੀਂ ਹੈ। ਤੁਸੀਂ ਦੂਜੇ ਸਫ਼ਿਆਂ ’ਤੇ [[Special:Search/{{PAGENAME}}|ਇਸ ਸਿਰਲੇਖ ਦੀ ਖੋਜ]] ਕਰ ਸਕਦੇ ਹੋ, <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ਸਬੰਧਿਤ ਚਿੱਠੇ ਖੋਜ] ਸਕਦੇ ਹੋ ਜਾਂ ਇਸ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ਸਫ਼ੇ ਵਿਚ ਲਿਖ] ਸਕਦੇ ਹੋ</span>।',
+'noarticletext-nopermission' => 'ਫ਼ਿਲਹਾਲ ਇਸ ਸਫ਼ੇ ’ਤੇ ਕੋਈ ਲਿਖਤ ਨਹੀਂ ਹੈ। ਤੁਸੀਂ ਦੂਸਰੇ ਸਫ਼ਿਆਂ ਤੇ [[Special:Search/{{PAGENAME}}|ਇਸ ਪਾਠ ਦੀ ਖੋਜ]] ਕਰ ਸਕਦੇ ਹੋ, ਸਬੰਧਤ <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ਚਿੱਠੇ] ਖੋਜ ਸਕਦੇ ਹੋ, ਜਾਂ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ਇਸ ਸਫ਼ੇ ਵਿਚ ਲਿਖ] ਸਕਦੇ ਹੋ</span>।',
+'userpage-userdoesnotexist' => 'ਮੈਂਬਰ ਖਾਤਾ "$1" ਰਜਿਸਟਰ ਨਹੀਂ ਹੈ।
+ਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਬਣਾਉਣਾ/ਸੋਧਣਾ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਮਿਰਬਾਨੀ ਕਰਕੇ ਜਾਂਚ ਕਰ ਲਓ।',
+'userpage-userdoesnotexist-view' => 'ਮੈਂਬਰ ਖਾਤਾ "$1" ਰਜਿਸਟਰ ਨਹੀਂ ਹੈ।',
+'blocked-notice-logextract' => 'ਇਹ ਮੈਂਬਰ ਇਸ ਵੇਲ਼ੇ ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੈ।
+ਹਵਾਲੇ ਲਈ ਪਾਬੰਦੀ ਚਿੱਠੇ ਦਾ ਨਵਾਂ ਦਾਖ਼ਲਾ ਹੇਠ ਦਿੱਤਾ ਗਿਆ ਹੈ:',
+'usercssyoucanpreview' => "'''ਟੋਟਕਾ:''' ਆਪਣੇ ਨਵੇਂ CSS ਸਫ਼ੇ ਨੂੰ ਸਾਂਭਣ ਤੋਂ ਪਹਿਲਾਂ ਪਰਖ ਕਰਨ ਲਈ \"{{int:showpreview}}\" ਬਟਨ ਵਰਤੋ।",
+'userjsyoucanpreview' => "'''ਟੋਟਕਾ:''' ਆਪਣੀ ਜਾਵਾਸਕ੍ਰਿਪਟ ਨੂੰ ਸਾਂਭਣ ਤੋਂ ਪਹਿਲਾਂ ਪਰਖ ਕਰਨ ਲਈ \"{{int:showpreview}}\" ਬਟਨ ਵਰਤੋ।",
+'usercsspreview' => "'''ਯਾਦ ਰੱਖੋ ਤੁਸੀਂ ਆਪਣੀ ਮੈਂਬਰ CSS ਦੀ ਸਿਰਫ਼ ਇਕ ਝਲਕ ਵੇਖ ਰਹੇ ਹੋ।'''
+'''ਇਹ ਹਾਲੇ ਸਾਂਭੀ ਨਹੀਂ ਗਈ ਹੈ!'''",
+'userjspreview' => "'''ਯਾਦ ਰੱਖੋ ਤੁਸੀਂ ਆਪਣੀ ਮੈਂਬਰ ਜਾਵਾਸਕ੍ਰਿਪਟ ਦੀ ਸਿਰਫ਼ ਇਕ ਪਰਖ/ਝਲਕ ਵੇਖ ਰਹੇ ਹੋ।'''
+'''ਇਹ ਹਾਲੇ ਸਾਂਭੀ ਨਹੀਂ ਗਈ ਹੈ!'''",
+'sitecsspreview' => "'''ਯਾਦ ਰੱਖੋ ਤੁਸੀਂ ਇਸ CSS ਦੀ ਸਿਰਫ਼ ਇਕ ਝਲਕ ਵੇਖ ਰਹੇ ਹੋ।'''
+'''ਇਹ ਹਾਲੇ ਸਾਂਭੀ ਨਹੀਂ ਗਈ ਹੈ!'''",
+'sitejspreview' => "'''ਯਾਦ ਰੱਖੋ ਤੁਸੀਂ ਇਸ ਜਾਵਾਸਕ੍ਰਿਪਟ ਕੋਡ ਦੀ ਸਿਰਫ਼ ਇਕ ਝਲਕ ਵੇਖ ਰਹੇ ਹੋ।'''
+'''ਇਹ ਹਾਲੇ ਸਾਂਭੀ ਨਹੀਂ ਗਈ ਹੈ!'''",
'updated' => '(ਅੱਪਡੇਟ)',
'note' => "'''ਨੋਟ:'''",
-'previewnote' => 'ਇਹ ਸਿਰਫ਼ ਇੱਕ ਝਲਕ ਹੈ; ਬਦਲਾਅ ਹਾਲੇ ਸੰਭਾਲੇ ਨਹੀਂ ਗਏ ਹਨ!',
+'previewnote' => 'ਯਾਦ ਰੱਖੋ ਇਹ ਸਿਰਫ਼ ਇੱਕ ਝਲਕ ਹੈ; ਤੁਹਾਡੀਆਂ ਤਬਦੀਲੀਆਂ ਹਾਲੇ ਸਾਂਭੀਆਂ ਨਹੀਂ ਗਈਆਂ!',
+'continue-editing' => 'ਸੋਧਣਾ ਜਾਰੀ ਰੱਖੋ',
+'previewconflict' => 'ਇਹ ਝਲਕ ਲਿਖਤ ਦਾ ਓਹ ਅਕਸ ਪੇਸ਼ ਕਰਦੀ ਹੈ ਜਿਵੇਂ ਓਹ ਤੁਹਾਡੇ ਸਾਂਭੇ ਜਾਣ ਤੋਂ ਬਾਅਦ ਦਿੱਸੇਗਾ।',
'editing' => '$1 ਸੋਧਿਆ ਜਾ ਰਿਹਾ ਹੈ',
-'editingsection' => '$1 (ਸ਼ੈਕਸ਼ਨ) ਸੋਧ',
+'creating' => '$1 ਬਣਾ ਰਹੇ ਹੋ',
+'editingsection' => '$1 ਜ਼ੇਰੇ ਸੁਧਾਈ ਹੈ (ਸ਼ੈਕਸ਼ਨ)',
'editingcomment' => '$1 (ਟਿੱਪਣੀ) ਸੋਧ',
'editconflict' => 'ਅਪਵਾਦ ਟਿੱਪਣੀ: $1',
'yourtext' => 'ਤੁਹਾਡਾ ਟੈਕਸਟ',
'storedversion' => 'ਸੰਭਾਲਿਆ ਵਰਜਨ',
'yourdiff' => 'ਅੰਤਰ',
-'templatesused' => 'ਇਸ ਸਫੇ ਤੇ ਪ੍ਰ੍ਯੋਗਿਤ {{PLURAL:$1|ਫਰਮਾ|ਫਰਮੇ}}:',
+'longpageerror' => "'''ਗ਼ਲਤੀ: ਤੁਹਾਡੀ ਪੇਸ਼ ਕੀਤੀ ਲਿਖਤ {{PLURAL:$1|ਇੱਕ ਕਿਲੋਬਾਈਟ|$1 ਕਿਲੋਬਾਈਟ}} ਦੀ ਹੈ ਜੋ ਕਿ {{PLURAL:$2|ਇੱਕ ਕਿਲੋਬਾਈਟ|$2 ਕਿਲੋਬਾਈਟ}} ਦੇ ਵੱਧ ਤੋਂ ਵੱਧ ਅਕਾਰ ਤੋਂ ਜ਼ਿਆਦਾ ਹੈ।'''
+ਇਹ ਸਾਂਭੀ ਨਹੀਂ ਜਾ ਸਕਦੀ।",
+'readonlywarning' => "'''ਖ਼ਬਰਦਾਰ: ਡੈਟਾਬੇਸ ਰੱਖ-ਰਖਾਵ ਦੇ ਕਰਕੇ ਤਾਲਾ-ਬੱਧ ਹੈ ਇਸ ਕਰਕੇ ਤੁਸੀਂ ਹੁਣੇ ਆਪਣੀ ਤਬਦੀਲੀ ਨਹੀਂ ਸਾਂਭ ਸਕਦੇ।'''
+ਸ਼ਾਇਦ ਤੁਸੀਂ ਇਸ ਲਿਖਤ ਨੂੰ ਕੱਟ ਅਤੇ ਪੇਸਟ ਕਰ ਕੇ ਇਕ ਫ਼ਾਈਲ ਵਜੋਂ ਬਾਅਦ ਵਿਚ ਵਰਤਣ ਲਈ ਸਾਂਭਣਾ ਚਾਹੋਗੇ।
+
+ਜਿਹੜੇ ਪ੍ਰਬੰਧਕ ਨੇ ਇਸਨੂੰ ਤਾਲਾ ਲਾਇਆ ਹੈ ਉਸਦਾ ਕਹਿਣਾ ਹੈ ਕਿ: $1",
+'protectedpagewarning' => "'''ਖ਼ਬਰਦਾਰ: ਇਹ ਸਫ਼ਾ ਸੁਰੱਖਿਅਤ ਹੈ ਜਿਸ ਕਰਕੇ ਸਿਰਫ਼ ਐਡਮਨਿਸਟ੍ਰੇਟਰ ਹੱਕ ਵਾਲ਼ੇ ਮੈਂਬਰ ਹੀ ਇਸ ਨੂੰ ਸੋਧ ਸਕਦੇ ਹਨ।'''
+ਚਿੱਠੇ ਦਾ ਨਵਾਂ ਦਾਖ਼ਲਾ ਹਵਾਲੇ ਲਈ ਹੇਠਾਂ ਦਿੱਤਾ ਗਿਆ ਹੈ:",
+'semiprotectedpagewarning' => "'''ਨੋਟ:''' ਇਹ ਸਫ਼ਾ ਸੁਰੱਖਿਅਤ ਹੈ ਤਾਂ ਕਿ ਸਿਰਫ਼ ਰਜਿਸਟਰ ਹੋਏ ਮੈਂਬਰ ਹੀ ਇਸ ਨੂੰ ਸੋਧ ਸਕਣ।
+ਚਿੱਠੇ ਵਿਚਲਾ ਨਵਾਂ ਦਾਖ਼ਲਾ ਹਵਾਲੇ ਲਈ ਹੇਠਾਂ ਦਿੱਤਾ ਗਿਆ ਹੈ:",
+'titleprotectedwarning' => "'''ਖ਼ਬਰਦਾਰ: ਇਹ ਸਫ਼ਾ ਸੁਰੱਖਿਅਤ ਹੈ ਸੋ ਇਸਨੂੰ ਬਣਾਉਣ ਲਈ [[Special:ListGroupRights|ਖ਼ਾਸ ਹੱਕਾਂ]] ਦੀ ਲੋੜ ਹੈ।'''
+ਚਿੱਠੇ ਦਾ ਨਵਾਂ ਦਾਖ਼ਲਾ ਹਵਾਲੇ ਲਈ ਹੇਠਾਂ ਦਿੱਤਾ ਗਿਆ ਹੈ:",
+'templatesused' => 'ਇਸ ਸਫੇ ’ਤੇ {{PLURAL:$1|ਵਰਤਿਆ ਸਾਂਚਾ|ਵਰਤੇ ਸਾਂਚੇ}}:',
'templatesusedpreview' => "{{PLURAL:$1|ਟੈਪਲੇਟ|ਟੈਪਲੇਟ}} ਇਹ ਝਲਕ 'ਚ ਵਰਤੇ ਜਾਂਦੇ ਹਨ:",
'templatesusedsection' => 'ਇਹ ਸ਼ੈਕਸ਼ਨ ਵਿੱਚ ਟੈਪਲੇਟ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ:',
'template-protected' => '(ਸੁਰੱਖਿਅਤ)',
-'template-semiprotected' => '(ਅਰਧ-ਸੁਰੱਖਿਅਤ)',
+'template-semiprotected' => '(ਨੀਮ-ਸੁਰੱਖਿਅਤ)',
+'hiddencategories' => 'ਇਹ ਸਫ਼ਾ {{PLURAL:$1|1 ਲੁਕਵੀਂ ਸ਼੍ਰੇਣੀ|
+$1 ਲੁਕਵੀਆਂ ਸ਼੍ਰੇਣੀਆਂ}} ਦਾ ਮੈਂਬਰ ਹੈ:',
+'nocreatetitle' => 'ਸਫ਼ਾ ਬਣਾਉਣ ਦੀ ਹੱਦ ਹੈ',
+'nocreatetext' => '{{SITENAME}} ਨੇ ਨਵੇਂ ਸਫ਼ੇ ਬਣਾਉਣ ਤੇ ਰੋਕ ਲਾਈ ਹੋਈ ਹੈ।
+ਤੁਸੀਂ ਵਾਪਸ ਜਾ ਕੇ ਮੌਜੂਦਾ ਸਫ਼ੇ ਸੋਧ ਸਕਦੇ ਹੋ ਜਾਂ [[Special:UserLogin|ਲਾਗਇਨ ਜਾਂ ਖਾਤਾ ਬਣਾ]] ਸਕਦੇ ਹੋ।',
+'nocreate-loggedin' => 'ਤੁਹਾਨੂੰ ਨਵੇਂ ਸਫ਼ੇ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
'permissionserrors' => 'ਅਧਿਕਾਰ ਗਲਤੀਆਂ',
'permissionserrorstext' => 'ਤੁਹਾਨੂੰ ਇੰਝ ਕਰਨ ਦੇ ਅਧਿਕਾਰ ਨਹੀਂ ਹਨ। ਹੇਠ ਦਿੱਤੇ {{PLURAL:$1|ਕਾਰਨ|ਕਾਰਨ}} ਨੇ:',
-'recreate-moveddeleted-warn' => "'''ਖ਼ਬਰਦਾਰ: ਤੁਸੀਂ ਐਸ ਪੰਨਾ ਰਚ ਰਹੇ ਹੋ ਜਿਸ ਨੂੰ ਪਹਿਲੇ ਹਟਾਇਆ ਜਾ ਚੁੱਕ ਹੈ।'''
-ਖਿਆਲ ਕਰੋ ਕਿ ਕੀ ਇਸ ਪੰਨੇ ਦਾ ਕਾਇਮ ਰਹਿਣਾ ਠੀਕ ਹੈ।
-ਇਸ ਪੰਨੇ ਨੂੰ ਹਟਾਉਣ ਯਾ ਜਘ੍ਹਾ ਬਦਲੀ ਦਾ ਚਿੱਠਾ ਹੇਠਾਂ ਦਿੱਤਾ ਹੈ।",
-'moveddeleted-notice' => 'ਇਹ ਪੰਨਾ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।
-ਪੰਨੇ ਦੇ ਹਟਾਣ ਯਾ ਜਗ੍ਹਾ ਬਦਲੀ ਦਾ ਚਿੱਠਾ,ਹਵਾਲੇ ਲਈ ਹੇਠ ਦਿੱਤਾ ਹੈ।',
+'permissionserrorstext-withaction' => '{{PLURAL:$1|ਇਸ ਕਾਰਨ|ਇਹਨਾਂ ਕਾਰਨਾਂ}} ਕਰਕੇ ਤੁਹਾਨੂੰ $2 ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ:',
+'recreate-moveddeleted-warn' => "'''ਖ਼ਬਰਦਾਰ:
+ਤੁਸੀਂ ਐਸਾ ਸਫ਼ਾ ਬਣਾ ਰਹੇ ਹੋ ਜੋ ਪਹਿਲਾਂ ਮਿਟਾਇਆ ਜਾ ਚੁੱਕ ਹੈ।'''
+
+ਖ਼ਿਆਲ ਕਰੋ ਕਿ ਕੀ ਇਸ ਸਫ਼ੇ ਦਾ ਕਾਇਮ ਰਹਿਣਾ ਠੀਕ ਹੈ।
+ਇਸਨੂੰ ਮਿਟਾਉਣ ਜਾਂ ਸਿਰਲੇਖ ਬਦਲੀ ਦਾ ਚਿੱਠਾ ਹੇਠਾਂ ਦਿੱਤਾ ਗਿਆ ਹੈ।",
+'moveddeleted-notice' => 'ਇਹ ਸਫ਼ਾ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।
+ਇਸਦੇ ਮਿਟਾਉਣ ਜਾਂ ਸਿਰਲੇਖ ਬਦਲੀ ਦਾ ਚਿੱਠਾ ਹਵਾਲੇ ਲਈ ਹੇਠ ਦਿੱਤਾ ਗਿਆ ਹੈ।',
+'log-fulllog' => 'ਪੂਰਾ ਚਿੱਠਾ ਵੇਖੋ',
+'edit-gone-missing' => 'ਸਫ਼ਾ ਅਪਡੇਟ ਨਹੀਂ ਹੋ ਸਕਿਆ।
+ਲਗਦਾ ਹੈ ਮਿਟਾਇਆ ਜਾ ਚੁੱਕਾ ਹੈ।',
+'edit-no-change' => 'ਤੁਹਾਡੀ ਸੋਧ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਦਿੱਤੀ ਗਈ ਹੈ ਕਿਉਂਕਿ ਲਿਖਤ ਵਿਚ ਕੋਈ ਤਬਦੀਲੀ ਨਹੀਂ ਕੀਤੀ ਗਈ।',
+'edit-already-exists' => 'ਨਵਾਂ ਸਫ਼ਾ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਿਆ।
+ਇਹ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।',
# Parser/template warnings
-'post-expand-template-inclusion-warning' => "'''ਖ਼ਬਰਦਾਰ: ਸੰਚੇ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ।ਕੁਝ ਸੰਚੇ ਛੁਟ ਜਾਣਗੇ।",
-'post-expand-template-inclusion-category' => 'ਉਹ ਪੰਨੇ ਜਿਥੇ ਸੰਚਿਆਂ ਦਾ ਅਕਾਰ ਨਿਣਮਿਤ ਹੱਦ ਤੌਂ ਵੱਧ ਹੈ।',
-'post-expand-template-argument-warning' => "'''ਖ਼ਬਰਦਾਰ'''ਇਸ ਸਫ਼ੇ ਤੇ ਕਿਸੇ ਫ਼ਰਮੇ ਵਿਚ ਘੱਟੋ ਘੱਟ ਇਕ ਸਁਘਟਕ ਐਸਾ ਹੈ ਜਿਸ ਦਾ ਵਿਸਤ੍ਰਿਤ ਰੂਪ ਬਹੁਤ ਵੱਡਾ ਹੈ।ਐਸੇ ਸਁਘਟਕਾਂ ਨੂਁ ਛੱਡ ਦਿੱਤਾ ਗਿਆ ਹੈ।",
+'post-expand-template-inclusion-warning' => "'''ਖ਼ਬਰਦਾਰ:''' ਟੈਂਪਲੇਟਾਂ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਕੁਝ ਟੈਂਪਲੇਟ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋਣਗੇ।",
+'post-expand-template-inclusion-category' => 'ਓਹ ਸਫ਼ੇ ਜਿੱਥੇ ਟੈਂਪਲੇਟਾਂ ਦੇ ਸ਼ਾਮਲ ਕਰਨ ਦਾ ਅਕਾਰ ਹੱਦੋਂ ਵਧ ਗਿਆ ਹੈ',
+'post-expand-template-argument-warning' => "'''ਖ਼ਬਰਦਾਰ:'''
+ਇਸ ਸਫ਼ੇ ਤੇ ਘੱਟੋ ਘੱਟ ਇਕ ਐਸੀ ਟੈਂਪਲੇਟ ਬਹਿਸ ਹੈ ਜਿਸ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਐਸੀਆਂ ਬਹਿਸਾਂ ਨੂੰ ਛੱਡ ਦਿੱਤਾ ਗਿਆ ਹੈ।",
'post-expand-template-argument-category' => 'ਐਸੇ ਸਫ਼ੇ ਜਿਨ੍ਹਾਂ ਵਿਚ ਫ਼ਰਮੇ ਦੇ ਸਁਘਟਕ ਛੁੱਟ ਗਏ ਹਨ ।',
+'parser-template-loop-warning' => 'ਸਾਂਚੇ ਦਾ ਲੂਪ ਲੱਭਿਆ: [[$1]]',
+
+# "Undo" feature
+'undo-success' => 'ਇਹ ਸੋਧ ਨਕਾਰੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਇਹ ਤਸਦੀਕ ਕਰਨ ਲਈ ਹੇਠਲੀ ਤੁਲਨਾ ਜਾਂਚੋ ਕਿ ਇਹ ਓਹੀ ਹੈ ਜੋ ਤੁਸੀਂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ ਅਤੇ ਫਿਰ ਸੋਧ ਨਕਾਰਨ ਲਈ ਤਬਦੀਲੀਆਂ ਸਾਂਭ ਦਿਓ।',
+'undo-norev' => 'ਸੋਧ ਨਕਾਰੀ ਨਹੀਂ ਜਾ ਸਕਦੀ ਕਿਉਂਕਿ ਇਹ ਮੌਜੂਦ ਨਹੀਂ ਜਾਂ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ।',
+'undo-summary' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|ਗੱਲ-ਬਾਤ]]) ਦੀ ਸੋਧ $1 ਨਕਾਰੀ',
# Account creation failure
'cantcreateaccounttitle' => 'ਅਕਾਊਂਟ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ',
+'cantcreateaccount-text' => "[[User:$3|$3]] ਨੇ ਇਸ IP ਪਤੇ ('''$1''') ਤੋਂ ਖਾਤਾ ਬਣਾਉਣ ਤੇ ਪਾਬੰਦੀ ਲਾਈ ਹੈ।
+
+$3 ਨੇ ਕਾਰਨ ਇਹ ਦੱਸਿਆ ਹੈ, ''$2''",
# History pages
-'viewpagelogs' => 'ਇਸ ਪੇਜ ਦੇ ਲਈ ਲਾਗ ਵੇਖੋ',
+'viewpagelogs' => 'ਇਸ ਸਫ਼ੇ ਲਈ ਚਿੱਠੇ ਵੇਖੋ',
+'nohistory' => 'ਇਸ ਸਫ਼ੇ ਦਾ ਕੋਈ ਸੋਧ ਅਤੀਤ ਨਹੀਂ ਹੈ।',
'currentrev' => 'ਮੌਜੂਦਾ ਰੀਵਿਜ਼ਨ',
-'currentrev-asof' => '$1 ਦà©\87 ਸਮà©\87à¨\82 ਦਾ ਵਰਨਣ',
-'revisionasof' => '$1 ਦà©\87 ਰà©\80ਵਿà¨\9c਼ਨ ਵਾà¨\82à¨\97',
-'revision-info' => ' $1ਦ ਬਦਲਾਅ $2ਦਵਾਰਾ ਕੀਤਾ ਹੋਇਆ',
+'currentrev-asof' => '$1 ਮà©\81ਤਾਬà¨\95 ਸਠਤà©\8bà¨\82 ਨਵਾà¨\82 ਰà©\80ਵਿà¨\9c਼ਨ',
+'revisionasof' => '$1 ਦਾ ਰà©\80ਵਿà¨\9c਼ਨ',
+'revision-info' => '$2 ਦਾ ਬਣਾਇਆ $1 ਦਾ ਰੀਵਿਜ਼ਨ',
'previousrevision' => '←ਪੁਰਾਣਾ ਰੀਵਿਜ਼ਨ',
'nextrevision' => 'ਨਵਾਂ ਰੀਵਿਜ਼ਨ→',
-'currentrevisionlink' => 'ਮà©\8cà¨\9cà©\82ਦਾ ਰੀਵਿਜ਼ਨ',
+'currentrevisionlink' => 'ਸਠਤà©\8b ਨਵਾà¨\82 ਰੀਵਿਜ਼ਨ',
'cur' => 'ਮੌਜੂਦਾ',
'next' => 'ਅੱਗੇ',
-'last' => 'ਆਖਰੀ',
+'last' => 'à¨\86à¨\96਼ਰà©\80',
'page_first' => 'ਪਹਿਲਾਂ',
'page_last' => 'ਆਖਰੀ',
-'history-fieldset-title' => 'ਇਤਿਹਾਸ ਤੇ ਇਕ ਨਜ਼ਰ ਮਾਰੋ ।',
-'history-show-deleted' => 'ਕੇਵਲ ਮਿਟਾਏ ਗਏ',
-'histfirst' => 'ਸਭ ਤੋਂ ਪਹਿਲਾਂ',
+'histlegend' => "ਫ਼ਰਕ ਵੇਖੋ:
+ਮੁਕਾਬਲਾ ਕਰਨ ਲਈ ਰੀਵਿਜ਼ਨਾਂ ਦੇ ਰੇਡੀਓ ਬਟਨਾਂ ਵਿਚ ਨਿਸ਼ਾਨ ਲਾਓ ਅਤੇ ਜਾਓ ਜਾਂ ਸਭ ਤੋਂ ਥੱਲੇ ਵਾਲ਼ੇ ਬਟਨ ਤੇ ਕਲਿੱਕ ਕਰੋ। <br />
+ਲੈਜਅੰਡ:
+'''({{int:cur}})''' = ਨਵੇਂ ਰੀਵਿਜ਼ਨ ਨਾਲ਼ੋਂ ਫ਼ਰਕ, '''({{int:last}})''' = ਆਖ਼ਰੀ ਰੀਵਿਜ਼ਨ ਨਾਲ਼ੋਂ ਫ਼ਰਕ, '''({{int:minoreditletter}})''' = ਛੋਟੀ ਸੋਧ।",
+'history-fieldset-title' => 'ਅਤੀਤ ’ਤੇ ਨਜ਼ਰ ਮਾਰੋ',
+'history-show-deleted' => 'ਸਿਰਫ਼ ਮਿਟਾਏ ਗਏ',
+'histfirst' => 'ਸਭ ਤੋਂ ਪਹਿਲਾ',
'histlast' => 'ਸਭ ਤੋਂ ਨਵਾਂ',
'historysize' => '($1 ਬਾਈਟ)',
'historyempty' => '(ਖਾਲੀ)',
# Revision feed
'history-feed-title' => 'ਰੀਵਿਜ਼ਨ ਅਤੀਤ',
-'history-feed-item-nocomment' => '$1 ਤੋਂ $2 ਵੱਜੇ',
+'history-feed-description' => 'ਵਿਕੀ ਤੇ ਇਸ ਸਫ਼ੇ ਦਾ ਰੀਵਿਜ਼ਨ ਅਤੀਤ',
+'history-feed-item-nocomment' => '$1 ਤੋਂ $2 ’ਤੇ',
+'history-feed-empty' => 'ਦਰਖ਼ਾਸਤਸ਼ੁਦਾ ਸਫ਼ਾ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।
+ਸ਼ਾਇਦ ਇਸਨੂੰ ਵਿਕੀ ਤੋਂ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਾਮ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ।
+ਵਿਕੀ ਦੇ ਨਵੇਂ ਮੁਨਾਸਿਬ ਸਫ਼ਿਆਂ ਵਿਚ [[Special:Search|ਲੱਭਣ]] ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।',
# Revision deletion
'rev-deleted-comment' => '(ਟਿੱਪਣੀ ਹਟਾਈ)',
'rev-deleted-user' => '(ਯੂਜ਼ਰ ਨਾਂ ਹਟਾਇਆ)',
'rev-deleted-event' => '(ਐਂਟਰੀ ਹਟਾਈ)',
-'rev-delundel' => 'ਵੇਖਾਓ/ਓਹਲੇ',
+'rev-deleted-user-contribs' => '[ਮੈਂਬਰ-ਨਾਂ ਜਾਂ IP ਪਤਾ ਹਟਾਇਆ - ਸੋਧ ਯੋਗਦਾਨਾਂ ਵਿਚੋਂ ਓਹਲੇ ਕੀਤੀ]',
+'rev-deleted-text-permission' => "ਸਫ਼ੇ ਦੀ ਇਹ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|
+page={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।",
+'rev-deleted-text-unhide' => "ਸਫ਼ੇ ਦੀ ਇਹ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|
+page={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਜੇ ਤੁਸੀਂ ਅੱਗੇ ਵਧਣਾ ਚਾਹੋ ਤਾਂ ਹਾਲੇ ਵੀ [$1 ਇਹ ਰੀਵਿਜ਼ਨ ਵੇਖ] ਸਕਦੇ ਹੋ।",
+'rev-deleted-no-diff' => "ਤੁਸੀਂ ਇਹ ਫ਼ਰਕ ਨਹੀਂ ਵੇਖ ਸਕਦੇ ਕਿਉਂਕਿ ਇਹਨਾਂ ਵਿੱਚੋਂ ਇੱਕ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|
+page={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।",
+'rev-suppressed-no-diff' => "ਤੁਸੀਂ ਇਹ ਫ਼ਰਕ ਨਹੀਂ ਵੇਖ ਸਕਦੇ ਕਿਉਂਕਿ ਇਹਨਾਂ ਵਿੱਚੋਂ ਇੱਕ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।",
+'rev-deleted-unhide-diff' => "ਇਸ ਫ਼ਰਕ ਵਿੱਚੋਂ ਇੱਕ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|
+page={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਜੇ ਤੁਸੀਂ ਅੱਗੇ ਵਧਣਾ ਚਾਹੋ ਤਾਂ ਹਾਲੇ ਵੀ [$1 ਇਹ ਰੀਵਿਜ਼ਨ ਵੇਖ] ਸਕਦੇ ਹੋ।",
+'rev-suppressed-diff-view' => "ਇਸ ਫ਼ਰਕ ਵਿੱਚੋਂ ਇੱਕ ਰੀਵਿਜ਼ਨ '''ਜ਼ਬਤ''' ਕੀਤੀ ਜਾ ਚੁੱਕੀ ਹੈ।
+ਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|
+page={{FULLPAGENAMEE}}}} ਜ਼ਬਤੀ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।",
+'rev-delundel' => 'ਦਿਖਾਓ/ਲੁਕਾਓ',
+'rev-showdeleted' => 'ਵਖਾਓ',
+'revisiondelete' => 'ਰੀਵਿਜ਼ਨ ਮਿਟਾਓ/ਮਿਟਾਈ ਰੱਦ ਕਰੋ',
'revdelete-nooldid-title' => 'ਕੋਈ ਟਾਰਗੇਟ ਰੀਵਿਜ਼ਨ ਨਹੀਂ',
+'revdelete-nologtype-title' => 'ਚਿੱਠੇ ਦੀ ਕਿਸਮ ਨਹੀਂ ਦੱਸੀ ਗਈ',
+'revdelete-nologtype-text' => 'ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਤੁਸੀਂ ਚਿੱਠੇ ਦੀ ਕਿਸਮ ਨਹੀਂ ਦੱਸੀ।',
+'revdelete-no-file' => 'ਦੱਸੀ ਗਈ ਫ਼ਾਈਲ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+'revdelete-show-file-confirm' => 'ਤੁਹਾਨੂੰ ਯਕੀਨ ਹੈ ਤੁਸੀਂ $2 ਨੂੰ $3 ਦੀ ਫ਼ਾਈਲ "<nowiki>$1</nowiki>" ਦੀ ਮਿਟਾਈ ਗਈ ਰੀਵਿਜ਼ਨ ਵੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?',
+'revdelete-show-file-submit' => 'ਹਾਂ',
+'revdelete-selected' => "'''[[:$1]] {{PLURAL:$2|ਦੀ ਚੁਣੀ ਹੋਈ ਰੀਵਿਜ਼ਨ|ਦੀਆਂ ਚੁਣੀਆਂ ਹੋਈਆਂ ਰੀਵਿਜ਼ਨਾਂ}}:'''",
'revdelete-legend' => 'ਪਾਬੰਦੀਆਂ ਸੈੱਟ ਕਰੋ:',
'revdelete-hide-text' => 'ਰੀਵਿਜ਼ਨ ਟੈਕਸਟ ਓਹਲੇ',
'revdelete-hide-image' => 'ਫਾਇਲ ਸਮੱਗਰੀ ਓਹਲੇ',
'revdelete-hide-name' => 'ਐਕਸ਼ਨ ਅਤੇ ਟਾਰਗੇਟ ਓਹਲੇ',
+'revdelete-hide-comment' => 'ਸੋਧ ਸਾਰ ਲੁਕਾਓ',
+'revdelete-hide-user' => 'ਸੋਧਣ ਵਾਲ਼ੇ ਦਾ ਮੈਂਬਰ-ਨਾਂ/IP ਪਤਾ ਲੁਕਾਓ',
+'revdelete-radio-same' => '(ਨਹੀਂ ਬਦਲਣਾ)',
'revdelete-radio-set' => 'ਹਾਂ',
+'revdelete-radio-unset' => 'ਨਹੀਂ',
+'revdelete-unsuppress' => 'ਮੁੜ ਬਹਾਲ ਕੀਤੀਆਂ ਰੀਵਿਜ਼ਨਾਂ ਤੋਂ ਰੋਕਾਂ ਹਟਾਓ',
'revdelete-log' => 'ਕਾਰਨ:',
'revdelete-submit' => 'ਚੁਣੇ ਰੀਵਿਜ਼ਨ ਉੱਤੇ ਲਾਗੂ ਕਰੋ',
+'logdelete-success' => "'''ਚਿੱਠੇ ਦੀ ਦਿੱਖ ਕਾਮਯਾਬੀ ਨਾਲ਼ ਸੈੱਟ ਕੀਤੀ।'''",
+'logdelete-failure' => "'''ਚਿੱਠੇ ਦੀ ਦਿੱਖ ਸੈੱਟ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ:''' $1",
'revdel-restore' => 'ਦਿੱਖ ਬਦਲੋ',
-'revdel-restore-deleted' => 'ਹà¨\9fਾà¨\8f à¨\97à¨\8f ਬਦਲਾà¨\85',
-'revdel-restore-visible' => 'ਦà©\8dਰਿਸ਼à¨\9f ਬਦਲਾà¨\85',
+'revdel-restore-deleted' => 'ਮਿà¨\9fਾà¨\8f à¨\97à¨\8f ਰà©\80ਵà©\80à¨\9c਼ਨ',
+'revdel-restore-visible' => 'ਦਿੱਸਣਯà©\8bà¨\97 ਰà©\80ਵà©\80à¨\9c਼ਨ',
'pagehist' => 'ਪੇਜ ਦਾ ਅਤੀਤ',
'deletedhist' => 'ਹਟਾਇਆ ਗਿਆ ਅਤੀਤ',
+'revdelete-hide-current' => 'ਤਾਰੀਖ਼ &2, $1 ਦੀ ਚੀਜ਼ ਲੁਕਾਉਣ ਵਿਚ ਗ਼ਲਤੀ: ਇਹ ਮੌਜੂਦਾ ਰੀਵਿਜ਼ਨ ਹੈ।
+ਇਹ ਲੁਕਾਈ ਨਹੀਂ ਜਾ ਸਕਦੀ।',
+'revdelete-otherreason' => 'ਹੋਰ/ਵਾਧੂ ਕਾਰਨ:',
+'revdelete-reasonotherlist' => 'ਹੋਰ ਕਾਰਨ',
+'revdelete-edit-reasonlist' => 'ਮਿਟਾਏ ਜਾਣ ਦੇ ਕਾਰਨ ਸੋਧੋ',
+'revdelete-offender' => 'ਰੀਵਿਜ਼ਨ ਲੇਖਕ:',
+
+# History merging
+'mergehistory' => 'ਸਫ਼ਿਆਂ ਦੇ ਅਤੀਤ ਰਲ਼ਾਓ',
+'mergehistory-from' => 'ਸਰੋਤ ਸਫ਼ਾ:',
+'mergehistory-list' => 'ਰਲ਼ਾਉਣਯੋਗ ਸੋਧ ਅਤੀਤ',
+'mergehistory-go' => 'ਰਲ਼ਾਉਣਯੋਗ ਸੋਧਾਂ ਵਖਾਓ',
+'mergehistory-empty' => 'ਕੋਈ ਰੀਵਿਜ਼ਨ ਰਲ਼ਾਈ ਨਹੀ ਜਾ ਸਕਦੀ।',
+'mergehistory-no-source' => 'ਸਰੋਤ ਸਫ਼ਾ $1 ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+'mergehistory-autocomment' => '[[:$1]] ਨੂੰ [[:$2]] ਵਿੱਚ ਰਲ਼ਾਇਆ',
+'mergehistory-comment' => '[[:$1]] ਨੂੰ [[:$2]] ਵਿੱਚ ਰਲ਼ਾਇਆ: $3',
+'mergehistory-same-destination' => 'ਸਰੋਤ ਸਫ਼ਾ ਅਤੇ ਮੰਜ਼ਿਲ ਸਫ਼ਾ ਇੱਕੋ ਜਿਹੇ ਨਹੀਂ ਹੋ ਸਕਦੇ',
+'mergehistory-reason' => 'ਕਾਰਨ:',
# Merge log
-'revertmerge' => 'ਬਿਨ-ਮਿਲਾਨ',
+'mergelog' => 'ਰਲ਼ਾਉਣ ਦਾ ਚਿੱਠਾ',
+'pagemerge-logentry' => '[[$1]] ਨੂੰ [[$2]] ਵਿੱਚ ਰਲ਼ਾਇਆ ($3 ਤੱਕ ਦੀਆ ਰੀਵਿਜ਼ਨਾਂ)',
+'revertmerge' => 'ਅਨ-ਮਰਜ',
+'mergelogpagetext' => 'ਹੇਠਾਂ ਇੱਕ ਸਫ਼ੇ ਦੇ ਅਤੀਤ ਨੂੰ ਦੂਜੇ ਦੇ ਅਤੀਤ ਵਿਚ ਰਲ਼ਾਉਣ ਦੀ ਸਭ ਤੋਂ ਤਾਜ਼ਾ ਲਿਸਟ ਹੈ।',
# Diffs
-'history-title' => '"$1" ਦੀ ਸ਼ੋਧ ਤਵਾਰੀਖ',
+'history-title' => '"$1" ਦੇ ਰੀਵਿਜ਼ਨ ਦਾ ਅਤੀਤ',
+'difference-title' => '"$1" ਦੇ ਰੀਵਿਜ਼ਨਾਂ ਵਿਚ ਫ਼ਰਕ',
+'difference-title-multipage' => 'ਸਫ਼ਿਆਂ "$1" ਅਤੇ "$2" ਵਿਚ ਫ਼ਰਕ',
+'difference-multipage' => '(ਦੋ ਸਫ਼ਿਆਂ ਵਿਚਕਾਰ ਫ਼ਰਕ)',
'lineno' => 'ਲਾਈਨ $1:',
'compareselectedversions' => 'ਚੁਣੇ ਵਰਜਨਾਂ ਦੀ ਤੁਲਨਾ',
-'editundo' => 'ਵਾਪਸ(undo)',
+'showhideselectedversions' => 'ਚੁਣੇ ਰੀਵਿਜ਼ਨ ਵਖਾਓ/ਲੁਕਾਓ',
+'editundo' => 'ਨਕਾਰੋ',
+'diff-multi' => '({{PLURAL:$2|ਮੈਂਬਰ ਦੀ|$2 ਮੈਂਬਰਾਂ ਦੀਆਂ}} {{PLURAL:$1|ਵਿਚਕਾਰਲੀ ਰੀਵਿਜ਼ਨ ਨਹੀਂ ਦਿਖਾਈ ਜਾ ਰਹੀ|ਵਿਚਕਾਰਲੀਆਂ $1 ਰੀਵਿਜ਼ਨਾਂ ਨਹੀਂ ਦਿਖਾਈਆਂ ਜਾ ਰਹੀਆਂ}})',
# Search results
'searchresults' => 'ਖੋਜ ਨਤੀਜੇ',
'notitlematches' => 'ਕੋਈ ਪੇਜ ਟਾਇਟਲ ਨਹੀਂ ਮਿਲਦਾ',
'textmatches' => 'ਪੇਜ ਟੈਕਸਟ ਮਿਲਦਾ',
'notextmatches' => 'ਕੋਈ ਪੇਜ ਟੈਕਸਟ ਨਹੀਂ ਮਿਲਦਾ',
-'prevn' => 'ਪਿੱਛੇ {{PLURAL:$1|$1}}',
-'nextn' => 'ਅੱਗੇ {{PLURAL:$1|$1}}',
-'prevn-title' => 'ਪਹਿਲਾ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}}',
-'nextn-title' => '↓
-ਅਗਲਾ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}}',
-'shown-title' => 'ਪ੍ਰਤੀ ਪੇਜ਼ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}} ਵੇਖਾਓ',
+'prevn' => 'ਪਿਛਲੇ {{PLURAL:$1|$1}}',
+'nextn' => 'ਅਗਲੇ {{PLURAL:$1|$1}}',
+'prevn-title' => 'ਪਿਛਲੇ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}}',
+'nextn-title' => 'ਅਗਲੇ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}}',
+'shown-title' => 'ਪ੍ਰਤੀ ਸਫ਼ਾ $1 {{PLURAL:$1|ਨਤੀਜਾ|ਨਤੀਜੇ}} ਵਖਾਓ',
'viewprevnext' => 'ਵੇਖੋ ($1 {{int:pipe-separator}} $2) ($3)',
-'searchmenu-exists' => "'''ਇਸ ਵਿਕਿ ਤੇ \"[[:\$1]]\" ਨਾਮ ਦਾ ਇਕ ਸਫ਼ਾ ਹੈ'''",
-'searchmenu-new' => "'''ਇਸ ਵਿਕਿ ਪਰ \"[[:\$1]]\" ਨਾਮ ਨਾਲ ਪੰਨਾ ਬਣਾਓ!'''",
+'searchmenu-legend' => 'ਖੋਜ ਇਖ਼ਤਿਆਰ',
+'searchmenu-exists' => "'''ਇਸ ਵਿਕੀ ’ਤੇ \"[[:\$1]]\" ਨਾਮ ਦਾ ਸਫ਼ਾ ਹੈ।'''",
+'searchmenu-new' => "'''ਇਸ ਵਿਕੀ ’ਤੇ \"[[:\$1]]\" ਸਫ਼ਾ ਬਣਾਓ!'''",
'searchhelp-url' => 'Help:ਸਮੱਗਰੀ',
-'searchprofile-articles' => 'ਸਮੱਗਰੀ ਪੇਜ',
-'searchprofile-project' => 'ਮੱਦਦ ਅਤੇ ਪ੍ਰੋਜੈਕਟ ਸਫ਼ੇ',
+'searchmenu-prefix' => '[[Special:PrefixIndex/$1|ਇਸ ਅਗੇਤਰ ਵਾਲ਼ੇ ਸਫ਼ੇ ਵੇਖੋ]]',
+'searchprofile-articles' => 'ਸਮੱਗਰੀ ਸਫ਼ੇ',
+'searchprofile-project' => 'ਮਦਦ ਅਤੇ ਪ੍ਰੋਜੈਕਟ ਸਫ਼ੇ',
'searchprofile-images' => 'ਮਲਟੀਮੀਡਿਆ',
-'searchprofile-everything' => 'ਹਰ à¨\9aà©\80à¨\9c਼',
-'searchprofile-advanced' => 'ਤà¨\95ਨà©\80à¨\95à©\80',
-'searchprofile-articles-tooltip' => "$1 'ਚ ਖੋਜ",
-'searchprofile-project-tooltip' => "$1 'ਚ ਖੋਜ",
-'searchprofile-images-tooltip' => 'ਫਾà¨\87ਲਾà¨\82 ਲà¨\88 à¨\96à©\8bà¨\9c',
-'searchprofile-everything-tooltip' => 'ਸਠਸਮੱà¨\97ਰà©\80 ਦà©\80 à¨\96à©\8bà¨\9c (à¨\9fਾà¨\95 ਸਫ਼ਿਆਂ ਸਮੇਤ)',
-'searchprofile-advanced-tooltip' => 'à¨\96ਾਸ ਸਿਰਲà©\87à¨\96ਾਂ ਵਿਚ ਖੋਜੋ',
+'searchprofile-everything' => 'ਸਠà¨\95à©\81à¨\9d',
+'searchprofile-advanced' => 'à¨\86ਧà©\81ਨਿà¨\95',
+'searchprofile-articles-tooltip' => '$1 ਵਿਚ ਖੋਜੋ',
+'searchprofile-project-tooltip' => '$1 ਵਿਚ ਖੋਜੋ',
+'searchprofile-images-tooltip' => 'ਫਾà¨\87ਲਾà¨\82 à¨\96à©\8bà¨\9cà©\8b',
+'searchprofile-everything-tooltip' => 'ਸਠà¨\9aà©\80à¨\9c਼ਾà¨\82 à¨\96à©\8bà¨\9cà©\8b (à¨\97ੱਲਬਾਤ ਸਫ਼ਿਆਂ ਸਮੇਤ)',
+'searchprofile-advanced-tooltip' => 'à¨\86ਪਣà©\87 ਬਣਾà¨\8f ਨਾਮ-ਥਾà¨\82ਵਾਂ ਵਿਚ ਖੋਜੋ',
'search-result-size' => '$1 ({{PLURAL:$2|੧ ਸ਼ਬਦ|$2 ਸ਼ਬਦ}})',
'search-redirect' => '($1 ਰੀ-ਡਿਰੈਕਟ)',
'search-section' => '(ਭਾਗ $1)',
'search-suggest' => 'ਕੀ ਤੁਹਾਡਾ ਮਤਲਬ ਸੀ: $1',
+'search-interwiki-caption' => 'ਸਾਥੀ ਪ੍ਰੋਜੈਕਟ',
'search-interwiki-default' => '$1 ਨਤੀਜੇ:',
'search-interwiki-more' => '(ਹੋਰ)',
'search-mwsuggest-enabled' => 'ਸੁਝਾਆਵਾਂ ਨਾਲ',
'search-mwsuggest-disabled' => 'ਕੋਈ ਸੁਝਾਅ ਨਹੀਂ',
-'searchrelated' => 'ਸੰਬੰਧਿਤ',
+'search-relatedarticle' => 'ਸਬੰਧਿਤ',
+'mwsuggest-disable' => 'AJAX ਸਲਾਹਾਂ ਬੰਦ ਕਰੋ',
+'searcheverything-enable' => 'ਸਾਰੇ ਥਾਂ-ਨਾਂਵਾਂ ਵਿਚ ਖੋਜੋ',
+'searchrelated' => 'ਸਬੰਧਿਤ',
'searchall' => 'ਸਭ',
-'search-nonefound' => 'ਤੁਹਾਡੀ ਖੋਜ ਨਾਲ ਮੇਲ ਖਾਂਦੇ ਕੋਈ ਸਿੱਟੇ ਨਹੀਂ ਮਿਲੇ।',
+'showingresults' => "ਹੇਠਾਂ #'''$2''' ਨਾਲ਼ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲ਼ੇ {{PLURAL:
+$1|'''1''' ਨਤੀਜਾ|'''$1''' ਤੱਕ ਨਤੀਜੇ}} ਵਖਾਓ।",
+'showingresultsnum' => "ਹੇਠਾਂ #'''$2''' ਨਾਲ਼ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲ਼ੇ {{PLURAL:
+$3|'''1''' ਨਤੀਜਾ|'''$3''' ਨਤੀਜੇ}} ਵਖਾਓ।",
+'showingresultsheader' => "'''$4''' ਵਾਸਤੇ {{PLURAL:$5|'''$3''' ਵਿਚੋਂ '''$1''' ਨਤੀਜੇ|'''$3''' ਵਿਚੋਂ '''$1 - $2''' ਨਤੀਜੇ}}",
+'search-nonefound' => 'ਤੁਹਾਡੀ ਖੋਜ ਨਾਲ਼ ਮੇਲ ਖਾਂਦੇ ਕੋਈ ਨਤੀਜੇ ਨਹੀਂ ਮਿਲੇ।',
'powersearch' => 'ਖੋਜ',
'powersearch-legend' => 'ਤਕਨੀਕੀ ਖੋਜ',
'powersearch-ns' => 'ਨੇਮ-ਸਪੇਸ ਵਿੱਚ ਖੋਜ:',
'powersearch-redir' => 'ਰੀ-ਡਿਰੈਕਟ ਲਿਸਟ',
'powersearch-field' => 'ਇਸ ਲਈ ਖੋਜ',
+'powersearch-togglelabel' => 'ਜਾਂਚੋ:',
+'powersearch-toggleall' => 'ਸਭ',
+'powersearch-togglenone' => 'ਕੋਈ ਨਹੀਂ',
+'search-external' => 'ਬਾਹਰੀ ਖੋਜ',
# Quickbar
'qbsettings' => 'ਤੁਰੰਤ ਬਾਰ',
# Preferences page
'preferences' => 'ਮੇਰੀ ਪਸੰਦ',
-'mypreferences' => 'ਮੇਰੀ ਪਸੰਦ',
+'mypreferences' => 'ਮੇਰੀਆਂ ਪਸੰਦਾਂ',
'prefs-edits' => 'ਸੋਧਾਂ ਦੀ ਗਿਣਤੀ:',
'prefsnologin' => 'ਲਾਗਇਨ ਨਹੀਂ',
'prefsnologintext' => 'ਯੂਜ਼ਰ ਪਸੰਦ ਸੈੱਟ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ [[Special:UserLogin|logged in]] ਕਰਨਾ ਪਵੇਗਾ।',
'skin-preview' => 'ਝਲਕ',
'datedefault' => 'ਕੋਈ ਪਸੰਦ ਨਹੀਂ',
'prefs-datetime' => 'ਮਿਤੀ ਅਤੇ ਸਮਾਂ',
+'prefs-user-pages' => 'ਮੈਂਬਰ ਸਫ਼ੇ',
'prefs-personal' => 'ਯੂਜ਼ਰ ਪਰੋਫਾਇਲ',
'prefs-rc' => 'ਤਾਜ਼ਾ ਬਦਲਾਅ',
'prefs-watchlist' => 'ਵਾਚ-ਲਿਸਟ',
+'prefs-watchlist-days' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਦਿਖਾਉਣ ਲਈ ਦਿਨ:',
+'prefs-watchlist-days-max' => 'ਵੱਧ ਤੋਂ ਵੱਧ $1 {{PLURAL:$1|ਦਿਨ|ਦਿਨ}}',
+'prefs-watchlist-edits' => 'ਵਧਾਈ ਹੋਈ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਦਿਖਾਉਣ ਲਈ ਵੱਧ ਤੋਂ ਵੱਧ ਤਬਦੀਲੀਆਂ:',
+'prefs-watchlist-edits-max' => 'ਵੱਧ ਤੋਂ ਵੱਧ ਨੰਬਰ: ੧੦੦੦',
+'prefs-watchlist-token' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਟੋਕਨ:',
'prefs-misc' => 'ਫੁਟਕਲ',
'prefs-resetpass' => 'ਪਾਸਵਰਡ ਬਦਲੋ',
+'prefs-changeemail' => 'ਈ-ਮੇਲ ਪਤਾ ਬਦਲੋ',
+'prefs-setemail' => 'ਈ-ਮੇਲ ਪਤਾ ਸੈੱਟ ਕਰੋ',
'prefs-email' => 'ਈਮੇਲ ਚੋਣਾਂ',
'prefs-rendering' => 'ਦਿੱਖ',
'saveprefs' => 'ਸੰਭਾਲੋ',
'resetprefs' => 'ਰੀ-ਸੈੱਟ',
'prefs-editing' => 'ਸੰਪਾਦਨ',
+'prefs-edit-boxsize' => 'ਸੋਧ ਖਿੜਕੀ ਦਾ ਅਕਾਰ',
'rows' => 'ਕਤਾਰਾਂ:',
'columns' => 'ਕਾਲਮ:',
'searchresultshead' => 'ਖੋਜ',
'resultsperpage' => 'ਪ੍ਰਤੀ ਪੇਜ ਹਿੱਟ:',
+'recentchangesdays' => 'ਤਾਜ਼ਾ ਤਬਦੀਲੀਆਂ ਵਿਚ ਦਿਖਾਉਣ ਲਈ ਦਿਨ:',
+'recentchangesdays-max' => 'ਵੱਧ ਤੋਂ ਵੱਧ $1 {{PLURAL:$1|ਦਿਨ|ਦਿਨ}}',
+'prefs-help-recentchangescount' => 'ਇਸ ਵਿਚ ਤਾਜ਼ਾ ਤਬਦੀਲੀਆਂ, ਸਫ਼ਿਆਂ ਦੇ ਅਤੀਤ ਅਤੇ ਚਿੱਠੇ ਸ਼ਾਮਲ ਹਨ।',
'savedprefs' => 'ਤੁਹਾਡੀ ਪਸੰਦ ਸੰਭਾਲੀ ਗਈ ਹੈ।',
'timezonelegend' => 'ਸਮਾਂ ਖੇਤਰ:',
'localtime' => 'ਲੋਕਲ ਸਮਾਂ:',
'timezoneuseserverdefault' => 'ਸਰਵਰ ਡਿਫਾਲਟ ਵਰਤੋਂ',
'servertime' => 'ਸਰਵਰ ਟਾਈਮ',
'guesstimezone' => 'ਬਰਾਊਜ਼ਰ ਤੋਂ ਭਰੋ',
+'timezoneregion-africa' => 'ਅਫ਼ਰੀਕਾ',
+'timezoneregion-america' => 'ਅਮਰੀਕਾ',
+'timezoneregion-antarctica' => 'ਅੰਟਾਰਕਟਿਕਾ',
+'timezoneregion-arctic' => 'ਆਰਕਟਿਕ',
+'timezoneregion-asia' => 'ਏਸ਼ੀਆ',
+'timezoneregion-atlantic' => 'ਅੰਧ ਮਹਾਂਸਾਗਰ',
+'timezoneregion-australia' => 'ਆਸਟ੍ਰੇਲੀਆ',
+'timezoneregion-europe' => 'ਯੂਰਪ',
+'timezoneregion-indian' => 'ਹਿੰਦ ਮਹਾਂਸਾਗਰ',
+'timezoneregion-pacific' => 'ਪ੍ਰਸ਼ਾਂਤ ਮਹਾਂਸਾਗਰ',
'allowemail' => 'ਹੋਰ ਯੂਜ਼ਰਾਂ ਤੋਂ ਈਮੇਲ ਯੋਗ ਕਰੋ',
+'prefs-searchoptions' => 'ਖੋਜ ਇਖ਼ਤਿਆਰ',
+'prefs-namespaces' => 'ਥਾਂ-ਨਾਮ',
+'defaultns' => 'ਨਹੀਂ ਤਾਂ ਇਹਨਾਂ ਥਾਂ-ਨਾਂਵਾਂ ਵਿਚ ਖੋਜੋ:',
'default' => 'ਡਿਫਾਲਟ',
'prefs-files' => 'ਫਾਇਲਾਂ',
-'youremail' => 'ਈਮੇਲ:',
+'prefs-emailconfirm-label' => 'ਈ-ਮੇਲ ਤਸਦੀਕ:',
+'prefs-textboxsize' => 'ਸੋਧ ਖਿੜਕੀ ਦਾ ਅਕਾਰ',
+'youremail' => 'ਈ-ਮੇਲ:',
'username' => 'ਯੂਜ਼ਰ ਨਾਂ:',
'uid' => 'ਯੂਜ਼ਰ ID:',
-'yourrealname' => 'à¨\85ਸਲà©\80 ਨਾà¨\82:',
+'yourrealname' => 'à¨\85ਸਲà©\80 ਨਾਮ:',
'yourlanguage' => 'ਭਾਸ਼ਾ:',
'yournick' => 'ਛੋਟਾ ਨਾਂ:',
+'prefs-help-signature' => 'ਗੱਲ-ਬਾਤ ਸਫ਼ਿਆਂ ਉੱਤੇ ਟਿੱਪਣੀਆਂ ਦੇ ਆਖ਼ਰ ਵਿਚ "<nowiki>~~~~</nowiki>" ਲਾਓ ਜੋ ਤੁਹਾਡੇ ਦਸਤਖ਼ਤ ਅਤੇ ਵਕਤ ਦੀ ਮੋਹਰ ਵਿਚ ਤਬਦੀਲ ਹੋ ਜਾਵੇਗਾ।',
'badsiglength' => 'ਛੋਟਾ ਨਾਂ (Nickname) ਬਹੁਤ ਲੰਮਾ ਹੋ ਗਿਆ ਹੈ, ਇਹ $1 ਅੱਖਰਾਂ ਤੋਂ ਘੱਟ ਚਾਹੀਦਾ ਹੈ।',
+'yourgender' => 'ਲਿੰਗ:',
+'gender-unknown' => 'ਜ਼ਾਹਿਰ ਨਹੀਂ ਕੀਤਾ',
+'gender-male' => 'ਮਰਦ',
+'gender-female' => 'ਔਰਤ',
'email' => 'ਈਮੇਲ',
'prefs-help-realname' => 'ਅਸਲੀ ਨਾਂ ਚੋਣਵਾਂ ਹੈ, ਅਤੇ ਜੇ ਤੁਸੀਂ ਇਹ ਦਿੱਤਾ ਹੈ ਤਾਂ ਤੁਹਾਡੇ ਕੰਮ ਵਾਸਤੇ ਗੁਣ ਦੇ ਤੌਰ ਉੱਤੇ ਵਰਤਿਆ ਜਾਵੇਗਾ।',
-'prefs-help-email' => 'ਈਮੇਲ ਐਡਰੈੱਸ ਚੋਣਵਾਂ ਹੈ, ਪਰ ਇਹ ਤੁਹਾਨੂੰ ਹੋਰਾਂ ਵਲੋਂ ਤੁਹਾਡੇ ਨਾਲ ਤੁਹਾਡੇ ਯੂਜ਼ਰ ਜਾਂ ਯੂਜ਼ਰ_ਗੱਲਬਾਤ ਰਾਹੀਂ ਬਿਨਾਂ ਤੁਹਾਡੇ ਪਛਾਣ ਦੇ ਸੰਪਰਕ ਲਈ ਮੱਦਦ ਦਿੰਦਾ ਹੈ।',
+'prefs-help-email' => 'ਤੁਹਾਡੀ ਮਰਜ਼ੀ ਹੈ ਈਮੇਲ ਪਤਾ ਦਿਓ ਜਾਂ ਨਾ ਦਿਓ ਪਰ ਪਾਸਵਰਡ ਭੁੱਲ ਜਾਣ ਤੇ ਨਵਾਂ ਪਾਸਵਰਡ ਹਾਸਲ ਕਰਨ ਲਈ ਇਹ ਜ਼ਰੂਰੀ ਹੈ।',
+'prefs-help-email-others' => 'ਤੁਸੀਂ ਇਹ ਵੀ ਚੁਣ ਸਕਦੇ ਹੋ ਕਿ ਤੁਹਾਡੇ ਮੈਂਬਰ ਜਾਂ ਗੱਲ-ਬਾਤ ਸਫ਼ੇ ਤੋਂ ਹੋਰ ਮੈਂਬਰ ਤੁਹਾਨੂੰ ਈ-ਮੇਲ ਭੇਜ ਸਕਣ?
+ਜਦੋਂ ਹੋਰ ਮੈਂਬਰ ਤੁਹਾਨੂੰ ਈ-ਮੇਲ ਭੇਜਦੇ ਹਨ ਤਾਂ ਤੁਹਾਡਾ ਈ-ਮੇਲ ਪਤਾ ਜ਼ਾਹਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ।',
+'prefs-help-email-required' => 'ਈ-ਮੇਲ ਪਤਾ ਚਾਹੀਦਾ ਹੈ।',
+'prefs-info' => 'ਮੁੱਢਲੀ ਜਾਣਕਾਰੀ',
+'prefs-signature' => 'ਦਸਤਖ਼ਤ',
+'prefs-dateformat' => 'ਤਾਰੀਖ਼ ਅੰਦਾਜ਼',
'prefs-advancedediting' => 'ਤਕਨੀਕੀ ਚੋਣਾਂ',
'prefs-advancedrc' => 'ਤਕਨੀਕੀ ਚੋਣਾਂ',
'prefs-advancedrendering' => 'ਤਕਨੀਕੀ ਚੋਣਾਂ',
'prefs-advancedsearchoptions' => 'ਤਕਨੀਕੀ ਚੋਣਾਂ',
'prefs-advancedwatchlist' => 'ਤਕਨੀਕੀ ਚੋਣਾਂ',
+'prefs-diffs' => 'ਫ਼ਰਕ',
+
+# User preference: e-mail validation using jQuery
+'email-address-validity-valid' => 'ਈ-ਮੇਲ ਪਤਾ ਸਹੀ ਲਗਦਾ ਹੈ',
+'email-address-validity-invalid' => 'ਸਹੀ ਈ-ਮੇਲ ਪਤਾ ਦਾਖ਼ਲ ਕਰੋ',
# User rights
+'userrights' => 'ਮੈਂਬਰ ਦੇ ਹੱਕਾਂ ਦਾ ਰੱਖ-ਰਖਾਓ',
'userrights-lookup-user' => 'ਯੂਜ਼ਰ ਗਰੁੱਪ ਦੇਖਭਾਲ',
'userrights-user-editname' => 'ਇੱਕ ਯੂਜ਼ਰ ਨਾਂ ਦਿਓ:',
'editusergroup' => 'ਯੂਜ਼ਰ ਗਰੁੱਪ ਸੋਧ',
'saveusergroups' => 'ਯੂਜ਼ਰ ਗਰੁੱਪ ਸੰਭਾਲੋ',
'userrights-groupsmember' => 'ਇਸ ਦਾ ਮੈਂਬਰ:',
'userrights-reason' => 'ਕਾਰਨ:',
+'userrights-no-interwiki' => 'ਤੁਹਾਨੂੰ ਦੂਜੇ ਵਿਕੀਆਂ ਤੇ ਮੈਂਬਰਾਂ ਦੇ ਹੱਕਾਂ ਵਿਚ ਤਬਦੀਲੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
+'userrights-nodatabase' => 'ਡੈਟਾਬੇਸ $1 ਮੌਜੂਦ ਨਹੀਂ ਜਾਂ ਮਕਾਮੀ ਨਹੀਂ ਹੈ।',
+'userrights-notallowed' => 'ਤੁਹਾਡੇ ਖਾਤੇ ਨੂੰ ਮੈਂਬਰ ਨੂੰ ਹੱਕ ਦੇਣ ਜਾਂ ਖੋਹਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
# Groups
'group' => 'ਗਰੁੱਪ:',
'group-user' => 'ਮੈਂਬਰ',
+'group-autoconfirmed' => 'ਖ਼ੁਦ-ਤਸਦੀਕਸ਼ੁਦਾ ਮੈਂਬਰ',
+'group-bot' => 'ਬੋਟ',
'group-all' => '(ਸਭ)',
'group-user-member' => 'ਮੈਂਬਰ',
# Rights
+'right-read' => 'ਸਫ਼ੇ ਪੜ੍ਹਨਾ',
'right-edit' => 'ਸਫ਼ੇ ਸੋਧ',
+'right-createpage' => 'ਸਫ਼ੇ ਬਣਾਉਣਾ (ਜੋ ਚਰਚਾ ਸਫ਼ੇ ਨਾ ਹੋਣ)',
+'right-createtalk' => 'ਚਰਚਾ ਸਫ਼ੇ ਬਣਾਉਣਾ',
+'right-minoredit' => 'ਸੋਧਾਂ ਦੇ ਛੋਟਾ ਹੋਣ ਲਈ ਨਿਸ਼ਾਨ ਲਾਉਣਾ',
+'right-move' => 'ਸਫ਼ੇ ਭੇਜਣਾ',
+'right-upload' => 'ਫ਼ਾਈਲਾਂ ਅੱਪਲੋਡ ਕਰਨਾ',
+'right-autoconfirmed' => 'ਨੀਮ-ਸੁਰੱਖਿਅਤ ਸਫ਼ਿਆਂ ਨੂੰ ਸੋਧਣਾ',
'right-delete' => 'ਸਫ਼ੇ ਹਟਾਓ',
+'right-bigdelete' => 'ਵੱਡੇ ਅਤੀਤਾਂ ਵਾਲ਼ੇ ਸਫ਼ੇ ਮਿਟਾਉਣੇ',
+'right-browsearchive' => 'ਮਿਟਾਏ ਗਏ ਸਫ਼ੇ ਖੋਜਣਾ',
+'right-undelete' => 'ਸਫ਼ੇ ਨੂੰ ਅਣ-ਮਿਟਾਇਆ ਕਰਨਾ',
+'right-block' => 'ਦੂਜੇ ਮੈਂਬਰਾਂ ਦੇ ਸੋਧ ਕਰਨ ਤੇ ਪਾਬੰਦੀ ਲਾਉਣੀ',
+'right-blockemail' => 'ਮੈਂਬਰ ਦੇ ਈ-ਮੇਲ ਭੇਜਣ ਤੇ ਪਾਬੰਦੀ ਲਾਉਣੀ',
+'right-hideuser' => 'ਮੈਂਬਰ-ਨਾਂ ਤੇ ਪਾਬੰਦੀ ਲਾਉਣੀ ਅਤੇ ਇਸਨੂੰ ਲੋਕਾਂ ਤੋਂ ਲੁਕਾਉਣਾ',
+'right-unwatchedpages' => 'ਨਜ਼ਰ ਨਾ ਰੱਖੇ ਜਾ ਰਹੇ ਸਫ਼ਿਆਂ ਦੀ ਲਿਸਟ ਵੇਖਣੀ',
+'right-mergehistory' => 'ਸਫ਼ਿਆਂ ਦੇ ਅਤੀਤਾਂ ਨੂੰ ਰਲ਼ਾਉਣਾ',
+'right-userrights' => 'ਸਾਰੇ ਮੈਂਬਰ ਹੱਕਾਂ ਵਿਚ ਸੋਧ ਕਰਨਾ',
+'right-userrights-interwiki' => 'ਦੂਜੇ ਵਿਕੀਆਂ ਤੇ ਮੈਂਬਰਾਂ ਦੇ ਮੈਂਬਰ ਹੱਕਾਂ ਵਿਚ ਸੋਧ ਕਰਨਾ',
+'right-siteadmin' => 'ਡੈਟਾਬੇਸ ਨੂੰ ਤਾਲਾ ਲਾਉਣਾ ਤੇ ਖੋਲ੍ਹਣਾ',
+'right-sendemail' => 'ਦੂਜੇ ਮੈਂਬਰਾਂ ਨੂੰ ਈ-ਮੇਲ ਭੇਜਣਾ',
# User rights log
+'rightslog' => 'ਮੈਂਬਰ ਹੱਕਾਂ ਦਾ ਚਿੱਠਾ',
+'rightslogtext' => 'ਇਹ ਮੈਂਬਰ ਹੱਕਾਂ ਵਿਚ ਹੋਈਆਂ ਤਬਦੀਲੀਆਂ ਦਾ ਚਿੱਠਾ ਹੈ।',
+'rightslogentry-autopromote' => '$2 ਤੋਂ ਆਪਣੇ ਆਪ $3 ਤੱਕ ਤਰੱਕੀ ਕਰੀ',
'rightsnone' => '(ਕੋਈ ਨਹੀਂ)',
# Associated actions - in the sentence "You do not have permission to X"
-'action-edit' => 'ਇਹ ਪੰਨਾ ਸੋਧੋ',
+'action-read' => 'ਇਹ ਸਫ਼ਾ ਪੜ੍ਹਨ',
+'action-edit' => 'ਇਹ ਸਫ਼ਾ ਸੋਧੋ',
+'action-createpage' => 'ਸਫ਼ੇ ਬਣਾਉਣ',
+'action-createtalk' => 'ਚਰਚਾ ਸਫ਼ੇ ਬਣਾਉਣ',
+'action-createaccount' => 'ਮੈਂਬਰ ਖਾਤਾ ਬਣਾਉਣ',
+'action-upload' => 'ਇਹ ਫ਼ਾਈਲ ਅੱਪਲੋਡ ਕਰਨ',
+'action-delete' => 'ਇਹ ਸਫ਼ਾ ਮਿਟਾਉਣ',
+'action-deleterevision' => 'ਇਹ ਰੀਵਿਜ਼ਨ ਮਿਟਾਉਣ',
+'action-deletedhistory' => 'ਇਸ ਸਫ਼ੇ ਦਾ ਮਿਟਾਇਆ ਅਤੀਤ ਵੇਖਣ',
+'action-browsearchive' => 'ਮਿਟਾਏ ਸਫ਼ੇ ਖੋਜਣ',
+'action-undelete' => 'ਇਹ ਸਫ਼ਾ ਅਣ-ਮਿਟਿਆ ਕਰਨ',
+'action-block' => 'ਇਸ ਮੈਂਬਰ ਦੇ ਸੋਧ ਕਰਨ ਤੇ ਪਾਬੰਦੀ ਲਾਉਣ',
+'action-protect' => 'ਇਸ ਸਫ਼ੇ ਦੀ ਸੁਰੱਖਿਆ ਬਦਲਣ',
+'action-unwatchedpages' => 'ਨਜ਼ਰ ਨਾ ਰੱਖੇ ਜਾ ਰਹੇ ਸਫ਼ਿਆਂ ਦੀ ਲਿਸਟ ਵੇਖਣ',
+'action-mergehistory' => 'ਇਸ ਸਫ਼ੇ ਦੇ ਅਤੀਤ ਨੂੰ ਰਲ਼ਾਉਣ',
+'action-userrights' => 'ਸਾਰੇ ਮੈਂਬਰ ਹੱਕ ਸੋਧਣ',
+'action-userrights-interwiki' => 'ਦੂਜੇ ਵਿਕੀਆਂ ਤੇ ਮੈਂਬਰਾਂ ਦੇ ਮੈਂਬਰ ਹੱਕ ਸੋਧਣ',
+'action-siteadmin' => 'ਡੈਟਾਬੇਸ ਨੂੰ ਤਾਲਾ ਲਾਉਣ ਜਾਂ ਖੋਲ੍ਹਣ',
+'action-sendemail' => 'ਈ-ਮੇਲਾਂ ਭੇਜਣ',
# Recent changes
-'recentchanges' => 'ਤਾਜ਼ਾ ਬਦਲਾਅ',
+'nchanges' => '$1 {{PLURAL:$1|ਤਬਦੀਲੀ|
+ਤਬਦੀਲੀਆਂ}}',
+'recentchanges' => 'ਤਾਜ਼ਾ ਤਬਦੀਲੀਆਂ',
'recentchanges-legend' => 'ਤਾਜ਼ਾ ਬਦਲਾਅ ਚੋਣਾਂ',
'recentchanges-summary' => 'ਇਸ ਵਿਕੀ ਪਰ ਹਾਲ ਵਿਚ ਹੋਏ ਬਦਲਾਅਦੇਖੇ ਜਾ ਸਕਦੇ ਹਨ।',
-'recentchanges-feed-description' => 'ਇਸ ਵਿਕੀ ਪਰ ਹਾਲ ਵਿਚ ਹੋਏ ਬਦਲਾਅ ਇਸ ਫ਼ੀਡ ਵਿਚ ਦੇਖੇ ਜਾ ਸਕਦੇ ਹਨ।',
+'recentchanges-feed-description' => 'ਇਸ ਵਿਕੀ ’ਤੇ ਹਾਲ ਹੀ ਵਿਚ ਹੋਈਆਂ ਤਬਦੀਲੀਆਂ ਇਸ ਫ਼ੀਡ ’ਚ ਵੇਖੀਆਂ ਜਾ ਸਕਦੀਆਂ ਹਨ।',
'recentchanges-label-newpage' => 'ਇਹ ਸੋਧ ਨੇ ਨਵਾਂ ਸਫ਼ਾ ਬਣਾਇਆ ਹੈ',
'recentchanges-label-minor' => 'ਇਹ ਛੋਟੀ ਸੋਧ ਹੈ',
-'recentchanges-label-bot' => 'à¨\87ਹ ਸà©\8bਧ ਨà©\82à©° ਬà©\8bà¨\9f ਵਲà©\8bà¨\82 à¨\95à©\80ਤਾ à¨\97ਿà¨\86 ਹੈ',
+'recentchanges-label-bot' => 'à¨\87ਹ ਸà©\8bਧ ਬà©\8bà¨\9f ਵਲà©\8bà¨\82 à¨\95à©\80ਤà©\80 à¨\97à¨\88 ਹੈ',
'recentchanges-label-unpatrolled' => 'ਇਸ ਸੰਪਾਦਨ ਦੀ ਅਜੇ ਨਿਗਰਾਨੀ ਨਹੀਂ ਹੋਈ',
-'rcnotefrom' => "'''$2'''ਤੌਂ ('''$1''' ਤਕ) ਬਦਲਾਅ ਥੱਲੇ ਦਰਸਾਏ ਗਏ ਹਨ।",
-'rclistfrom' => '$1 ਤੌਨ ਨਵੇਂ ਬਦਲਾਅ ਦਿਖਾਓ',
+'rcnote' => "$4, $5 ਤੱਕ ਆਖ਼ਰੀ {{PLURAL:$2|ਦਿਨ|'''$2''' ਦਿਨਾਂ}} ਵਿਚ {{PLURAL:$1|'''1''' ਤਬਦੀਲੀ ਹੋਈ ਹੈ।|'''$1''' ਤਬਦੀਲੀਆਂ ਹੋਈਆਂ ਹਨ।}}",
+'rcnotefrom' => "'''$2''' ਤੱਕ ('''$1''' ਤੱਕ ਦਿੱਸਦੀਆਂ) ਤਬਦੀਲੀਆਂ ਹੇਠ ਦਿੱਤੀਆਂ ਹਨ।",
+'rclistfrom' => '$1 ਤੋਂ ਸ਼ੁਰੂ ਕਰਕੇ ਨਵੀਆਂ ਤਬਦੀਲੀਆਂ ਦਿਖਾਓ',
'rcshowhideminor' => '$1 ਛੋਟੀਆਂ ਸੋਧਾਂ',
'rcshowhidebots' => '$1 ਬੋਟ',
-'rcshowhideliu' => '$1 ਲਾਗਇਨ ਹੋਏ ਯੂਜ਼ਰ',
-'rcshowhideanons' => '$1 ਅਗਿਆਤ ਯੂਜ਼ਰ',
-'rcshowhidemine' => '$1 ਮੇਰਾ ਐਡਿਟ',
-'rclinks' => 'ਪਿਛਲੇ $2 ਦਿਨਾਂ ਵਿਚ ਹੋਏ $1 ਬਦਲਾਅ ਦਿਖਾਓ<br />$3',
-'diff' => 'ਅੰਤਰ',
+'rcshowhideliu' => '$1 ਲਾਗਇਨ ਹੋਏ ਮੈਂਬਰ',
+'rcshowhideanons' => '$1 ਗੁਮਨਾਮ ਮੈਂਬਰ',
+'rcshowhidepatr' => 'ਵੇਖੀਆਂ ਜਾ ਚੁੱਕੀਆਂ ਸੋਧਾਂ $1',
+'rcshowhidemine' => 'ਮੇਰੀਆਂ ਸੋਧਾਂ $1',
+'rclinks' => 'ਪਿਛਲੇ $2 ਦਿਨਾਂ ਵਿਚ ਹੋਈਆਂ $1 ਤਬਦੀਲੀਆਂ ਦਿਖਾਓ <br /> $3',
+'diff' => 'ਫ਼ਰਕ',
'hist' => 'ਅਤੀਤ',
-'hide' => 'à¨\93ਹਲà©\87',
-'show' => 'ਵà©\87à¨\96à©\8b',
+'hide' => 'ਲà©\81à¨\95ਾà¨\93',
+'show' => 'ਵà¨\96ਾà¨\93',
'minoreditletter' => 'ਛ',
'newpageletter' => 'ਨ',
'boteditletter' => 'ਬ',
'rc_categories_any' => 'ਕੋਈ ਵੀ',
-'rc-enhanced-expand' => 'ਵà©\87ਰਵਾ ਵà©\87à¨\96à©\8b (à¨\9cਾਵਾਸà¨\95à©\8dਰਿਪà¨\9f ਲà©\8bà©\9cà©\80ਦੀ ਹੈ)',
-'rc-enhanced-hide' => 'ਵà©\87ਰਵਾ à¨\93ਹਲà©\87',
+'rc-enhanced-expand' => 'ਵà©\87ਰਵਾ ਵà¨\96ਾà¨\93 (à¨\9cਾਵਾਸà¨\95à©\8dਰਿਪà¨\9f ਲà©\8bà©\9cà©\80à¨\82ਦੀ ਹੈ)',
+'rc-enhanced-hide' => 'ਵà©\87ਰਵਾ ਲà©\81à¨\95ਾà¨\93',
# Recent changes linked
-'recentchangeslinked' => 'ਸਬੰਧਿਤ ਬਦਲਾà¨\85',
+'recentchangeslinked' => 'ਸਬੰਧਿਤ ਤਬਦà©\80ਲà©\80à¨\86à¨\82',
'recentchangeslinked-feed' => 'ਸਬੰਧਿਤ ਬਦਲਾਅ',
-'recentchangeslinked-toolbox' => 'ਸਬੰਧਿਤ ਬਦਲਾà¨\85',
-'recentchangeslinked-title' => '"$1" ਨਾਲ ਸੰਬੰਧਿਤ ਬਦਲਾਅ',
-'recentchangeslinked-noresult' => 'à¨\9cà©\81à©\9cà©\87 ਹà©\8bà¨\8f ਸਫਿà¨\86à¨\82 ਤà©\87 ,ਦਿੱਤà©\87 ਸਮà©\87à¨\82 ਵਿà¨\9a à¨\95à©\81à¨\9d ਨਹà©\80à¨\82 ਬਦਲਿà¨\86 ।',
-'recentchangeslinked-summary' => 'à¨\87ਹ ਸà©\82à¨\9aà©\80 à¨\8aਨà©\8dਹਾà¨\82 ਪੰਨਿà¨\86à¨\82 ,à¨\9cà©\8b à¨\87à¨\95 à¨\96ਾਸ ਪੰਨà©\87 ਨਾਲ ਸੰਬੰਧਿਤ ਹਨ, (ਯਾ à¨\95ਿਸà©\87 à¨\96ਾਸ ਸ਼à©\8dਰà©\87ਣà©\80 ਦà©\87 ਮà©\88à¨\82ਬਰਾà¨\82) ਦà©\87 ਹਾਲ ਵਿà¨\9a ਹà©\8bà¨\8f ਬਦਲਾਵਾà¨\82 ਨà©\82à©° ਦਰਸ਼ਾà¨\82ਦà©\80 ਹà©\88 [[Special:Watchlist|ਤà©\81ਹਾਡà©\80 ਦà©\8dਰਿਸ਼à¨\9fà©\80 à¨\97à©\8bà¨\9aਰ ਸà©\82à¨\9aà©\80]] ਵਿà¨\9a ਮà©\8cà¨\9cà©\82ਦ ਪੰਨà©\87 ਮà©\8bà¨\9fà©\87 à¨\85ਖਰਾਂ ਵਿਚ ਦਿਖਾਈ ਦੇਣਗੇ।',
-'recentchangeslinked-page' => 'ਸਫ਼ਾ ਨਾà¨\82:',
-'recentchangeslinked-to' => 'ਇਸ ਦੇ ਬਦਲੇ ਇਸ ਪੰਨੇ ਨਾਲ ਜੁੜੇ ਪੰਨਿਆਂ ਵਿਚ ਹੋਏ ਬਦਲਾਅ ਦਿਖਾਓ',
+'recentchangeslinked-toolbox' => 'ਸਬੰਧਿਤ ਤਬਦà©\80ਲà©\80à¨\86à¨\82',
+'recentchangeslinked-title' => '"$1" ਨਾਲ਼ ਸਬੰਧਿਤ ਤਬਦੀਲੀਆਂ',
+'recentchangeslinked-noresult' => 'à¨\9cà©\81à©\9cà©\87 ਸਫਿà¨\86à¨\82 â\80\99ਤà©\87, ਦਿੱਤà©\87 ਸਮà©\87à¨\82 â\80\99à¨\9a à¨\95à©\8bà¨\88 ਤਬਦà©\80ਲà©\80 ਨਹà©\80à¨\82 ਹà©\8bà¨\88।',
+'recentchangeslinked-summary' => 'à¨\87ਹ ਲਿਸà¨\9f à¨\87à¨\95 à¨\96਼ਾਸ ਸਫ਼à©\87 ਨਾਲ ਸਬੰਧਿਤ ਸਫ਼ਿà¨\86à¨\82 à¨\9cਾà¨\82 à¨\95ਿਸà©\87 à¨\96਼ਾਸ ਸ਼à©\8dਰà©\87ਣà©\80 ਦà©\87 ਮà©\88à¨\82ਬਰਾà¨\82 ਦà©\87 ਹਾਲ ਵਿà¨\9a ਹà©\8bà¨\8f ਬਦਲਾਵਾà¨\82 ਨà©\82à©° ਦਰਸਾà¨\82à¨\89ਦà©\80 ਹà©\88। [[Special:Watchlist|ਤà©\81ਹਾਡà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸà¨\9f]] ਵਿà¨\9a ਮà©\8cà¨\9cà©\82ਦ ਸਫ਼à©\87 ਮà©\8bà¨\9fà©\87 à¨\85ੱਖਰਾਂ ਵਿਚ ਦਿਖਾਈ ਦੇਣਗੇ।',
+'recentchangeslinked-page' => 'ਸਫ਼à©\87 ਦਾ ਨਾਮ:',
+'recentchangeslinked-to' => 'ਇਸਦੇ ਬਦਲੇ ਇਸ ਸਫ਼ੇ ਨਾਲ਼ ਜੁੜੇ ਸਫ਼ਿਆਂ ਵਿਚ ਹੋਏ ਬਦਲਾਅ ਦਿਖਾਓ',
# Upload
'upload' => 'ਫਾਇਲ ਅੱਪਲੋਡ ਕਰੋ',
'uploadnologintext' => 'ਤੁਹਾਨੂੰ[[Special:UserLogin|logged in] ਕਰਨਾ ਪਵੇਗਾ]
to upload files.',
'uploaderror' => 'ਅੱਪਲੋਡ ਗਲਤੀ',
+'upload-recreate-warning' => "'''ਖ਼ਬਰਦਾਰ: ਇਸ ਨਾਮ ਦੀ ਫ਼ਾਈਲ ਮਿਟਾਈ ਜਾਂ ਹੋਰ ਨਾਮ ਤੇ ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।'''
+ਮਿਟਾਉਣ ਅਤੇ ਭੇਜੇ ਜਾਣ ਦਾ ਚਿੱਠਾ ਸਹੂਲਤ ਲਈ ਇੱਥੇ ਦਿੱਤਾ ਗਿਆ ਹੈ:",
'uploadlog' => 'ਅੱਪਲੋਡ ਲਾਗ',
-'uploadlogpage' => 'à¨\85ੱਪਲà©\8bਡ ਲਾà¨\97',
+'uploadlogpage' => 'à¨\85ੱਪਲà©\8bਡ ਦਾ à¨\9aਿੱਠਾ',
'filename' => 'ਫਾਇਲ ਨਾਂ',
-'filedesc' => 'ਸੰà¨\96à©\87ਪ',
+'filedesc' => 'ਸਾਰ',
'fileuploadsummary' => 'ਸੰਖੇਪ:',
'filestatus' => 'ਕਾਪੀਰਾਈਟ ਹਾਲਤ:',
'filesource' => 'ਸੋਰਸ:',
'uploadedfiles' => 'ਅੱਪਲੋਡ ਕੀਤੀਆਂ ਫਾਇਲਾਂ',
'ignorewarning' => 'ਚੇਤਾਵਨੀ ਅਣਡਿੱਠੀ ਕਰਕੇ ਕਿਵੇਂ ਵੀ ਫਾਇਲ ਸੰਭਾਲੋ।',
+'ignorewarnings' => 'ਕੋਈ ਚੇਤਾਵਨੀ ਹੋਈ ਤਾਂ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰੋ',
'minlength1' => 'ਫਾਇਲ ਨਾਂ ਵਿੱਚ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਅੱਖਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।',
'badfilename' => 'ਫਾਇਲ ਨਾਂ "$1" ਬਦਲਿਆ ਗਿਆ ਹੈ।',
'filetype-missing' => 'ਫਾਇਲ ਦੀ ਕੋਈ ਐਕਸ਼ਟੇਸ਼ਨ ਨਹੀਂ ਹੈ (ਜਿਵੇਂ ".jpg").',
+'filename-tooshort' => 'ਫ਼ਾਈਲ ਬਹੁਤ ਛੋਟੀ ਹੈ।',
+'filetype-banned' => 'ਇਸ ਕਿਸਮ ਦੀ ਫ਼ਾਈਲ ਦੀ ਮਨਾਹੀ ਹੈ।',
+'illegal-filename' => 'ਇਸ ਫ਼ਾਈਲ-ਨਾਮ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।',
'fileexists' => "ਇਹ ਫਾਇਲ ਨਾਂ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਹ ਬਦਲਣ ਬਾਰੇ ਜਾਣਦੇ ਨਹੀਂ ਹੋ ਤਾਂ '''<tt>[[:$1]]</tt>''' ਵੇਖੋ ਜੀ। [[$1|thumb]]",
'fileexists-extension' => "ਇਸ ਨਾਂ ਨਾਲ ਰਲਦੀ ਫਾਇਲ ਮੌਜੂਦ ਹੈ: [[$2|thumb]]
* ਅੱਪਲੋਡ ਕੀਤੀ ਫਾਇਲ ਦਾ ਨਾਂ: '''<tt>[[:$1]]</tt>'''
* ਮੌਜੂਦ ਫਾਇਲ ਦਾ ਨਾਂ: '''<tt>[[:$2]]</tt>'''
ਇੱਕ ਵੱਖਰਾ ਨਾਂ ਚੁਣੋ ਜੀ",
+'file-exists-duplicate' => 'ਇਹ ਫ਼ਾਈਲ {{PLURAL:$1|ਇਸ ਫ਼ਾਈਲ|ਇਹਨਾਂ ਫ਼ਾਈਲਾਂ}} ਦੀ ਨਕਲ ਹੈ:',
'uploadwarning' => 'ਅੱਪਲੋਡ ਚੇਤਾਵਨੀ',
'savefile' => 'ਫਾਇਲ ਸੰਭਾਲੋ',
-'uploadedimage' => '"[[$1]]" ਅੱਪਲੋਡ',
+'uploadedimage' => '"[[$1]]" ਅੱਪਲੋਡ ਕੀਤੀ',
+'overwroteimage' => '"[[$1]]" ਦਾ ਨਵਾਂ ਰੂਪ ਅੱਪਲੋਡ ਕਰੋ',
'uploaddisabled' => 'ਅੱਪਲੋਡ ਆਯੋਗ ਹੈ',
'uploadvirus' => 'ਇਹ ਫਾਇਲ ਵਿੱਚ ਵਾਇਰਸ ਹੈ! ਵੇਰਵੇ ਲਈ ਵੇਖੋ: $1',
'sourcefilename' => 'ਸੋਰਸ ਫਾਇਲ ਨਾਂ:',
+'upload-maxfilesize' => 'ਫ਼ਾਈਲ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਅਕਾਰ: $1',
+'upload-description' => 'ਫ਼ਾਈਲ ਦਾ ਵੇਰਵਾ',
'watchthisupload' => 'ਇਸ ਫਾਇਲ ਨੂੰ ਵਾਚ ਕਰੋ',
'upload-success-subj' => 'ਠੀਕ ਤਰ੍ਹਾਂ ਅੱਪਲੋਡ',
'upload-warning-subj' => 'ਅੱਪਲੋਡ ਚੇਤਾਵਨੀ',
'upload-file-error' => 'ਅੰਦਰੂਨੀ ਗਲਤੀ',
'upload-misc-error' => 'ਅਣਜਾਣ ਅੱਪਲੋਡ ਗਲਤੀ',
+# File backend
+'backend-fail-notexists' => 'ਫ਼ਾਈਲ $1 ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+'backend-fail-delete' => 'ਫ਼ਾਈਲ "$1" ਮਿਟਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।',
+'backend-fail-alreadyexists' => 'ਫ਼ਾਈਲ "$1" ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।',
+'backend-fail-store' => 'ਫ਼ਾਈਲ "$1", "$2" ਵਿਚ ਸਾਂਭੀ ਨਹੀਂ ਜਾ ਸਕੀ।',
+'backend-fail-copy' => 'ਫ਼ਾਈਲ "$1", "$2" ਵਿਚ ਨਕਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ।',
+'backend-fail-move' => 'ਫ਼ਾਈਲ "$1", "$2" ਤੇ ਭੇਜੀ ਨਹੀਂ ਜਾ ਸਕੀ।',
+'backend-fail-opentemp' => 'ਆਰਜ਼ੀ ਫ਼ਾਈਲ ਖੋਲ੍ਹੀ ਨਹੀਂ ਜਾ ਸਕੀ।',
+
+# Special:UploadStash
+'uploadstash-refresh' => 'ਫ਼ਾਈਲਾਂ ਦੀ ਲਿਸਟ ਨੂੰ ਤਾਜ਼ਾ ਕਰੋ',
+
+# img_auth script messages
+'img-auth-nofile' => 'ਫ਼ਾਈਲ "$1" ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+
# Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
'upload-curl-error28' => 'ਅੱਪਲੋਡ ਟਾਈਮ-ਆਉਟ',
'license' => 'ਲਾਈਸੈਂਸਿੰਗ:',
-'license-header' => 'ਵਰਤਣ ਲà¨\88 ਮੰà¨\9c਼à©\82ਰà©\80 ਦà©\87ਣਾ',
+'license-header' => 'ਲਾà¨\88ਸੰਸ',
'nolicense' => 'ਕੁਝ ਵੀ ਚੁਣਿਆ',
'license-nopreview' => '(ਝਲਕ ਉਪਲੱਬਧ ਨਹੀਂ)',
'upload_source_file' => ' (ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਉੱਤੇ ਇੱਕ ਫਾਇਲ)',
# Special:ListFiles
'imgfile' => 'ਫਾਇਲ',
'listfiles' => 'ਫਾਇਲ ਲਿਸਟ',
+'listfiles_thumb' => 'ਨਮੂਨਾ-ਤਸਵੀਰ',
'listfiles_date' => 'ਮਿਤੀ',
'listfiles_name' => 'ਨਾਂ',
'listfiles_user' => 'ਯੂਜ਼ਰ',
'listfiles_count' => 'ਵਰਜਨ',
# File description page
-'file-anchor-link' => 'ਫਾà¨\87ਲ',
-'filehist' => 'ਫਾà¨\87ਲ ਅਤੀਤ',
-'filehist-help' => 'à¨\87à¨\9bਿਤ ਸਮà©\87à¨\82 ਤà©\87 à¨\9fਿà¨\95-à¨\9fਿਕ ਕਰੋ ਤਾਂ ਉਸ ਸਮੇਂ ਦੀ ਫਾਈਲ ਪੇਸ਼ ਹੋ ਜਾਵੇਗੀ।',
+'file-anchor-link' => 'ਫ਼ਾà¨\88ਲ',
+'filehist' => 'ਫ਼ਾà¨\88ਲ ਦਾ ਅਤੀਤ',
+'filehist-help' => 'ਤਾਰà©\80à¨\96਼/ਸਮà©\87à¨\82 â\80\99ਤà©\87 à¨\95ਲਿੱਕ ਕਰੋ ਤਾਂ ਉਸ ਸਮੇਂ ਦੀ ਫਾਈਲ ਪੇਸ਼ ਹੋ ਜਾਵੇਗੀ।',
'filehist-deleteall' => 'ਸਭ ਹਟਾਓ',
'filehist-deleteone' => 'ਇਹ ਹਟਾਓ',
-'filehist-revert' => 'ਰà©\80ਵਰà¨\9f',
+'filehist-revert' => 'à¨\89ਲà¨\9fਾà¨\93',
'filehist-current' => 'ਮੌਜੂਦਾ',
-'filehist-datetime' => 'ਮਿਤà©\80/ਸਮਾਂ',
-'filehist-thumb' => 'à¨\85à©°à¨\97à©\82ਠਾà¨\95ਾਰ',
+'filehist-datetime' => 'ਤਾਰà©\80à¨\96਼/ਸਮਾਂ',
+'filehist-thumb' => 'ਨਮà©\82ਨਾ',
'filehist-thumbtext' => '$1 ਦੇ ਸਮੇਂ ਦੇ ਸੰਸਕਰਨ ਦਾ ਅੰਗੂਠਾਕਾਰ ਪ੍ਰਤੀਰੂਪ',
-'filehist-user' => 'ਯੂਜ਼ਰ',
-'filehist-dimensions' => 'ਮਾਪ',
+'filehist-nothumb' => 'ਕੋਈ ਨਮੂਨਾ-ਤਸਵੀਰ ਨਹੀਂ',
+'filehist-user' => 'ਮੈਂਬਰ',
+'filehist-dimensions' => 'ਨਾਪ',
'filehist-filesize' => 'ਫਾਇਲ ਆਕਾਰ',
'filehist-comment' => 'ਟਿੱਪਣੀ',
-'imagelinks' => 'ਫ਼ਾਈਲ ਦੀ ਵਰਤੌਂ',
-'linkstoimage' => 'ਹੇਠ ਲਿਖਿਤ {{PLURAL:$1|ਪੰਨੇ ਦਾ ਹਵਾਲਾ ਹੈ|$1 ਪੰਨੇ ਦੇ ਹਵਾਲੇ ਹਨ}} to this file:',
-'nolinkstoimage' => 'ਇਸ ਮਿਸਲ ਨਾਲ ਕੋਈ ਵਿ ਸਫ਼ੇ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ ।',
-'sharedupload-desc-here' => 'ਇਹ ਮਿਸਲ $1 ਦੀ ਹੈ ਅਤੇ ਹੋਰ ਪ੍ਰਾਜੈਕਟਾਂ ਵਿਚ ਵੀ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ । ਇਸ ਦੇ [$2 ਮਿਸਲ ਵਾਲੇ ਬ੍ਰਿਤਾਂਤ ਪੰਨੇ] ਵਿਚ ਮੌਜੂਦ ਵਰਨਣ ਨਿਮਨ ਲਿਖਿਤ ਹੈ।',
+'imagelinks' => 'ਫ਼ਾਈਲ ਦੀ ਵਰਤੋਂ',
+'linkstoimage' => 'ਇਹ {{PLURAL:$1|ਸਫ਼ੇ ਦੇ ਲਿੰਕ|$1 ਸਫ਼ੇ}} ਇਸ ਫ਼ਾਈਲ ਨਾਲ਼ ਜੋੜਦੇ ਹਨੇ:',
+'nolinkstoimage' => 'ਕੋਈ ਵੀ ਸਫ਼ਾ ਇਸ ਫ਼ਾਈਲ ਨਾਲ਼ ਨਹੀਂ ਜੋੜਦਾ।',
+'sharedupload' => 'ਇਹ ਫ਼ਾਈਲ $1 ਤੋਂ ਹੈ ਅਤੇ ਸ਼ਾਇਦ ਦੂਜੇ ਪ੍ਰੋਜੈਕਟਾਂ ਤੇ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ।',
+'sharedupload-desc-there' => 'ਇਹ ਫ਼ਾਈਲ $1 ਤੋਂ ਹੈ ਅਤੇ ਸ਼ਾਇਦ ਦੂਜੇ ਪ੍ਰੋਜੈਕਟਾਂ ਦੁਆਰਾ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਜ਼ਿਆਦਾ ਜਾਣਕਾਰੀ ਲਈ ਮਿਹਰਬਾਨੀ ਕਰਕੇ [$2 ਫ਼ਾਈਲ ਦਾ ਵੇਰਵਾ ਸਫ਼ਾ] ਵੇਖੋ।',
+'sharedupload-desc-here' => 'ਇਹ ਫ਼ਾਈਲ $1 ਦੀ ਹੈ ਅਤੇ ਹੋਰ ਪ੍ਰਾਜੈਕਟਾਂ ਵਿਚ ਵੀ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ । ਇਸ [$2 ਫ਼ਾਈਲ ਦੇ ਵੇਰਵਾ ਸਫ਼ੇ] ਵਿਚ ਮੌਜੂਦ ਵੇਰਵਾ ਹੇਠ ਦਿਸ ਰਿਹਾ ਹੈ।',
+'sharedupload-desc-edit' => 'ਇਹ ਫ਼ਾਈਲ $1 ਤੋਂ ਹੈ ਅਤੇ ਸ਼ਾਇਦ ਦੂਜੇ ਪ੍ਰੋਜੈਕਟਾਂ ਦੁਆਰਾ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਸ਼ਾਇਦ ਤੁਸੀਂ [$2 ਫ਼ਾਈਲ ਦੇ ਵੇਰਵੇ ਸਫ਼ੇ] ਤੇ ਇਸਦਾ ਵੇਰਵਾ ਬਦਲਣਾ ਚਾਹੋ।',
+'sharedupload-desc-create' => 'ਇਹ ਫ਼ਾਈਲ $1 ਤੋਂ ਹੈ ਅਤੇ ਸ਼ਾਇਦ ਦੂਜੇ ਪ੍ਰੋਜੈਕਟਾਂ ਦੁਆਰਾ ਵਰਤੀ ਜਾ ਸਕਦੀ ਹੈ।
+ਸ਼ਾਇਦ ਤੁਸੀਂ [$2 ਫ਼ਾਈਲ ਦੇ ਵੇਰਵੇ ਸਫ਼ੇ] ਤੇ ਇਸਦਾ ਵੇਰਵਾ ਬਦਲਣਾ ਚਾਹੋ।',
+'filepage-nofile' => 'ਇਸ ਨਾਮ ਦੀ ਕੋਈ ਫ਼ਾਈਲ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+'filepage-nofile-link' => 'ਇਸ ਨਾਮ ਦੀ ਕੋਈ ਫ਼ਾਈਲ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਪਰ ਤੁਸੀਂ [$1 ਇਸਨੂੰ ਅੱਪਲੋਡ ਕਰ] ਸਕਦੇ ਹੋ।',
'uploadnewversion-linktext' => 'ਇਸ ਫਾਇਲ ਦਾ ਇੱਕ ਨਵਾਂ ਵਰਜਨ ਅੱਪਲੋਡ ਕਰੋ',
+'shared-repo-from' => '$1 ਤੋਂ',
# File reversion
'filerevert' => '$1 ਰੀਵਰਟ',
'download' => 'ਡਾਊਨਲੋਡ',
# Random page
-'randompage' => 'ਰਲਵਾà¨\82 ਪà©\87à¨\9c਼',
+'randompage' => 'ਰਲ਼ਵਾà¨\82 ਸਫ਼ਾ',
# Statistics
'statistics' => 'ਅੰਕੜੇ',
'statistics-header-edits' => 'ਸੋਧ ਅੰਕੜੇ',
'statistics-header-views' => 'ਵੇਖਣ ਅੰਕੜੇ',
'statistics-header-users' => 'ਯੂਜ਼ਰ ਅੰਕੜੇ',
+'statistics-edits-average' => 'ਪ੍ਰਤੀ ਸਫ਼ਾ ਔਸਤਨ ਸੋਧਾਂ',
+'statistics-users-active' => 'ਚੁਸਤ ਮੈਂਬਰ',
'statistics-mostpopular' => 'ਸਭ ਤੋਂ ਵੱਧ ਵੇਖੇ ਪੇਜ',
'brokenredirects-edit' => 'ਸੋਧ',
'unusedcategories' => 'ਅਣਵਰਤੀਆਂ ਕੈਟਾਗਰੀਆਂ',
'unusedimages' => 'ਅਣਵਰਤੀਆਂ ਫਾਇਲਾਂ',
'popularpages' => 'ਪਾਪੂਲਰ ਪੇਜ',
-'prefixindex' => 'à¨\87ਸ à¨\85à¨\97à©\87ਤਰ ਵਾਲà©\87 ਸਾਰà©\87 ਪੰਨੇ',
+'prefixindex' => 'à¨\87ਸ à¨\85à¨\97à©\87ਤਰ ਵਾਲ਼à©\87 ਸਾਰà©\87 ਸਫ਼ੇ',
'shortpages' => 'ਛੋਟੇ ਪੇਜ',
'listusers' => 'ਯੂਜ਼ਰ ਲਿਸਟ',
-'usercreated' => ' $1 ਨੂੰ $2 ਵਜੇ {{GENDER:$3|ਮੈਂਬਰ ਨੇ ਰਚਿਆ}}',
-'newpages' => 'ਨਵà©\87à¨\82 ਪà©\87à¨\9c',
+'usercreated' => '$1 ਨੂੰ $2 ’ਤੇ {{GENDER:$3|ਰਚਿਆ}}',
+'newpages' => 'ਨਵà©\87à¨\82 ਸਫ਼à©\87',
'newpages-username' => 'ਯੂਜ਼ਰ ਨਾਂ:',
'ancientpages' => 'ਸਭ ਤੋਂ ਪੁਰਾਣੇ ਪੇਜ',
'move' => 'ਭੇਜੋ',
'movethispage' => 'ਇਹ ਪੇਜ ਭੇਜੋ',
'notargettitle' => 'ਟਾਰਗੇਟ ਨਹੀਂ',
'pager-newer-n' => '{{PLURAL:$1|੧ ਨਵਾਂ|$1 ਨਵੇਂ}}',
+'pager-older-n' => '{{PLURAL:$1|੧ ਪੁਰਾਣਾ|$1 ਪੁਰਾਣੇ}}',
# Book sources
'booksources' => 'ਕਿਤਾਬ ਸਰੋਤ',
-'booksources-search-legend' => 'à¨\95ਿਤਾਬ ਸਰà©\8bਤ ਲà¨\88 à¨\96à©\8bà¨\9c',
+'booksources-search-legend' => 'à¨\95ਿਤਾਬ ਸਰà©\8bਤ à¨\96à©\8bà¨\9cà©\8b',
'booksources-go' => 'ਜਾਓ',
# Special:Log
'specialloguserlabel' => 'ਯੂਜ਼ਰ:',
'speciallogtitlelabel' => 'ਟਾਇਟਲ:',
-'log' => 'ਲਾà¨\97',
+'log' => 'à¨\9aਿੱਠà©\87',
'all-logs-page' => 'ਸਭ ਲਾਗ',
# Special:AllPages
-'allpages' => 'ਸਠਪà©\87à¨\9c',
+'allpages' => 'ਸਠਸਫ਼à©\87',
'alphaindexline' => '$1 ਤੋਂ $2',
'nextpage' => 'ਅੱਗੇ ਪੇਜ ($1)',
'prevpage' => 'ਪਿੱਛੇ ਪੇਜ ($1)',
-'allarticles' => 'ਸਠਲà©\87à¨\96',
+'allarticles' => 'ਸਠਸਫ਼à©\87',
'allinnamespace' => 'ਸਭ ਪੇਜ ($1 ਨੇਮਸਪੇਸ)',
'allnotinnamespace' => 'ਸਭ ਪੇਜ ($1 ਨੇਮਸਪੇਸ ਵਿੱਚ ਨਹੀਂ)',
'allpagesprev' => 'ਪਿੱਛੇ',
'listusers-noresult' => 'ਕੋਈ ਯੂਜ਼ਰ ਨਹੀਂ ਲੱਭਿਆ।',
# Special:Log/newusers
-'newuserlogpage' => 'ਮà©\88à¨\82ਬਰ à¨\96ਾਤਾ à¨\89ਸਾਰà©\80 ਚਿੱਠਾ',
+'newuserlogpage' => 'ਬਣਾà¨\8f à¨\96ਾਤਿà¨\86à¨\82 ਦਾ ਚਿੱਠਾ',
# Special:ListGroupRights
'listgrouprights-group' => 'ਗਰੁੱਪ',
# E-mail user
'mailnologin' => 'ਕੋਈ ਭੇਜਣ ਐਡਰੈੱਸ ਨਹੀਂ',
-'emailuser' => 'à¨\87ਹ ਯà©\82à¨\9c਼ਰ ਨà©\82à©° à¨\88ਮà©\87ਲ à¨\95ਰੋ',
+'emailuser' => 'à¨\87ਸ ਮà©\88à¨\82ਬਰ ਨà©\82à©° à¨\88-ਮà©\87ਲ à¨à©\87à¨\9cੋ',
'emailpage' => 'ਯੂਜ਼ਰ ਨੂੰ ਈਮੇਲ ਕਰੋ',
'defemailsubject' => '{{SITENAME}} ਈਮੇਲ',
'noemailtitle' => 'ਕੋਈ ਈਮੇਲ ਐਡਰੈੱਸ ਨਹੀਂ',
'emailsenttext' => 'ਤੁਹਾਡੀ ਈਮੇਲ ਭੇਜੀ ਗਈ ਹੈ।',
# Watchlist
-'watchlist' => 'ਮà©\87ਰà©\80 ਵਾà¨\9a-ਲਿਸਟ',
-'mywatchlist' => 'ਮà©\87ਰà©\80 ਵਾà¨\9a-ਲਿਸਟ',
+'watchlist' => 'ਮà©\87ਰà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ',
+'mywatchlist' => 'ਮà©\87ਰà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ',
'watchlistfor2' => '$1 $2 ਲਈ',
'watchnologin' => 'ਲਾਗਇਨ ਨਹੀਂ',
-'watch' => 'ਵਾà¨\9a',
+'watch' => 'ਨà¨\9c਼ਰ ਰੱà¨\96à©\8b',
'watchthispage' => 'ਇਹ ਪੇਜ ਵਾਚ ਕਰੋ',
-'unwatch' => 'ਅਣ-ਵਾਚ',
-'wlshowlast' => 'ਆਖਰੀ $1 ਦਿਨ $2 ਘੰਟੇ $3 ਵੇਖੋ',
-'watchlist-options' => 'ਧਿਆਨ ਗੋਚਰ ਸੂਚੀ ਵਿਕਲਪ',
+'unwatch' => 'ਨਜ਼ਰ ਹਟਾਓ',
+'watchlist-details' => 'ਗੱਲ-ਬਾਤ ਸਫ਼ੇ ਨਾ ਗਿਣਦੇ ਹੋਏ, ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ {{PLURAL:$1|$1 ਸਫ਼ਾ ਹੈ|$1 ਸਫ਼ੇ ਹਨ}}।',
+'wlshowlast' => 'ਆਖ਼ਰੀ $1 ਦਿਨ $2 ਘੰਟੇ $3 ਵਖਾਓ',
+'watchlist-options' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਦੇ ਇਖ਼ਤਿਆਰ',
# Displayed when you click the "watch" button and it is in the process of watching
'watching' => 'ਨਿਗ੍ਹਾ (ਵਾਚ) ਰੱਖੀ ਜਾ ਰਹੀ ਹੈ...',
'exblank' => 'ਪੇਜ ਖਾਲੀ ਹੈ',
'delete-confirm' => '"$1" ਹਟਾਓ',
'delete-legend' => 'ਹਟਾਓ',
-'actioncomplete' => 'à¨\90à¨\95ਸ਼ਨ ਪà©\82ਰਾ ਹà©\8bà¨\87à¨\86',
-'actionfailed' => 'ਹਰà¨\95ਤ ਨਿਸ਼ਫ਼ਲ',
-'dellogpage' => 'ਹà¨\9fਾà¨\89ਣ ਲਾà¨\97',
+'actioncomplete' => 'à¨\95ਾਰਵਾà¨\88 ਪà©\82ਰà©\80 ਹà©\8bà¨\88',
+'actionfailed' => 'à¨\95ਾਰਵਾà¨\88 ਨਾà¨\95ਾਮ',
+'dellogpage' => 'ਮਿà¨\9fਾà¨\89ਣ ਦਾ à¨\9aਿੱਠਾ',
'deletecomment' => 'ਕਾਰਨ:',
'deleteotherreason' => 'ਹੋਰ/ਵਾਧੂ ਕਾਰਨ:',
'deletereasonotherlist' => 'ਹੋਰ ਕਾਰਨ',
# Rollback
'rollback_short' => 'ਰੋਲਬੈਕ',
-'rollbacklink' => 'ਰà©\8bਲਬà©\88à¨\95',
+'rollbacklink' => 'ਵਾਪਸ ਮà©\8bà©\9cà©\8b',
'rollbackfailed' => 'ਰੋਲਬੈਕ ਫੇਲ੍ਹ',
# Protect
-'protectlogpage' => 'ਸੁਰੱਖਿਆ ਲਾਗ',
+'protectlogpage' => 'ਸੁਰੱਖਿਆ ਚਿੱਠਾ',
+'protectedarticle' => '"[[$1]]" ਸੁਰੱਖਿਅਤ ਕੀਤਾ',
'protect-legend' => 'ਸੁਰੱਖਿਆ ਕਨਫਰਮ',
'protectcomment' => 'ਕਾਰਨ:',
'protectexpiry' => 'ਮਿਆਦ:',
# Undelete
'undeletebtn' => 'ਰੀਸਟੋਰ',
-'undeletelink' => 'ਵà©\87à¨\96à©\8b/ਰà©\80ਸà¨\9fà©\8bਰ',
-'undeleteviewlink' => 'ਦੇਖੋ',
+'undeletelink' => 'ਵà©\87à¨\96à©\8b/ਮà©\81à©\9c ਬਹਾਲ à¨\95ਰà©\8b',
+'undeleteviewlink' => 'ਵੇਖੋ',
'undeletereset' => 'ਰੀ-ਸੈੱਟ',
'undeletecomment' => 'ਟਿੱਪਣੀ:',
'undelete-show-file-submit' => 'ਹਾਂ',
# Namespace form on various pages
-'namespace' => 'ਨਾà¨\82-ਥਾà¨\82:',
+'namespace' => 'ਥਾà¨\82-ਨਾਮ:',
'invert' => 'ਉਲਟ ਚੋਣ',
'blanknamespace' => '(ਮੁੱਖ)',
# Contributions
-'contributions' => 'ਯà©\82à¨\9c਼ਰ ਯੋਗਦਾਨ',
-'contributions-title' => '$1 ਦà©\80 ਰà¨\9aਨਾ',
+'contributions' => 'ਮà©\88à¨\82ਬਰ ਯੋਗਦਾਨ',
+'contributions-title' => '$1 ਲà¨\88 ਮà©\88à¨\82ਬਰ ਯà©\8bà¨\97ਦਾਨ',
'mycontris' => 'ਮੇਰਾ ਯੋਗਦਾਨ',
'contribsub2' => '$1 ($2) ਲਈ',
-'uctop' => '(à¨\89ੱਤà©\87)',
-'month' => 'ਇਸ(ਯਾ ਹੋਰ ਪਿਛਲੇ) ਮਹੀਨੇ ਤੌਂ',
-'year' => 'ਇਸ(ਜਾਂ ਹੋਰ ਪਿਛਲੇ) ਸਾਲ ਤੌਂ',
+'uctop' => '(à¨\9fà©\80ਸà©\80)',
+'month' => 'ਇਸ (ਅਤੇ ਪਿਛਲੇ) ਮਹੀਨੇ ਤੋਂ :',
+'year' => 'ਇਸ (ਅਤੇ ਪਿਛਲੇ) ਸਾਲ ਤੋਂ :',
+'sp-contributions-newbies' => 'ਸਿਰਫ਼ ਨਵੇਂ ਮੈਂਬਰਾਂ ਦੇ ਯੋਗਦਾਨ ਵਖਾਓ',
'sp-contributions-newbies-sub' => 'ਨਵੇਂ ਅਕਾਊਂਟਾਂ ਲਈ',
-'sp-contributions-blocklog' => 'ਪਾਬੰਦà©\80 ਲਾà¨\97',
+'sp-contributions-blocklog' => 'ਪਾਬੰਦà©\80 à¨\9aਿੱਠਾ',
'sp-contributions-uploads' => 'ਅਪਲੋਡ',
-'sp-contributions-logs' => 'ਲਾà¨\97',
-'sp-contributions-talk' => 'ਗੱਲਬਾਤ',
+'sp-contributions-logs' => 'à¨\9aਿੱਠà©\87',
+'sp-contributions-talk' => 'ਗੱਲ-ਬਾਤ',
'sp-contributions-search' => 'ਯੋਗਦਾਨ ਖੋਜੋ',
-'sp-contributions-username' => 'IP à¨\90ਡਰà©\88ੱਸ ਜਾਂ ਯੂਜ਼ਰ ਨਾਂ:',
-'sp-contributions-toponly' => 'à¨\95à©\87ਵਲ à¨\89ਹà©\80 ਸੰਪਾਦਨ ਦਿà¨\96ਾà¨\93 à¨\9cà©\8b ਨਵà©\80ਨਤਮ ਸà©\8bਧਾà¨\82 ਹਨ।',
-'sp-contributions-submit' => 'ਖੋਜ',
+'sp-contributions-username' => 'IP ਪਤਾ ਜਾਂ ਯੂਜ਼ਰ ਨਾਂ:',
+'sp-contributions-toponly' => 'ਸਿਰਫ਼ à¨\89ਹà©\80 ਸà©\8bਧਾà¨\82 ਵà¨\96ਾà¨\93 à¨\9cà©\8b ਸਠਤà©\8bà¨\82 ਨਵà©\80à¨\82à¨\86à¨\82 ਹਨ',
+'sp-contributions-submit' => 'ਖੋਜੋ',
# What links here
-'whatlinkshere' => 'à¨\87ੱਥà©\87 à¨\95ਿਹà©\9cà©\87 ਲਿੰà¨\95',
-'whatlinkshere-title' => '$1 ਨਾਲ ਜੁੜੇ ਹੋਏ ਪੰਨੇ',
+'whatlinkshere' => 'à¨\95ਿਹà©\9cà©\87 (ਸਫ਼à©\87) à¨\87ੱਥà©\87 à¨\9cà©\8bà©\9cਦà©\87 ਹਨ',
+'whatlinkshere-title' => '$1 ਨਾਲ਼ ਜੋੜਦੇ ਸਫ਼ੇ',
'whatlinkshere-page' => 'ਸਫਾ:',
-'linkshere' => "ਹà©\87ਠਦਿੱਤà©\87 ਪੰਨà©\87 '''[[:$1]]''' ਨਾਲ à¨\9cà©\8bà©\9cਦà©\87 ਹਨ।",
-'nolinkshere' => "'''[[:$1]]'''ਨਾਲ ਮਿਲਦਾ ਜੁਲਦਾ ਕੋਈ ਪੰਨਾ ਨਹੀਂ ਹੈ।",
-'isredirect' => 'ਰà©\80-ਡਿਰà©\88à¨\95à¨\9f ਪà©\87à¨\9c',
+'linkshere' => "à¨\87ਹ ਸਫ਼à©\87 '''[[:$1]]''' ਨਾਲ਼ à¨\9cà©\8bà©\9cਦà©\87 ਹਨ:",
+'nolinkshere' => "ਕੋਈ ਵੀ ਸਫ਼ਾ '''[[:$1]]''' ਨਾਲ਼ ਨਹੀਂ ਜੋੜਦਾ।",
+'isredirect' => 'ਰà©\80-ਡਿਰà©\88à¨\95à¨\9f ਸਫ਼ਾ',
'istemplate' => 'ਟਾਕਰਾ ਕਰੋ',
-'isimage' => 'ਮਿਸਲ ਦà©\80 à¨\95à©\9cà©\80',
+'isimage' => 'ਫ਼ਾà¨\88ਲ ਦਾ ਲਿੰà¨\95',
'whatlinkshere-prev' => '{{PLURAL:$1|ਪਿਛਲਾ|ਪਿਛਲੇ $1}}',
'whatlinkshere-next' => '{{PLURAL:$1|ਅਗਲਾ|ਅਗਲੇ $1}}',
'whatlinkshere-links' => '← ਲਿੰਕ',
-'whatlinkshere-hideredirs' => '$1 ਗੈਰਸਿਧਾ',
+'whatlinkshere-hideredirs' => 'ਅਸਿੱਧੇ ਰਾਹ $1',
'whatlinkshere-hidetrans' => '$1 ਇੱਥੇ ਕੀ ਕੀ ਜੁੜਦਾ ਹੈ।',
'whatlinkshere-hidelinks' => '$1 ਲਿੰਕ',
-'whatlinkshere-hideimages' => '$1 ਸੰਬੰਧਿਤ ਚਿਤਰ',
-'whatlinkshere-filters' => 'ਫਿਲà¨\9fਰ',
+'whatlinkshere-hideimages' => 'ਤਸਵੀਰ ਲਿੰਕ $1',
+'whatlinkshere-filters' => 'à¨\9bਾਨਣà©\80à¨\86à¨\82',
# Block/unblock
'blockip' => 'ਯੂਜ਼ਰ ਬਲਾਕ ਕਰੋ',
'ipbreasonotherlist' => 'ਹੋਰ ਕਾਰਨ',
'ipbsubmit' => 'ਇਹ ਯੂਜ਼ਰ ਲਈ ਪਾਬੰਦੀ',
'ipbother' => 'ਹੋਰ ਟਾਈਮ:',
-'ipboptions' => '੨ à¨\98à¨\82à¨\9fà©\87:2 hours, ੧ ਦਿਨ:1 day, à©© ਦਿਨ:3 days, ੧ ਹਫà©\8dਤਾ:1 week, ੨ ਹਫà©\8dਤੇ:2 weeks, ੧ ਮਹੀਨਾ:1 month, ੩ ਮਹੀਨੇ:3 months, ੬ ਮਹੀਨੇ:6 months, ੧ ਸਾਲ:1 year, ਹਮੇਸ਼ਾ ਲਈ:infinite',
+'ipboptions' => '੨ à¨\98à©°à¨\9fà©\87:2 hours, ੧ ਦਿਨ:1 day, à©© ਦਿਨ:3 days, ੧ ਹਫ਼ਤਾ:1 week, ੨ ਹਫ਼ਤੇ:2 weeks, ੧ ਮਹੀਨਾ:1 month, ੩ ਮਹੀਨੇ:3 months, ੬ ਮਹੀਨੇ:6 months, ੧ ਸਾਲ:1 year, ਹਮੇਸ਼ਾ ਲਈ:infinite',
'ipbotheroption' => 'ਹੋਰ',
'ipbotherreason' => 'ਹੋਰ/ਆਮ ਕਾਰਨ:',
'badipaddress' => 'ਗਲਤ IP ਐਡਰੈੱਸ',
'ipb-unblock-addr' => '$1 ਅਣ-ਬਲਾਕ',
'ipb-unblock' => 'ਇੱਕ ਯੂਜ਼ਰ ਨਾਂ ਜਾਂ IP ਐਡਰੈੱਸ ਅਣ-ਬਲਾਕ ਕਰੋ',
'unblockip' => 'ਯੂਜ਼ਰ ਅਣ-ਬਲਾਕ ਕਰੋ',
+'ipblocklist' => 'ਪਾਬੰਦੀਸ਼ੁਦਾ ਮੈਂਬਰ',
'ipblocklist-submit' => 'ਖੋਜ',
'infiniteblock' => 'ਬੇਅੰਤ',
'expiringblock' => '$1 $2 ਮਿਆਦ ਖਤਮ',
'anononlyblock' => 'anon. ਹੀ',
'emailblock' => 'ਈਮੇਲ ਬਲਾਕ ਹੈ',
-'blocklink' => 'ਬਲਾà¨\95',
-'unblocklink' => 'à¨\85ਣ-ਬਲਾà¨\95',
-'change-blocklink' => 'ਬਲਾà¨\95 ਬਦਲੋ',
+'blocklink' => 'ਪਾਬੰਦà©\80 ਲਾà¨\93',
+'unblocklink' => 'ਪਾਬੰਦà©\80 ਰੱਦ à¨\95ਰà©\8b',
+'change-blocklink' => 'ਪਾਬੰਦà©\80 ਬਦਲੋ',
'contribslink' => 'ਯੋਗਦਾਨ',
+'blocklogpage' => 'ਪਾਬੰਦੀ ਚਿੱਠਾ',
+'blocklogentry' => '[[$1]] ’ਤੇ $2 ਲਈ ਪਾਬੰਦੀ ਲਾਈ। $3',
'unblocklogentry' => '$1 ਤੋਂ ਪਾਬੰਦੀ ਹਟਾਈ',
+'block-log-flags-nocreate' => 'ਖਾਤਾ ਬਣਾਉਣ ’ਤੇ ਪਾਬੰਦੀ ਹੈ',
'proxyblocksuccess' => 'ਪੂਰਾ ਹੋਇਆ',
# Developer tools
'pagemovedsub' => 'ਭੇਜਣਾ ਸਫ਼ਲ ਰਿਹਾ',
'movepage-moved' => '\'\'\'"$1" ਨੂੰ "$2"\'\'\' ਉੱਤੇ ਭੇਜਿਆ',
'movedto' => 'ਮੂਵ ਕੀਤਾ',
-'movelogpage' => 'ਮà©\82ਵ ਲਾà¨\97',
+'movelogpage' => 'à¨à©\87à¨\9cà©\87 à¨\9cਾਣ ਦਾ à¨\9aਿੱਠਾ',
'movereason' => 'ਕਾਰਨ:',
-'revertmove' => 'ਰà©\80ਵਰà¨\9f',
+'revertmove' => 'ਰੱਦ à¨\95ਰà©\8b',
'delete_and_move' => 'ਹਟਾਓ ਅਤੇ ਮੂਵ ਕਰੋ',
# Export
# Namespace 8 related
'allmessages' => 'ਸਿਸਟਮ ਸੁਨੇਹੇ',
-'allmessagesname' => 'ਨਾà¨\82',
+'allmessagesname' => 'ਨਾਮ',
'allmessagesdefault' => 'ਡਿਫਾਲਟ ਟੈਕਸਟ',
'allmessagescurrent' => 'ਮੌਜੂਦਾ ਟੈਕਸਟ',
'allmessages-language' => 'ਭਾਸ਼ਾ:',
'allmessages-filter-submit' => 'ਜਾਓ',
# Thumbnails
-'thumbnail-more' => 'ਫà©\88ਲਾਓ',
+'thumbnail-more' => 'ਵਧਾਓ',
'filemissing' => 'ਫਾਇਲ ਗੁੰਮ ਹੈ',
-'thumbnail_error' => 'à¨\85à¨\81à¨\97à©\82ਠਾ à¨\9dਲà¨\95 ਬਨਾà¨\89ਣ ਵਿà¨\9a à¨\97ਲਤà©\80 ਹà©\8bà¨\88 ਹà©\88 : $1',
+'thumbnail_error' => 'ਨਮà©\82ਨਾ ਬਣਾà¨\89ਣ ਵਿà¨\9a à¨\97਼ਲਤà©\80 ਹà©\8bà¨\88 ਹà©\88: $1',
# Special:Import
'import' => 'ਪੇਜ ਇੰਪੋਰਟ ਕਰੋ',
'import-logentry-upload-detail' => '$1 ਰੀਵਿਜ਼ਨ',
# Tooltip help for the actions
-'tooltip-pt-userpage' => 'ਤà©\81ਹਾਡਾ ਯà©\82à¨\9c਼ਰ ਸਫ਼ਾ',
-'tooltip-pt-mytalk' => 'ਤà©\81ਹਾਡਾ à¨\9aਰà¨\9aਾ ਪà©\87à¨\9c',
-'tooltip-pt-preferences' => 'ਮà©\87ਰà©\80 ਪਸੰਦ',
-'tooltip-pt-watchlist' => 'ਤà©\81ਹਾਡà©\87 ਵਿà¨\9aਾਰ à¨\97à©\8bà¨\9aਰà©\87 ਰੱà¨\96à©\87 ਪੰਨਿà¨\86à¨\82 ਦਿ ਸà©\82à¨\9aà©\80।',
-'tooltip-pt-mycontris' => 'ਮà©\87ਰà©\87 ਯà©\8bà¨\97ਦਾਨ ਦੀ ਲਿਸਟ',
-'tooltip-pt-login' => 'ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨ ਲਈ ਉਤਸ਼ਾਹਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਪਰ ਇਹ ਲਾਜ਼ਮੀ ਨਹੀਂ ਹੈ',
+'tooltip-pt-userpage' => 'ਤà©\81ਹਾਡਾ ਮà©\88à¨\82ਬਰ ਸਫ਼ਾ',
+'tooltip-pt-mytalk' => 'ਤà©\81ਹਾਡਾ à¨\97ੱਲਬਾਤ ਸਫ਼ਾ',
+'tooltip-pt-preferences' => 'ਤà©\81ਹਾਡà©\80à¨\86à¨\82 ਪਸੰਦਾà¨\82',
+'tooltip-pt-watchlist' => 'à¨\93ਹਨਾà¨\82 ਸਫ਼ਿà¨\86à¨\82 ਦà©\80 ਲਿਸà¨\9f à¨\9cà©\8b ਤà©\81ਸà©\80à¨\82 ਤਬਦà©\80ਲà©\80à¨\86à¨\82 ਲà¨\88 ਵà©\87à¨\96 ਰਹà©\87 ਹà©\8b',
+'tooltip-pt-mycontris' => 'ਤà©\81ਹਾਡà©\87 ਯà©\8bà¨\97ਦਾਨਾà¨\82 ਦੀ ਲਿਸਟ',
+'tooltip-pt-login' => 'ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨ ਲਈ ਉਤਸ਼ਾਹਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ; ਪਰ ਇਹ ਕੋਈ ਲਾਜ਼ਮੀ ਨਹੀਂ',
'tooltip-pt-logout' => 'ਲਾਗ ਆਉਟ',
'tooltip-ca-talk' => 'ਸਮਗੱਰੀ ਸਫ਼ੇ ਬਾਰੇ ਚਰਚਾ',
-'tooltip-ca-edit' => 'ਤà©\81ਸà©\80à¨\82 à¨\87ਹ ਸਫ਼ਾ ਸà©\8bਧ ਸà¨\95ਦà©\87 ਹà©\8b। ਸੰà¨à¨¾à¨²à¨£ ਤà©\8bà¨\82 ਪਹਿਲਾà¨\82 à¨\9dਲà¨\95 ਬà¨\9fਨ ਵਰਤ à¨\95à©\87 ਵà©\87à¨\96à©\8b à¨\9cà©\80',
-'tooltip-ca-addsection' => 'ਨਵਾà¨\82 à¨\96ੰਡ ਸ਼à©\81à©\8dਰੂ ਕਰੋ',
-'tooltip-ca-viewsource' => 'à¨\87ਹ ਪà©\87à¨\9c਼ ਸੁਰੱਖਿਅਤ ਹੈ।
-ਤੁਸੀਂ ਇਸ ਦਾ ਸਰੋਤ ਵੇਖ ਸਕਦੇ ਹੋ।',
-'tooltip-ca-history' => 'à¨\87ਹ ਸਫ਼à©\87 ਦਾ ਪਿà¨\9bਲਾ ਰੀਵਿਜ਼ਨ',
-'tooltip-ca-protect' => 'à¨\87ਹ ਪà©\87à¨\9c ਸà©\81ਰੱà¨\96ਿà¨\85ਤ ਬਣਾà¨\93',
-'tooltip-ca-delete' => 'à¨\87ਹ ਪà©\87à¨\9c ਹਟਾਓ',
-'tooltip-ca-move' => 'à¨\87ਹ ਪà©\87à¨\9c ਭੇਜੋ',
-'tooltip-ca-watch' => "ਇਹ ਸਫ਼ੇ ਆਪਣੀ ਵਾਚ-ਲਿਸਟ 'ਚੋਂ ਹਟਾਓ",
-'tooltip-ca-unwatch' => 'à¨\87ਹ ਸਫ਼ਾ à¨\86ਪਣà©\80 ਵਾà¨\9a-ਲਿਸà¨\9f ਤੋਂ ਹਟਾਓ',
-'tooltip-search' => 'ਖੋਜ {{SITENAME}}',
-'tooltip-search-go' => 'ਠà©\80à¨\95 à¨\87ਹ ਨਾà¨\82 ਵਾਲà©\87 ਸਫ਼à©\87 à¨\89ੱਤà©\87 à¨\9cਾà¨\89, à¨\9cà©\87 ਮà©\8cà¨\9cà©\82ਦ ਹà©\88',
-'tooltip-search-fulltext' => 'à¨\87ਸ à¨\9fà©\88à¨\95ਸà¨\9f ਲà¨\88 ਸਫ਼ਿà¨\86à¨\82 ਦà©\80 à¨\96à©\8bà¨\9c।',
-'tooltip-p-logo' => 'ਮà©\81ੱà¨\96 ਪà©\87à¨\9c',
-'tooltip-n-mainpage' => 'ਮà©\81ੱà¨\96 ਪà©\87à¨\9c à¨\96à©\8bਲà©\8dਹà©\8b',
-'tooltip-n-mainpage-description' => 'ਮà©\81ੱà¨\96 ਪà©\87à¨\9c਼ à¨\89ੱਤੇ ਜਾਓ',
-'tooltip-n-portal' => 'ਪਰੋਜੈਕਟ ਬਾਰੇ, ਤੁਸੀਂ ਕੀ ਕਰ ਸਕਦੇ ਹੋ, ਕਿੱਥੇ ਕੁਝ ਲੱਭ ਸਕਦੇ ਹੋ',
+'tooltip-ca-edit' => 'ਤà©\81ਸà©\80à¨\82 à¨\87ਹ ਸਫ਼ਾ ਸà©\8bਧ ਸà¨\95ਦà©\87 ਹà©\8b। ਮਿਹਰਬਾਨà©\80 à¨\95ਰà¨\95à©\87 ਸੰà¨à¨¾à¨²à¨£ ਤà©\8bà¨\82 ਪਹਿਲਾà¨\82 à¨\9dਲà¨\95 ਬà¨\9fਨ ਵਰਤà©\8b',
+'tooltip-ca-addsection' => 'ਨਵਾà¨\82 à¨à¨¾à¨\97 ਸ਼à©\81ਰੂ ਕਰੋ',
+'tooltip-ca-viewsource' => 'à¨\87ਹ ਸਫ਼ਾ ਸੁਰੱਖਿਅਤ ਹੈ।
+ਤੁਸੀਂ ਇਸਦਾ ਸਰੋਤ ਵੇਖ ਸਕਦੇ ਹੋ।',
+'tooltip-ca-history' => 'à¨\87ਸ ਸਫ਼à©\87 ਦà©\87 ਪਿà¨\9bਲà©\87 ਰੀਵਿਜ਼ਨ',
+'tooltip-ca-protect' => 'à¨\87ਹ ਸਫ਼ਾ ਮਹਿਫ਼à©\82à¨\9c਼ à¨\95ਰà©\8b',
+'tooltip-ca-delete' => 'à¨\87ਹ ਸਫ਼ਾ ਮਿਟਾਓ',
+'tooltip-ca-move' => 'à¨\87ਹ ਸਫ਼ਾ ਭੇਜੋ',
+'tooltip-ca-watch' => 'ਇਹ ਸਫ਼ਾ ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਸ਼ਾਮਲ ਕਰੋ',
+'tooltip-ca-unwatch' => 'à¨\87ਹ ਸਫ਼ਾ à¨\86ਪਣà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸà¨\9f â\80\99à¨\9aੋਂ ਹਟਾਓ',
+'tooltip-search' => '{{SITENAME}} ’ਤੇ ਖੋਜੋ',
+'tooltip-search-go' => 'ਠà©\80à¨\95 à¨\87ਸ ਨਾਮ ਵਾਲ਼à©\87 ਸਫ਼à©\87 â\80\99ਤà©\87 à¨\9cਾà¨\89, à¨\9cà©\87 ਮà©\8cà¨\9cà©\82ਦ ਹà©\88 ਤਾà¨\82',
+'tooltip-search-fulltext' => 'à¨\87ਸ ਲਿà¨\96ਤ ਲà¨\88 ਸਫ਼à©\87 ਲੱà¨à©\8b',
+'tooltip-p-logo' => 'ਮà©\81ੱà¨\96 ਸਫ਼à©\87 â\80\99ਤà©\87 à¨\9cਾà¨\93',
+'tooltip-n-mainpage' => 'ਮà©\81ੱà¨\96 ਸਫ਼à©\87 â\80\99ਤà©\87 à¨\9cਾà¨\93',
+'tooltip-n-mainpage-description' => 'ਮà©\81ੱà¨\96 ਸਫ਼à©\87 â\80\99ਤੇ ਜਾਓ',
+'tooltip-n-portal' => 'ਪਰੋਜੈਕਟ ਬਾਰੇ, ਤੁਸੀਂ ਕੀ ਕਰ ਸਕਦੇ ਹੋ, ਕਿੱਥੇ ਕੁਝ ਲੱਭਣਾ ਹੈ',
'tooltip-n-currentevents' => 'ਮੌਜੂਦਾ ਸਮਾਗਮ ਬਾਰੇ ਪਿਛਲੀ ਜਾਣਕਾਰੀ ਲੱਭੋ',
-'tooltip-n-recentchanges' => 'ਵਿà¨\95ਿ ਵਿੱà¨\9a ਤਾà¨\9c਼ਾ ਬਦਲਾà¨\85 ਦੀ ਲਿਸਟ',
-'tooltip-n-randompage' => 'à¨\87ੱà¨\95 ਰਲਵਾà¨\82 ਪà©\87à¨\9c ਲੋਡ ਕਰੋ',
+'tooltip-n-recentchanges' => 'ਵਿà¨\95à©\80 â\80\99à¨\9a ਤਾà¨\9c਼ਾ ਤਬਦà©\80ਲà©\80à¨\86à¨\82 ਦੀ ਲਿਸਟ',
+'tooltip-n-randompage' => 'à¨\87à¨\95 ਰਲ਼ਵਾà¨\82 ਸਫ਼ਾ ਲੋਡ ਕਰੋ',
'tooltip-n-help' => 'ਖੋਜਣ ਲਈ ਥਾਂ',
-'tooltip-t-whatlinkshere' => 'ਸਠਵਿà¨\95ਿ ਸਫ਼ਿà¨\86à¨\82 ਦà©\80 ਲਿਸà¨\9f, à¨\9cà©\8b à¨\87ੱਥà©\87 ਲਿੰà¨\95 à¨\95à©\80ਤੇ ਹਨ',
-'tooltip-t-recentchangeslinked' => 'à¨\87ਹ ਸਫ਼à©\87 ਤà©\8bà¨\82 ਲਿੰà¨\95 à¨\95à©\80ਤà©\87 ਸਫ਼ਿà¨\86à¨\82 ਵਿੱà¨\9a ਤਾà¨\9c਼ਾ ਬਦਲਾà¨\85',
-'tooltip-feed-atom' => 'à¨\87ਸ ਪੰਨੇ ਦੀ ਐਟਮ ਫ਼ੀਡ',
-'tooltip-t-contributions' => 'à¨\87ਸ ਮà©\88à¨\82ਬਰ ਦਾ ਯà©\8bà¨\97ਦਾਨ ਦਿà¨\96ਾà¨\93',
-'tooltip-t-emailuser' => 'à¨\87ਹ ਯà©\82à¨\9c਼ਰ ਨà©\82à©° ਮੇਲ ਭੇਜੋ',
+'tooltip-t-whatlinkshere' => 'ਵਿà¨\95à©\80 ਦà©\87 ਸਾਰà©\87 ਸਫ਼ਿà¨\86à¨\82 ਦà©\80 ਲਿਸà¨\9f, à¨\9cà©\8b à¨\87ੱਥà©\87 à¨\9cà©\8bà©\9cਦੇ ਹਨ',
+'tooltip-t-recentchangeslinked' => 'à¨\87ਸ ਸਫ਼à©\87 ਤà©\8bà¨\82 ਲਿੰà¨\95 à¨\95à©\80ਤà©\87 ਸਫ਼ਿà¨\86à¨\82 ਵਿੱà¨\9a ਤਾà¨\9c਼ਾ ਤਬਦà©\80ਲà©\80à¨\86à¨\82',
+'tooltip-feed-atom' => 'à¨\87ਸ ਸਫ਼ੇ ਦੀ ਐਟਮ ਫ਼ੀਡ',
+'tooltip-t-contributions' => 'à¨\87ਸ ਮà©\88à¨\82ਬਰ ਦà©\87 ਯà©\8bà¨\97ਦਾਨ ਦà©\80 ਲਿਸà¨\9f',
+'tooltip-t-emailuser' => 'à¨\87ਸ ਮà©\88à¨\82ਬਰ ਨà©\82à©° à¨\88-ਮੇਲ ਭੇਜੋ',
'tooltip-t-upload' => 'ਚਿੱਤਰ ਜਾਂ ਮੀਡਿਆ ਫਾਇਲਾਂ ਅੱਪਲੋਡ ਕਰੋ',
-'tooltip-t-specialpages' => 'ਸਠà¨\96ਾਸ ਸਫ਼ਿਆਂ ਦੀ ਲਿਸਟ',
+'tooltip-t-specialpages' => 'ਸਾਰà©\87 à¨\96਼ਾਸ ਸਫ਼ਿਆਂ ਦੀ ਲਿਸਟ',
'tooltip-t-print' => 'ਇਹ ਸਫ਼ੇ ਦਾ ਛਪਣਯੋਗ ਵਰਜਨ',
-'tooltip-t-permalink' => 'ਸਫ਼à©\87 ਦà©\87 à¨\87ਹ ਰà©\80ਵਿà¨\9c਼ਨ ਲà¨\88 ਪੱà¨\95à©\87 ਲਿੰਕ',
-'tooltip-ca-nstab-main' => 'ਸਮà¨\97ੱਰà©\80 ਪà©\87à¨\9c ਵੇਖੋ',
-'tooltip-ca-nstab-user' => 'ਯà©\82à¨\9c਼ਰ ਪà©\87à¨\9c ਵੇਖੋ',
+'tooltip-t-permalink' => 'ਸਫ਼à©\87 ਦà©\87 à¨\87ਸ ਰà©\80ਵਿà¨\9c਼ਨ ਲà¨\88 ਪੱà¨\95ਾ ਲਿੰਕ',
+'tooltip-ca-nstab-main' => 'ਸਮੱà¨\97ਰà©\80 ਸਫ਼ਾ ਵੇਖੋ',
+'tooltip-ca-nstab-user' => 'ਮà©\88à¨\82ਬਰ ਸਫ਼ਾ ਵੇਖੋ',
'tooltip-ca-nstab-media' => 'ਮੀਡਿਆ ਪੇਜ ਵੇਖੋ',
-'tooltip-ca-nstab-special' => 'à¨\87ਹ à¨\96ਾਸ ਸਫ਼ਾ ਹà©\88, ਤà©\81ਸà©\80à¨\82 à¨\87ਸ ਸਫ਼à©\87 ਨà©\82à©° ਸà©\8bਧ ਨਹà©\80à¨\82 ਸà¨\95ਦà©\87 ਹà©\8b',
-'tooltip-ca-nstab-project' => 'ਪਰà©\8bà¨\9cà©\88à¨\95à¨\9f ਪà©\87à¨\9c ਵੇਖੋ',
-'tooltip-ca-nstab-image' => 'à¨\9aਿੱਤਰ ਪà©\87à¨\9c ਵੇਖੋ',
+'tooltip-ca-nstab-special' => 'à¨\87ਹ à¨\96਼ਾਸ ਸਫ਼ਾ ਹà©\88, ਤà©\81ਸà©\80à¨\82 à¨\87ਸ ਸਫ਼à©\87 ਨà©\82à©° ਸà©\8bਧ ਨਹà©\80à¨\82 ਸà¨\95ਦà©\87।',
+'tooltip-ca-nstab-project' => 'ਪà©\8dਰà©\8bà¨\9cà©\88à¨\95à¨\9f ਸਫ਼ਾ ਵੇਖੋ',
+'tooltip-ca-nstab-image' => 'ਫ਼ਾà¨\88ਲ ਸਫ਼ਾ ਵੇਖੋ',
'tooltip-ca-nstab-mediawiki' => 'ਸਿਸਟਮ ਸੁਨੇਹੇ ਵੇਖੋ',
-'tooltip-ca-nstab-template' => 'à¨\9fà©\88ਪਲà©\87à¨\9f ਵੇਖੋ',
+'tooltip-ca-nstab-template' => 'ਸਾà¨\82à¨\9aਾ ਵੇਖੋ',
'tooltip-ca-nstab-help' => 'ਮੱਦਦ ਪੇਜ ਵੇਖੋ',
-'tooltip-ca-nstab-category' => 'à¨\95à©\88à¨\9fਾà¨\97ਰà©\80 ਪà©\87à¨\9c ਵੇਖੋ',
-'tooltip-minoredit' => 'ਇਸ ਤੇ ਛੋਟੇ ਬਦਲਾਅ ਦਾ ਨਿਸ਼ਾਨ ਲਾਓ',
-'tooltip-save' => 'à¨\86ਪਣà©\87 ਬਦਲਾà¨\85 ਸੰà¨à¨¾à¨²ੋ',
-'tooltip-preview' => 'à¨\86ਪਣà©\87 ਬਦਲਾà¨\85 ਦà©\80 à¨\9dਲà¨\95 ਵà©\87à¨\96à©\8b, ਸੰà¨à¨¾à¨²à¨£ ਤà©\8bà¨\82 ਪਹਿਲਾà¨\82 à¨\87ਹ ਵਰਤà©\8bà¨\82 à¨\9cà©\80!',
-'tooltip-diff' => 'à¨\87ਸ ਪਾਠਵਿà¨\9a à¨\86ਪਣà©\87 à¨\95à©\80ਤà©\87 ਹà©\8bà¨\8f ਬਦਲਾà¨\85 ਦà©\87à¨\96à©\8b',
-'tooltip-compareselectedversions' => 'à¨\87ਸ ਪੰਨà©\87 ਦà©\87 à¨\9aà©\81ਣà©\87 ਹà©\8bà¨\8f ਸ਼à©\8bਧਾà¨\82 ਵਿà¨\9a ਫ਼ਰà¨\95 ਦੇਖੋ',
-'tooltip-watch' => 'à¨\87ਹ ਸਫ਼à©\87 ਨà©\82à©° à¨\86ਪਣà©\80 ਵਾà¨\9a-ਲਿਸਟ ਵਿਚ ਪਾਓ',
+'tooltip-ca-nstab-category' => 'à¨\95à©\88à¨\9fਾà¨\97ਰà©\80 ਸਫ਼ਾ ਵੇਖੋ',
+'tooltip-minoredit' => 'ਇਸ ’ਤੇ ਛੋਟੀ ਤਬਦਲੀ ਦੇ ਤੌਰ ’ਤੇ ਨਿਸ਼ਾਨ ਲਾਓ',
+'tooltip-save' => 'à¨\86ਪਣà©\80à¨\86à¨\82 ਤਬਦà©\80ਲà©\80à¨\86à¨\82 ਸਾà¨\82à¨ੋ',
+'tooltip-preview' => 'à¨\86ਪਣà©\80 ਤਬਦà©\80ਲà©\80 ਦà©\80 à¨\9dਲà¨\95 ਵà©\87à¨\96à©\8b, ਸਾà¨\82à¨à¨£ ਤà©\8bà¨\82 ਪਹਿਲਾà¨\82 à¨\87ਹ ਵਰਤà©\8bà¨\82!',
+'tooltip-diff' => 'ਤà©\81ਹਾਡà©\87 ਦà©\81à¨\86ਰਾ ਲਿà¨\96ਤ ਵਿà¨\9a à¨\95à©\80ਤà©\80à¨\86à¨\82 ਤਬਦà©\80ਲà©\80à¨\86à¨\82 ਵà¨\96ਾà¨\89à¨\82ਦਾ ਹà©\88',
+'tooltip-compareselectedversions' => 'à¨\87ਸ ਸਫ਼à©\87 ਦà©\87 ਦà©\8b à¨\9aà©\81ਣà©\87 ਹà©\8bà¨\8f ਸà©\8bਧਾà¨\82 ਵਿà¨\9a ਫ਼ਰà¨\95 ਵੇਖੋ',
+'tooltip-watch' => 'à¨\87ਸ ਸਫ਼à©\87 ਨà©\82à©° à¨\86ਪਣà©\80 ਨਿà¨\97ਰਾਨà©\80-ਲਿਸਟ ਵਿਚ ਪਾਓ',
'tooltip-upload' => 'ਅੱਪਲੋਡ ਸਟਾਰਟ ਕਰੋ',
-'tooltip-rollback' => "'' ਵਾਪਸ ਲੈ ਜਾਓ '' ਇਕ ਝਟਕੇ ਵਿਚ ਹੀ ਪਿਛਲੇ ਯੂਜ਼ਰ ਦੇ ਬਦਲਾਵਾਂ ਨੂੰ ਗਾਇਬ ਕਰ ਦੇਂਦਾ ਹੈ।",
-'tooltip-undo' => '" ਪੁਰਾਣੀ ਹਾਲਤ ਵਿਚ ਪਰਤੋ " ਇਸ ਬਦਲਾਅ ਨੂੰ ਰੱਦ ਕਰਕੇ ਸੰਪਾਦਨ ਫਾਰਮ ਨੂੰ ਝਲਕ ਦੇ ਅੰਦਾਜ਼ ਵਿਚ ਦਿਖਾਂਦਾ ਹੈ।
-à¨\87ਸ ਦਵਾਰਾ ਸਾਰ ਵਿà¨\9a ਪà©\81ਰਾਣà©\87 ਹਾਲਤ ਵਿà¨\9a ਪਰਤਣ ਦਾ ਕਾਰਨ ਲਿਖਿਆ ਜਾ ਸਕਦਾ ਹੈ।',
-'tooltip-summary' => 'à¨\87à¨\95 ਸੰà¨\96à©\87ਪ à¨\96à©\81ਲਾਸਾ ਦਰਜ ਕਰੋ',
+'tooltip-rollback' => "''ਵਾਪਸ ਮੋੜੋ'' ਇਕ ਹੀ ਕਲਿੱਕ ਨਾਲ਼ ਆਖ਼ਰੀ ਯੋਗਦਾਨ ਨੂੰ ਰੱਦ ਕਰ ਦਿੰਦਾ ਹੈ",
+'tooltip-undo' => '"ਨਕਾਰੋ" ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਰੱਦ ਕਰਕੇ ਸੋਧ ਫ਼ਾਰਮ ਨੂੰ ਝਲਕ ਦੇ ਅੰਦਾਜ਼ ਵਿਚ ਦਿਖਾਉਂਦਾ ਹੈ।
+à¨\87à©°à¨\9d "ਸਾਰ" ਵਿà¨\9a ਤਬਦà©\80ਲà©\80 ਨà¨\95ਾਰਨ ਦਾ ਕਾਰਨ ਲਿਖਿਆ ਜਾ ਸਕਦਾ ਹੈ।',
+'tooltip-summary' => 'ਸੰà¨\96à©\87ਪ ਸਾਰ ਦਰਜ ਕਰੋ',
# Attribution
'others' => 'ਹੋਰ',
# Browsing diffs
'previousdiff' => '← ਪੁਰਾਣੀ ਸੋਧ',
-'nextdiff' => 'à¨\85ੰਤਰ à¨\85ੱà¨\97à©\87 →',
+'nextdiff' => 'ਨਵà©\80à¨\82 ਸà©\8bਧ →',
# Media information
'thumbsize' => 'ਥੰਮਨੇਲ ਆਕਾਰ:',
'widthheightpage' => '$1 × $2, $3 ਪੇਜ਼',
'file-info' => 'ਫਾਇਲ ਆਕਾਰ: $1, MIME ਕਿਸਮ: $2',
-'file-info-size' => '$1 Ã\97 $2 ਪਿà¨\95ਸਲ, ਫਾà¨\87ਲ ਆਕਾਰ: $3, MIME ਕਿਸਮ: $4',
-'file-nohires' => 'à¨\87ਸ ਤà©\8cà¨\82 à¨\9c਼ਿà¨\86ਦਾ ਰà©\88à¨\9c਼à©\8bਲà¨\8aਸ਼ਨ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
-'svg-long-desc' => 'SVG ਫਾà¨\87ਲ, nominally $1 Ã\97 $2 pixels, file size: $3',
+'file-info-size' => '$1 Ã\97 $2 ਪਿà¨\95ਸਲ, ਫ਼ਾà¨\88ਲ ਆਕਾਰ: $3, MIME ਕਿਸਮ: $4',
+'file-nohires' => 'à¨\87ਸ ਤà©\8bà¨\82 ਵੱਡà©\80 ਤਸਵà©\80ਰ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।',
+'svg-long-desc' => 'SVG ਫ਼ਾà¨\88ਲ, à¨\86ਮ ਤà©\8cਰ â\80\99ਤà©\87 $1 Ã\97 $2 ਪਿà¨\95ਸਲ, ਫ਼ਾà¨\88ਲ ਦਾ à¨\85à¨\95ਾਰ: $3',
'show-big-image' => 'ਪੂਰਾ ਰੈਜ਼ੋਲੇਸ਼ਨ',
# Special:NewFiles
# Bad image list
'bad_image_list' => 'ਤਰਤੀਬ ਇਸ ਤਰਾਂ ਹੈ:
-à¨\95à©\87ਵਲ ਸà©\82à¨\9aà©\80ਬੱਧ ਮੱਦਾà¨\82 (* ਨਾਲ ਸ਼à©\81ਰà©\82 ਹà©\8bਣ ਵਾਲà©\80à¨\86à¨\82 ਪੰà¨\95ਤà©\80à¨\86à¨\82) ਹà©\80 ਵਿà¨\9aਾਰ à¨\85ਧà©\80ਨ ਹà©\8bਣà¨\97à©\80à¨\86à¨\82।
-ਪੰà¨\95ਤà©\80 ਵਿà¨\9a ਪਹਿਲà©\80 à¨\95à©\9cà©\80 à¨\97ਲਤ ਫਾà¨\88ਲ ਦà©\80 à¨\95à©\9cà©\80 ਹà©\8bਣà©\80 à¨\9aਾਹà©\80ਦà©\80 ਹà©\88।à¨\89ਸ ਪੰà¨\95ਤà©\80 ਵਿà¨\9a à¨\85ੱà¨\97à©\87 ਦਿਤà©\80à¨\86à¨\82 à¨\95à©\9cà©\80à¨\86à¨\82 ਨà©\82à©° à¨\85ਪਵਾਦ ਮੰਨਿà¨\86 à¨\9cਾਵà©\87à¨\97ਾ, à¨à¨¾à¨µ à¨\89ਹ ਪੰਨà©\87 à¨\9cਿਨà©\8dਹਾà¨\82 ਵਿà¨\9a ਫਾà¨\88ਲ à¨\95ਿਸà©\87 ਪੰà¨\95ਤà©\80 ਵਿà¨\9a ਸਥਿਤ ਹà©\8b ਸà¨\95ਦà©\80 ਹà©\87।',
+ਸਿਰਫ਼ ਲਿਸà¨\9f ਵਿà¨\9aਲà©\80à¨\86à¨\82 à¨\9aà©\80à¨\9c਼ਾà¨\82 (* ਨਾਲ ਸ਼à©\81ਰà©\82 ਹà©\8bਣ ਵਾਲà©\80à¨\86à¨\82 à¨\95ਤਾਰਾà¨\82) â\80\99ਤà©\87 ਹà©\80 à¨\97਼à©\8cਰ à¨\95à©\80ਤਾ à¨\9cਾਵà©\87à¨\97ਾ।
+ਲਾà¨\88ਨ ਵਿà¨\9a ਪਹਿਲà©\80 à¨\95à©\9cà©\80 à¨\97਼ਲਤ ਫ਼ਾà¨\88ਲ ਦà©\80 à¨\95à©\9cà©\80 ਹà©\8bਣà©\80 à¨\9aਾਹà©\80ਦà©\80 ਹà©\88। à¨\89ਸ ਲਾà¨\88ਨ â\80\99à¨\9a à¨\85ੱà¨\97à©\87 ਦਿਤà©\80à¨\86à¨\82 à¨\95à©\9cà©\80à¨\86à¨\82 ਨà©\82à©° à¨\87ਤਰਾà¨\9c਼ਯà©\8bà¨\97 ਮੰਨਿà¨\86 à¨\9cਾਵà©\87à¨\97ਾ, à¨à¨¾à¨µ à¨\89ਹ ਸਫ਼à©\87 à¨\9cਿਨà©\8dਹਾà¨\82 ਵਿà¨\9a ਫ਼ਾà¨\88ਲ à¨\95ਿਸà©\87 ਲਾà¨\88ਨ ਵਿà¨\9a ਸਥਿਤ ਹà©\8b ਸà¨\95ਦà©\80 ਹà©\88।',
# Metadata
'metadata' => 'ਮੇਟਾ ਡੈਟਾ',
-'metadata-help' => 'à¨\87ਸ ਮਿਸਲ ਵਿà¨\9a ਵਾਧà©\82 à¨\9cਾਨà¨\95ਾਰà©\80à¨\86à¨\82 ਹਨ , à¨\9cà©\8b ਸ਼ਾà¨\87ਦ à¨\89ਸ à¨\95à©\88ਮਰà©\87 ਯਾ ਸà¨\95à©\88ਨਰ ਦà©\80 ਦà©\87ਣ ਹਨ à¨\9cà©\8bà¨\95ਿ à¨\87ਸ ਮਿਸਲ ਨà©\82à©° ਬਨਾà¨\89ਣ ਲà¨\88 ਵਰਤਿà¨\86 à¨\97ਿà¨\86 ਹà©\88।à¨\85à¨\97਼ਰ à¨\87ਹ ਮਿਸਲ ਬਦਲਾà¨\88 à¨\97à¨\88 ਹà©\88 ਤਾà¨\82 ਹà©\8b ਸà¨\95ਦਾ ਹà©\88 à¨\95à©\81à¨\9d ਵà©\87ਰਵà©\87 ਬਦਲà©\80 ਮਿਸਲ ਦਾ ਸਹੀ ਰੂਪਮਾਨ ਨਾ ਹੋਣ।',
-'metadata-fields' => 'ਮà©\88à¨\9fਾਡà©\88à¨\9fਾ ਸà©\82à¨\9aà©\80 ਪà¨\9f ਨà©\82à©° à¨\9bà©\8bà¨\9fਾ à¨\95ਰਣ ਨਾਲ à¨\87ਸ ਸà©\81ਨà©\87ਹà©\87 ਵਿà¨\9a ਸà©\82à¨\9aà©\80ਬੱਧ ਫ਼à©\80ਲਡ ਮà©\82ਰਤ ਦà©\87 ਦà©\8dਰਿਸ਼ ਵਿà¨\9a ਸ਼ਾਮਲ à¨\95à©\80ਤà©\87 à¨\9cਾਣà¨\97à©\87।ਬਾà¨\95à©\80 ਫ਼à©\80ਲਡਾà¨\82 ਨà©\82à©° à¨\9bਿਪਾà¨\87à¨\86 à¨\9cਾà¨\8fà¨\97ਾ।',
+'metadata-help' => 'à¨\87ਸ ਫ਼ਾà¨\88ਲ ਵਿà¨\9a ਵਾਧà©\82 à¨\9cਾਣà¨\95ਾਰà©\80à¨\86à¨\82 ਹਨ, à¨\9cà©\8b ਸ਼ਾà¨\87ਦ à¨\89ਸ à¨\95à©\88ਮਰà©\87 à¨\9cਾà¨\82 ਸà¨\95à©\88ਨਰ ਦà©\80 ਦà©\87ਣ ਹਨ à¨\9cà©\8b à¨\87ਸਨà©\82à©° ਬਣਾà¨\89ਣ ਲà¨\88 ਵਰਤਿà¨\86 à¨\97ਿà¨\86। à¨\9cà©\87 à¨\87ਸ ਫ਼ਾà¨\88ਲ ਵਿà¨\9a à¨\95à©\8bà¨\88 ਤਬਦà©\80ਲà©\80 à¨\95à©\80ਤà©\80 à¨\97à¨\88 ਹà©\88 ਤਾà¨\82 ਹà©\8b ਸà¨\95ਦਾ ਹà©\88 à¨\95à©\81à¨\9d ਵà©\87ਰਵà©\87 ਬਦਲà©\80 ਫ਼ਾà¨\88ਲ ਦਾ ਸਹੀ ਰੂਪਮਾਨ ਨਾ ਹੋਣ।',
+'metadata-fields' => 'à¨\87ਸ ਸà©\81ਨà©\87ਹà©\87 ਵਿà¨\9a ਸà©\82à¨\9aà©\80ਬੱਧ à¨\96à©\87ਤਰ ਤਸਵà©\80ਰ ਸਫ਼à©\87 â\80\99à¨\9a ਸ਼ਾਮਲ à¨\95à©\80ਤà©\87 à¨\9cਾਣà¨\97à©\87 à¨\9cà©\8b à¨\89ਦà©\8bà¨\82 ਦਿੱਸਦà©\87 ਹਨ à¨\9cਦà©\8b ਮà©\88à¨\9fਾਡà©\88à¨\9fਾ à¨\96਼ਾà¨\95ਾ ਬੰਦ ਹà©\8bਵà©\87। ਬਾà¨\95à©\80 à¨\89à¨\82à¨\9e ਹà©\80 ਲà©\81à¨\95à©\87 ਹà©\8bਣà¨\97à©\87।',
# EXIF tags
'exif-imagewidth' => 'ਚੌੜਾਈ',
'exif-gpsspeed-m' => 'ਮੀਲ ਪ੍ਰਤੀ ਘੰਟਾ',
# External editor support
-'edit-externally' => 'ਬਾਹਰà©\80 ਫਾਰਮà©\82ਲਾ ਲà¨\97ਾ à¨\95à©\87 à¨\87ਸ ਮਿਸਲ ਨà©\82à©° ਸੰਪਾਦਨ à¨\95ਰà©\8b।',
-'edit-externally-help' => '(ਵਧà©\87ਰà©\87 à¨\9cਾਣà¨\95ਾਰà©\80 ਲà¨\87 [//www.mediawiki.org/wiki/Manual:External_editors setup instructions] à¨\87ਥà©\87 ਦਬੋ)',
+'edit-externally' => 'ਬਾਹਰà©\80 à¨\90ਪਲà©\80à¨\95à©\87ਸ਼ਨ ਵਰਤਦà©\87 ਹà©\8bà¨\8f à¨\87ਸ ਫ਼ਾà¨\88ਲ ਨà©\82à©° ਸà©\8bਧà©\8b',
+'edit-externally-help' => '(à¨\9c਼ਿà¨\86ਦਾ à¨\9cਾਣà¨\95ਾਰà©\80 ਲà¨\88 [//www.mediawiki.org/wiki/Manual:External_editors setup instructions] ਵà©\87à¨\96ੋ)',
# 'all' in various places, this might be different for inflected languages
'watchlistall2' => 'ਸਭ',
'watchlistedit-raw-removed' => '{{PLURAL:$1|1 title was|$1 titles were}} ਹਟਾਓ:',
# Watchlist editing tools
-'watchlisttools-edit' => 'ਵਾਚਲਿਸਟ ਵੇਖੋ ਤੇ ਸੋਧੋ',
+'watchlisttools-view' => 'ਮੌਕੇ ਮੁਤਾਬਕ ਤਬਦੀਲੀਆਂ ਵੇਖੋ',
+'watchlisttools-edit' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵੇਖੋ ’ਤੇ ਸੋਧੋ',
+'watchlisttools-raw' => 'ਕੱਚੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ',
# Special:Version
'version' => 'ਵਰਜਨ',
# Special:SpecialPages
-'specialpages' => 'à¨\96ਾਸ ਪà©\87à¨\9c',
+'specialpages' => 'à¨\96਼ਾਸ ਸਫ਼à©\87',
'specialpages-group-login' => 'ਲਾਗ ਇਨ / ਅਕਾਊਂਟ ਬਣਾਓ',
# Special:BlankPage
'blankpage' => 'ਖ਼ਾਲੀ ਪੇਜ',
+# External image whitelist
+'external_image_whitelist' => " #ਇਸ ਲਾਈਨ ਨੂੰ ਇੰਝ ਹੀ ਰਹਿਣ ਦਿਓ <pre>
+#ਹੇਠਾਂ ਓਹੀ ਐਕਸਪ੍ਰੈਸ਼ਨ ਪਾਓ (ਜਿਹੜਾ ਹਿੱਸਾ // ਦੇ ਵਿਚਾਲੇ ਹੈ)
+#ਇਹ ਬਾਹਰੀ ਤਸਵੀਰਾਂ ਦੇ URLs (ਹੌਟਲਿੰਕਡ) ਨਾਲ਼ ਮਿਲਣਗੀਆਂ
+#ਜਿਹੜੀਆਂ ਮਿਲਣਗੀਆਂ ਓਹ ਬਤੌਰ ਤਸਵੀਰਾਂ ਦਿੱਸਣਗੀਆਂ ਨਹੀਂ ਤਾਂ ਤਸਵੀਰ ਦਾ ਸਿਰਫ਼ ਲਿੰਕ ਨਜ਼ਰ ਆਵੇਗਾ
+#'#' ਨਾਲ਼ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲ਼ੀਆਂ ਲਾਈਨਾਂ ਟਿੱਪਣੀਆਂ ਵਾਂਗ ਲਈਆਂ ਜਾਂਦੀਆਂ ਹਨ
+#ਇਹ ਕੇਸ-ਇਨਸੈਂਸਟਿਵ ਹੈ
+
+#ਸਾਰੇ ਰੈਜੈਕਸ ਫ਼ਰੈਗਮੈਂਟ ਇਸ ਲਾਈਨ ਤੋਂ ਉੱਪਰ ਪਾਓ। ਇਸ ਲਾਈਨ ਨੂੰ ਇੰਝ ਹੀ ਰਹਿਣ ਦਿਓ </pre>",
+
# Special:Tags
-'tag-filter' => '[[Special:Tags|à¨\9aਿà¨\9f]] ਛਾਨਣੀ:',
+'tag-filter' => '[[Special:Tags|à¨\9fà©\88à¨\97]] ਛਾਨਣੀ:',
# HTML forms
'htmlform-submit' => 'ਭੇਜੋ',
'searchmenu-exists' => "'''Atin bulung a mikilagiung \"[[:\$1]]\" keng wiking ini.'''",
'searchhelp-url' => 'Help:Kalamnan',
'searchprofile-everything' => 'Eganagana',
+'searchprofile-articles-tooltip' => 'Paintunan king$1',
+'searchprofile-images-tooltip' => 'Manintun makasimpan',
+'searchprofile-everything-tooltip' => 'Manintun karing eganaganang laman (kayabe no reng bulung pamisabi-sabi)',
'search-result-size' => '$1 ({{PLURAL:$2|1 a kataya|$2 kataya}})',
'search-result-score' => 'Kaugnayan (relevance): $1%',
'search-redirect' => '(pamanalis direksiun $1)',
* @ingroup Language
* @file
*
+ * @author Ankry
* @author Bartek50003
* @author BdgwksxD
* @author Beau
* @author Holek
* @author Jwitos
* @author Kaganer
+ * @author Karol007
* @author Lajsikonik
* @author Lampak
* @author Lazowik
'youhavenewmessages' => 'Masz $1 ($2).',
'newmessageslink' => 'nowe wiadomości',
'newmessagesdifflink' => 'różnica z poprzednią wersją',
+'youhavenewmessagesfromusers' => 'Masz $1 od {{PLURAL:$3|innego użytkownika|$3 innych użytkowników}} ($2).',
+'youhavenewmessagesmanyusers' => 'Masz $1 od wielu użytkowników ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|jedną wiadomość|$1 wiadomości}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|ostatnia zmiana|ostatnie $1 zmiany|ostatnie $1 zmian}}',
'youhavenewmessagesmulti' => 'Masz nowe wiadomości na $1',
'editsection' => 'edytuj',
'editold' => 'edytuj',
'remembermypassword' => 'Zapamiętaj moje hasło na tym komputerze (maksymalnie przez $1 {{PLURAL:$1|dzień|dni}})',
'securelogin-stick-https' => 'Po zalogowaniu utrzymuj połączenie poprzez HTTPS',
'yourdomainname' => 'Twoja domena',
+'password-change-forbidden' => 'Nie można zmieniać haseł na tej wiki.',
'externaldberror' => 'Wystąpił błąd zewnętrznej bazy autentyfikacyjnej lub nie posiadasz uprawnień koniecznych do aktualizacji zewnętrznego konta.',
'login' => 'Zaloguj się',
'nav-login-createaccount' => 'Logowanie i rejestracja',
'noarticletext-nopermission' => 'Na tej stronie nie ma jeszcze artykułu.
Możesz [[Special:Search/{{PAGENAME}}|wyszukać ten tytuł]] w treści innych stron
lub <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} przeszukać powiązane logi].</span>',
+'missing-revision' => 'Wersja #$1 strony "{{PAGENAME}}" nie istnieje.
+
+Zazwyczaj jest to spowodowane przestarzałym linkiem do usuniętej strony. Powód usunięcia znajduje się w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rejestrze].',
'userpage-userdoesnotexist' => 'Użytkownik „<nowiki>$1</nowiki>” nie jest zarejestrowany.
Upewnij się, czy na pewno zamierza{{GENDER:|łeś|łaś|sz}} utworzyć lub zmodyfikować właśnie tę stronę.',
'userpage-userdoesnotexist-view' => 'Konto użytkownika „$1” nie jest zarejestrowane.',
'expansion-depth-exceeded-warning' => 'Strona przekroczyła głębokość rozbudowy',
'parser-unstrip-loop-warning' => 'Wykryto nieskończoną pętlę',
'parser-unstrip-recursion-limit' => 'Przekroczono maksymalną głębokość zagnieżdżania ($1)',
+'converter-manual-rule-error' => 'Błąd w językowych regułach konwersji',
# "Undo" feature
'undo-success' => 'Edycja może zostać wycofana. Porównaj ukazane poniżej różnice między wersjami, a następnie zapisz zmiany.',
'editundo' => 'anuluj edycję',
'diff-multi' => '(Nie pokazano $1 wersji {{PLURAL:$1|utworzonej|utworzonych}} przez {{PLURAL:$2|jednego użytkownika|$2 użytkowników}})',
'diff-multi-manyusers' => '(Nie pokazano $1 {{PLURAL:$1|pośredniej wersji utworzonej|pośrednich wersji utworzonych}} przez {{PLURAL:$2|jednego użytkownika|$2 użytkowników}})',
+'difference-missing-revision' => '{{PLURAL:$2|Wersja|$2 wersje|$2 wersji}} #$1 strony "{{PAGENAME}}" nie {{PLURAL:$2|została znaleziona|zostały znalezione|zostało znalezionych}}.
+
+Zazwyczaj jest to spowodowane przestarzałym linkiem do usuniętej strony. Powód usunięcia znajduje się w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rejestrze].',
# Search results
'searchresults' => 'Wyniki wyszukiwania',
'right-writeapi' => 'Zapis poprzez interfejs API',
'right-delete' => 'Usuwanie stron',
'right-bigdelete' => 'Usuwanie stron z długą historią edycji',
+'right-deletelogentry' => 'Usuwanie i przywracanie wpisów rejestru',
'right-deleterevision' => 'Usuwanie i odtwarzanie określonej wersji strony',
'right-deletedhistory' => 'Podgląd usuniętych wersji, bez przypisanego im tekstu',
'right-deletedtext' => 'Podgląd usuniętego tekstu i zmian pomiędzy usuniętymi wersjami',
'backend-fail-connect' => 'Nie można nawiązać połączenia do wewnętrznych funkcji magazynowania "$1".',
'backend-fail-internal' => 'Wystąpił nieznany błąd w wewnętrznych funkcjach magazynowania "$1".',
'backend-fail-contenttype' => 'Nie można określić typ zawartości pliku do przechowywania w "$1".',
-'backend-fail-batchsize' => 'Wewnętrzne funkcje magazynowania otrzymały $1 {{PLURAL:$1|operację|operacje|operacji}} na pliku; limit wynosi $2 {{PLURAL:$2| operacja|operacje|operacji}}.',
+'backend-fail-batchsize' => 'Wewnętrzne funkcje magazynowania otrzymały $1 {{PLURAL:$1|operację|operacje|operacji}} na pliku; limit to $2 {{PLURAL:$2|operacja|operacje|operacji}}.',
'backend-fail-usable' => 'Nie można zapisać pliku $1 ze względu na niewystarczające uprawnienia lub brak katalogów/kontenerów.',
# File journal errors
'morelinkstoimage' => 'Pokaż [[Special:WhatLinksHere/$1|więcej odnośników]] do tego pliku.',
'linkstoimage-redirect' => '$1 (przekierowanie do pliku) $2',
'duplicatesoffile' => '{{PLURAL:$1|Następujący plik jest kopią|Następujące pliki są kopiami}} pliku ([[Special:FileDuplicateSearch/$2|więcej informacji]]):',
-'sharedupload' => 'Ten plik znajduje się na $1 i może być używany w innych projektach.',
-'sharedupload-desc-there' => 'Ten plik znajduje się na $1 i może być używany w innych projektach.
+'sharedupload' => 'Ten plik znajduje się w $1 i może być używany w innych projektach.',
+'sharedupload-desc-there' => 'Ten plik znajduje się w $1 i może być używany w innych projektach.
Więcej informacji odnajdziesz na [$2 stronie opisu pliku].',
-'sharedupload-desc-here' => 'Ten plik znajduje się na $1 i może być używany w innych projektach.
+'sharedupload-desc-here' => 'Ten plik znajduje się w $1 i może być używany w innych projektach.
Poniżej znajdują się informacje ze [$2 strony opisu] tego pliku.',
'sharedupload-desc-edit' => 'Plik ten pochodzi z $1 i może być wykorzystany w innych projektach.
Być może zechcesz zmienić opis na tej [$2 stronie opisu pliku].',
'exblank' => 'Strona była pusta',
'delete-confirm' => 'Usuwanie „$1”',
'delete-legend' => 'Usuń',
-'historywarning' => "'''Uwaga!''' Strona, którą chcesz usunąć, ma w przybliżeniu {{PLURAL:$1|starszą wersję|$1 starsze wersje|$1 starszych wersji}}:",
+'historywarning' => "'''Uwaga!''' Strona, którą chcesz usunąć, ma w przybliżeniu {{PLURAL:$1|jedną starszą wersję|$1 starsze wersje|$1 starszych wersji}}:",
'confirmdeletetext' => 'Zamierzasz usunąć stronę razem z całą dotyczącą jej historią.
Upewnij się, czy na pewno chcesz to zrobić, że rozumiesz konsekwencje i że robisz to w zgodzie z [[{{MediaWiki:Policy-url}}|zasadami]].',
'actioncomplete' => 'Operacja wykonana',
'rollback' => 'Cofnij edycję',
'rollback_short' => 'Cofnij',
'rollbacklink' => 'cofnij',
+'rollbacklinkcount' => 'cofnij $1 {{PLURAL:$1|edycję|edycje|edycji}}',
+'rollbacklinkcount-morethan' => 'cofnij więcej niż $1 {{PLURAL:$1|edycję|edycje|edycji}}',
'rollbackfailed' => 'Nie udało się cofnąć zmiany',
'cantrollback' => 'Nie można cofnąć edycji, ponieważ jest tylko jedna wersja tej strony.',
'alreadyrolled' => 'Nie można dla strony [[:$1|$1]] cofnąć ostatniej zmiany, którą wykonał [[User:$2|$2]] ([[User talk:$2|dyskusja]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]).
'undeletepage' => 'Odtwarzanie usuniętych stron',
'undeletepagetitle' => "'''Poniżej znajdują się usunięte wersje strony [[:$1]]'''.",
'viewdeletedpage' => 'Zobacz usunięte wersje',
-'undeletepagetext' => '{{PLURAL:$1|Następująca strona została usunięta, ale jej|Następujące $1 strony zostały usunięte, ale ich}} kopia wciąż znajduje się w archiwum.
+'undeletepagetext' => '{{PLURAL:$1|Następująca strona została usunięta, ale jej kopia wciąż znajduje|Następujące $1 strony zostały usunięte, ale ich kopie wciąż znajdują|Następujące $1 stron zostało usuniętych, ale ich kopie wciąż znajdują}} się w archiwum.
Archiwum co jakiś czas może być oczyszczane.',
'undelete-fieldset-title' => 'Odtwarzanie wersji',
'undeleteextrahelp' => "Jeśli chcesz odtworzyć całą historię edycji strony, pozostaw wszystkie pola niezaznaczone i kliknij '''''{{int:undeletebtn}}'''''.
# Special:SpecialPages
'specialpages' => 'Strony specjalne',
-'specialpages-note' => '* Typowe strony specjalne.
-* <span class="mw-specialpagerestricted">Strony specjalne o ograniczonym dostępie.</span>
-* <span class="mw-specialpagecached">Buforowane strony specjalne (mogą być nieaktualne).</span>',
+'specialpages-note' => '----
+* Normalne strony specjalne.
+* <span class="mw-specialpagerestricted">Zastrzeżone strony specjalne.</span>',
'specialpages-group-maintenance' => 'Raporty konserwacyjne',
'specialpages-group-other' => 'Inne strony specjalne',
'specialpages-group-login' => 'Logowanie i rejestracja',
'api-error-duplicate' => '{{PLURAL:$1|Jest już [$2 inny plik]|Są już [$2 inne pliki]}} o tej samej zawartości',
'api-error-duplicate-archive' => '{{PLURAL:$1|Był już [$2 inny plik]|Były już [$2 inne pliki]}} o takiej samej zawartości, ale {{PLURAL:$1|został usunięty|zostały usunięte}}.',
'api-error-duplicate-archive-popup-title' => '{{PLURAL:$1|Zdublowany plik, który został już usunięty|Zdublowane pliki, które zostały już usunięte}}',
-'api-error-duplicate-popup-title' => '{{PLURAL:$1|Zdublowany plik|Zdublowane plik}}',
+'api-error-duplicate-popup-title' => '{{PLURAL:$1|Zdublowany plik|Zdublowane pliki}}',
'api-error-empty-file' => 'Przesłany przez Ciebie plik jest pusty.',
'api-error-emptypage' => 'Tworzenie nowych, pustych stron jest niedozwolone.',
'api-error-fetchfileerror' => 'Błąd wewnętrzny – wystąpił błąd w trakcie pobierania pliku.',
'duration-centuries' => '$1 {{PLURAL:$1|stulecie|stulecia|stuleci}}',
'duration-millennia' => '$1 {{PLURAL:$1|tysiąclecie|tysiąclecia|tysiącleci}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 nie {{PLURAL:$4|jest dozwolonym typem pliku|są dozwolonymi typami plików}}. Dopuszczalne są pliki w {{PLURAL:$3|formacie|formatach}} $2.',
);
'tog-editsectiononrightclick' => 'Abilité la modìfica dle session ën sgnacand-je ansima<br /> al tìtol col tast drit dël rat (a-i va Javascript)',
'tog-showtoc' => "Buta le tàole dij contnù<br />(për j'artìcoj che l'han pì che 3 session)",
'tog-rememberpassword' => "Visesse ëd mia ciav ansima a 's navigador (për al pi $1 {{PLURAL:$1|di|di}})",
-'tog-watchcreations' => 'Gionta le pàgine che i creo mi a la lista ëd lòn che im ten-o sot euj',
+'tog-watchcreations' => "Gionta le pàgine che i creo mi e j'archivi che i cario mi a la lista ëd lòn che im ten-o sot euj",
'tog-watchdefault' => "Gionta le pàgine che i modìfico mi a la lista dle ròbe ch'i ten-o sot-euj",
-'tog-watchmoves' => 'Gionta le pàgine che i tramudo a lòn che im ten-o sot euj',
-'tog-watchdeletion' => 'Gionta le pàgine che i scancelo via a la lista ëd lòn che im ten-o sot euj',
+'tog-watchmoves' => "Gionta le pàgine e j'archivi che i tramudo a lòn che im ten-o sot euj",
+'tog-watchdeletion' => "Gionta le pàgine e j'archivi che i scancelo via a la lista ëd lòn che im ten-o sot euj",
'tog-minordefault' => 'Marca tute le modìfice coma cite<br />(mach coma predefinission dla casela)',
'tog-previewontop' => 'Smon-e la preuva dzora al quàder ëd modìfica dël test e nen sota',
'tog-previewonfirst' => 'Smon na preuva la prima vira che as fa na modìfica',
'tog-nocache' => 'Disabilité la memòria local ëd le pàgine dël navigador',
-'tog-enotifwatchlistpages' => "Mand-me un messagi an pòsta eletrònica quand a-i son dle modìfiche a le pàgine ch'im ten-o sot euj",
+'tog-enotifwatchlistpages' => "Mandeme un mëssagi an pòsta eletrònica quand a-i son dle modìfiche a le pàgine ch'im ten-o sot euj",
'tog-enotifusertalkpages' => 'Mand-me un messagi ëd pòsta eletrònica quand a-i son dle modìfiche a mia pàgina dle ciaciarade',
-'tog-enotifminoredits' => 'Mand-me un messagi an pòsta eletrònica bele che për le modìfiche cite',
+'tog-enotifminoredits' => "Mandeme un mëssagi an pòsta eletrònica bele che për le modìfiche cite dle pàgine o dj'archivi",
'tog-enotifrevealaddr' => 'Lassa che a së s-ciàira mia adrëssa ëd pòsta eletrònica ant ij messagi ëd notìfica',
'tog-shownumberswatching' => "Smon ël nùmer d'utent che as ten-o la pàgina sot euj",
'tog-oldsig' => 'Firma esistenta:',
'youhavenewmessages' => "A l'ha $1 ($2).",
'newmessageslink' => 'mëssagi neuv',
'newmessagesdifflink' => "A-i é chèich-còs ëd diferent da 'nt l'ùltima revision",
+'youhavenewmessagesfromusers' => "It l'has $1 da {{PLURAL:$3|n'autr utent|$3 utent}} ($2).",
+'youhavenewmessagesmanyusers' => "It l'has $1 da vaire utent ($2).",
+'newmessageslinkplural' => '{{PLURAL:$1|un mëssagi neuv|$1 mëssagi neuv}}',
+'newmessagesdifflinkplural' => 'ùltim {{PLURAL:$1|cambiament|cambiament}}',
'youhavenewmessagesmulti' => "A l'ha dij neuv mëssagi an $1",
'editsection' => 'modìfica',
'editold' => 'modìfica',
'remembermypassword' => "Vis-te mia ciav ansima a st'ordinator-sì (për al pi $1 {{PLURAL:$1|di|di}})",
'securelogin-stick-https' => "Resté colegà an HTTPS apress d'esse intrà ant ël sistema",
'yourdomainname' => 'Sò domini',
+'password-change-forbidden' => 'It peule pa cangé le ciav dzora a sta wiki-sì.',
'externaldberror' => "Ò che a l'é rivaje n'eror d'autenticassion esterna, ò pura a l'é chiel (chila) che a l'é nen autorisà a agiornesse sò cont estern.",
'login' => 'Rintré ant ël sistema',
'nav-login-createaccount' => 'rintré ant ël sistema',
'noarticletext-nopermission' => 'Al moment a-i é pa gnun test an sta pàgina-sì.
It peule [[Special:Search/{{PAGENAME}}|sërché sto tìtol ëd pàgina-sì]] an d\'àutre pàgine,
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sërché j\'argistrassion colegà]</span>.',
+'missing-revision' => "La revision #\$1 dla pàgina ciamà \"{{PAGENAME}}\" a esist pa.
+
+Sòn a l'é normalment causà da l'andèje dapress a na vej liura stòrica a na pàgina ch'a l'é stàita scancelà. Ij detaj a peulo esse trovà ant ël [registr ëd jë scancelament ëd {{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}].",
'userpage-userdoesnotexist' => 'Lë stranòm "<nowiki>$1</nowiki>" a l\'é pa registrà. Për piasì ch\'a varda se da bon a veul creé/modifiché sta pàgina.',
'userpage-userdoesnotexist-view' => 'Ël cont utent "$1" a l\'é pa registrà.',
'blocked-notice-logextract' => "S'utent a l'é al moment blocà.
'expansion-depth-exceeded-warning' => "La pàgina a l'ha sorpassà la profondità d'espansion",
'parser-unstrip-loop-warning' => 'Trovà un sicl nen dësmontàbil',
'parser-unstrip-recursion-limit' => "Sorpassà ël lìmit d'arcorensa nen dësmontàbil: $1",
+'converter-manual-rule-error' => 'Eror trovà ant la régola ëd conversion manual ëd la lenga',
# "Undo" feature
'undo-success' => "Sta modìfica-sì as peul scancelesse. Për piasì, ch'a contròla ambelessì sota për esse sigur che a l'é pro lòn che a veul fé, e peuj ch'as salva lòn ch'a l'ha butà chiel/chila për finì dë scancelé la modìfica ch'a-i era.",
'editundo' => "buta 'me ch'a l'era",
'diff-multi' => "({{PLURAL:$1|Na revision antërmedia|$1 revision antërmedie}} ëd {{PLURAL:$2|n'utent|$2 utent}} pa mostrà)",
'diff-multi-manyusers' => "({{PLURAL:$1|Na revision antërmedia|$1 revision antërmedie}} da pi che $2 {{PLURAL:$2|n'utent|utent}} pa mostrà)",
+'difference-missing-revision' => "{{PLURAL:$2|Na revision|$2 revision}} dë sta diferensa ($1) a {{PLURAL:$2|l'é pa stàita|son pa stàite}} trovà.
+
+
+
+Sòn a l'é normalment causà da l'andèje dapress a na veja liura stòrica a na pàgina ch'a l'é stàita scancelà. Ij detaj a peulo esse trovà ant ël [registr ëd jë scanselament ëd {{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}].",
# Search results
'searchresults' => "Arzultà dl'arserca",
'right-writeapi' => "Dovré l'API dë scritura",
'right-delete' => 'Scancelé dle pàgine',
'right-bigdelete' => 'Scancelé dle pàgine con na stòria longa',
+'right-deletelogentry' => 'Scancelé e ripristiné dle vos ëd registr specìfiche',
'right-deleterevision' => 'Scancelé e disdëscancelé na version ëspessìfica ëd na pàgina',
'right-deletedhistory' => 'Vardé le revision ëscancelà ëd la stòria, sensa sò test',
'right-deletedtext' => 'Vëdde ël test ëscancelà e le modìfiche antra le revision ëscancelà',
'disambiguations' => "Pàgine ch'a men-o vers dle pàgine d'omonimìe",
'disambiguationspage' => "Template:Gestion dj'omonimìe",
-'disambiguations-text' => "Ste pàgine-sì a men-o a na '''pàgina ëd gestion dj'omònim''', mach che a dovrìo ëmné bele drit a n'artìcol.<br />
-Na pàgina as trata coma \"pàgina ëd gestion dj'omònim\" se a deuvra në stamp dont l'anliura as treuva ant ël [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Ste pàgine-sì a men-o a na '''pàgina ëd gestion dj'omònim'''.
+Mach che a dovrìo ëmné bele drit a n'artìcol.<br />
+Na pàgina as trata coma pàgina ëd gestion dj'omònim se a deuvra në stamp dont l'anliura as treuva ant ël [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Ridiression dobie',
'doubleredirectstext' => "Sta pàgina-sì a a lista dle pàgine ch'a armando a d'àutre pàgine ëd ridiression.
'rollback' => 'Gavé via le modìfiche',
'rollback_short' => 'Ripristiné',
'rollbacklink' => "ripristiné j'archivi",
+'rollbacklinkcount' => 'tiré andré $1 {{PLURAL:$1|modìfica|modìfiche}}',
+'rollbacklinkcount-morethan' => 'tiré andré pi che $1 {{PLURAL:$1|modìfica|modìfiche}}',
'rollbackfailed' => "A l'é pa podusse ripristiné",
'cantrollback' => "As peul pa tornesse a na version pì veja: l'ùltima modìfica a l'ha fala l'ùnich utent che a l'abia travajà a cost artìcol-sì.",
'alreadyrolled' => "As peulo pa anulé j'ultime modìfiche ëd [[:$1]] fàite da [[User:$2|$2]] ([[User talk:$2|Talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Soa pàgina utent',
-'tooltip-pt-anonuserpage' => 'Pàgina Utent për l',
-'tooltip-pt-mytalk' => 'Toa pàgina ëd discussion e ciaciarade.',
-'tooltip-pt-anontalk' => 'Pàgina ëd ciaciarade për l',
+'tooltip-pt-anonuserpage' => "La pàgina utent për l'IP con ël qual chiel a contribuiss",
+'tooltip-pt-mytalk' => 'Soa pàgina ëd discussion e ciaciarade',
+'tooltip-pt-anontalk' => 'La pàgina ëd ciaciarade an sle contribussion da costa adrëssa IP',
'tooltip-pt-preferences' => 'Coma che i veuj mia {{SITENAME}}.',
'tooltip-pt-watchlist' => 'Lista dle pàgine che chiel as ten sot euj.',
-'tooltip-pt-mycontris' => 'Lista ëd toe contribussion',
-'tooltip-pt-login' => "Un a l'é nen obligà a rintré ant al sistema, ma se a lo fa a l",
-'tooltip-pt-anonlogin' => "Un a l'é nen obligà a rintré ant al sistema, ma se a lo fa a l",
+'tooltip-pt-mycontris' => 'Lista ëd soe contribussion',
+'tooltip-pt-login' => "Un a l'é nen obligà a rintré ant al sistema, ma se a lo fa a l'é mej",
+'tooltip-pt-anonlogin' => "Un a l'é nen obligà a rintré ant al sistema, ma se a lo fa a l'é mej",
'tooltip-pt-logout' => 'Seurte da',
'tooltip-ca-talk' => 'Discussion ansima a sta pàgina ëd contnù.',
'tooltip-ca-edit' => 'Modifiché sta pàgina-sì. Për piasì, che as fasa na preuva anans che salvé .',
* <span class="mw-specialpagecached">Pàgine speciaj mach an memòria local (a peulo esse veje).</span>',
'specialpages-group-maintenance' => 'Rapòrt ëd manutension',
'specialpages-group-other' => 'Àutre pàgine speciaj',
-'specialpages-group-login' => 'Login / registrassion',
+'specialpages-group-login' => 'Intra / crea un cont',
'specialpages-group-changes' => 'Ùltime modìfiche e registr',
'specialpages-group-media' => 'Rapòrt dij file multimediaj e dle carie',
'specialpages-group-users' => 'Utent e drit',
'duration-centuries' => '$1 {{PLURAL:$1|sécol|sécoj}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenari|milenari}}',
+# Unknown messages
+'api-error-filetype-banned-type' => "$1 {{PLURAL:$4|a l'é na sòrt d'archivi proibìa|a son ëd sòrt d'archivi proibìe}}. {{PLURAL:$3|Sòrt d'archivi consentìa a l'é|Sòrt d'archivi consentìe a son}} $2.",
);
'api-error-uploaddisabled' => 'فائل جڑھانا ایس وکی تے بند اے۔',
'api-error-verification-error' => 'اے فائل کرپٹ ہو سکدی یا فیر ایدا فارمیٹ غلط اے۔',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|اینج دی فائل دی اجازت نئیں|اینج دیاں فائلاں دی اجازت نئیں}} اجازت دتی {{PLURAL:$3|فائل ٹائپ اے|فائل ٹائپ نیں}} $2۔',
);
'previewnote' => "'''هېر مو نه شي چې دا يواځې يوه مخليدنه ده.'''
ستاسې لخوا ترسره شوي بدلونونه لا تر اوسه پورې نه دي خوندي شوي!!",
'editing' => 'د $1 سمونه',
+'creating' => '$1 جوړېدنې کې دی',
'editingsection' => 'سمونه $1 (برخه)',
'editingcomment' => 'د $1 سمون (نوې برخه)',
'editconflict' => 'په سمادولو کې خنډ: $1',
'post-expand-template-inclusion-warning' => "'''ګواښنه:''' دا کينډۍ د خپل ټاکلي بريد نه ډېره لويه ده.
ځينې کينډۍ به په کې ګډې نه شي.",
'post-expand-template-inclusion-category' => 'هغه مخونه چې په کې د کارېدلو کينډيو شمېر له ټاکلې کچې ډېر دی',
+'post-expand-template-argument-warning' => "'''ګواښنه:''' دا مخ لږ تر لږه د يوې کينډۍ عاملين لري چې بې حده لوی دی.
+دا عاملين ړنګ شول.",
+'post-expand-template-argument-category' => 'هغه مخونه چې د کينډۍ ړنګ شوي عاملين لري.',
# "Undo" feature
'undo-norev' => 'دا سمون ناکړ کېدلای نه شي دا ځکه چې دا سمون نشته او يا هم ړنګ شوی.',
'saveprefs' => 'خوندي کول',
'resetprefs' => 'بيا سمول',
'restoreprefs' => 'ټولې تلواليزې امستنې پرځای کول',
-'prefs-editing' => 'د سÙ\85Ù\88Ù\84Ù\88 Ù¾Ù\87 ØاÙ\84 Ú©Û\90',
+'prefs-editing' => 'سÙ\85Û\90دÙ\86Û\90 Ú©Û\90 دÛ\8c',
'prefs-edit-boxsize' => 'د سمون کړکۍ کچه.',
'rows' => 'ليکې:',
'columns' => 'ستنې:',
'tooltip-ca-move' => 'همدا مخ لېږدول',
'tooltip-ca-watch' => 'دا مخ په خپل کتنلړکې ګډول',
'tooltip-ca-unwatch' => 'همدا مخ خپل کتنلړ نه لرې کول',
-'tooltip-search' => 'د {{SITENAME}} لټون',
+'tooltip-search' => '{{SITENAME}} پلټل',
'tooltip-search-go' => 'په دې نوم د کټ مټ ورته مخ شتون په صورت کې، هماغه مخ ته ورځه',
'tooltip-search-fulltext' => 'په مخونو کې دا متن وپلټه',
'tooltip-p-logo' => 'لومړی مخ',
'autosumm-new' => 'د "$1" تورو مخ جوړ شو',
# Live preview
-'livepreview-loading' => 'د برسÛ\90رÛ\90دÙ\84Ù\88 Ù¾Ù\87 ØاÙ\84 Ú©Û\90...',
+'livepreview-loading' => 'برسÛ\90رÛ\90دÙ\86Û\90 Ú©Û\90 دÛ\8c...',
'livepreview-ready' => 'برسېرېدنه ... چمتو ده!',
# Watchlist editor
'watchlistedit-noitems' => 'ستاسې کتنلړ کې هېڅ کوم سرليک نشته.',
'watchlistedit-normal-title' => 'کتنلړ سمول',
'watchlistedit-normal-legend' => 'د کتنلړ نه سرليکونه لرې کول',
-'watchlistedit-normal-submit' => 'سرليکونه لرکول',
+'watchlistedit-normal-submit' => 'سرليکونه لرې کول',
'watchlistedit-normal-done' => '{{PLURAL:$1|1 سرليک ستاسې له کتنلړ نه ليري شو|$1 سرليکونه ستاسې له کتنلړ نه ليري شوه}}:',
'watchlistedit-raw-title' => 'خام کتنلړ سمول',
'watchlistedit-raw-legend' => 'خام کتنلړ سمول',
# Signatures
'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|خبرې اترې]])',
+# Core parser functions
+'duplicate-defaultsort' => '\'\'\'ګواښنه:\'\'\'د "$2" تلواليزه اوډون تڼۍ تر دې پخوا ټاکلې تلواليزه اوډون تڼۍ "$1" پر ځای چارنه کېږي.',
+
# Special:Version
'version' => 'بڼه',
'version-extensions' => 'لګېدلي شاتاړي',
'tog-editsectiononrightclick' => 'Possibilitar a edição de secções por clique com o botão direito no título da secção (JavaScript)',
'tog-showtoc' => 'Mostrar índice (para páginas com mais de três secções)',
'tog-rememberpassword' => 'Recordar os meus dados neste browser (no máximo, durante $1 {{PLURAL:$1|dia|dias}})',
-'tog-watchcreations' => 'Adicionar as páginas que eu criar às minhas páginas vigiadas',
-'tog-watchdefault' => 'Adicionar as páginas que eu editar às minhas páginas vigiadas',
-'tog-watchmoves' => 'Adicionar as páginas que eu mover às minhas páginas vigiadas',
-'tog-watchdeletion' => 'Adicionar as páginas que eu eliminar às minhas páginas vigiadas',
+'tog-watchcreations' => 'Adicionar as páginas e ficheiros que eu criar às minhas páginas vigiadas',
+'tog-watchdefault' => 'Adicionar as páginas e ficheiros que eu editar às minhas páginas vigiadas',
+'tog-watchmoves' => 'Adicionar as páginas e ficheiros que eu mover às minhas páginas vigiadas',
+'tog-watchdeletion' => 'Adicionar as páginas e ficheiros que eu eliminar às minhas páginas vigiadas',
'tog-minordefault' => 'Por omissão, marcar todas as edições como menores',
'tog-previewontop' => 'Mostrar a antevisão antes da caixa de edição',
'tog-previewonfirst' => 'Mostrar a antevisão na primeira edição',
'tog-nocache' => 'Desactivar a cache de páginas do browser',
-'tog-enotifwatchlistpages' => 'Notificar-me por correio electrónico quando uma página vigiada é alterada',
+'tog-enotifwatchlistpages' => 'Notificar-me por correio electrónico quando uma página ou ficheiro vigiado for alterado',
'tog-enotifusertalkpages' => 'Notificar-me por correio electrónico quando a minha página de discussão é editada',
-'tog-enotifminoredits' => 'Notificar-me por correio electrónico também quando as edições forem menores',
+'tog-enotifminoredits' => 'Notificar-me por correio electrónico também sobre edições menores de páginas ou ficheiros',
'tog-enotifrevealaddr' => 'Revelar o meu endereço de correio electrónico nas notificações',
'tog-shownumberswatching' => 'Mostrar o número de utilizadores a vigiar',
'tog-oldsig' => 'Assinatura existente:',
'youhavenewmessages' => 'Tem $1 ($2).',
'newmessageslink' => 'mensagens novas',
'newmessagesdifflink' => 'comparar com a penúltima revisão',
+'youhavenewmessagesfromusers' => 'Você tem $1 de {{PLURAL:$3|outro utilizador|$3 utilizadores}} ($2).',
+'youhavenewmessagesmanyusers' => 'Você tem $1 de muitos utilizadores ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|uma mensagem nova|mensagens novas}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|última alteração|últimas alterações}}',
'youhavenewmessagesmulti' => 'Tem mensagens novas em $1',
'editsection' => 'editar',
'editold' => 'editar',
'nosuchspecialpage' => 'Esta página especial não existe',
'nospecialpagetext' => '<strong>Solicitou uma página especial inválida.</strong>
-Uma lista das páginas especiais válidas pode ser encontrada em [[Special:SpecialPages|{{int:specialpages}}]].',
+Encontra uma lista das páginas especiais válidas em [[Special:SpecialPages|{{int:specialpages}}]].',
# General errors
'error' => 'Erro',
'cannotdelete' => 'Não foi possível eliminar a página ou ficheiro "$1".
Pode já ter sido eliminado por outro utilizador.',
'cannotdelete-title' => 'Não é possível eliminar a página "$1"',
+'delete-hook-aborted' => 'A eliminação foi cancelada por um "hook".
+Não foi dada nenhuma explicação.',
'badtitle' => 'Título inválido',
'badtitletext' => 'O título de página solicitado era inválido, vazio, ou um link interlínguas ou interwikis incorrecto.
Talvez contenha um ou mais caracteres que não podem ser usados em títulos.',
'actionthrottledtext' => 'Como medida anti-spam, está impedido de realizar esta operação demasiadas vezes num espaço de tempo curto e já excedeu esse limite. Tente de novo dentro de alguns minutos, por favor.',
'protectedpagetext' => 'Esta página foi protegida contra novas edições.',
'viewsourcetext' => 'Pode ver e copiar o conteúdo desta página:',
-'viewyourtext' => "Você pode ver e copiar o código-fonte das '''suas edições''' a esta página:",
+'viewyourtext' => "Pode ver e copiar o código-fonte das '''suas edições''' desta página:",
'protectedinterface' => 'Esta página fornece o texto da interface ao software e está protegida para prevenir abusos.',
'editinginterface' => "'''Aviso:''' Está a editar uma página usada para fornecer texto de interface ao software. Alterações a esta página afectarão a aparência da interface de utilizador para os outros utilizadores. Para traduções, considere utilizar a [//translatewiki.net/wiki/Main_Page?setlang=pt translatewiki.net], um projecto destinado à tradução do MediaWiki.",
'sqlhidden' => '(Consulta SQL em segundo-plano)',
O administrador que efetuou o bloqueio deu a seguinte explicação: "$3".',
'invalidtitle-knownnamespace' => 'Título inválido com o espaço nominal "$2" e texto "$3"',
'invalidtitle-unknownnamespace' => 'Título inválido com número de espaço nominal $1 desconhecido e texto "$2"',
+'exception-nologin' => 'Não está autenticado',
+'exception-nologin-text' => 'Esta página ou operação requer que esteja autenticado nesta wiki.',
# Virus scanner
'virus-badscanner' => "Má configuração: antivírus desconhecido: ''$1''",
'remembermypassword' => 'Recordar os meus dados neste computador (no máximo, por $1 {{PLURAL:$1|dia|dias}})',
'securelogin-stick-https' => 'Manter a ligação HTTPS após a autenticação',
'yourdomainname' => 'O seu domínio:',
+'password-change-forbidden' => 'Não podes alterar senhas nesta wiki.',
'externaldberror' => 'Ocorreu um erro externo à base de dados durante a autenticação ou não lhe é permitido actualizar a sua conta externa.',
'login' => 'Autenticação',
'nav-login-createaccount' => 'Entrar / criar conta',
'emailconfirmlink' => 'Confirme o seu endereço de correio electrónico',
'invalidemailaddress' => 'O endereço de correio electrónico não pode ser aceite porque parece ter um formato inválido.
Introduza um endereço formatado correctamente ou deixe o campo vazio.',
-'cannotchangeemail' => 'A conta de e-mail não pode ser alterado nesta wiki.',
+'cannotchangeemail' => 'Os endereços de correio electrónico das contas não podem ser alterados nesta wiki.',
'emaildisabled' => 'Este site não consegue enviar e-mails.',
'accountcreated' => 'Conta criada',
'accountcreatedtext' => 'A conta de utilizador para $1 foi criada.',
'usernamehasherror' => 'O nome de utilizador não pode conter o símbolo de cardinal (#).',
'login-throttled' => 'Realizou demasiadas tentativas de autenticação com esta conta.
Aguarde antes de tentar novamente, por favor.',
-'login-abort-generic' => 'A sua autenticação não teve êxito - Abortada',
+'login-abort-generic' => 'A sua autenticação não teve êxito - Cancelada',
'loginlanguagelabel' => 'Língua: $1',
'suspicious-userlogout' => 'O seu pedido para sair foi negado porque parece ter sido enviado por um browser danificado ou por um proxy com cache.',
'newpassword' => 'Palavra-chave nova:',
'retypenew' => 'Repita a palavra-chave nova:',
'resetpass_submit' => 'Definir palavra-chave e entrar',
-'resetpass_success' => 'Sua palavra-chave foi alterada com sucesso! Autenticando-se...',
+'resetpass_success' => 'A sua palavra-chave foi alterada! Autenticação em curso...',
'resetpass_forbidden' => 'Não é possível alterar palavras-chave',
'resetpass-no-info' => 'Precisa estar autenticado para aceder directamente a esta página.',
'resetpass-submit-loggedin' => 'Alterar palavra-chave',
'passwordreset' => 'Repor palavra-chave',
'passwordreset-text' => 'Preencha este formulário para recuperar os dados da sua conta por correio electrónico.',
'passwordreset-legend' => 'Reiniciar a palavra-chave',
-'passwordreset-disabled' => 'Reiniciar a palavra-chave foi impossibilitado nesta wiki.',
+'passwordreset-disabled' => 'O reinício da palavra-chave foi impossibilitado nesta wiki.',
'passwordreset-pretext' => '{{PLURAL:$1||Introduza um dos dados abaixo}}',
'passwordreset-username' => 'Nome de utilizador:',
'passwordreset-domain' => 'Domínio:',
'noarticletext-nopermission' => 'Ainda não existe texto nesta página.
Pode [[Special:Search/{{PAGENAME}}|pesquisar o título desta página]] noutras páginas
ou <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} procurar registos relacionados]</span>.',
+'missing-revision' => 'A revisão #$1 da página denominada "{{PAGENAME}}" não existe.
+
+Isto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.
+Os detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminação].',
'userpage-userdoesnotexist' => 'A conta "<nowiki>$1</nowiki>" não se encontra registada.
Verifique se deseja realmente criar ou editar esta página, por favor.',
'userpage-userdoesnotexist-view' => 'A conta de utilizador "$1" não está registada.',
'node-count-exceeded-warning' => 'A página excedeu o total de nós',
'expansion-depth-exceeded-category' => 'Páginas em que a profundidade de expansão é excedida',
'expansion-depth-exceeded-warning' => 'A página excedeu a profundidade de expansão',
-'parser-unstrip-loop-warning' => 'Detectado loop unstrip',
+'parser-unstrip-loop-warning' => 'Foi detectado um ciclo infinito unstrip',
'parser-unstrip-recursion-limit' => 'Limite de recursão do unstrip excedido ($1)',
+'converter-manual-rule-error' => 'Erro detetado na regra de conversão de língua manual',
# "Undo" feature
'undo-success' => 'É possível desfazer a edição.
'mergehistory-from' => 'Página de origem:',
'mergehistory-into' => 'Página de destino:',
'mergehistory-list' => 'Histórico de edições fundíveis',
-'mergehistory-merge' => 'As seguintes revisões de [[:$1]] podem fundir-se em [[:$2]].
-Usando os botões de opção, pode escolher fundir apenas as revisões até àquela que marcar.
+'mergehistory-merge' => 'As seguintes edições de [[:$1]] podem ser fundidas em [[:$2]].
+Usando os botões de opção, pode escolher fundir apenas as edições até àquela que marcar.
Note que, se usar os links de navegação, os botões de opção voltarão aos valores originais.',
'mergehistory-go' => 'Mostrar edições que podem ser fundidas',
'mergehistory-submit' => 'Fundir edições',
# Diffs
'history-title' => 'Histórico de edições de "$1"',
+'difference-title' => 'Diferenças entre edições de "$1"',
+'difference-title-multipage' => 'Diferenças entre as páginas "$1" e "$2"',
'difference-multipage' => '(Diferenças entre páginas)',
'lineno' => 'Linha $1:',
'compareselectedversions' => 'Comparar as versões seleccionadas',
'editundo' => 'desfazer',
'diff-multi' => '({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de {{PLURAL:$2|um utilizador|$2 utilizadores}} {{PLURAL:$1|não apresentada|não apresentadas}})',
'diff-multi-manyusers' => '({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de mais de {{PLURAL:$2|um utilizador|$2 utilizadores}} não {{PLURAL:$1|apresentada|apresentadas}})',
+'difference-missing-revision' => '{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.
+
+Isto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.
+Os detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminação].',
# Search results
'searchresults' => 'Resultados da pesquisa',
'showingresultsheader' => "{{PLURAL:$5|Resultado '''$1''' de '''$3'''|Resultados '''$1–$2''' de '''$3'''}} para '''$4'''",
'nonefound' => "'''Nota''': Por omissão, só alguns dos espaços nominais são pesquisados.
Tente usar o prefixo ''all:'' para pesquisar todo o conteúdo (incluindo páginas de discussão, predefinições, etc.), ou use como prefixo o espaço nominal desejado.",
-'search-nonefound' => 'A pesquisa não produziu quaisquer resultados.',
+'search-nonefound' => 'A pesquisa não produziu resultados.',
'powersearch' => 'Pesquisa avançada',
'powersearch-legend' => 'Pesquisa avançada',
'powersearch-ns' => 'Pesquisar nos espaços nominais:',
'qbsettings-fixedright' => 'Fixa à direita',
'qbsettings-floatingleft' => 'Flutuante à esquerda',
'qbsettings-floatingright' => 'Flutuante à direita',
-'qbsettings-directionality' => 'Fixado, a dependener da direccionalidade do script da sua língua',
+'qbsettings-directionality' => 'Fixa, conforme a direccionalidade da escrita na sua língua',
# Preferences page
'preferences' => 'Preferências',
'prefs-beta' => 'Funcionalidades beta',
'prefs-datetime' => 'Data e hora',
'prefs-labs' => 'Funcionalidades dos laboratórios',
+'prefs-user-pages' => 'Páginas de utilizador',
'prefs-personal' => 'Perfil de utilizador',
'prefs-rc' => 'Mudanças recentes',
'prefs-watchlist' => 'Páginas vigiadas',
'prefs-help-realname' => 'Opcional: se optar por revelar o seu nome verdadeiro, este será utilizado para atribuir-lhe crédito pelo seu trabalho.',
'prefs-help-email' => 'Opcional: o endereço de correio electrónico é opcional, mas será necessário para reiniciar a palavra-chave caso esqueça a antiga.',
'prefs-help-email-others' => 'Também pode optar por permitir que outros entrem em contacto consigo por correio electrónico, através de um link nas suas páginas de utilizador ou de discussão, sem revelar o seu endereço de correio electrónico.',
-'prefs-help-email-required' => 'O endereço de correio electrónico é requerido.',
+'prefs-help-email-required' => 'É necessário o endereço de correio electrónico.',
'prefs-info' => 'Informações básicas',
'prefs-i18n' => 'Internacionalização',
'prefs-signature' => 'Assinatura',
'group-user-member' => '{{GENDER:$1|utilizador|utilizadora}}',
'group-autoconfirmed-member' => '{{GENDER:$1|utilizador autoconfirmado|utilizadora autoconfirmada}}',
-'group-bot-member' => 'robô',
+'group-bot-member' => '{{GENDER:$1|robô}}',
'group-sysop-member' => '{{GENDER:$1|administrador|administradora}}',
-'group-bureaucrat-member' => 'burocrata',
+'group-bureaucrat-member' => '{{GENDER:$1|burocrata}}',
'group-suppress-member' => '{{GENDER:$1|supressor|supressora}}',
'grouppage-user' => '{{ns:project}}:Utilizadores',
'right-writeapi' => 'Usar a API de escrita',
'right-delete' => 'Eliminar páginas',
'right-bigdelete' => 'Eliminar páginas com histórico grande',
+'right-deletelogentry' => 'Eliminar e restaurar entradas específicas de registos',
'right-deleterevision' => 'Eliminar e restaurar edições específicas de páginas',
'right-deletedhistory' => 'Ver entradas de histórico eliminadas, sem o texto associado',
'right-deletedtext' => 'Ver texto eliminado e mudanças entre revisões eliminadas',
'backend-fail-closetemp' => 'Não foi possível fechar o ficheiro temporário.',
'backend-fail-read' => 'Não foi possível ler o ficheiro $1.',
'backend-fail-create' => 'Não foi possível gravar o ficheiro $1.',
-'backend-fail-maxsize' => 'Não foi possível criar o ficheiro $1 porque ele é maior do que {{PLURAL:$2| um byte| $2 bytes}}.',
+'backend-fail-maxsize' => 'Não foi possível gravar o ficheiro $1 porque tem mais do que {{PLURAL:$2|um byte|$2 bytes}}.',
'backend-fail-readonly' => 'O servidor de armazenamento "$1" está actualmente no modo "somente leitura". A razão dada foi: "$2"',
-'backend-fail-synced' => 'O ficheiro" $1 " está em um estado inconsistente dentro da base de dados',
+'backend-fail-synced' => 'O ficheiro "$1" está num estado inconsistente nos servidores de armazenamento interno',
'backend-fail-connect' => 'Não foi possível estabelecer ligação com o servidor de armazenamento "$1".',
'backend-fail-internal' => 'Ocorreu um erro desconhecido no servidor de armazenamento "$1".',
-'backend-fail-contenttype' => 'Não foi possível determinar o tipo de conteúdo do ficheiro para armazenar em " $1 ".',
-'backend-fail-batchsize' => 'Foi fornecido um bloco de $1 {{PLURAL:$1|operação|operações}} sobre ficheiros ao servidor backend de armazenamento; o limite é de $2 {{PLURAL:$2|operação|operações}}.',
+'backend-fail-contenttype' => 'Não foi possível determinar o tipo de conteúdo do ficheiro para armazenar em "$1".',
+'backend-fail-batchsize' => 'Foi fornecido um bloco de $1 {{PLURAL:$1|operação|operações}} sobre ficheiros ao servidor de armazenamento; o limite é de $2 {{PLURAL:$2|operação|operações}}.',
+'backend-fail-usable' => 'Não foi possível gravar o ficheiro $1 devido a permissões insuficientes ou a directórios ou repositórios inexistentes.',
# File journal errors
-'filejournal-fail-dbconnect' => 'Não foi possível ligar à base de dados de registos no "backend" de armazenamento "$1".',
-'filejournal-fail-dbquery' => 'Não foi possível atualizar a base de dados de registos do "backend" de armazenamento "$1".',
+'filejournal-fail-dbconnect' => 'Não foi possível estabelecer ligação à base de dados de registos no servidor de armazenamento "$1".',
+'filejournal-fail-dbquery' => 'Não foi possível atualizar a base de dados de registos do servidor de armazenamento "$1".',
# Lock manager
-'lockmanager-notlocked' => 'Não foi possível desbloquear " $1 "; não se encontra bloqueado.',
+'lockmanager-notlocked' => 'Não foi possível desbloquear "$1" porque não se encontra bloqueado.',
'lockmanager-fail-closelock' => 'Não foi possível encerrar a referência de bloqueio para "$1".',
'lockmanager-fail-deletelock' => 'Não foi possível eliminar a referência de bloqueio para "$1".',
'lockmanager-fail-acquirelock' => 'Não foi possível adquirir bloqueio para "$1".',
-'lockmanager-fail-openlock' => 'Não foi possível abrir ficheiro de bloqueio para "$1".',
-'lockmanager-fail-releaselock' => 'Não foi possível libertar bloqueio para "$1".',
+'lockmanager-fail-openlock' => 'Não foi possível abrir o ficheiro de bloqueio de "$1".',
+'lockmanager-fail-releaselock' => 'Não foi possível libertar o bloqueio de "$1".',
'lockmanager-fail-db-bucket' => 'Não foi possível contactar bases de dados de bloqueio suficientes no "bucket" $1.',
'lockmanager-fail-db-release' => 'Não foi possível libertar bloqueios na base de dados $1.',
+'lockmanager-fail-svr-acquire' => 'Não foi possível obter bloqueios no servidor $1.',
'lockmanager-fail-svr-release' => 'Não foi possível libertar bloqueios no servidor $1.',
# ZipDirectoryReader
'zip-file-open-error' => 'Foi encontrado um erro ao abrir o ficheiro ZIP para verificação.',
'zip-wrong-format' => 'O ficheiro especificado não é um ficheiro ZIP.',
'zip-bad' => 'O ficheiro ZIP encontra-se corrompido ou não é legível.
-A sua segurança não pode ser devidamente verificada.',
+A segurança do mesmo não pode ser devidamente verificada.',
'zip-unsupported' => 'Este ficheiro ZIP usa funcionalidades ZIP não suportadas pelo MediaWiki.
A sua segurança não pode ser devidamente verificada.',
'img-auth-nopathinfo' => 'PATH_INFO em falta.
O seu servidor não está configurado para passar esta informação.
Pode ser baseado em CGI e não consegue suportar img_auth.
-Consulte a documentação em [//www.mediawiki.org/wiki/Manual:Image_Authorization Image Authorization].',
+Consulte a documentação em https://www.mediawiki.org/wiki/Manual:Image_Authorization.',
'img-auth-notindir' => 'O endereço especificado não conduz ao directório de carregamento de ficheiros configurado.',
'img-auth-badtitle' => 'Não é possível construir um título válido a partir de "$1".',
'img-auth-nologinnWL' => 'Não está autenticado e o ficheiro "$1" não está na lista branca.',
'upload_source_file' => ' (um ficheiro no seu computador)',
# Special:ListFiles
-'listfiles-summary' => 'Esta página especial mostra todos os ficheiros carregados.
-Quando filtrados pelo usuário, os últimos ficheiros carregados aparecem no topo da lista.',
+'listfiles-summary' => 'Esta página especial lista todos os ficheiros carregados.
+Quando filtrada por utilizador, só lista os ficheiros cuja última versão foi carregada pelo utilizador.',
'listfiles_search_for' => 'Pesquisar por nome de imagem:',
'imgfile' => 'ficheiro',
'listfiles' => 'Ficheiros',
'sharedupload-desc-there' => 'Este ficheiro provém de $1 e pode ser usado por outros projectos.
Consulte a [$2 página de descrição do ficheiro] para mais informações, por favor.',
'sharedupload-desc-here' => 'Este ficheiro provém de $1 e pode ser usado por outros projectos.
-A descrição na [$2 página de descrição] é mostrada abaixo.',
+A descrição na [$2 página original de descrição do ficheiro] é mostrada abaixo.',
'sharedupload-desc-edit' => 'Este ficheiro provém de $1 e pode ser utilizado por outros projetos.
-Talvez você pretenda editar a descrição na sua [$2 página de descrição de ficheiro] lá.',
+Talvez queira editar a descrição na [$2 página original de descrição do ficheiro].',
'sharedupload-desc-create' => 'Este ficheiro provém de $1 e pode ser utilizado por outros projetos.
-Talvez você pretenda editar a descrição na sua [$2 página de descrição de ficheiro] lá.',
+Talvez queira editar a descrição na [$2 página original de descrição do ficheiro].',
'filepage-nofile' => 'Não existe nenhum ficheiro com este nome.',
'filepage-nofile-link' => 'Não existe nenhum ficheiro com este nome, mas pode [$1 carregá-lo].',
'uploadnewversion-linktext' => 'Carregar uma nova versão deste ficheiro',
** Ficheiro duplicado',
'filedelete-edit-reasonlist' => 'Editar motivos de eliminação',
'filedelete-maintenance' => 'Eliminação e restauro de ficheiros foram temporariamente impossibilitadas durante a manutenção.',
-'filedelete-maintenance-title' => 'Não é possível excluir o ficheiro',
+'filedelete-maintenance-title' => 'Não é possível eliminar o ficheiro',
# MIME search
'mimesearch' => 'Pesquisa MIME',
'disambiguations' => 'Páginas com ligações para páginas de desambiguação',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => 'As páginas abaixo contêm links para uma página de desambiguação.
-Estes links deviam ser desambiguados, apontando-os para a página apropriada.<br />
-Considera-se que uma página é de desambiguação se nela for utilizada uma predefinição que esteja definida em [[MediaWiki:Disambiguationspage]].',
+'disambiguations-text' => "As páginas abaixo contêm pelo menos um link para uma '''página de desambiguação'''.
+Estes links deviam ser desambiguados, apontando-os para uma página mais apropriada.<br />
+Considera-se que uma página é de desambiguação se nela for utilizada uma predefinição que esteja definida em [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redireccionamentos duplos',
'doubleredirectstext' => 'Esta página lista todas as páginas que redireccionam para outras páginas de redireccionamento.
'wantedpages' => 'Páginas desejadas',
'wantedpages-badtitle' => 'Título inválido no conjunto de resultados: $1',
'wantedfiles' => 'Ficheiros desejados',
-'wantedfiletext-cat' => 'Os seguintes ficheiros são usados, mas não existem. Ficheiros de repositórios externos podem ser listados apesar de existirem. Tais falsos positivos aparecerão <del>riscados</del>. Adicionalmente, páginas que incorporam ficheiros que não existem estão listadas em [[:$1]].',
+'wantedfiletext-cat' => 'Os seguintes ficheiros são usados, mas não existem. Ficheiros de repositórios externos podem ser listados apesar de existirem. Tais falsos positivos aparecerão <del>riscados</del>. Adicionalmente, as páginas que incorporam ficheiros que não existem estão listadas em [[:$1]].',
'wantedfiletext-nocat' => 'Os seguintes ficheiros são usados, mas não existem. Ficheiros de repositórios externos podem ser listados apesar de existirem. Tais falsos positivos aparecerão <del>riscados</del>.',
'wantedtemplates' => 'Predefinições desejadas',
'mostlinked' => 'Páginas com mais afluentes',
'protectedpages' => 'Páginas protegidas',
'protectedpages-indef' => 'Apenas protecções infinitas',
'protectedpages-cascade' => 'Apenas protecções em cascata',
-'protectedpagestext' => 'As seguintes páginas encontram-se protegidas contra edições ou movimentações',
+'protectedpagestext' => 'As seguintes páginas estão protegidas contra edição ou movimentação',
'protectedpagesempty' => 'Neste momento, nenhuma das páginas está protegida com estes parâmetros.',
'protectedtitles' => 'Títulos protegidos',
'protectedtitlestext' => 'Os títulos a seguir encontram-se protegidos contra criação',
Pode reduzir a lista escolhendo um tipo de registo, um nome de utilizador ou um título de página. Respeite maiúsculas e minúsculas.',
'logempty' => 'Não há dados a apresentar.',
'log-title-wildcard' => 'Procurar títulos iniciados por este texto',
+'showhideselectedlogentries' => 'Mostrar ou ocultar as entradas seleccionadas',
# Special:AllPages
'allpages' => 'Todas as páginas',
'allpages-hide-redirects' => 'Ocultar redirecionamentos',
# SpecialCachedPage
-'cachedspecial-viewing-cached-ttl' => 'Você está a visualizar uma versão desta página em cache que tem uma antiguidade máxima de $1.',
-'cachedspecial-viewing-cached-ts' => 'Você está a visualizar uma versão desta página em cache que pode não refletir totalmente a situação atual.',
+'cachedspecial-viewing-cached-ttl' => 'Está a ver uma versão desta página guardada na cache há pelo menos $1.',
+'cachedspecial-viewing-cached-ts' => 'Está a ver uma versão da página guardada na cache, que pode estar desatualizada.',
'cachedspecial-refresh-now' => 'Ver mais recente.',
# Special:Categories
'listgrouprights-removegroup-self-all' => 'Remover a própria conta de todos os grupos',
# E-mail user
-'mailnologin' => 'Nenhum endereço de envio',
+'mailnologin' => 'Não existe endereço de envio',
'mailnologintext' => 'Precisa de estar [[Special:UserLogin|autenticado]] e possuir um endereço de correio válido nas suas [[Special:Preferences|preferências]], para poder enviar correio electrónico a outros utilizadores.',
-'emailuser' => 'Enviar um e-mail ao utilizador',
-'emailpage' => 'Contactar utilizador',
+'emailuser' => 'Enviar correio electrónico a este utilizador',
+'emailpage' => 'Enviar correio electrónico ao utilizador',
'emailpagetext' => 'Pode usar o formulário abaixo para enviar uma mensagem por correio electrónico para este utilizador.
O endereço de correio que introduziu nas suas [[Special:Preferences|preferências]] irá aparecer no campo do remetente da mensagem "De:", para que o destinatário lhe possa responder directamente.',
'usermailererror' => 'O sistema de correio devolveu o erro:',
-'defemailsubject' => 'E-mail do usuário "$1" da {{SITENAME}}',
+'defemailsubject' => 'Correio electrónico da {{SITENAME}}, do utilizador "$1"',
'usermaildisabled' => 'Correio electrónico do utilizador foi desactivado',
'usermaildisabledtext' => 'Não pode enviar correio electrónico aos outros utilizadores desta wiki',
'noemailtitle' => 'Sem endereço de correio electrónico',
'watchlistfor2' => 'Para $1 $2',
'nowatchlist' => 'A sua lista de páginas vigiadas está vazia.',
'watchlistanontext' => 'Precisa de $1 para ver ou editar a sua lista de páginas vigiadas, por favor.',
-'watchnologin' => 'Não está autenticado',
-'watchnologintext' => 'Precisa de estar [[Special:UserLogin|autenticado]] para modificar a sua lista de páginas vigiadas.',
+'watchnologin' => 'Não está autenticado(a)',
+'watchnologintext' => 'Precisa de [[Special:UserLogin|autenticar-se]] para modificar a sua lista de páginas vigiadas.',
'addwatch' => 'Adicionar às páginas vigiadas',
'addedwatchtext' => "A página \"[[:\$1]]\" foi adicionada à sua lista de [[Special:Watchlist|páginas vigiadas]], onde serão indicadas quaisquer
modificações futuras desta página e da respectiva página de discussão.
'rollback' => 'Reverter edições',
'rollback_short' => 'Voltar',
'rollbacklink' => 'voltar',
+'rollbacklinkcount' => 'reverter $1 {{PLURAL:$1|edição|edições}}',
+'rollbacklinkcount-morethan' => 'reverter mais do que $1 {{PLURAL:$1|edição|edições}}',
'rollbackfailed' => 'A reversão falhou',
'cantrollback' => 'Não foi possível reverter a edição; o último contribuidor é o único autor desta página',
'alreadyrolled' => 'Não foi possível reverter as edições de [[:$1]] por [[User:$2|$2]] ([[User talk:$2|discussão]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
# Edit tokens
'sessionfailure-title' => 'Erro de sessão',
'sessionfailure' => 'Foram detectados problemas com a sua sessão;
-esta operação foi cancelada como medida de protecção contra a intercepção de sessões.
-Clique o botão "Voltar" e recarregue a página de onde veio, depois tente novamente.',
+a operação foi cancelada como medida de protecção contra a interceptação de sessões.
+Volte à página anterior, refresque-a e tente novamente.',
# Protect
'protectlogpage' => 'Registo de protecção',
Esta é a configuração presente para a página '''$1''':",
'protect-locked-dblock' => "Não é possível alterar os níveis de protecção, porque a base de dados está bloqueada.
Esta é a configuração actual para a página '''$1''':",
-'protect-locked-access' => "A sua conta não tem permissão de alterar os níveis de protecção de uma página.
+'protect-locked-access' => "A sua conta não tem permissões para alterar os níveis de protecção de uma página.
Esta é a configuração actual da página '''$1''':",
'protect-cascadeon' => 'Esta página está protegida porque se encontra incluída {{PLURAL:$1|na página listada a seguir, protegida|nas páginas listadas a seguir, protegidas}} com protecção em cascata.
Pode alterar o nível de protecção desta página, mas isso não afectará a protecção em cascata.',
'undeletepage' => 'Ver e restaurar páginas eliminadas',
'undeletepagetitle' => "'''Seguem-se as edições eliminadas de [[:$1]]'''.",
'viewdeletedpage' => 'Ver páginas eliminadas',
-'undeletepagetext' => '{{PLURAL:$1|A seguinte página foi eliminada|As seguintes páginas foram eliminadas}}, mas ainda {{PLURAL:$1|permanece|permanecem}} na base de dados e poderem ser restauradas. O arquivo pode ser limpo periodicamente.',
+'undeletepagetext' => '{{PLURAL:$1|A seguinte página foi eliminada|As seguintes páginas foram eliminadas}}, mas ainda {{PLURAL:$1|permanece|permanecem}} em arquivo e podem ser restauradas. O arquivo pode ser limpo periodicamente.',
'undelete-fieldset-title' => 'Restaurar edições',
'undeleteextrahelp' => "Para restaurar o histórico de edições completo desta página, desmarque todas as caixas de selecção e clique '''''{{int:undeletebtn}}'''''.
Para efectuar uma restauração selectiva, marque as caixas correspondentes às edições que pretende restaurar e clique '''''{{int:undeletebtn}}'''''.",
# Contributions
'contributions' => 'Contribuições do utilizador',
'contributions-title' => 'Contribuições {{GENDER:$1|do utilizador|da utilizadora}} $1',
-'mycontris' => 'Minhas contribuições',
+'mycontris' => 'Contribuições',
'contribsub2' => 'Para $1 ($2)',
'nocontribs' => 'Não foram encontradas alterações com este critério.',
'uctop' => ' (edição actual)',
'sp-contributions-logs' => 'registos',
'sp-contributions-talk' => 'discussão',
'sp-contributions-userrights' => 'gestão de privilégios de utilizador',
-'sp-contributions-blocked-notice' => 'Este utilizador encontra-se actualmente bloqueado.
-Para referência, o último registo de bloqueio é apresentado abaixo:',
+'sp-contributions-blocked-notice' => 'Este utilizador está bloqueado neste momento.
+Para referência é apresentado abaixo o último registo de bloqueio:',
'sp-contributions-blocked-notice-anon' => 'Este endereço IP está bloqueado neste momento.
-Para sua referência, encontra abaixo a entrada mais recente no registo de bloqueios:',
+Para referência é apresentado abaixo o último registo de bloqueio:',
'sp-contributions-search' => 'Pesquisar contribuições',
'sp-contributions-username' => 'Endereço IP ou utilizador:',
'sp-contributions-toponly' => 'Mostrar somente as revisões mais recentes',
A página de destino ("[[:$1]]") já existe. Deseja eliminá-la de modo a poder mover?',
'delete_and_move_confirm' => 'Sim, eliminar a página',
'delete_and_move_reason' => 'Eliminada para poder mover "[[$1]]" para este título',
-'selfmove' => 'O título de origem e de destinato são os mesmos;
+'selfmove' => 'Os títulos de origem e destino são iguais;
não é possível mover uma página para ela mesma.',
'immobile-source-namespace' => 'Não é possível mover páginas no espaço nominal "$1"',
'immobile-target-namespace' => 'Não é possível mover páginas para o espaço nominal "$1"',
'thumbnail_error' => 'Erro ao criar miniatura: $1',
'djvu_page_error' => 'página DjVu inacessível',
'djvu_no_xml' => 'Não foi possível aceder ao XML para o ficheiro DjVU',
-'thumbnail-temp-create' => 'Não foi possível criar ficheiro temporário de miniatura',
-'thumbnail-dest-create' => 'Não é possível salvar miniatura',
+'thumbnail-temp-create' => 'Não foi possível criar o ficheiro temporário da miniatura',
+'thumbnail-dest-create' => 'Não é possível gravar a miniatura no destino',
'thumbnail_invalid_params' => 'Parâmetros de miniatura inválidos',
'thumbnail_dest_directory' => 'Não foi possível criar o directório de destino',
'thumbnail_image-type' => 'Tipo de imagem não suportado',
Este bloqueio foi provavelmente causado por um link para um site externo que consta da lista negra.",
'spamprotectionmatch' => 'O seguinte texto activou o filtro de spam: $1',
'spambot_username' => 'MediaWiki limpeza de spam',
-'spam_reverting' => 'Revertendo para a última revisão que não contém links para $1',
-'spam_blanking' => 'Todas as revisões continham links para $1, limpando',
+'spam_reverting' => 'A reverter para a última revisão que não contém links para $1',
+'spam_blanking' => 'Todas as revisões continham links para $1; a esvaziar',
+'spam_deleting' => 'Todas as revisões continham links para $1; a eliminar',
# Info page
'pageinfo-title' => 'Informações sobre "$1"',
'watchlistedit-raw-title' => 'Editar a lista de páginas vigiadas em forma de texto',
'watchlistedit-raw-legend' => 'Editar a lista de páginas vigiadas em forma de texto',
'watchlistedit-raw-explain' => 'A lista de páginas vigiadas é apresentada abaixo.
-Pode adicionar novas linhas ou remover linhas para aumentar ou reduzir a lista, desde que mantenha uma única página por linha.
+Pode adicionar ou remover linhas, para aumentar ou reduzir a lista.
+Liste uma só página por linha.
Quando terminar, clique "{{int:Watchlistedit-raw-submit}}".
Também pode [[Special:EditWatchlist|editar a lista da maneira convencional]].',
'watchlistedit-raw-titles' => 'Páginas:',
# Database error messages
'dberr-header' => 'Esta wiki tem um problema',
-'dberr-problems' => 'Desculpe! Este site está a experienciar dificuldades técnicas.',
+'dberr-problems' => 'Desculpe! Este site está com dificuldades técnicas.',
'dberr-again' => 'Experimente esperar uns minutos e actualizar.',
'dberr-info' => '(Não foi possível contactar o servidor da base de dados: $1)',
'dberr-usegoogle' => 'Pode tentar pesquisar no Google entretanto.',
'logentry-newusers-newusers' => '$1 criou uma conta de utilizador',
'logentry-newusers-create' => '$1 criou uma conta de utilizador',
'logentry-newusers-create2' => '$1 criou uma conta de utilizador $3',
-'logentry-newusers-autocreate' => 'A conta $1 foi criada automaticalmente',
+'logentry-newusers-autocreate' => 'A conta $1 foi criada automaticamente',
'newuserlog-byemail' => 'palavra-chave enviada por correio-electrónico',
# Feedback
-'feedback-bugornote' => 'Se está pronto para descrever um problema técnico em detalhe, por favor, [$1 denuncie um defeito].
+'feedback-bugornote' => 'Se está pronto para descrever um problema técnico em detalhe, por favor, [$1 comunique o defeito].
Caso contrário, pode facilmente usar o formulário abaixo. O seu comentário será adicionado à página "[$3 $2]", junto com o seu nome de utilizador e o navegador que está a usar.',
'feedback-subject' => 'Assunto:',
'feedback-message' => 'Mensagem:',
'feedback-thanks' => 'Obrigado! O seu comentário foi adicionado à página "[ $2 $1 ]".',
'feedback-close' => 'Feito',
'feedback-bugcheck' => 'Perfeito! Verifique apenas que não é já um dos [$1 defeitos conhecidos].',
-'feedback-bugnew' => 'Eu verifiquei. Denunciar um novo defeito.',
+'feedback-bugnew' => 'Eu verifiquei. Comunicar um novo defeito.',
# API errors
'api-error-badaccess-groups' => 'Não tem permissão para enviar ficheiros para esta wiki.',
'api-error-empty-file' => 'O ficheiro que enviou está vazio.',
'api-error-emptypage' => 'Não é permitido criar páginas novas vazias.',
'api-error-fetchfileerror' => 'Erro interno: Ocorreu um problema indeterminado ao aceder ao ficheiro.',
+'api-error-fileexists-forbidden' => 'Já existe um ficheiro com o nome "$1" e não pode ser substituído.',
+'api-error-fileexists-shared-forbidden' => 'Já existe um ficheiro com o nome "$1" no repositório de ficheiros partilhados e não pode ser substituído.',
'api-error-file-too-large' => 'O ficheiro que enviou era demasiado grande.',
'api-error-filename-tooshort' => 'O nome do ficheiro é demasiado curto.',
'api-error-filetype-banned' => 'Este tipo de ficheiro é proibido.',
'api-error-filetype-missing' => 'Falta a extensão do ficheiro.',
-'api-error-hookaborted' => 'A modificação que tentou fazer foi abortada pelo hook de uma extensão.',
+'api-error-hookaborted' => 'A modificação que tentou fazer foi cancelada por uma extensão.',
'api-error-http' => 'Erro interno: Ocorreu um problema na ligação ao servidor.',
'api-error-illegal-filename' => 'Este nome de ficheiro não é permitido.',
'api-error-internal-error' => 'Erro interno: Ocorreu um erro indeterminado na wiki ao processar o ficheiro que enviou.',
'api-error-mustbeloggedin' => 'Tem de estar autenticado para enviar ficheiros.',
'api-error-mustbeposted' => 'Erro interno: O pedido necessita do HTTP POST.',
'api-error-noimageinfo' => 'O envio correu bem, mas o servidor não forneceu nenhuma informação sobre o ficheiro.',
-'api-error-nomodule' => 'Erro interno: Não está definido nenhum módulo para recebimento de ficheiros.',
+'api-error-nomodule' => 'Erro interno: Não está definido nenhum módulo para upload de ficheiros.',
'api-error-ok-but-empty' => 'Erro interno: o servidor não respondeu.',
'api-error-overwrite' => 'Não é permitido sobrescrever um ficheiro existente.',
'api-error-stashfailed' => 'Erro interno: O servidor não conseguiu armazenar o ficheiro temporário.',
'duration-centuries' => '$1 {{PLURAL:$1|século|séculos}}',
'duration-millennia' => '$1 {{PLURAL:$1|milénio|milénios}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|não é um tipo de ficheiro permitido|não são tipos de ficheiro permitidos}}. {{PLURAL:$3|O tipo de ficheiro permitido é|Os tipos de ficheiro permitidos são}} $2.',
);
* @author Raylton P. Sousa
* @author Rodrigo Calanca Nishino
* @author Sir Lestaty de Lioncourt
+ * @author Teles
* @author TheGabrielZaum
* @author Urhixidur
* @author Vuln
'passwordreset' => 'Redefinir senha',
'passwordreset-text' => 'Preencha este formulário para recuperar os dados da sua conta pelo e-mail.',
'passwordreset-legend' => 'Reiniciar a senha',
-'passwordreset-disabled' => 'Redefinições de senha foram desabilitadas neste wiki.',
+'passwordreset-disabled' => 'Redefinições de senha foram desabilitadas nesta wiki.',
'passwordreset-pretext' => '{{PLURAL:$1||Introduza um dos dados abaixo}}',
'passwordreset-username' => 'Nome de usuário:',
'passwordreset-domain' => 'Domínio:',
Você pode contatar $1 ou outro [[{{MediaWiki:Grouppage-sysop}}|administrador]] para discutir sobre o bloqueio.
-Note que não poderá utilizar a funcionalidade "Contatar usuário" se não possuir uma conta neste wiki ({{SITENAME}}) com um endereço de \'\'e-mail\'\' válido indicado nas suas [[Special:Preferences|preferências de usuário]] ou se tiver sido bloqueado de utilizar tal recurso.
+Note que não poderá utilizar a funcionalidade "Contatar usuário" se não possuir uma conta nesta wiki ({{SITENAME}}) com um endereço de \'\'e-mail\'\' válido indicado nas suas [[Special:Preferences|preferências de usuário]] ou se tiver sido bloqueado de utilizar tal recurso.
Seu endereço de IP no momento é $3 e sua ID de bloqueio é #$5.
Por favor, inclua tais dados em qualquer tentativa de esclarecimentos que for realizar.',
Poderá haver detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].",
'rev-suppressed-no-diff' => "Você não pode ver esta comparação porque uma das revisões foi '''eliminada'''.",
'rev-deleted-unhide-diff' => "Uma das revisões desta diferença entre revisões foi '''eliminada'''.
-Podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminações].
+Podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminações].
Pode mesmo assim [$1 ver estas diferenças] se deseja prosseguir.",
'rev-suppressed-unhide-diff' => "Uma das revisões desta diferença entre revisões foi '''suprimida'''.
-Podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registo de supressões].
+Podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supressões].
Pode mesmo assim [$1 ver estas diferenças] se deseja prosseguir.",
'rev-deleted-diff-view' => "Uma das revisões desta diferença entre revisões foi '''eliminada'''.
-Você pode ver a diferença entre revisões; podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminações].",
+Você pode ver a diferença entre revisões; podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminações].",
'rev-suppressed-diff-view' => "Uma das revisões desta comparação foi '''suprimida''''.
-Você pode ver esta comparação; detalhes podem ser encontradas no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supressão].",
+Você pode ver esta comparação; detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de supressão].",
'rev-delundel' => 'exibir/ocultar',
'rev-showdeleted' => 'exibir',
'revisiondelete' => 'Eliminar/restaurar edições',
'logdelete-selected' => "'''{{PLURAL:$1|Evento de registro selecionado|Eventos de registro selecionados}}:'''",
'revdelete-text' => "'''Revisões eliminadas e eventos continuarão aparecendo no histórico da página e nos registros, apesar de o seu conteúdo textual estar inacessível ao público.'''
Outros administradores no {{SITENAME}} continuarão podendo acessar ao conteúdo escondido e restaurá-lo através desta mesma ''interface'', a menos que uma restrição adicional seja definida.",
-'revdelete-confirm' => 'Por favor confirme que pretende executar esta acção, que compreende as suas consequências e que o faz em concordância com as [[{{MediaWiki:Policy-url}}|políticas e recomendações]].',
+'revdelete-confirm' => 'Por favor confirme que pretende executar esta ação, que compreende as suas consequências e que o faz em concordância com as [[{{MediaWiki:Policy-url}}|políticas e recomendações]].',
'revdelete-suppress-text' => "A supressão deverá ser usada '''apenas''' para os seguintes casos:
* Informação pessoal inapropriada
*: ''endereços de domicílio e números de telefone, números da segurança social, etc''",
'revdelete-modify-missing' => 'Erro ao modificar o item ID $1: está faltando na base de dados!',
'revdelete-no-change' => "'''Aviso:''' o item datado de $2, $1 já possui as configurações de visualização requeridas.",
'revdelete-concurrent-change' => 'Erro ao modificar o item datado de $2, $1: o seu estado parece ter sido alterado por outra pessoa enquanto você tentava modificá-lo.
-Por favor, verifique os registos.',
+Por favor, verifique os registros.',
'revdelete-only-restricted' => 'Erro ao ocultar o item de $2 às $1: você não pode impedir que itens sejam visualizados por administradores sem também selecionar uma das outras opções de visibilidade.',
'revdelete-reason-dropdown' => '*Motivos comuns para eliminação
** Violação de direitos autorais
'shown-title' => 'Mostrar $1 {{PLURAL:$1|resultado|resultados}} por página',
'viewprevnext' => 'Ver ($1 {{int:pipe-separator}} $2) ($3).',
'searchmenu-legend' => 'Opções de pesquisa',
-'searchmenu-exists' => "'''Há uma página com o nome \"[[:\$1]]\" neste wiki'''",
-'searchmenu-new' => "'''Criar a página \"[[:\$1|\$1]]\" neste wiki!'''",
+'searchmenu-exists' => "'''Há uma página com o nome \"[[:\$1]]\" nesta wiki'''",
+'searchmenu-new' => "'''Criar a página \"[[:\$1|\$1]]\" nesta wiki!'''",
'searchhelp-url' => 'Help:Conteúdos',
'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Navegue pelas páginas com este prefixo]]',
'searchprofile-articles' => 'Páginas de conteúdo',
'recentchangesdays' => 'Dias a serem exibidos nas Mudanças recentes:',
'recentchangesdays-max' => '(máximo: $1 {{PLURAL:$1|dia|dias}})',
'recentchangescount' => 'Número de edições a serem exibidas por padrão:',
-'prefs-help-recentchangescount' => 'Isto inclui mudanças recentes, histórico de páginas e registos.',
+'prefs-help-recentchangescount' => 'Isto inclui mudanças recentes, histórico de páginas e registros.',
'prefs-help-watchlist-token' => "O preenchimento deste campo com uma senha secreta irá gerar um ''feed'' RSS para a sua lista de páginas vigiadas.
Qualquer um que conheça a senha deste campo será capaz de ler sua lista de páginas vigiadas, então escolha um valor seguro.
Eis um valor gerado aleatoriamente que você pode usar: $1",
'duration-centuries' => '$1 {{PLURAL:$1|século|séculos}}',
'duration-millennia' => '$1 {{PLURAL:$1|milênio|milênios}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|não é um tipo de arquivo permitido|não são tipos de arquivos permitidos}}. {{PLURAL:$3|O tipo de arquivo permitido é|Os tipos de arquivos permitidos são}} $2.',
);
* @author Ahonc
* @author Aleator
* @author AlexSm
+ * @author Amahoney
* @author Amire80
* @author AnakngAraw
* @author Ans
* @author Purodha
* @author Rancher
* @author Raymond
+ * @author Reedy
* @author Robby
* @author Rotemliss
* @author Ryan Schmidt
{{Identical|Copyright}}',
'currentevents' => 'Standard link in the sidebar, for news. See also {{msg|currentevents-url}} for the link url.',
'currentevents-url' => "Target page of ''{{Mediawiki:currentevents}}'' in the sidebar. See also {{msg|currentevents}}.
-{{doc-important|Do not translate <tt>Project:</tt> part.}}",
+{{doc-important|Do not translate the \"<tt>Project:</tt>\" part.}}",
'disclaimers' => 'Used as display name for the link to [[{{MediaWiki:Disclaimerpage}}]] shown at the bottom of every page on the wiki. Example [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].',
'disclaimerpage' => 'Used as page for that contains the site disclaimer. Used at the bottom of every page on the wiki. Example: [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].
{{doc-important|Do not change the "<tt>Project:</tt>" part.}}',
{{Identical|New messages}}',
'newmessagesdifflink' => 'This is the second link displayed in an orange rectangle when a user gets a message on his talk page. Used in message {{msg-mw|youhavenewmessages}} (as parameter $2).',
+'youhavenewmessagesfromusers' => 'New talk indicator message: the message appearing when someone edited your user talk page.
+The message takes three parameters;
+*$1 {{msg-mw|newmessageslinkplural}},
+*$2 {{msg-mw|newmessagesdifflinkplural}}, and
+*$3 the number of authors who have edited the talk page since the owning user last viewed it.',
+'youhavenewmessagesmanyusers' => 'New talk indicator message: the message appearing when someone edited your user talk page. Used when more than 10 users edited the user talk page since the owning user last viewed it, similar to{{msg-mw|youhavenewmessages}}. Parameters:
+* $1 is {{msg-mw|newmessageslinkplural}},
+* $2 is {{msg-mw|newmessagesdifflinkplural}}.',
+'newmessageslinkplural' => 'Like {{msg-mw|newmessageslink}} but supporting pluralization. Used in message {{msg-mw|youhavenewmessagesfromusers}} (as parameter $1).
+This message itself takes one parameter, $1, which is 1 if there was one new edit, or 2 if there was more than one new edit
+since the last time the user has seen his or her talk page.',
+'newmessagesdifflinkplural' => 'Like {{msg-mw|newmessagesdifflink}} but supporting pluralization. Used in message {{msg-mw|youhavenewmessagesfromusers}} (as parameter $2).
+This message itself takes one parameter, $1, which is the number of new edits since the last time the user has seen his or her talk page.',
'youhavenewmessagesmulti' => 'The alternative of {{msg|youhavenewmessages}} as used on wikis with a special setup so they can receive the "new message" notice on other wikis as well. Used on [http://www.wikia.com/ Wikia].
The format is: "{{int:youhavenewmessagesmulti| [[MediaWiki:Newmessageslink/{{SUBPAGENAME}}|{{int:newmessageslink}}]]}}"',
'editsection' => 'Display name of link to edit a section on a content page. Example: [{{MediaWiki:Editsection}}].
'remembermypassword' => 'A check box in [[Special:UserLogin]]
{{Identical|Remember my login on this computer}}',
+'password-change-forbidden' => 'Error message shown when an external authentication source does not allow the password to be changed.',
'externaldberror' => 'This message is thrown when a valid attempt to change the wiki password for a user fails because of a database error or an error from an external system.',
'login' => "Shown as the caption of the button at [[Special:UserLogin]], and also to anonymous users in the upper right corner of the page when they can't create an account (otherwise the message {{msg|nav-login-createaccount}} is shown there).
'gotaccountlink' => 'Text of the link to the log in form. Before that link, the message [[MediaWiki:Gotaccount/{{SUBPAGENAME}}]] appears.
{{Identical|Log in}}',
+'userlogin-resetlink' => 'Used on the login page.',
'createaccountmail' => 'Button text for creating a new account and sending the new password to the specified e-mail address directly, as used on [[Special:UserLogin/signup]] if creating accounts by e-mail is allowed.',
'createaccountreason' => '{{Identical|Reason}}',
'createaccounterror' => 'Parameters:
{{Identical|Reset password}}',
'passwordreset-text' => 'Text on [[Special:PasswordReset]]',
'passwordreset-legend' => '{{Identical|Reset password}}',
-'passwordreset-pretext' => 'Parameters:
+'passwordreset-pretext' => 'These instructions are shown on the password reset dialogue, which can, in principle, take the user\'s email address as well as, or instead of, their username. This text displays above one or more fields, at least one of which needs to be completed, and the message does not know which routes are available, so it needs to refer to some vague noun rather than specifically "username".
+"One of the pieces of data" means "an info"/"a datum" (probably to be translatea with a singular noun in your language if available). Parameters:
* $1 is the number of password reset routes. This is never 1, but always two or more. Thus, the first plural option is empty in English.',
'passwordreset-username' => '{{Identical|Username}}',
'passwordreset-domain' => 'A domain like used in Domain Name System (DNS) or more specifically like a domain component in the Lightweight Directory Access Protocol (LDAP)',
* $2 - message {{msg-mw|passwordreset-emailelement|notext=1}} repeated $3 times
* $3 - the number of repetitions in $2
* $4 - base URL of the wiki',
-'passwordreset-emailelement' => "This is a body of a reminder email to allow them into the system with a new password.
-$1 will be the user's login name.
-$2 will be the temporary password given by the system.",
+'passwordreset-emailelement' => "This is a body of a reminder email to allow them into the system with a new password. Parameters:
+* $1 will be the user's login name. This parameter can be used for GENDER.
+* $2 will be the temporary password given by the system.",
'passwordreset-emailerror-capture' => 'Error message displayed when sending an e-mail fails. Parameters:
* $1 is the name of a user who was supposed to get the e-mail.',
See also {{msg-mw|Noarticletext-nopermission}}.',
'noarticletext-nopermission' => 'See also {{msg-mw|Noarticletext}}.',
+'missing-revision' => 'Text displayed when the requested revision does not exist using a permalink.
+
+Example: [http://translatewiki.net/w/i.php?title=Project:News&oldid=9999999 Permalink with invalid revision#]
+
+* $1 is the ID of the missing revision',
'userpage-userdoesnotexist' => 'Error message displayed when trying to edit or create a page or a subpage that belongs to a user who is not registered on the wiki. Parameters:
* $1 is a possible username that has not been registered.',
'userpage-userdoesnotexist-view' => 'Shown in user pages of non existing users. See for example [http://translatewiki.net/wiki/User:Foo User:Foo]. Parameters:
"Unstrip" refers to the internal function of the parser, called \'unstrip\', which recursively puts the output of parser functions in the place of the parser function call and which would enter an infinite loop in the situation above. See also:
*{{msg-mw|Parser-unstrip-loop-warning}}',
+'converter-manual-rule-error' => "This message is shown when a manual conversion rule for the language converter has errors. For example it's not using the correct syntax, or not supplying text in all variants.",
# "Undo" feature
'undo-success' => 'Text on special page to confirm edit revert. You arrive on this page by clicking on the "undo" link on a revision history special page.
[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]',
# Suppression log
-'suppressionlog' => 'Title of the suppression log. Shown in the drop down menu at [[Special:log]] and as header of [[Special:log/suppress]].',
+'suppressionlog' => '{{doc-logpage}}
+
+Title of the suppression log. Shown in the drop down menu at [[Special:log]] and as header of [[Special:log/suppress]].',
'suppressionlogtext' => 'Description text of the suppression log. Shown at top of [[Special:log/suppress]].',
# History merging
* $6 is a revision comment',
# Merge log
-'mergelog' => 'This is the name of a log of merge actions done on [[Special:MergeHistory]]. This special page and this log is not enabled by default.',
+'mergelog' => '{{doc-logpage}}
+This is the name of a log of merge actions done on [[Special:MergeHistory]]. This special page and this log is not enabled by default.',
'pagemerge-logentry' => "This log message is used in a merge log entry.
*Parameter $1 is the page name of the source of the content to be merged.
'diff-multi-manyusers' => "This message appears in the revision history of a page when comparing two versions which aren't consecutive, and the intermediate revisions have been edited by more than 100 users. Parameters:
* $1 is the number of revisions, will always be 101 or more.
* $2 is the number of users that were found, which was limited at 100.",
+'difference-missing-revision' => 'Text displayed when the requested revision does not exist using a diff link.
+
+Example: [http://translatewiki.net/w/i.php?title=Project:News&diff=426850&oldid=99999999 Diff with invalid revision#]
+
+* $1 is the list of missing revisions IDs
+* $2 is the number of items in $1 (one or two)',
# Search results
-'searchresults' => '{{Identical|Search results}}',
+'searchresults' => 'This is the title of the page that contains the results of a search.
+
+{{Identical|Search results}}',
'searchresults-title' => 'Appears as page title in the html header of the search result special page.',
'notitlematches' => 'Header of results page after a search for a title for which no page exists',
'textmatches' => 'When displaying search results',
'right-passwordreset' => '{{doc-right|passwordreset}}',
# User rights log
-'rightslog' => 'In [[Special:Log]]',
+'rightslog' => '{{doc-logpage}}
+In [[Special:Log]]',
'rightslogtext' => 'Text in [[Special:Log/rights]].',
'rightslogentry' => 'This message is displayed in the [[Special:Log/rights|User Rights Log]] when a bureaucrat changes the user groups for a user.
'upload-permitted' => 'Used in [[Special:Upload]].',
'upload-preferred' => 'Used in [[Special:Upload]].',
'upload-prohibited' => 'Used in [[Special:Upload]].',
-'uploadlogpage' => 'Page title of [[Special:Log/upload]].',
+'uploadlogpage' => '{{doc-logpage}}
+Page title of [[Special:Log/upload]].',
'filename' => '{{Identical|Filename}}',
'filedesc' => '{{Identical|Summary}}',
'fileuploadsummary' => '{{Identical|Summary}}',
'http-invalid-scheme' => 'The message appears in the Mediawiki code as follows:
if ( $this->parsedUrl[\'scheme\'] != \'http\' ) {
- $this->status->fatal( \'http-invalid-scheme\', $this->parsedUrl[\'scheme\'] );
+ $this->status->fatal( \'http-invalid-scheme\', $this->parsedUrl[\'scheme\'] );
}
Siebrand think this has to do with allowing MediaWiki to fetch remote URLs, and in that not allowing anything but "http://" request. So if this for example is "irc://" or "https://", $1 would be "irc" or "https" respectively.
* '''Note:''' Do not change the link [[MediaWiki:Disambiguationspage]], even because it is listed as problematic. Be sure the \"D\" is in uppercase, so not \"d\".
-* '''Background information:''' Beyond telling about links going to disambiguation pages, that they are generally bad, it should explain which pages in the article namespace are seen as diambiguations: [[MediaWiki:Disambiguationspage]] usually holds a list of diambiguation templates of the local wiki. Pages linking to one of them (by transclusion) will count as disambiguation pages. Pages linking to these disambiguation pages, instead to the disambiguated article itself, are listed on [[:Special:Disambiguations]].",
+* '''Background information:''' Beyond telling about links going to disambiguation pages, that they are generally bad, it should explain which pages in the article namespace are seen as disambiguations: [[MediaWiki:Disambiguationspage]] usually holds a list of disambiguation templates of the local wiki. Pages linking to one of them (by transclusion) will count as disambiguation pages. Pages linking to these disambiguation pages, instead to the disambiguated article itself, are listed on [[:Special:Disambiguations]].",
'doubleredirects' => 'Name of [[Special:DoubleRedirects]] displayed in [[Special:SpecialPages]]',
'doubleredirectstext' => 'Shown on top of [[Special:Doubleredirects]]',
'specialloguserlabel' => 'Used in [[Special:Log]] as a label for an input field with which the log can be filtered for entries describing actions \'\'performed\'\' by the specified user. "Carried out" and "done" are possible alternatives for "performed".',
'speciallogtitlelabel' => 'Used in [[Special:Log]] as a label for an input field with which the log can be filtered. This filter selects for pages or users on which a log action was performed.',
'log' => 'Name of special page displayed in [[Special:SpecialPages]]',
-'all-logs-page' => 'Title of [[Special:Log]].',
+'all-logs-page' => '{{doc-logpage}}
+Title of [[Special:Log]].',
'alllogstext' => 'Header of [[Special:Log]]',
'log-title-wildcard' => '* Appears in: [[Special:Log]]
* Description: A check box to enable prefix search option',
'activeusers-noresult' => 'identical with {{msg-mw|listusers-noresult}}',
# Special:Log/newusers
-'newuserlogpage' => 'Part of the "Newuserlog" extension. It is both the title of [[Special:Log/newusers]] and the link you can see in [[Special:RecentChanges]].',
+'newuserlogpage' => '{{doc-logpage}}
+
+Part of the "Newuserlog" extension. It is both the title of [[Special:Log/newusers]] and the link you can see in [[Special:RecentChanges]].',
'newuserlogpagetext' => 'Part of the "Newuserlog" extension. It is the description you can see on [[Special:Log/newusers]].',
# Special:ListGroupRights
'deletedtext' => 'Parameters:
* $1 is a page that was deleted
* $2 is {{msg-mw|deletionlog}}',
-'dellogpage' => 'The name of the deletion log. Used as heading on [[Special:Log/delete]] and in the drop down menu for selecting logs on [[Special:Log]].
+'dellogpage' => '{{doc-logpage}}
+The name of the deletion log. Used as heading on [[Special:Log/delete]] and in the drop down menu for selecting logs on [[Special:Log]].
{{Identical|Deletion log}}',
'dellogpagetext' => 'Text in [[Special:Log/delete]].',
'rollback_short' => '{{Identical|Rollback}}',
'rollbacklink' => '{{Identical|Rollback}}
This message has a tooltip {{msg-mw|tooltip-rollback}}',
+'rollbacklinkcount' => 'Text of the rollback link showing the number of edits to be rolled back. See also {{msg-mw|rollbacklink}}.
+* $1: the number of edits that will be rollbacked. If $1 is over the value of $wgShowRollbackEditCount (default: 10) {{msg-mw|rollbacklinkcount-morethan}} is used.',
+'rollbacklinkcount-morethan' => 'Text of the rollback link when a greater number of edits is to be rolled back. See also {{msg-mw|rollbacklink}}.
+
+When the number of edits rolled back is smaller than [[mw:Manual:$wgShowRollbackEditCount|$wgShowRollbackEditCount]], {{msg-mw|rollbacklinkcount}} is used instead.',
'rollbackfailed' => '{{Identical|Rollback}}',
'cantrollback' => '{{Identical|Revert}}
{{Identical|Rollback}}',
{{Identical|Rollback}}',
# Protect
-'protectlogpage' => 'Title of [[Special:Log/protect]].',
+'protectlogpage' => '{{doc-logpage}}
+Title of [[Special:Log/protect]].',
'protectlogtext' => 'Text in [[Special:Log/protect]].',
'protectedarticle' => 'Text describing an action on [[Special:Log]]. $1 is a page title.',
'modifiedarticleprotection' => 'Text describing an action on [[Special:Log]]. $1 is a page title.',
'ipbotherreason' => '{{Identical|Other/additional reason}}',
'ipbhidename' => 'This is the label for a checkbox in the user block form on [[Special:Block]].',
'ipbwatchuser' => 'This is an option on [[Special:BlockIP]] to watch the user page and talk page of the blocked user',
+'ipb-disableusertalk' => '{{doc-singularthey}}',
'ipb-change-block' => 'Confirmation checkbox required for blocks that would override an earlier block. Appears together with {{msg|ipb-needreblock}}.',
'badipaddress' => 'An error message shown when one entered an invalid IP address in blocking page.',
'blockipsuccesstext' => '<nowiki>{{</nowiki>[[Gender|GENDER]]<nowiki>}}</nowiki> is supported.',
'emaillink' => 'Used as display name for a link to send an e-mail to a user in the user tool links. Example: "(Talk | contribs | block | send e-mail)".
{{Identical|E-mail}}',
-'blocklogpage' => "The page name of [[Special:Log/block]]. Also appears in the drop down menu of [[Special:Log]] pages and in the action links of Special:Contributions/''Username'' pages (e.g. \"For Somebody (talk | block log | logs)\").
+'blocklogpage' => "{{doc-logpage}}
+
+The page name of [[Special:Log/block]]. Also appears in the drop down menu of [[Special:Log]] pages and in the action links of Special:Contributions/''Username'' pages (e.g. \"For Somebody (talk | block log | logs)\").
{{Identical|Block log}}",
'blocklog-showlog' => 'Parameters:
'ipb_already_blocked' => '{{Identical|$1 is already blocked}}',
'ipb-otherblocks-header' => '[[File:Special.Block with other blocks from GlobalBlocking and TorBlocks.png|thumb|Example]]
Used on [[Special:Block]] as header for other blocks, i.e. from GlobalBlocking or TorBlocks',
+'unblock-hideuser' => '{{doc-singularthey}}',
'blockme' => 'The page title of [[Special:Blockme]], a feature which is disabled by default.',
'proxyblocksuccess' => '{{Identical|Done}}',
'sorbs' => '{{optional}}',
'move-subpages' => 'The text of an option on the special page [[Special:MovePage|MovePage]]. If this option is ticked, any subpages will be moved with the main page to a new title.',
'move-talk-subpages' => 'The text of an option on the special page [[Special:MovePage|MovePage]]. If this option is ticked, any talk subpages will be moved with the talk page to a new title.',
'movepage-max-pages' => 'PROBABLY (A GUESS): when moving a page, you can select an option of moving its subpages, but there is a maximum that can be moved automatically.',
-'movelogpage' => 'Title of [[Special:Log/move]]. Used as heading on that page, and in the dropdown menu on log pages.',
+'movelogpage' => '{{doc-logpage}}
+Title of [[Special:Log/move]]. Used as heading on that page, and in the dropdown menu on log pages.',
'movelogpagetext' => "Text on the special page 'Move log'.",
'movesubpage' => "This is a section header on [[Special:MovePage]], below is a list of subpages.
Parameters:
* {{msg-mw|import-error-edit}}',
# Import log
-'importlogpage' => '',
+'importlogpage' => '{{doc-logpage}}',
'importlogpagetext' => 'This text appears at the top of the [//translatewiki.net/w/i.php?title=Special:Log&type=import import log] special page.',
'import-logentry-upload' => 'This is the text of an entry in the Import log (and Recent Changes), after hour (and date, only in the Import log) and sysop name:
* $1 is the name of the imported file',
'markedaspatrolledtext' => '{{Identical|Markedaspatrolled}}',
# Patrol log
-'patrol-log-page' => 'Name of log.',
+'patrol-log-page' => '{{doc-logpage}}',
'patrol-log-header' => 'Text that appears above the log entries on the [[Special:log|patrol log]].',
'log-show-hide-patrol' => '* $1 is one of {{msg|show}} or {{msg|hide}}',
'api-error-uploaddisabled' => 'API error message that can be used for client side localisation of API errors.',
'api-error-verification-error' => 'The word "extension" refers to the part behind the last dot in a file name, that by convention gives a hint about the kind of data format which a files contents are in.',
+# Unknown messages
+'api-error-filetype-banned-type' => "API error message that can be used for client side localisation of API errors.
+
+* $1 is the extension(s) of the file which cannot be uploaded
+* $2 is the list of file extensions that can be uploaded (Example: ''png, gif, jpg, jpeg, ogg, pdf, svg.'')
+* $3 is the number of allowed file formats (to be used for the PLURAL function)
+* $4 is the number of extensions that could not be uploaded (to be used for the PLURAL function)",
);
'duration-centuries' => '{{PLURAL:$1|pachakwata|pachakwatakuna}}',
'duration-millennia' => '{{PLURAL:$1|waranqawata|waranqawatakuna}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 nisqaqa manam saqillasqachu willañiqi {{PLURAL:$4|laya|layakuna}}. Saqillasqa willañiqi {{PLURAL:$3|layaqa|layakunaqa}} kaymi: $2.',
);
* @author Aryaz
* @author Dalinanir
* @author Jose77
+ * @author MoubarikBelkasim
* @author Urhixidur
*/
$messages = array(
# Dates
+'sunday' => 'Asamas (Eřḥedd)',
+'monday' => 'Aynas (Řetnayen)',
+'tuesday' => 'Asinas (Ettřata)',
+'wednesday' => 'Akṛas (Řarbeɛ)',
+'thursday' => 'Akwas (Řexmis)',
+'friday' => 'Asimwas (Ejjemɛa)',
+'saturday' => 'Asiḍyas (Esseft)',
'sun' => 'Asamas',
'mon' => 'Aynas',
'tue' => 'Asinas',
'january' => 'Yennayer',
'february' => 'Yebrayer',
'march' => 'Mares',
-'april' => 'Ibrir',
+'april' => 'Abril',
'may_long' => 'May',
'june' => 'Yunyu',
'july' => 'Yulyuz',
-'august' => 'Ghuct',
-'september' => 'Cutanbir',
+'august' => 'Ɣuct',
+'september' => 'Cutenbir',
'october' => 'Ktubar',
'november' => 'Nuwanbir',
-'december' => 'Dujanbir',
+'december' => 'Dujembir',
'january-gen' => 'Ynnayr',
'february-gen' => 'Ybrayr',
'march-gen' => 'Mars',
'jan' => 'Yennayer',
'feb' => 'Yebrayer',
'mar' => 'Mars',
-'apr' => 'Ybrir',
+'apr' => 'Abrir',
'may' => 'May',
'jun' => 'Yunyu',
'jul' => 'Yulyuz',
-'aug' => 'Ghuct',
-'sep' => 'Cutanbir',
+'aug' => 'Ɣuct',
+'sep' => 'Cutembir',
'oct' => 'Ktubar',
'nov' => 'Nuwanbir',
-'dec' => 'Dujanbir',
+'dec' => 'Dujenbir',
# Categories related messages
'category_header' => 'Tasniwin di taggayt "$1"',
'qbedit' => 'Ẓṛeg',
'qbspecialpages' => 'Tudmawin Special',
+# Vector skin
+'vector-action-move' => 'Smuṭṭi',
+'vector-view-create' => 'Seɣnu',
+'vector-view-edit' => 'Ẓṛeg',
+'vector-view-history' => 'Ẓeṛ amezruy',
+'vector-view-view' => 'Ɣeṛ',
+'actions' => 'Timegga',
+
'errorpagetitle' => 'Anezri',
'returnto' => 'Dwl ghar $1.',
'tagline' => 'Zi {{SITENAME}}',
'editthispage' => 'Ẓṛg tasna ya',
'delete' => 'Kks',
'protect' => 'Mstn',
-'protect_change' => 'sbadl',
+'protect_change' => 'beddeř',
'newpage' => 'Tasna d-tamaynut',
'talkpage' => 'Siwl xf tasna ya',
'talkpagelinktext' => 'Awal',
'disclaimerpage' => 'Project:Asmigel amatu',
'edithelp' => 'Tallalt deg uẓareg',
'edithelppage' => 'Help:Aẓareg',
-'helppage' => 'Help:tallalt',
+'helppage' => 'Help:Tira d yiwlafen',
'mainpage' => 'Tasna Tamezwarut',
-'mainpage-description' => 'Tasna Tamzwarut',
+'mainpage-description' => 'Tasna Tamezwarut',
'portal' => 'Tawwart n timetti',
-'portal-url' => 'Project:tawwart n timetti',
+'portal-url' => 'Project:tawwart n yiwdan',
'privacy' => 'Tasertit n tusligi',
'privacypage' => 'Project:Tasertit n tusligi',
'newmessagesdifflink' => 'Taẓṛigt tanggarut',
'editsection' => 'Ẓṛeg',
'editold' => 'ẓṛeg',
+'viewsourceold' => 'ẓeṛ aɣbalu',
'editlink' => 'ẓṛg',
'viewsourcelink' => 'ẓṛ aghbalu',
'editsectionhint' => 'Ẓṛeg tigezmi: $1',
'showtoc' => 'sskn-d',
'hidetoc' => 'snuffar',
'site-rss-feed' => 'Tilgha n RSS n $1',
-'site-atom-feed' => 'Talghut n Atom n $1',
+'site-atom-feed' => 'Talɣut n Atom n $1',
'page-rss-feed' => 'Asudem n RSS n "$1"',
-'red-link-title' => '$1 (tasna ur telli)',
+'red-link-title' => '$1 (tasna wer telli)',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Tasna',
'nstab-user' => 'Tasna n User',
'nstab-project' => 'Tasna usenfar',
'nstab-image' => 'Asatul',
-'nstab-template' => 'Tamudmt',
+'nstab-template' => 'Tamudemt',
'nstab-category' => 'Taggayt(category)',
# General errors
'badtitle' => 'isem war icni ca',
'badtitletext' => 'Isem n Tasna itexised war icni ca, ixwa, niɣ isem n ajar-tutlayt niɣ ajar-wiki war icni ca.
teqqad ad yilli days ca n usekkil war itwagg deg isem .',
-'viewsource' => 'Ẓṛ aghbalu',
+'viewsource' => 'Ẓeṛ aɣbalu',
'viewsourcetext' => 'Tzemred a tẓerd u atsneɣled aɣbal n Tasna ya :',
# Login and logout pages
'rev-delundel' => 'sken/ffer',
# Diffs
-'history-title' => 'Amezruy n ufegged n "$1"',
+'history-title' => 'Amezruy n teẓṛigt n "$1"',
'lineno' => 'Tabrit $1:',
'compareselectedversions' => 'Smequdda tunɣilin a',
-'editundo' => 'kkes min ggigh',
+'editundo' => 'kkes min ggiɣ',
'diff-multi' => '({{PLURAL:$1|ijj n ufegged|$1 ifeggiden}} war ad twamlen ca.)',
# Search results
'prevn' => 'Amzray {{PLURAL:$1|$1}}',
'nextn' => 'wn d-itasn {{PLURAL:$1|$1}}',
'viewprevnext' => 'Ẓeṛ ($1 {{int:pipe-separator}} $2) ($3)',
+'searchprofile-everything' => 'Marra',
+'searchprofile-articles-tooltip' => 'Rzu di $1',
+'searchprofile-project-tooltip' => 'Rzu di $1',
'search-result-size' => '$1 ({{PLURAL:$2|1 tawalt|$2 tiwalin}})',
+'search-redirect' => '(awi $1)',
'search-suggest' => 'Ttugha txsd a tinid: $1',
'search-interwiki-caption' => 'Awmatn n usnfar',
'search-interwiki-more' => '(ujar)',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|tiẓṛegt|tiẓṛigin}}',
-'recentchanges' => 'Tiẓṛigin tinggura',
+'recentchanges' => 'Tiẓṛigin tineggura',
'recentchanges-feed-description' => 'Bbar tiẓṛigin timayutin n wiki deg usudem(feed) a .',
'rcnote' => "Awadday {{PLURAL:$1|d '''1''' taẓṛigt|d '''$1''' tiẓṛigin tinggura}} deg {{PLURAL:$2|ass anggaru |'''$2''' ussan inggura}}, am di $5, $4.",
'rcnotefrom' => "ɣar wadday d tiẓṛigin zi '''$2''' (ar '''$1''' ).",
'diff' => 'imṣebḍan',
'hist' => 'Amezruy',
'hide' => 'Snuffar',
-'show' => 'smmel-ad',
+'show' => 'semmel-d',
'minoreditletter' => 'm',
'newpageletter' => 'N',
'boteditletter' => 'b',
'filehist-dimensions' => 'Tisektiwin',
'filehist-filesize' => 'Tiddi n ufaylu',
'filehist-comment' => 'Tinit',
-'imagelinks' => 'Iwurn n usdaw',
+'imagelinks' => 'Aseqdec usatul',
'linkstoimage' => '{{PLURAL:$1|Tasna ya teqn-ad|$1 Tasniwin a qnent-id}} ɣa ufaylu ya :',
'nolinkstoimage' => 'war telli ca n Tasna teqqen-d ɣa ufaylu ya.',
'sharedupload' => 'Wa d ijj ufaylu itwacrec jar aṭṭas n isenfaren(projects).',
'unusedtemplates' => 'Timudmiwin war twasexedment',
# Random page
-'randompage' => 'Tasna zi ṭṭarf',
+'randompage' => 'Tasna mamec ma tella',
# Random redirect
'randomredirect' => '(redirect) zi ṭṭarf',
# Undelete
'undeletebtn' => 'Ar-ad',
+'undeleteviewlink' => 'ẓeṛ',
'undelete-search-submit' => 'Tarzzut',
# Namespace form on various pages
'nolinkshere' => "war tlli ca n Tasna tqqen-d da '''[[:$1]]'''.",
'isredirect' => 'Tasna n (redirect)',
'istemplate' => 'Asidef',
-'isimage' => 'amaqqan n tugna',
+'isimage' => 'amaqqan usatul',
'whatlinkshere-prev' => '{{PLURAL:$1|deffar|deffar $1}}',
'whatlinkshere-next' => '{{PLURAL:$1|zzat|zzat $1}}',
'whatlinkshere-links' => '← tizdayin',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Tasna inu',
'tooltip-pt-mytalk' => 'Tasna usiwl inu',
-'tooltip-pt-preferences' => 'Isemyifiyen inu',
+'tooltip-pt-preferences' => 'Min d-ac itteɛjiben',
'tooltip-pt-watchlist' => 'Tabdart n Tasniwin umi txmamd bac ad-ten teẓṛegd',
'tooltip-pt-mycontris' => 'Umuɣ n tiwuriwin inu',
'tooltip-pt-login' => 'Neqqar ac adef s umiḍan nnek; maca malla texsed waha',
-'tooltip-pt-logout' => 'Ufugh',
+'tooltip-pt-logout' => 'Ufuɣ',
'tooltip-ca-talk' => 'Amsawal xef tasna n ukettur',
'tooltip-ca-edit' => 'Tzemmared a tẓeṛged tasna ya.
Bbeẓ x ufeskar n uzar-timeẓṛi zzat i gha txemmled min turid',
'tooltip-ca-unwatch' => 'Kkes Tasna ya zi Tabdart uḥṭṭu inec',
'tooltip-search' => 'Rzu {{SITENAME}}',
'tooltip-search-go' => 'Uyur ghar tasna s yizwel a s imant nnes malla tella',
-'tooltip-search-fulltext' => 'Rzu di tasniwin x waḍṛis a',
+'tooltip-search-fulltext' => 'Rzu di tasniwin xef waḍṛis a',
'tooltip-p-logo' => 'Tasbtirt Tamzwarut',
-'tooltip-n-mainpage' => 'Adef ghar tasna tamezwarut',
+'tooltip-n-mainpage' => 'Adef ɣar tasna tamezwarut',
'tooltip-n-mainpage-description' => 'Adef ghar tasna tamezwarut',
-'tooltip-n-portal' => 'Xf usenfar, mayn tzemmared a tegged, mani gha tafed tighawsiwin',
+'tooltip-n-portal' => 'Xef usenfar, mayen tzemmared ad tegged, mani ɣa tafed tiɣawsiwin',
'tooltip-n-currentevents' => 'Af tilgha n ugilal xf tmsarin titurawin',
'tooltip-n-recentchanges' => 'Tabdart n isenfilen imaynuten di Wiki.',
'tooltip-n-randompage' => 'Zdem ict tasna d tagacurant',
'ilsubmit' => 'Tarzzut',
# Bad image list
-'bad_image_list' => 'Tameslayt amya :
+'bad_image_list' => 'Talɣa tella ammu :
-imagraden n tebdart (ɣarsent * deg umzwaru) ig iteten waha.
-Tazdayt tamzwarut di tebrit ixessa atzdi ɣa afaylu war icni .
-kur tazdayt tdeffad a tmekkas .',
+Imagraden n tebdart (ɣar-sent * deg umezwaru) waha iy yellan nican, inneḍni uhu.
+Amaqqan amezwarutdi tebridt ixessa ad tili teqqen ɣer ijen usatul aɛeffan.
+Marra imaqqanen nneḍni xef ijen uceṛṛid simant nnes ad ilin d tuksawin, amecnaw tasniwin mani izemmer usatul ad d-yeffeɣ deg uceṛṛiḍ.',
# Metadata
'metadata' => 'Timuca Meta',
'api-error-uploaddisabled' => 'La funcziun da chargiar datotecas sin quest vichi è deactivada.',
'api-error-verification-error' => 'Questa datoteca pudess esser corrupta ni avair ina extensiun faussa.',
+# Unknown messages
+'api-error-filetype-banned-type' => "$1 {{PLURAL:$4|n'è betg in tip da datoteca lubì|n'èn betg tips da datoteca lubids}}. Lubidas èn datotecas {{PLURAL:$3|dal tip|dals tips}} $2.",
);
'youhavenewmessages' => 'Aveți $1 ($2).',
'newmessageslink' => 'mesaje noi',
'newmessagesdifflink' => 'comparație cu versiunea precedentă',
+'youhavenewmessagesfromusers' => 'Aveți $1 de la {{PLURAL:$3|un alt utilizator|$3 utilizatori}} ($2).',
+'youhavenewmessagesmanyusers' => 'Aveți $1 de la mulți utilizatori ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|un mesaj nou|mesaje noi}}',
+'newmessagesdifflinkplural' => 'ultima/(ele) {{PLURAL:$1|schimbare|schimbări}}',
'youhavenewmessagesmulti' => 'Aveți mesaje noi la $1',
'editsection' => 'modificare',
'editold' => 'modificare',
'readonlytext' => 'Baza de date {{SITENAME}} este momentan blocată la scriere, probabil pentru o operațiune de rutină, după care va fi deblocată și se va reveni la starea normală.
Administratorul care a blocat-o a oferit această explicație: $1',
-'missing-article' => 'Baza de date nu găsește textul unei pagini care ar fi trebuit găsit, numit „$1” $2.
+'missing-article' => 'Baza de date nu găsește textul unei pagini care ar fi trebuit găsită, numită „$1” $2.
-În mod normal faptul este cauzat de urmărirea unei dif neactualizată sau a unei legături din istoric spre o pagină care a fost ștearsă.
+În mod normal faptul este cauzat de accesarea unei dif neactualizată sau a unei legături din istoric spre o pagină care a fost ștearsă.
Dacă nu acesta e motivul, s-ar putea să fi găsit un bug în program.
-Te rog anunță acest aspect unui [[Special:ListUsers/sysop|administrator]], indicându-i adresa URL.',
+Vă rugăm să-i semnalați acest aspect unui [[Special:ListUsers/sysop|administrator]], indicându-i adresa URL.',
'missingarticle-rev' => '(versiunea#: $1)',
'missingarticle-diff' => '(Dif: $1, $2)',
'readonly_lag' => 'Baza de date a fost închisă automatic în timp ce serverele secundare ale bazei de date îl urmează pe cel principal.',
'remembermypassword' => 'Autentificare automată de la acest calculator (expiră după {{PLURAL:$1|24 de ore|$1 zile|$1 de zile}})',
'securelogin-stick-https' => 'Rămâi conectat la HTTPS după autentificare',
'yourdomainname' => 'Domeniul dumneavoastră:',
+'password-change-forbidden' => 'Nu puteți schimba parole pe acest wiki.',
'externaldberror' => 'A fost fie o eroare de bază de date pentru o autentificare extenă sau nu aveți permisiunea să actualizați contul extern.',
'login' => 'Autentificare',
'nav-login-createaccount' => 'Creare cont / Autentificare',
'noarticletext-nopermission' => 'Actualmente, această pagină este lipsită de conținut.
Puteți [[Special:Search/{{PAGENAME}}|căuta acest titlu]] în alte pagini
sau puteți <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} căuta înregistrări în jurnale]</span>.',
+'missing-revision' => 'Versiunea nr. $1 a paginii „{{PAGENAME}}” nu există.
+
+Acest lucru se întâmplă de obicei atunci când se accesează o legătură expirată către istoricul unei pagini șterse.
+Detalii se pot găsi în [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} jurnalul ștergerilor].',
'userpage-userdoesnotexist' => 'Contul de utilizator „<nowiki>$1</nowiki>” nu este înregistrat.
Asigurați-vă că doriți să creați/modificați această pagină.',
'userpage-userdoesnotexist-view' => 'Contul de utilizator „$1” nu este înregistrat.',
'language-converter-depth-warning' => 'Limita adâncimii convertorului de limbă a fost depășită ($1)',
'node-count-exceeded-category' => 'Pagini unde numărul de noduri este depășit',
'node-count-exceeded-warning' => 'Pagina a depășit numărul de noduri',
+'expansion-depth-exceeded-category' => 'Pagini unde profunzimea de expansiune este depășită',
+'expansion-depth-exceeded-warning' => 'Pagina depășește profunzimea de expansiune',
+'parser-unstrip-loop-warning' => 'Buclă nedetașabilă detectată',
+'parser-unstrip-recursion-limit' => 'Limita de recursivitate nedetașabilă depășită ($1)',
+'converter-manual-rule-error' => 'Eroare detectată în regula manuală de conversie a limbii',
# "Undo" feature
'undo-success' => 'Modificarea poate fi anulată. Verificați diferența de dedesupt și apoi salvați pentru a termina anularea modificării.',
'editundo' => 'anulare',
'diff-multi' => '({{PLURAL:$1|O revizie intermediară|$1 revizii intermediare|$1 de revizii intermediare}} efectuată de {{PLURAL:$2|un utilizator|$2 utilizatori|$2 de utilizatori}} {{PLURAL:$1|neafișată|neafișate}})',
'diff-multi-manyusers' => '({{PLURAL:$1|O versiune intermediară efectuată de|$1 (de) versiuni intermediare efectuate de peste}} $2 {{PLURAL:$2|utilizator|utilizatori}} {{PLURAL:$1|neafișată|neafișate}})',
+'difference-missing-revision' => '{{PLURAL:$2|O versiune a|$2 versiuni ale|$2 de versiuni ale}} acestei diferențe ($1) nu {{PLURAL:$2|a fost găsită|au fost găsite}}.
+
+Acest lucru se întâmplă de obicei atunci când se accesează o legătură expirată către istoricul unei pagini șterse.
+Detalii se pot găsi în [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} jurnalul ștergerilor].',
# Search results
'searchresults' => 'Rezultatele căutării',
'right-writeapi' => 'Utilizează API la scriere',
'right-delete' => 'Şterge pagini',
'right-bigdelete' => 'Şterge pagini cu istoric lung',
+'right-deletelogentry' => 'Șterge și recuperează intrări specifice din jurnale',
'right-deleterevision' => 'Șterge și recuperează versiuni specifice ale paginilor',
'right-deletedhistory' => 'Vezi intrările șterse din istoric, fără textul asociat',
'right-deletedtext' => 'Vizualizați textul șters și modificările dintre versiunile șterse',
'nchanges' => '$1 {{PLURAL:$1|modificare|modificări|de modificări}}',
'recentchanges' => 'Schimbări recente',
'recentchanges-legend' => 'Opțiuni schimbări recente',
-'recentchanges-summary' => 'Schimbări recente ... (Log)',
+'recentchanges-summary' => 'Urmăriți cele mai recente modificări din wiki pe această pagină.',
'recentchanges-feed-description' => 'Urmărește cele mai recente schimbări folosind acest flux.',
'recentchanges-label-newpage' => 'Această modificare a creat o pagină nouă',
'recentchanges-label-minor' => 'Aceasta este o modificare minoră',
'backend-fail-read' => 'Imposibil de citit fișierul $1.',
'backend-fail-create' => 'Imposibil de scris fișierul $1.',
'backend-fail-maxsize' => 'Nu s-a putut scrie fișierul $1 pentru că acesta este mai mare de {{PLURAL:$2|un octet|$2 octeți|$2 de octeți}}.',
+'backend-fail-readonly' => "Suportul de stocare „$1” este în prezent doar în citire. Motivul dat este: „''$2''”",
+'backend-fail-synced' => 'Fișierul „$1” este într-o stare de inconsistență în suporturile de stocare internă',
+'backend-fail-connect' => 'Imposibil de conectat la suportul de stocare „$1”.',
+'backend-fail-internal' => 'O eroare necunoscută s-a produs în suportul de stocare „$1”.',
'backend-fail-contenttype' => 'Nu s-a putut determina tipul de conținut al fișierului de stocat la „$1”.',
+'backend-fail-batchsize' => 'Suportul de stocare a furnizat un lot de $1 {{PLURAL:$1|operațiune|operațiuni|de operațiuni}} de fișier; limita este $2 {{PLURAL:$2|operațiune|operațiuni|de operațiuni}}.',
'backend-fail-usable' => 'Imposibil de scris fișierul $1 din cauza permisiunilor insuficiente sau din cauza directoarelor/containerelor lipsă.',
+# File journal errors
+'filejournal-fail-dbconnect' => 'Imposibil de conectat la baza de date a jurnalului pentru terminatul de stocare „$1”.',
+'filejournal-fail-dbquery' => 'Imposibil de actualizat baza de date a jurnalului pentru terminalul de stocare „$1”.',
+
# Lock manager
'lockmanager-notlocked' => 'Imposibil de deblocat „$1”; nu este blocată.',
'lockmanager-fail-closelock' => 'Imposibil de închis fișierul de blocare pentru „$1”.',
'lockmanager-fail-releaselock' => 'Imposibil de eliberat blocarea pentru „$1”.',
'lockmanager-fail-db-bucket' => 'Imposibil de contactat suficient baza de date cu blocări în găleata $1.',
'lockmanager-fail-db-release' => 'Imposibil de eliberat blocările din baza de date $1.',
+'lockmanager-fail-svr-acquire' => 'Imposibil de obținut blocări pe serverul $1.',
'lockmanager-fail-svr-release' => 'Imposibil de eliberat blocările de pe serverul $1.',
# ZipDirectoryReader
'filehist' => 'Istoricul fișierului',
'filehist-help' => "Apăsați pe '''Data și ora''' pentru a vedea versiunea fișierului trimisă la momentul respectiv.",
'filehist-deleteall' => 'șterge tot',
-'filehist-deleteone' => 'șterge',
+'filehist-deleteone' => 'ștergere',
'filehist-revert' => 'revenire',
'filehist-current' => 'actuală',
'filehist-datetime' => 'Data și ora',
'disambiguations' => 'Pagini care trimit către pagini de dezambiguizare',
'disambiguationspage' => 'Template:Dezambiguizare',
-'disambiguations-text' => "Paginile următoare conțin legături către o '''pagină de dezambiguizare'''.
-În locul acesteia ar trebui să conțină legături către un articol.<br />
-O pagină este considerată o pagină de dezambiguizare dacă folosește formate care apar la [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Paginile următoare conțin cel puțin o legătură către o '''pagină de dezambiguizare'''.
+Acestea ar trebui să conțină legături către un articol mai potrivit.<br />
+O pagină este considerată o pagină de dezambiguizare dacă folosește formate care apar la [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Redirecționări duble',
'doubleredirectstext' => 'Această listă conține pagini care redirecționează la alte pagini de redirecționare.
'brokenredirects' => 'Redirecționări greșite',
'brokenredirectstext' => 'Următoarele redirecționări conduc spre articole inexistente:',
'brokenredirects-edit' => 'modificare',
-'brokenredirects-delete' => 'șterge',
+'brokenredirects-delete' => 'ștergere',
'withoutinterwiki' => 'Pagini fără legături interwiki',
'withoutinterwiki-summary' => 'Următoarele pagini nu se leagă la versiuni ale lor în alte limbi:',
'rollback' => 'Editări de revenire',
'rollback_short' => 'Revenire',
'rollbacklink' => 'revenire',
+'rollbacklinkcount' => 'revenire asupra {{PLURAL:$1|unei modificări|a $1 modificări|a $1 de modificări}}',
+'rollbacklinkcount-morethan' => 'revenire asupra a mai mult de {{PLURAL:$1|o modificare|$1 modificări|$1 de modificări}}',
'rollbackfailed' => 'Revenirea nu s-a putut face',
'cantrollback' => 'Nu se poate reveni; ultimul contribuitor este autorul acestui articol.',
'alreadyrolled' => 'Nu se poate reveni peste ultima modificare a articolului [[:$1]] făcută de către [[User:$2|$2]] ([[User talk:$2|discuție]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); altcineva a modificat articolul sau a revenit deja.
* <span class="mw-specialpagecached">Pagini speciale aflate doar în memoria cache (pot fi neactualizate).</span>',
'specialpages-group-maintenance' => 'Întreținere',
'specialpages-group-other' => 'Alte pagini speciale',
-'specialpages-group-login' => 'Autentificare / Înregistrare',
+'specialpages-group-login' => 'Autentificare / creare cont',
'specialpages-group-changes' => 'Schimbări recente și jurnale',
'specialpages-group-media' => 'Fișiere',
'specialpages-group-users' => 'Utilizatori și permisiuni',
'newuserlog-byemail' => 'parola trimisă prin e-mail',
# Feedback
-'feedback-bugornote' => 'Dacă sunteți pregătit să descrieți o problemă tehnică în detaliu vă rugăm să [ $1 raportați un bug].
-În caz contrar, puteți utiliza formularul de mai jos. Comentariul dumneavoastră va fi adăugat pe pagina „[ $3 $2 ]”, împreună cu numele de utilizator și numele navigatorului pe care îl folosiți.',
+'feedback-bugornote' => 'Dacă sunteți pregătit să descrieți o problemă tehnică în detaliu vă rugăm să [$1 raportați un bug].
+În caz contrar, puteți utiliza formularul de mai jos. Comentariul dumneavoastră va fi adăugat pe pagina „[$3 $2]”, împreună cu numele de utilizator și numele navigatorului pe care îl folosiți.',
'feedback-subject' => 'Subiect:',
'feedback-message' => 'Mesaj:',
'feedback-cancel' => 'Revocare',
'duration-centuries' => '$1 {{PLURAL:$1|secol|secole|de secole}}',
'duration-millennia' => '$1 {{PLURAL:$1|mileniu|milenii|de milenii}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|este un tip de fișier nepermis|sunt tipuri de fișier nepermise}}. {{PLURAL:$3|Tip de fișier permis este|Tipuri de fișier permise sunt}} $2.',
);
'youhavenewmessages' => 'Tu tine $1 ($2).',
'newmessageslink' => 'messàgge nuève',
'newmessagesdifflink' => 'urteme cangiaminde',
+'youhavenewmessagesfromusers' => "Tu è $1 da {{PLURAL:$3|'n'otre utende|$3 utinde}} ($2).",
+'youhavenewmessagesmanyusers' => 'Tu è $1 da assaije utinde ($2).',
+'newmessageslinkplural' => "{{PLURAL:$1|'nu messàgge nuève|messàgge nuève}}",
+'newmessagesdifflinkplural' => 'urteme {{PLURAL:$1|cangiamende|cangiaminde}}',
'youhavenewmessagesmulti' => "T'onne arrevete mèssagge nueve sus 'a $1",
'editsection' => 'cange',
'editsection-brackets' => '[$1]',
'cannotdelete' => '\'A pàgene o \'u file "$1" non ge pò essere scangellate.
Pò essere ca ggià ha state scangellete da quacche otre.',
'cannotdelete-title' => 'Non ge puè scangellà \'a pàgene "$1"',
+'delete-hook-aborted' => "Cangiamende annullete da 'nu ''hook''.
+Non g'à date nisciune mutive.",
'badtitle' => 'Titele sbagliete',
'badtitletext' => "'A pàgene ca è cerchete tène 'nu titele errete, vacande, o jè 'nu collegamende inter-lènghe o inter-uicchi errete.
Pò essere ca tène une o cchiù carattere ca non ge ponne essere ausete jndr'à le titele.",
'ns-specialprotected' => 'Le pàgene speciale no ponne essere cangete.',
'titleprotected' => "Stu titele ha state prutette da 'a ccreazione da [[User:$1|$1]].
'U mutive jè ''$2''.",
+'filereadonlyerror' => 'Non ge pozze cangià \'u file "$1" purcé l\'archivije de le file "$2" ste in mode sola letture.
+
+L\'amministratore ca l\'ha bloccate dèje sta spiegazione: "$3".',
+'invalidtitle-knownnamespace' => 'Titole invalide cu \'u namespace "$2" e teste "$3"',
+'invalidtitle-unknownnamespace' => 'Titele invalide cu numere de namespace scanusciute $1 e teste "$2"',
+'exception-nologin' => 'Non ge sì collegate',
+'exception-nologin-text' => "Sta pàgene o azione richiede ca a trasè jndr'à sta uicchi.",
# Virus scanner
'virus-badscanner' => "Configurazione ca fece schife: Virus scanner scanusciute: ''$1''",
'remembermypassword' => "Arrencuerdete 'u nome mije sus a stu combiuter (pe 'nu massime de $1 {{PLURAL:$1|sciurne|sciurne}})",
'securelogin-stick-https' => "Statte collegate ô HTTPS apprisse 'a trasute",
'yourdomainname' => "'U nome d'u dominie tue:",
+'password-change-forbidden' => 'Non ge puè cangià le passuord sus a sta uicchi.',
'externaldberror' => "Vide bbuene, o stè 'n'errore de autendicazione a 'u database oppure tu non ge puè aggiorna 'u cunde tue esterne.",
'login' => 'Tràse',
'nav-login-createaccount' => 'Tràse / Reggistrete',
'noarticletext-nopermission' => "Pe mò non ge stè teste jndr'à sta pàgene.
Tu puè [[Special:Search/{{PAGENAME}}|cercà pe stu titele]] jndr'à otre pàggene,
o <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cirche jndr'à l'archivije cullegate]</span>.",
+'missing-revision' => "'A revisione #\$1 d'a pàgene chiamate \"{{PAGENAME}}\" non g'esiste.
+
+Quiste succede normalmende purcé 'u cunde jè collegate a 'na pàgene ca ha state scangellate.
+Le dettaglie le puè acchià jndr'à l'[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} archivije de le scangellaziune].",
'userpage-userdoesnotexist' => '\'U cunde utende "<nowiki>$1</nowiki>" non g\'è reggistrete.
Pe piacere, condrolle ce tu vuè cu ccreje/cange sta pàgene.',
'userpage-userdoesnotexist-view' => '\'U cunde utende "$1" non g\'è reggistrate.',
'node-count-exceeded-warning' => "Pagene ha sbunnate 'u condegge de le node",
'expansion-depth-exceeded-category' => "Pàggene addò 'a profonnetà de l'espanzione jè supranate",
'expansion-depth-exceeded-warning' => "Pàggene ca sbonnane 'a profonnetà de espanzione",
+'parser-unstrip-loop-warning' => 'Cicle infinite acchiate',
+'parser-unstrip-recursion-limit' => 'Limite de ricorsione infinite sbunnate ($1)',
+'converter-manual-rule-error' => "Errore assute jndr'à le regole de conversione d'a lènghe manuale",
# "Undo" feature
'undo-success' => "'U cangiamende pò essere annullate.
'editundo' => 'annulle',
'diff-multi' => "({{PLURAL:$1|'na versione de mmienze|$1 cchiù versiune de mmienze}} de {{PLURAL:$2|'n'utende|$2 utinde}} non ge se vèdene)",
'diff-multi-manyusers' => "({{PLURAL:$1|'Na revisione de 'mmienze|$1 revisiune de 'mmienze}} non g'è viste da cchiù de $2 {{PLURAL:$2|utende|utinde}})",
+'difference-missing-revision' => "{{PLURAL:$2|'Na revisione|$2 revisiune}} de sta differenze ($1) {{PLURAL:$2|non g'onne|non g'onne}} state acchiate.
+
+Quiste succede normalmende purcé 'u cunde jè collegate a 'na pàgene ca ha state scangellate.
+Le dettaglie le puè acchià jndr'à l'[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} archivije de le scangellaziune].",
# Search results
'searchresults' => "Resultete d'a ricerche",
'right-writeapi' => 'Ause de le API scritte',
'right-delete' => 'Scangille le pàggene',
'right-bigdelete' => "Scangille le pàggene cu 'na storia longa longa",
+'right-deletelogentry' => "Scangille e ripristine vôsce specifiche de l'archivije",
'right-deleterevision' => 'Scangille o repristine le revisiune specifiche de le pàggene',
'right-deletedhistory' => "Vide le versiune, d'u cunde, scangellate, senza 'u teste lore associate",
'right-deletedtext' => "Vide 'u teste scangellate e le cangiaminde 'mbrà le versiune scangellate",
'backend-fail-closetemp' => 'Non ge pozze achiudere file temboranèe.',
'backend-fail-read' => "Non ge pozze leggere 'u file $1.",
'backend-fail-create' => "Non ge pozze scrivere 'u file $1.",
+'backend-fail-maxsize' => 'Non ge pozze scrivere \'u file "$1" purcé jè cchiù granne de {{PLURAL:$2|\'nu byte|$2 byte}}',
'backend-fail-readonly' => 'L\'archivije de rete "$1" jè pe stu mumende in sole letture. \'U mutive ha state: "$2"',
'backend-fail-synced' => "'U file \"\$1\" jè jndr'à 'nu state ingonsistende jndr'à l'archivije inderne",
'backend-fail-connect' => 'Non ge pozze connettere \'a memorie de rrete "$1".',
'backend-fail-internal' => "'N'errore scanusciute s'à verificate jndr'à l'archivije de rrete \"\$1\".",
'backend-fail-contenttype' => 'Non ge pozze capìe \'u tipe de condenute d\'u file da reggistrà sus a "$1".',
+'backend-fail-batchsize' => "L'archivije de rrete ha date 'nu processe de $1 {{PLURAL:$1|operazione|operaziune}} sus a le file; 'u limite jè $2 {{PLURAL:$2|operazione|operaziune}}.",
+'backend-fail-usable' => 'Non ge pozze scrivere \'u file "$1" purcé mangane le permesse sufficiende o non g\'esiste \'a cartelle/contenitore.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Non ge pozze collegà a l\'archivije d\'u database pe memorizzà \'u rrete "$1".',
+'filejournal-fail-dbquery' => 'Non ge pozze aggiornà l\'archivije d\'u database pe memorizzà \'u rrete "$1".',
# Lock manager
'lockmanager-notlocked' => 'Non ge pozze sbloccà "$1"; jidde non g\'è bloccate.',
'lockmanager-fail-releaselock' => 'Non ge pozze relassà blocche pe "$1".',
'lockmanager-fail-db-bucket' => "Non ge pozze condattà 'u database purcé stonne troppe blocche jndr'à $1.",
'lockmanager-fail-db-release' => "Non ge pozze relassà le blocche sus a 'u database $1.",
+'lockmanager-fail-svr-acquire' => "Non ge pozze pigghià le blocche sus a 'u server $1.",
'lockmanager-fail-svr-release' => "Non ge pozze relassà le blocche sus a 'u server $1.",
# ZipDirectoryReader
Pe piacere vide 'a [$2 pàgene de descrizione d'u file] pe maggiore 'mbormaziune.",
'sharedupload-desc-here' => "Stu file è da $1 e pò essere ausate pe otre pruggette.<br />
'A descriziona sus a [$2 pàgene de descrizione d'u file] ste aqquà sotte.",
+'sharedupload-desc-edit' => "Stu file avène da $1 e pò essere ausate da otre pruggette.
+Pò essere ca tu vuè cangià 'a descrizione de jidde [$2 pàgene de descrizione d'u file] aqquà.",
+'sharedupload-desc-create' => "Stu file avène da $1 e pò essere ausate da otre pruggette.
+Pò essere ca tu vuè cangià 'a descrizione de jidde [$2 pàgene de descrizione d'u file] aqquà.",
'filepage-nofile' => 'Nisciune file cu stu nome esiste.',
'filepage-nofile-link' => "Nisciune file cu stu nome esiste, ma tu 'u puè [$1 carecà].",
'uploadnewversion-linktext' => "Careche 'na versiona nove de stu fail",
'disambiguationspage' => 'Template:disambigue',
'disambiguations-text' => "Le pàggene seguende appondene a 'na '''pàgene de disambiguazione'''.
'Nvece avessere appondà a 'a temateca appropriate.<br />
-'Na pàgene jè trattate cumme pàgene de disambiguazione ce tu ause 'nu template ca è appundate da [[MediaWiki:Disambiguationspage|Pàggene de disambiguazione]]",
+'Na pàgene jè trattate cumme pàgene de disambiguazione ce tu ause 'nu template ca è appundate da [[MediaWiki:Disambiguationspage]]",
'doubleredirects' => 'Ridirezionaminde a doppie',
'doubleredirectstext' => "Sta pàgene elenghe le pàggene ca se ridirezionane sus a otre pàggene de ridirezionaminde.
'wantedpages' => 'Pàggene cchiù cerchete',
'wantedpages-badtitle' => "Titele invalide in mmienze a l'inzieme de le resultate: $1",
'wantedfiles' => 'File cchiù cerchete',
+'wantedfiletext-cat' => "Le seguende file onne state ausate ma non g'esistene, Le file de le archivije de fore ponne essere elengate fine ca esistene. Ogne false allarme avène <del>signate</del>. In aggiunde, le pàggene ca tènene ste file ca non g'esistene avène elengate jndr'à [[:$1]]",
+'wantedfiletext-nocat' => "Le file seguende onne state ausate ma non g'esistene. Le file da le archivije esterne ponne stà in liste fine ca esistente. Ogne false allarme avène <del>segnalate</del>.",
'wantedtemplates' => 'Template cchiù ausete',
'mostlinked' => 'Pàggene cchiù appundete',
'mostlinkedcategories' => 'Categorije cchiù appundete',
Tu puè restringere 'a viste selezionanne 'u tipe de archivije, 'u nome utende (senzibbile a le maiuscole), o le pàggene coinvolte (pure chiste senzibbile a le maiuscole).",
'logempty' => "Non ge stè 'n'anema de priatorie jndr'à l'archivije.",
'log-title-wildcard' => 'Cirche le titele ca accumenzene cu stu teste',
+'showhideselectedlogentries' => "Fà vedè/scunne le righe scacchiate de l'archivije",
# Special:AllPages
'allpages' => 'Tutte le pàggene',
'rollback' => 'Annulle le cangiaminde',
'rollback_short' => 'Annulle',
'rollbacklink' => "annulle 'u cangiaminde",
+'rollbacklinkcount' => 'annulle $1 {{PLURAL:$1|cangiamende|cangiaminde}}',
+'rollbacklinkcount-morethan' => 'annulle cchiù de $1 {{PLURAL:$1|cangiamende|cangiaminde}}',
'rollbackfailed' => 'Annullamende fallite',
'cantrollback' => "Non ge se pò annullà stu cangiamende;
l'urteme condrebbutore jè sulamende l'autore de sta pàgene.",
'spambot_username' => "Sdevacatore d'u spam de MediaUicchi",
'spam_reverting' => "Turnanne a l'urtema revisione no ge condiene collegaminde a $1",
'spam_blanking' => 'Tutte le revisiune condènene collegaminde a $1, vacande',
+'spam_deleting' => 'Tutte le revisiune condènene collegaminde a $1, stoche a scangelle',
# Info page
'pageinfo-title' => '\'Mbormaziune pe "$1"',
* <span class="mw-specialpagecached">Pàggene speciale in memorie cache (ponne essere vecchie).</span>',
'specialpages-group-maintenance' => "Report d'a manutenzione",
'specialpages-group-other' => 'Otre pàggene speciele',
-'specialpages-group-login' => 'Tràse / Reggistrete',
+'specialpages-group-login' => 'Tràse / Reggistrate',
'specialpages-group-changes' => 'Cangiaminde recende e archivie',
'specialpages-group-media' => 'Riepileghe de media e carecaminde',
'specialpages-group-users' => 'Utinde e deritte',
'api-error-badtoken' => 'Errore inderne: Gettone errate.',
'api-error-copyuploaddisabled' => "'U carecamende da URL jè disabbilitate sus a stu server.",
'api-error-duplicate' => "{{PLURAL:$1|Stè [$2 'n'otre file]|Stonne [$2 otre file]}} sus a 'u site cu 'u stesse condenute.",
+'api-error-duplicate-archive' => "{{PLURAL:$1|Stave [$2 'n'otre file]|Stavane [$2 otre file]}} già sus a 'u site cu 'u stesse condenute, ma {{PLURAL:$1|ha state|onne state}} scangellate.",
'api-error-duplicate-archive-popup-title' => "Dupliche {{PLURAL:$1|'u file ca ha state|le file ca onne state}} scangellate.",
'api-error-duplicate-popup-title' => 'Dupleche {{PLURAL:$1|file|file}}',
'api-error-empty-file' => "'U file ca tu è confermate ere vacande.",
'api-error-emptypage' => 'Quanne se ne ccreje une, le pàggene vacande non ge sò permesse.',
'api-error-fetchfileerror' => "Errore inderne: Quacchecose ha sciute stuèrte quanne ste analizzave 'u file.",
+'api-error-fileexists-forbidden' => '\'Nu file cu \'u nome "$1" esiste, e non ge pò essere sovrascritte.',
+'api-error-fileexists-shared-forbidden' => "'Nu file cu 'u nome \"\$1\" esiste jndr'à l'archivije de le file comune, e non ge pò essere sovrascritte.",
'api-error-file-too-large' => "'U file ca tu è confermate jè troppe granne.",
'api-error-filename-tooshort' => "'U nome d'u file jè troppe curte.",
'api-error-filetype-banned' => 'Stu tipe de file jè vietate.',
'duration-centuries' => '$1 {{PLURAL:$1|sechele|sechele}}',
'duration-millennia' => '$1 {{PLURAL:$1|millennie|millennie}}',
+# Unknown messages
+'api-error-filetype-banned-type' => "$1 {{PLURAL:$4|ète 'nu tipe de file ca non g'è permesse|sonde tipe de file ca no sonde permesse}}. {{PLURAL:$3|'U tipe de file permesse ète|Le tipe de file permesse sonde}} $2.",
);
* @author KorneySan
* @author Kv75
* @author Lockal
+ * @author MaxBioHazard
* @author MaxSem
* @author Ola
* @author Ole Yves
# User preference toggles
'tog-underline' => 'Подчёркивать ссылки:',
'tog-justify' => 'Выравнивать текст по ширине страницы',
-'tog-hideminor' => 'СкÑ\80Ñ\8bваÑ\82Ñ\8c малÑ\8bе пÑ\80авки в Ñ\81пиÑ\81ке Ñ\81вежиÑ\85 изменений',
+'tog-hideminor' => 'СкÑ\80Ñ\8bваÑ\82Ñ\8c малÑ\8bе пÑ\80авки в Ñ\81пиÑ\81ке Ñ\81вежиÑ\85 пÑ\80авок',
'tog-hidepatrolled' => 'Скрывать патрулированные правки в списке свежих правок',
'tog-newpageshidepatrolled' => 'Скрывать отпатрулированные страницы в списке новых страниц',
'tog-extendwatchlist' => 'Расширенный список наблюдения, включающий все изменения, а не только последние',
-'tog-usenewrc' => 'Ð\93Ñ\80Ñ\83ппиÑ\80оваÑ\82Ñ\8c изменениÑ\8f на Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\81вежиÑ\85 пÑ\80авок и в Ñ\81пиÑ\81ке наблÑ\8eдениÑ\8f (Ñ\82Ñ\80ебÑ\83еÑ\82Ñ\81Ñ\8f JavaScript)',
+'tog-usenewrc' => 'Ð\93Ñ\80Ñ\83ппиÑ\80оваÑ\82Ñ\8c изменениÑ\8f в Ñ\81вежиÑ\85 пÑ\80авкаÑ\85 и Ñ\81пиÑ\81ке наблÑ\8eдениÑ\8f (JavaScript)',
'tog-numberheadings' => 'Автоматически нумеровать заголовки',
'tog-showtoolbar' => 'Показывать верхнюю панель инструментов при редактировании (JavaScript)',
'tog-editondblclick' => 'Править страницы по двойному щелчку (JavaScript)',
'unexpected' => 'Неподходящее значение: «$1»=«$2».',
'formerror' => 'Ошибка: невозможно передать данные формы',
'badarticleerror' => 'Это действие не может быть выполнено на данной странице.',
-'cannotdelete' => 'Невозможно удалить страницу или файл «$1».
-Ð\92озможно, Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\83же оказалаÑ\81Ñ\8c удалена.',
+'cannotdelete' => 'Невозможно удалить или переименовать страницу или файл «$1».
+Ð\92озможно, Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\83же бÑ\8bла удалена.',
'cannotdelete-title' => 'Нельзя удалить страницу «$1»',
'delete-hook-aborted' => 'Правка отменена процедурой-перехватчиком.
Дополнительных пояснений не приведено.',
'remembermypassword' => 'Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
'securelogin-stick-https' => 'Продолжить подключение по HTTPS после входа',
'yourdomainname' => 'Ваш домен:',
+'password-change-forbidden' => 'Вы не можете изменить пароль в этой вики.',
'externaldberror' => 'Произошла ошибка при аутентификации с помощью внешней базы данных или у вас недостаточно прав для внесения изменений в свою внешнюю учётную запись.',
'login' => 'Представиться системе',
'nav-login-createaccount' => 'Представиться / зарегистрироваться',
Подобные проблемы могут возникать при использовании анонимизирующих веб-прокси, содержащих ошибки.'''",
'edit_form_incomplete' => "'''Некоторые части формы редактирования не достигли сервера. Внимательно проверьте, что ваши правки не повреждены, и попробуйте ещё раз.'''",
'editing' => 'Редактирование $1',
-'creating' => 'Создание $1',
+'creating' => 'Создание страницы «$1»',
'editingsection' => 'Редактирование: $1 (раздел)',
'editingcomment' => 'Редактирование $1 (новый раздел)',
'editconflict' => 'Конфликт редактирования: $1',
'right-writeapi' => 'использование API для записи',
'right-delete' => 'удаление страниц',
'right-bigdelete' => 'удаление страниц с длинными историями изменений',
+'right-deletelogentry' => 'удаление и восстановление конкретных записей журнала.',
'right-deleterevision' => 'удаление и восстановление конкретных версий страниц',
'right-deletedhistory' => 'просмотр истории удалённых страниц без доступа к удалённому тексту',
'right-deletedtext' => 'просмотр удалённого текста и изменений между удалёнными версиями страниц',
'lockmanager-fail-releaselock' => 'Не удалось разблокировать "$1".',
'lockmanager-fail-db-bucket' => 'Не удалось связаться с достаточным количеством баз блокировок в сегменте $1.',
'lockmanager-fail-db-release' => 'Не удалось снять блокировку базы данных $1 .',
+'lockmanager-fail-svr-acquire' => 'Не удалось получить блокировку на сервере $1.',
'lockmanager-fail-svr-release' => 'Не удалось снять блокировки на сервере $1 .',
# ZipDirectoryReader
'disambiguations' => 'Страницы, ссылающиеся на страницы разрешения неоднозначности',
'disambiguationspage' => 'Template:Неоднозначность',
-'disambiguations-text' => "Следующие страницы ссылаются на '''многозначные страницы'''.
-Вместо этого они, вероятно, должны указывать на соответствующую конкретную статью.<br />
+'disambiguations-text' => "Следующие страницы содержат по меньшей мере одну ссылку на '''многозначную страницу'''.
+Вместо этого они, вероятно, должны указывать на соответствующую конкретную страницу.<br />
Страница считается многозначной, если на ней размещён шаблон, имя которого указано на странице [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Двойные перенаправления',
'rollback' => 'Откатить изменения',
'rollback_short' => 'Откат',
'rollbacklink' => 'откатить',
+'rollbacklinkcount' => 'откатить $1 {{PLURAL:$1|правку|правки|правок}}',
+'rollbacklinkcount-morethan' => 'откатить больше, чем $1 {{PLURAL:$1|правку|правки|правок}}',
'rollbackfailed' => 'Ошибка при совершении отката',
'cantrollback' => 'Невозможно откатить изменения. Последний, кто вносил изменения, является единственным автором этой страницы.',
'alreadyrolled' => 'Невозможно откатить последние изменения страницы «[[:$1]]», совершённые [[User:$2|$2]] ([[User talk:$2|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]),
Ниже приведён журнал блокировок:',
'blocklog-showsuppresslog' => '{{GENDER:$1|Этот участник уже заблокирован и скрыт|Эта участница уже заблокирована и скрыта}}. Журнал сокрытий приведён ниже:',
'blocklogentry' => 'заблокировал [[$1]] на период $2 $3',
-'reblock-logentry' => 'изменил наÑ\81Ñ\82Ñ\80ойки блокиÑ\80овки длÑ\8f [[$1]], иÑ\81Ñ\82екаеÑ\82 $2 $3',
+'reblock-logentry' => 'изменил блокиÑ\80овкÑ\83 [[$1]] на пеÑ\80иод $2 $3',
'blocklogtext' => 'Журнал блокировок и разблокировок участников. Автоматически блокируемые IP-адреса здесь не указываются. См. [[Special:BlockList|список текущих блокировок]].',
'unblocklogentry' => 'разблокировал $1',
'block-log-flags-anononly' => 'только анонимные пользователи',
'duration-millennia' => '$1 {{PLURAL:$1|тысячелетие|тысячелетия|тысячелетий}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Не удалось получить блокировку на сервере $1.',
+'api-error-filetype-banned-type' => '$1 — {{PLURAL:$4|запрещённый тип файла|запрещённые типы файлов}}. {{PLURAL:$3|Разрешённым типом файла является|Разрешённые типы файлов:}} $2.',
);
'tog-hidepatrolled' => 'Сховати патролёваны едітованя в списку послїднїх змін',
'tog-newpageshidepatrolled' => 'Сховати патролёваны сторінкы зо списку новых сторінок',
'tog-extendwatchlist' => 'Росшыреный список слїдованых сторінок, обсягує вшыткы зміны, не лем послїднї',
-'tog-usenewrc' => 'ХоÑ\81новаÑ\82и здоконаленÑ\8b поÑ\81лÑ\97днÑ\97 змÑ\96нÑ\8b (JavaScript)',
+'tog-usenewrc' => 'Ð\92 поÑ\81лÑ\97днÑ\97Ñ\85 змÑ\96наÑ\85 Ñ\96 Ñ\81лÑ\97дованÑ\8bÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нкаÑ\85 зÒ\91Ñ\80Ñ\83поваÑ\82и змÑ\96нÑ\8b по Ñ\81Ñ\82оÑ\80Ñ\96нкаÑ\85 (JavaScript)',
'tog-numberheadings' => 'Автоматічно чісловати надписы',
'tog-showtoolbar' => 'Вказати панел інштрументів (потрібный JavaScript)',
'tog-editondblclick' => 'Едітовати двоїтым кликом (JavaScript)',
-'tog-editsection' => 'Ð\9fоволити едітованя секції сторінкы через одказ [едіт.]',
-'tog-editsectiononrightclick' => 'Ð\9fоволити едітованя секції сторінкы через кликаня правов клапков мышкы на надписы сторінок (JavaScript)',
+'tog-editsection' => 'Ð\94озволити едітованя секції сторінкы через одказ [едіт.]',
+'tog-editsectiononrightclick' => 'Ð\94озволити едітованя секції сторінкы через кликаня правов клапков мышкы на надписы сторінок (JavaScript)',
'tog-showtoc' => 'Вказовати обсяг (на сторінках з веце як трёма надписами)',
'tog-rememberpassword' => 'Запамятати моє приголошіня на тім переглядачу (максімално $1 {{PLURAL:$1|день|днів}})',
-'tog-watchcreations' => 'Придавати сторінкы створены мнов до мого списку слїдованых',
-'tog-watchdefault' => 'Придавати мнов едітованы сторінкы до списку слїдованых',
-'tog-watchmoves' => 'Придавати переменованы сторінкы до мого списку слїдованых',
-'tog-watchdeletion' => 'Придавати сторінкы котры змажу, міджі слїдованы',
+'tog-watchcreations' => 'Придавати сторінкы створены мнов тай файлы мнов заладованы до мого списку слїдованых',
+'tog-watchdefault' => 'Придавати мнов едітованы сторінкы і файлы до списку слїдованых',
+'tog-watchmoves' => 'Придавати переменованы сторінкы і файлы до мого списку слїдованых',
+'tog-watchdeletion' => 'Придавати сторінкы і файлы, котры змажу, міджі слїдованы',
'tog-minordefault' => 'Імпліцітно позначіти вшыткы зміны як малы',
'tog-previewontop' => 'Вказовати нагляд перед окном едітованя (не за ним)',
'tog-previewonfirst' => 'Вказати нагляд при першій едітації',
'tog-nocache' => 'Выпнути кешованя сторінок бровсером',
-'tog-enotifwatchlistpages' => 'Послати електронічну пошту, кідь ся змінила сторінка з мого списку слїдованя',
+'tog-enotifwatchlistpages' => 'Ð\9fоÑ\81лаÑ\82и елекÑ\82Ñ\80онÑ\96Ñ\87нÑ\83 поÑ\88Ñ\82Ñ\83, кÑ\96дÑ\8c Ñ\81Ñ\8f змÑ\96нила Ñ\81Ñ\82оÑ\80Ñ\96нка або Ñ\84айл з мого Ñ\81пиÑ\81кÑ\83 Ñ\81лÑ\97дованÑ\8f',
'tog-enotifusertalkpages' => 'Послати електронічну пошту при змінї моёй діскузной сторінкы',
-'tog-enotifminoredits' => 'Послати електронічну пошту і про меншы едітованя',
+'tog-enotifminoredits' => 'Послати електронічну пошту і про меншы едітованя сторінок і файлів',
'tog-enotifrevealaddr' => 'Прозрадити мою поштову адресу в поштї увідомлїня',
'tog-shownumberswatching' => 'Вказати кілько хоснователїв придало сторінку до свого списку слїдованых',
'tog-oldsig' => 'Екзістуючій підпис:',
'moredotdotdot' => 'Детайлнїше…',
'mypage' => 'Моя сторінка',
'mytalk' => 'Моя діскузія',
-'anontalk' => 'Діскузія ку тотїй IP-адресі',
+'anontalk' => 'Діскузія к тїй IP-адресї',
'navigation' => 'Навіґація',
'and' => ' і',
'vector-action-addsection' => 'Придати тему',
'vector-action-delete' => 'Вымазати',
'vector-action-move' => 'Переменовати',
-'vector-action-protect' => 'Ð¥Ñ\80анити',
+'vector-action-protect' => 'Ð\92Ñ\81окоÑ\82ити',
'vector-action-undelete' => 'Обновити',
'vector-action-unprotect' => 'Змінити замок',
'vector-simplesearch-preference' => 'Поволити росшырены пропозіції гляданя (лем взгляд Vector )',
'vector-view-create' => 'Створити',
'vector-view-edit' => 'Едітовати',
-'vector-view-history' => 'Відїти історію',
+'vector-view-history' => 'Видїти історію',
'vector-view-view' => 'Чітати',
-'vector-view-viewsource' => 'Відїти код',
+'vector-view-viewsource' => 'Видїти код',
'actions' => 'Дїї',
'namespaces' => 'Просторы назв',
'variants' => 'Варіанты',
'history' => 'Історія сторінкы',
'history_short' => 'Історія',
'updatedmarker' => 'змінено од послїдный навщівы',
-'printableversion' => 'Ð\92еÑ\80зÑ\96Ñ\8f пÑ\80о дÑ\80Ñ\83к',
+'printableversion' => 'Ð\92еÑ\80зÑ\96Ñ\8f до дÑ\80Ñ\83кÑ\83',
'permalink' => 'Перманентный одказ',
'print' => 'Друк',
'view' => 'Видїти',
'deletethispage' => 'Змазати тоту сторінку',
'undelete_short' => 'Обновити $1 {{PLURAL:$1|верзію|верзії|верзії}}',
'viewdeleted_short' => 'Видїти {{PLURAL:$1|змазанов едітаціёв|$1 змазаны едітації|$1 змазаных едітацій}}',
-'protect' => 'Ð¥Ñ\80анити',
+'protect' => 'Ð\92Ñ\81окоÑ\82ити',
'protect_change' => 'змінити',
-'protectthispage' => 'Ð¥Ñ\80анити тоту сторінку',
+'protectthispage' => 'СокоÑ\82ити тоту сторінку',
'unprotect' => 'Змінити замок',
'unprotectthispage' => 'Змінити замок той сторінкы',
'newpage' => 'Нова сторінка',
'specialpage' => 'Шпеціална сторінка',
'personaltools' => 'Особны інштрументы',
'postcomment' => 'Нова секція',
-'articlepage' => 'Ð\9fеÑ\80еглÑ\8fднÑ\83ти сторінку',
+'articlepage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку',
'talk' => 'Діскузія',
'views' => 'Перегляды',
'toolbox' => 'Інштрументы',
-'userpage' => 'Ð\92идÑ\97ти сторінку хоснователя',
-'projectpage' => 'Ð\92идÑ\97ти сторінку проєкту',
-'imagepage' => 'Ð\92идÑ\97ти сторінку файлу',
-'mediawikipage' => 'Ð\92идÑ\97ти сторінку повідомлїнь',
-'templatepage' => 'Ð\92идÑ\97ти шаблону',
-'viewhelppage' => 'Ð\92идÑ\97ти сторінку помочі',
-'categorypage' => 'Ð\92идÑ\97ти сторінку катеґорії',
-'viewtalkpage' => 'Ð\92идÑ\97ти діскузію',
+'userpage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку хоснователя',
+'projectpage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку проєкту',
+'imagepage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку файлу',
+'mediawikipage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку повідомлїнь',
+'templatepage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити шаблону',
+'viewhelppage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку помочі',
+'categorypage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити сторінку катеґорії',
+'viewtalkpage' => 'Ð\9fоÑ\81моÑ\82Ñ\80ити діскузію',
'otherlanguages' => 'Іншыма языками',
'redirectedfrom' => '(Напрямленый з $1)',
'redirectpagesub' => 'Сторінка-напрямлїня',
'pool-errorunknown' => 'Незнама хыба',
# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
-'aboutsite' => 'O {{grammar:accusative|{{SITENAME}}}}',
-'aboutpage' => 'Project:Описаня',
+'aboutsite' => 'О {{grammar:genitive|{{SITENAME}}}}',
+'aboutpage' => 'Project:О проєктї',
'copyright' => 'Обсяг є доступный з $1.',
'copyrightpage' => '{{ns:project}}:Авторьске право',
'currentevents' => 'Актуалны подїї',
'currentevents-url' => 'Project:Актуалны подїї',
-'disclaimers' => 'Ð\92Ñ\8bлÑ\83Ñ\87Ñ\96нÑ\8f зодповÑ\96дноÑ\81Ñ\82и',
-'disclaimerpage' => 'Project:Ð\92Ñ\8bлÑ\83Ñ\87Ñ\96нÑ\8f зодповÑ\96дноÑ\81Ñ\82и',
-'edithelp' => 'Ð\9fомÑ\96Ñ\87 пÑ\80о едÑ\96Ñ\82ованÑ\8f',
+'disclaimers' => 'Вылучіня одповідности',
+'disclaimerpage' => 'Project:Вылучіня одповідности',
+'edithelp' => 'Поміч едітованя',
'edithelppage' => 'Help:Едітованя',
'helppage' => 'Help:Обсяг',
'mainpage' => 'Головна сторінка',
'policy-url' => 'Project:Правила',
'portal' => 'Портал комуніты',
'portal-url' => 'Project:Портал комуніты',
-'privacy' => 'Політіка охраны особных дат',
-'privacypage' => 'Project:Ð\9eÑ\85Ñ\80ана оÑ\81обнÑ\8bÑ\85 даÑ\82',
+'privacy' => 'Політіка сокочіня пріватных дан',
+'privacypage' => 'Project:СокоÑ\87Ñ\96нÑ\8f пÑ\80Ñ\96ваÑ\82нÑ\8bÑ\85 дан',
-'badaccess' => 'Ð¥Ñ\8bба опÑ\80авнÑ\97нÑ\8f',
-'badaccess-group0' => 'Ð\92ам не Ñ\94 поволено выконавати тоту дїю.',
-'badaccess-groups' => 'Ð\94Ñ\97Ñ\8f, Ñ\8fкÑ\83 Ñ\81Ñ\8cÑ\82е Ñ\85оÑ\82Ñ\97ли зÑ\80обиÑ\82и, поволена лем хоснователям із {{PLURAL:$2|ґрупы|ґруп}}: $1.',
+'badaccess' => 'Ð\91Ñ\80ак пÑ\80ав пÑ\80иÑ\81Ñ\82Ñ\83пÑ\83',
+'badaccess-group0' => 'Ð\92ам не Ñ\94 дозволено выконавати тоту дїю.',
+'badaccess-groups' => 'Ð\94Ñ\97Ñ\8f, Ñ\8fкÑ\83 Ñ\81Ñ\8cÑ\82е Ñ\85оÑ\82Ñ\97ли зÑ\80обиÑ\82и, дозволена лем хоснователям із {{PLURAL:$2|ґрупы|ґруп}}: $1.',
'versionrequired' => 'Потрібна MediaWiki верзії $1',
'versionrequiredtext' => 'Про роботу з тов сторінков потрібна MediaWiki верзії $1. Відь [[Special:Version|сторінку верзії]].',
'youhavenewmessages' => 'Маєте $1 ($2).',
'newmessageslink' => 'новы повідомлїня',
'newmessagesdifflink' => 'послїдня зміна',
+'youhavenewmessagesfromusers' => 'Мате $1 од {{PLURAL:$3|іншого хоснователя|$3 іншых хоснователїв}} ($2).',
+'youhavenewmessagesmanyusers' => 'Мате $1 од много далшых хоснователїв ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|ное повідомлїня|новы повідомлїня}}',
+'newmessagesdifflinkplural' => 'остатня {{PLURAL:$1|зміна|зміны|змін}}',
'youhavenewmessagesmulti' => 'Маєте новы ознамы на $1',
'editsection' => 'едіт.',
'editold' => 'едіт.',
-'viewsourceold' => 'відїти код',
+'viewsourceold' => 'видїти код',
'editlink' => 'едітовати',
'viewsourcelink' => 'видїти код',
'editsectionhint' => 'Едітовати секцію: $1',
'site-atom-feed' => '$1 Atom канал',
'page-rss-feed' => '"$1" RSS канал',
'page-atom-feed' => '"$1" Atom канал',
-'red-link-title' => '$1 (Ñ\82ака Ñ\81Ñ\82оÑ\80Ñ\96нка не екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\94)',
-'sort-descending' => 'Сортовати зоступно',
-'sort-ascending' => 'Сортовати взоступно',
+'red-link-title' => '$1 (Ñ\82акой Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b нÑ\97Ñ\82)',
+'sort-descending' => 'Сортовати спадаючі',
+'sort-ascending' => 'Сортовати ступаючі',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Сторінка',
'nosuchaction' => 'Такой дїї не має',
'nosuchactiontext' => 'Дїя, уведжена в URL, неправилна.
Могли сьте неправилно написати URL або перейти через некоректный одказ .
-Може тыж значіти хыбу в програмовім забезпечіню {{GRAMMAR:genitive|{{SITENAME}}}}.',
+Може тыж значіти ґанч в проґрамовім забеспечіню {{GRAMMAR:genitive|{{SITENAME}}}}.',
'nosuchspecialpage' => 'Такой шпеціалной сторінкы нїт',
'nospecialpagetext' => '<strong>Така шпеціална сторінка не екзістує.</strong>
'error' => 'Хыба',
'databaseerror' => 'Датабазова хыба',
'dberrortext' => 'Найджена сінтактічна хыба в запросї до датабазы.
-Тото може вказовати на хыбу в проґрамовім забезпечіню.
+Тото може вказовати на хыбу в проґрамовім забеспечіню.
Послїднїй запрос до датабазы:
<blockquote><tt>$1</tt></blockquote>
з функції "<tt>$2</tt>".
'readonly_lag' => 'Датабаза автоматічно заблокована од змін, докы ся другый датабазовый сервер не сінхронізує з мастером',
'internalerror' => 'Інтерна хыба',
'internalerror_info' => 'Інтерна хыба: $1',
-'fileappenderrorread' => 'Ð\92 Ñ\87аÑ\81Ñ\97 пÑ\80идаванÑ\8f Ñ\81Ñ\8f не подаÑ\80ило прочітати "$1".',
-'fileappenderror' => 'Ð\9dе подаÑ\80ило ся придати «$1» до «$2».',
+'fileappenderrorread' => 'Ð\92 Ñ\87аÑ\81Ñ\97 пÑ\80идаванÑ\8f Ñ\81Ñ\8f не вдало прочітати "$1".',
+'fileappenderror' => 'Ð\9dе вдало ся придати «$1» до «$2».',
'filecopyerror' => 'Не было можне копіровати файл «$1» на «$2».',
'filerenameerror' => 'Не было можне переменовати файл «$1» на «$2».',
'filedeleteerror' => 'Не было можне змазаты файл «$1».',
'cannotdelete' => 'Не є можне вымазати сторінку або файл "$1".
Може уж быв(а) змазаный(а) дакым іншым.',
'cannotdelete-title' => 'Не годен змазати сторінку "$1"',
+'delete-hook-aborted' => 'Едітованя было сторноване процедуров пунктом припоёваня без близшого пояснїня.',
'badtitle' => 'Неприпустна назва',
'badtitletext' => 'Пожадована назва сторінкы неправилна, порожня, або неправилно одказована як міджі-язычного ці міджі-вікі назва.
Може ся хоснують сімболы, котры не можуть быти хоснованы в назвах.',
'wrong_wfQuery_params' => 'Неправилны параметры функцій wfQuery()<br />
Функція: $1<br />
Запрос: $2',
-'viewsource' => 'Відїти код',
-'viewsource-title' => 'Відїти жрідло сторінкы $1',
+'viewsource' => 'Видїти код',
+'viewsource-title' => 'Видїти жрідло сторінкы $1',
'actionthrottled' => 'Акція была придушена',
'actionthrottledtext' => 'Взглядом ку протиспамовым опатрїням не можете жадану акцію провести барз часто в короткім часї. Спробуйте то знову о пару мінут.',
'protectedpagetext' => 'Тота сторінка была замкнута, также ся не дасть едітовати',
'viewsourcetext' => 'Можете видїти і копіровати код той сторінкы:',
'viewyourtext' => "Можете собі посмотрити і скопіровати жрідловый текст '''вашых змін''' той сторінкы:",
-'protectedinterface' => 'Тота сторінка є частинов інтрефейсу проґрамового забезпечіня і єй можуть едітовати лем адміністраторы проєкту.',
+'protectedinterface' => 'Тота сторінка є частёв інтрефейсу проґрамового забеспечіня і єй можуть едітовати лем адміністраторы проєкту.',
'editinginterface' => "'''Увага:''' Едітуєте сторінку,котра є частинов текстового інтерфейсу. Зміны той сторінкы выкличуть зміну інтерфейсу про іншых хоснователїв. Про переклад увідомлїня хоснуйте [//translatewiki.net/wiki/Main_Page?setlang=uk translatewiki.net] — проєкт, што ся занимає локалізаціёв MediaWiki.",
'sqlhidden' => '(SQL запрос скрытый)',
'cascadeprotected' => 'Сторінка є замнкута, бо є вложена до {{PLURAL:$1|наслїдуючой сторінкы замкнуты|наслїдуючіх сторінок замнкнутых|наслїдуючіх сторінок замнкнутых}} каскадовым замком:
Адміністратор сервера, котрый архів заблоковав, додав тото пояснїня: „''$3''“.",
'invalidtitle-knownnamespace' => 'Непряавилна назва в просторї назв „$2“ і текстом „$3“',
'invalidtitle-unknownnamespace' => 'Неправилна назва з незнамым чіслом простору назв $1 і текстом „$2“',
+'exception-nologin' => 'Не сьте приголошеный(а)',
+'exception-nologin-text' => 'Гевся сторінка або дїя потребує, жебы сьте были на тотїй вікі приголошены.',
# Virus scanner
'virus-badscanner' => "Зла конфіґурація: незнамый антивіровый проґрам: ''$1''",
-'virus-scanfailed' => 'Ñ\81кенованÑ\8f Ñ\81Ñ\8f не подаÑ\80ило (код $1)',
+'virus-scanfailed' => 'Ñ\81кенованÑ\8f Ñ\81Ñ\8f не вдало (код $1)',
'virus-unknownscanner' => 'незнамый антівірус',
# Login and logout pages
'welcomecreation' => '== Вітаєме вас, $1! ==
Ваше конто было вытворене.
Не забудьте змінити свої [[Special:Preferences|наставлїня сайту]].',
-'yourname' => 'Ð\9cено хоснователя:',
+'yourname' => 'Ð\86мÑ\8f хоснователя:',
'yourpassword' => 'Гесло:',
'yourpasswordagain' => 'Повторяйте гесло:',
'remembermypassword' => 'Запамятати моє приголошіня на тім компютерї (максімално $1 {{PLURAL:$1|день|днів}})',
'securelogin-stick-https' => 'Останьте припоєны через HTTPS по приголошіню',
'yourdomainname' => 'Ваша домена:',
-'externaldberror' => 'Або ся стала хыба екстерной автентіфікачной датабазы, або не маєте поволено мінити своє екстерне конто.',
+'password-change-forbidden' => 'На тій вікі не можете мінити гесла.',
+'externaldberror' => 'Або ся стала хыба екстерной автентіфікачной датабазы, або не маєте дозволено мінити своє екстерне конто.',
'login' => 'Приголошіня',
'nav-login-createaccount' => 'Приголошіня / створїня конта',
-'loginprompt' => 'Ку приголошіню до {{grammar:2sg|{{SITENAME}}}} мусите мати поволены cookies.',
+'loginprompt' => 'К приголошіню до {{grammar:2sg|{{SITENAME}}}} мусите мати актівованы cookies.',
'userlogin' => 'Приголошіня / створїня конта',
'userloginnocreate' => 'Приголошіня',
'logout' => 'Одголосити',
'createaccount' => 'Вытворити конто',
'gotaccount' => "Уж сьте реґістрованы? '''$1'''.",
'gotaccountlink' => 'Приголошіня',
-'userlogin-resetlink' => 'Забыли сьте вашы дата про приголошіня?',
+'userlogin-resetlink' => 'Забыли сьте вашы даны на приголошіня?',
'createaccountmail' => 'електроничнов поштов',
'createaccountreason' => 'Причіна:',
'badretype' => 'Вами написаны гесла не сугласять.',
'userexists' => 'Уведжене імя хоснователя ся уж хоснує.
Просиме, выберьте інше імя.',
'loginerror' => 'Хыба при приголошованю',
-'createaccounterror' => 'Ð\9dе подаÑ\80ило ся створити конто хоснователя: $1',
+'createaccounterror' => 'Ð\9dе вдало ся створити конто хоснователя: $1',
'nocookiesnew' => 'Конто хоснователя было створене, але не сьте приголошены.
{{SITENAME}} хоснує cookies про приголошіня але вы маєте cookies выпнуты .
Просиме Вас, повольте їх, а потім ся приголоште знову з вашым новым меном і геслом.',
'nocookieslogin' => '{{SITENAME}} хоснує cookies про приголошіня хоснователїв. Вы маєте cookies выпнуты. Просиме Вас, повольте їх і спобуйте знова.',
-'nocookiesfornew' => 'Конто хоснователя не было створене, бо сьме не были годны підтветдити ёго походжіня.
-УÑ\82веÑ\80диÑ\82е Ñ\81Ñ\8f, же маÑ\94Ñ\82е поволены cookies, обновте тоту сторінку і спробуйте то знову.',
+'nocookiesfornew' => 'Конто хоснователя не было створене, бо сьме не были годны потвердити ёго походжіня.
+УÑ\82веÑ\80диÑ\82е Ñ\81Ñ\8f, же маÑ\94Ñ\82е дозволены cookies, обновте тоту сторінку і спробуйте то знову.',
'noname' => 'Мусите увести мено свого конта.',
'loginsuccesstitle' => 'Успішне приголошіня',
'loginsuccess' => "'''Теперь працуєте {{grammar:locative|{{SITENAME}}}} під меном $1.'''",
'nosuchusershort' => 'Хоснователь з меном $1 не екзістує.
Перевірте правилность написаня мена.',
'nouserspecified' => 'Мусите задати мено хоснователя.',
-'login-userblocked' => 'Тот хоснователь є заблокованый. Приголошіня не є доволене.',
+'login-userblocked' => 'ТоÑ\82 Ñ\85оÑ\81новаÑ\82елÑ\8c Ñ\94 заблокованÑ\8bй. Ð\9fÑ\80иголоÑ\88Ñ\96нÑ\8f не Ñ\94 дозволене.',
'wrongpassword' => 'Задали сьте хыбне гесло. Спробуйте іщі раз.',
'wrongpasswordempty' => 'Было задане порожнє гесло. Спробуйте іщі раз.',
'passwordtooshort' => 'Гесло мусить быти довге холем $1 {{PLURAL:$1|знак|знакы|знаків}}.',
'noemailcreate' => 'Мусите задати правилну адресу електронічной пошты',
'passwordsent' => 'Нове гесло было послане на адресу електронічной пошты реґістровану про „$1“. Приголосьте ся зясь, кідь го обтримете.',
'blocked-mailpassword' => 'Вашій IP адресї была заблокована можность едітації і сучасно з тым є заблокована функція про засланя нового гесла.',
-'eauthentsent' => 'На задану адресу електронічной пошты было послане підтверджіня,
-Передтым як вам на тоту адресу буде мочі быти засылана далша пошта, наслїдуйте інштрукції в ел. пошті, жебы сьте підтвердили, же тота адреса справды належыть вам.',
-'throttled-mailpassword' => 'Ð\93еÑ\81ло Ñ\83ж бÑ\8bло Ñ\80аз заÑ\81лане поÑ\87аÑ\81 уплынулых $1 годин.
+'eauthentsent' => 'На задану адресу електронічной пошты было послане потверджіня,
+Передтым як вам на тоту адресу буде мочі быти засылана далша пошта, слїдуйте інштрукції в ел. пошті, жебы сьте потвердили, же тота адреса справды належыть вам.',
+'throttled-mailpassword' => 'Ð\93еÑ\81ло Ñ\83ж бÑ\8bло Ñ\80аз заÑ\81лане бÑ\96гом уплынулых $1 годин.
Гесло може быти заслане лем раз за $1 {{PLURAL:$1|годину|годины|годин}}.',
'mailerror' => 'Хыба засыланя ел. пошты: $1',
-'acct_creation_throttle_hit' => 'ХоÑ\81новаÑ\82елÑ\97 пÑ\80иÑ\85одÑ\8fÑ\87Ñ\96 з ваÑ\88ой IP адÑ\80еÑ\81Ñ\8b Ñ\83ж днеÑ\81Ñ\8c Ñ\81Ñ\82воÑ\80или {{PLURAL:$1|конÑ\82о|конÑ\82а|конÑ\82}}, Ñ\88Ñ\82о Ñ\94 доволене макÑ\81Ñ\96мÑ\83м. Ð\97аÑ\82о Ñ\82епеÑ\80Ñ\8c не Ñ\94 доволено з той IP адресы закладати далшы конта.',
+'acct_creation_throttle_hit' => 'ХоÑ\81новаÑ\82елÑ\97 пÑ\80иÑ\85одÑ\8fÑ\87Ñ\96 з ваÑ\88ой IP адÑ\80еÑ\81Ñ\8b Ñ\83ж днеÑ\81Ñ\8c Ñ\81Ñ\82воÑ\80или {{PLURAL:$1|конÑ\82о|конÑ\82а|конÑ\82}}, Ñ\88Ñ\82о Ñ\94 дозволене макÑ\81Ñ\96мÑ\83м. Ð\97аÑ\82о Ñ\82епеÑ\80Ñ\8c не Ñ\94 дозволено з той IP адресы закладати далшы конта.',
'emailauthenticated' => 'Адреса вашой ел. пошты была овірена дня $2 о $3.',
'emailnotauthenticated' => 'Адреса вашой ел. пошты дотеперь не была овірена, функції ел. пошты суть недоступны.',
'noemailprefs' => 'Шпеціфікуйте адресу ел. пошты, жебы наслїднуючі можности могли фунґовати.',
-'emailconfirmlink' => 'Підтвердьте свою адресу ел. пошты',
+'emailconfirmlink' => 'Потвердьте свою адресу ел. пошты',
'invalidemailaddress' => 'Уведена адреса ел. пошты не може быти прията, бо она не має правилный формат.
Просиме Вас, уведьте коректну адесу або зохабте поле порожнє.',
'cannotchangeemail' => 'В тій вікі не годен мінити імейловы адресы.',
'usernamehasherror' => 'Мено хоснователя не сміє обсяговати сімбол мережка (#)',
'login-throttled' => 'Зробили сьте дуже много спроб о приголошіня.
Просиме Вас, почекайте перед далшов спробов.',
-'login-abort-generic' => 'Ð\9dе подаÑ\80ило ся войти до сістемы.',
+'login-abort-generic' => 'Ð\9dе вдало ся войти до сістемы.',
'loginlanguagelabel' => 'Язык: $1',
'suspicious-userlogout' => 'Ваша пожадавка на одголошіня была одвергнута, бо вызерає то так, же была послана розбитым переглядачом або кешуючім проксі-сервером.',
Дочасне гесло: $2',
'passwordreset-emailsent' => 'Імейл з геслом быв посланый.',
'passwordreset-emailsent-capture' => 'Быв выґенерованый припоминаючій імейл, котрый є указаный ниже.',
-'passwordreset-emailerror-capture' => 'Ð\91Ñ\8bв вÑ\8bÒ\91енеÑ\80ованÑ\8bй пÑ\80ипоминаÑ\8eÑ\87Ñ\96й Ñ\96мейл, коÑ\82Ñ\80Ñ\8bй Ñ\94 Ñ\83казанÑ\8bй ниже, але одоÑ\81лаÑ\82и Ñ\85оÑ\81новаÑ\82елÑ\91ви Ñ\81Ñ\8f го не подаÑ\80ило: $1',
+'passwordreset-emailerror-capture' => 'Ð\91Ñ\8bв вÑ\8bÒ\91енеÑ\80ованÑ\8bй пÑ\80ипоминаÑ\8eÑ\87Ñ\96й Ñ\96мейл, коÑ\82Ñ\80Ñ\8bй Ñ\94 Ñ\83казанÑ\8bй ниже, але одоÑ\81лаÑ\82и Ñ\85оÑ\81новаÑ\82елÑ\91ви Ñ\81Ñ\8f го не вдало: $1',
# Special:ChangeEmail
'changeemail' => 'Зміна імейловой адресы',
Просиме Вас, пиште тоты детайлы во вшыткых запытах на адміністратора.",
'blockednoreason' => 'причіна не была задана',
'whitelistedittext' => 'Про едітованя ся мусите $1.',
-'confirmedittext' => 'Мусите підтвердити вашу адресу ел. пошты передтым як будете едітовати сторінкы.
-На сторінцї [[Special:Preferences|наставлїня]] задайте і зохабте собі підтвердити свою адресу ел. пошты.',
+'confirmedittext' => 'Мусите потвердити вашу адресу ел. пошты передтым як будете едітовати сторінкы.
+На сторінцї [[Special:Preferences|наставлїня]] задайте і зохабте собі потвердити свою адресу ел. пошты.',
'nosuchsectiontitle' => 'Секція ненайдена',
'nosuchsectiontext' => 'Пробуєте едітовати секцію, котра не екзістує.
Може была переменована або змазана, покы сьте перезерали сторінку.',
'loginreqlink' => 'Приголосити ся',
'loginreqpagetext' => 'Ку перезераню іншых сторінок ся мусите $1.',
'accmailtitle' => 'Гесло одослане.',
-'accmailtext' => 'Ð\9dагодно выґенероване гесло про хоснователя [[User talk:$1|$1]] было послане на $2.
-Гесло того нового конта буде можне по приголошіню змінити на [[Special:ChangePassword|сторінцї про зміну гесла]].',
+'accmailtext' => 'ТÑ\80аÑ\84Ñ\83нково выґенероване гесло про хоснователя [[User talk:$1|$1]] было послане на $2.
+Гесло того нового конта буде мочі по приголошіню змінити на [[Special:ChangePassword|сторінцї про зміну гесла]].',
'newarticle' => '(Нова)',
'newarticletext' => "Перешли сте на сторінку, котра іщі не екзістує.
Нову сторінку створите так, же зачнете писати в окнї ниже (вид. [[{{MediaWiki:Helppage}}|сторінка помочі]], про вецей інформації).
'noarticletext-nopermission' => 'Теперь на тїй сторінцї не є текст.
Можете [[Special:Search/{{PAGENAME}}|глядати тоту назву]] в іншых сторінках,
або <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} глядати в лоґах]</span>.',
+'missing-revision' => 'Ревізія #$1 сторінкы з назвов „{{PAGENAME}}“ не є.
+
+Гевсе звычайно запрічінене так, же наслїдовали сьте застарїлый історічный одказ на сторінку, котра была уж змазана.
+Детайлы можуть быти найджены в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} книзї змазаных сторінок].',
'userpage-userdoesnotexist' => 'Хосновательске конто під назвов "<nowiki>$1</nowiki>" не є реґістроване. Сконтролюйте ці хочете вытворити/едітовати тоту сторінку.',
'userpage-userdoesnotexist-view' => 'Хосновательске конто „$1 не є реґістроване.',
'blocked-notice-logextract' => 'Тот хоснователь є теперь блокованый.
Послїднїй запис в лоґах блоковань є такый:',
-'clearyourcache' => "'''Позначка: По уложіню мусите вымазати кеш вашого перезерача, інакше зміны не будете відїти.'''
+'clearyourcache' => "'''Позначка: По уложіню мусите вымазати кеш вашого перезерача, інакше зміны не будете видїти.'''
'''Mozilla / Firefox / Safari:''' При кликнутю на ''Актуалізовати'' тримайте ''Shift'', або стиснийте ''Ctrl-F5'' або ''Ctrl-R'' (на Macintosh ''Command-R'');
'''Konqueror''': Кликнийте на ''Актуалізовати'' або стиснийте ''F5'';
'''Opera:''' Вымажте обсяг кеш в меню ''Інштрументы → Наставлїня'';
Зміны іщі не суть уложены!",
'continue-editing' => 'Продовжыти едітованя',
'previewconflict' => 'Тот нагляд зображує текст так, як буде вызерати по уложіню сторінкы.',
-'session_fail_preview' => "'''Вашу пожадавку ся не подарило зпрацовати, бо были страчены дата сеансу.
+'session_fail_preview' => "'''Вашу пожадавку ся не удало спрацовати, бо были страчены дата сеансу.
Просиме, спробуйте то зясь.
-Ð\9aÑ\96дÑ\8c Ñ\81Ñ\8f Ñ\82оÑ\82 пÑ\80облем бÑ\83де опаковаÑ\82и, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\81Ñ\8f [[Special:UserLogout|одголоÑ\81иÑ\82и]] Ñ\96 зновÑ\83 пÑ\80иголоÑ\81иÑ\82и до Ñ\81Ñ\96Ñ\81Ñ\82емÑ\83.'''",
-'session_fail_preview_html' => "'''Вашу пожадавку ся не подарило зпрацовати, бо были страчены дата сеансу..'''
+Ð\9aÑ\96дÑ\8c Ñ\81Ñ\8f Ñ\82оÑ\82 пÑ\80облем бÑ\83де опаковаÑ\82и, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\81Ñ\8f [[Special:UserLogout|одголоÑ\81иÑ\82и]] Ñ\96 зновÑ\83 пÑ\80иголоÑ\81иÑ\82и до Ñ\81Ñ\96Ñ\81Ñ\82емÑ\8b.'''",
+'session_fail_preview_html' => "'''Вашу пожадавку ся не удало спрацовати, бо были страчены дата сеансу..'''
-''Зато же {{SITENAME}} має запнуте хоснованя чістого HTML, нагляд ся про превенцію проти утокам JavaScript-ом не зображує.''
+''Зато же {{SITENAME}} має запнуте хоснованя чістого HTML, нагляд ся про превенцію проти атакам JavaScript-ом не зображує.''
-'''Ð\9aÑ\96дÑ\8c Ñ\96де о Ñ\80Ñ\8fднÑ\83 едÑ\96Ñ\82аÑ\86Ñ\96Ñ\8e, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\82о зновÑ\83. Ð\9aÑ\96дÑ\8c Ñ\81Ñ\8f Ñ\82оÑ\82 пÑ\80облем бÑ\83де опаковаÑ\82и, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\81Ñ\8f [[Special:UserLogout|одголоÑ\81иÑ\82и]] Ñ\96 зновÑ\83 пÑ\80иголоÑ\81иÑ\82и до Ñ\81Ñ\96Ñ\81Ñ\82емÑ\83.'''",
+'''Ð\9aÑ\96дÑ\8c Ñ\96де о Ñ\80Ñ\8fднÑ\83 едÑ\96Ñ\82аÑ\86Ñ\96Ñ\8e, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\82о зновÑ\83. Ð\9aÑ\96дÑ\8c Ñ\81Ñ\8f Ñ\82оÑ\82 пÑ\80облем бÑ\83де повÑ\82оÑ\80иÑ\82и, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\81Ñ\8f [[Special:UserLogout|одголоÑ\81иÑ\82и]] Ñ\96 зновÑ\83 пÑ\80иголоÑ\81иÑ\82и до Ñ\81Ñ\96Ñ\81Ñ\82емÑ\8b.'''",
'token_suffix_mismatch' => "'''Ваша едітація не была схвалена, бо ваш вебовый переглядач комолить дакотры знакы в едітованім текстї.
Едітація не была схвалена, жебы ся заборонило пошкоджіню тексту сторінкы.
Тото ся може даколи стати, кідь хоснуєте хыбный вебовый анонімізер.'''",
'''Лем''' выше вказаный текст зістане ухованый по кликнутю на „{{int:savearticle}}“.",
'yourtext' => 'Ваш текст',
'storedversion' => 'Уложена верзія',
-'nonunicodebrowser' => "'''Увага: Ваш переглядач не є способный працовати із знаками Unicode. Абы сьте могли тоту сторінку безпечно едітовати: вшыткы знакы мімо ASCII суть зображены в гексадецімалных кодах.'''",
+'nonunicodebrowser' => "'''Увага: Ваш переглядач не є способный працовати із знаками Unicode. Абы сьте могли тоту сторінку беспечно едітовати: вшыткы знакы мімо ASCII суть зображены в гексадецімалных кодах.'''",
'editingold' => "'''Увага: Нынї едітуєте застаралу верзію той сторінкы. Кідь єй уложыте, вшыткы пізнїшы зміны ся стратять.'''",
'yourdiff' => 'Роздїлы',
-'copyrightwarning' => "Просиме Вас, уважте, што вшыткы додаваня і зміны до {{grammar:genitive|{{SITENAME}}}} будуть выпущены під ліценціёв $2 (від. $1).
+'copyrightwarning' => "Просиме Вас, уважте, што вшыткы додаваня і зміны до {{grammar:genitive|{{SITENAME}}}} будуть выпущены під ліценціов $2 (від. $1).
Кідь не хочете, жебы написане вами ся немилосердно едітовало і шырило, пак ту не пиште.<br />
-Вы тыж підтверджуєте, што написане вами ту належыть вам або взяте із жрідла, што є верейным ці вольным жрідлом.
-'''Ð\9dÐ\95 Ð\9fУÐ\91Ð\9bÐ\86Ð\9aУÐ\99ТÐ\95 ТУ Ð\91Ð\95Ð\97 Ð\9fÐ\9eÐ\92Ð\9eÐ\9bÐ\87Ð\9dЯ Ð\9cÐ\90ТÐ\95Ð Ð\86Ð\90Ð\9bЫ, ШТÐ\9e СЯ Ð\9eХРÐ\90Ð\9dЮЮТЬ АВТОРЬСКЫМ ПРАВОМ!''",
+Вы тыж потверджуєте, што написане вами ту належыть вам або взяте із жрідла, што є публічным ці вольным жрідлом.
+'''Ð\9dÐ\95 Ð\9fУÐ\91Ð\9bÐ\86Ð\9aУÐ\99ТÐ\95 ТУ Ð\91Ð\95Ð\97 Ð\94Ð\9eÐ\97Ð\92Ð\9eÐ\9bÐ\87Ð\9dЯ Ð\9cÐ\90ТÐ\95Ð Ð\86Ð\90Ð\9bЫ, ШТÐ\9e СЯ СÐ\9eÐ\9aÐ\9eТЯТЬ АВТОРЬСКЫМ ПРАВОМ!''",
'copyrightwarning2' => "Просиме Вас, уважте, што вшыткы додаваня і зміны до {{grammar:2sg|{{SITENAME}}}} можуть быти другыма хоснователями управлены, змінены ці одстранены. Покы собі не желате, жебы ваш текст быв немилосердно управляный, пак го до {{grammar:2sg|{{SITENAME}}}} не укладайте.<br />
Уложінём приспевку ся завязуєте, же є вашым дїлом або є скопірованый із жрідел, котры не суть хоронены авторьскым правом (тзв. <em>public domain</em>), детайлы найдете на $1. '''Не копіруйте дїла хоронены авторьскым правом без дозволїня!'''",
-'longpageerror' => "'''ХЫÐ\91Ð\90: Ð\9fÑ\80обÑ\83Ñ\94Ñ\82е Ñ\83ложÑ\8bÑ\82и Ñ\82екÑ\81Ñ\82 о великоÑ\81Ñ\82и {{PLURAL:$1|$1 Ð\9aÑ\96Ð\91}}, пÑ\80иÑ\87Ñ\96м доволене максімум є {{PLURAL:$2|$2 КіБ}}. Ваша едітація не може быти уложена.'''",
+'longpageerror' => "'''ХЫÐ\91Ð\90: Ð\9fÑ\80обÑ\83Ñ\94Ñ\82е Ñ\83ложÑ\8bÑ\82и Ñ\82екÑ\81Ñ\82 о великоÑ\81Ñ\82и {{PLURAL:$1|$1 Ð\9aÑ\96Ð\91}}, але дозволене максімум є {{PLURAL:$2|$2 КіБ}}. Ваша едітація не може быти уложена.'''",
'readonlywarning' => "'''УВАГА: Датабаза была замкнута про утримованя, также не будете мочі уложыти свої зміны. Можете сі го окопіровати до файлу і уложыти го пізнїше.'''
Адміністратор сервера, котрый датабазу замкнув, зохабив тото пояснїня: $1",
'templatesused' => '{{PLURAL:$1|Шаблона, хоснована|Шаблоны, хоснованы}} на тій сторінці:',
'templatesusedpreview' => '{{PLURAL:$1|Шаблона, хоснована|Шаблоны, хоснованы}} у тім перегляді:',
'templatesusedsection' => '{{PLURAL:$1|Шаблона, хоснована|Шаблоны, хоснованы}} у тій секції:',
-'template-protected' => '(хранена)',
-'template-semiprotected' => '(частково хранено)',
+'template-protected' => '(всокочена)',
+'template-semiprotected' => '(частково всокочене)',
'hiddencategories' => 'Тота сторінка належыть до $1 {{PLURAL:$1|схованa катеґорія|схованы катеґорії|схованых катеґорій}}:',
'nocreatetitle' => 'Створїня сторінок обмеджено',
'nocreatetext' => 'На {{grammar:6sg|{{SITENAME}}}} є можливость створїна новых сторінок обмеджена.
'sectioneditnotsupported-text' => 'На тій сторінцї не є підпороване едітованя єдной секції.',
'permissionserrors' => 'Хыба оправнїня',
'permissionserrorstext' => 'Не маєте поволїня той операції з {{PLURAL:$1|такой причіны|такых причін}}:',
-'permissionserrorstext-withaction' => 'Ð\9dе маÑ\94Ñ\82е поволÑ\97нÑ\8f на $2 з {{PLURAL:$1|Ñ\82акой пÑ\80иÑ\87Ñ\96нÑ\8b|Ñ\82акÑ\8bÑ\85 пÑ\80ичін}}:',
+'permissionserrorstext-withaction' => 'Ð\9dе маÑ\94Ñ\82е дозволÑ\97нÑ\8f на $2 з {{PLURAL:$1|Ñ\82акой пÑ\80Ñ\96Ñ\87Ñ\96нÑ\8b|Ñ\82акÑ\8bÑ\85 пÑ\80Ñ\96чін}}:',
'recreate-moveddeleted-warn' => "'''Увага: Пробуєте знову створити сторінку, котра была в минулости змазана.'''
Уважте, ці справды треба знову створити тоту сторінку.
Запис змазаня а переменованя сі можете посмотрити ниже.',
'log-fulllog' => 'Зобразити вшыток запис',
'edit-hook-aborted' => 'Едітованя было сторноване процедуров без близшого пояснїня.',
-'edit-gone-missing' => 'СÑ\82оÑ\80Ñ\96нкÑ\83 Ñ\81Ñ\8f не подаÑ\80ило обновити.
+'edit-gone-missing' => 'СÑ\82оÑ\80Ñ\96нкÑ\83 Ñ\81Ñ\8f не вдало обновити.
Асі была змазана.',
'edit-conflict' => 'Конфлікт едітованя.',
-'edit-no-change' => 'Ваша едітація была іґнорована, бо не дішло ку жадній змінї тексту.',
-'edit-already-exists' => 'Ð\9dе подаÑ\80ило ся створити нову сторінку, бо она уж екзістує.',
+'edit-no-change' => 'Ваша едітація была іґнорована, бо ся не зробила жадна зміна тексту.',
+'edit-already-exists' => 'Ð\9dе вдало ся створити нову сторінку, бо она уж екзістує.',
'defaultmessagetext' => 'Преднаставленый текст повідомлїня',
# Parser/template warnings
-'expensive-parserfunction-warning' => 'Увага: Тота сторінка обсягує дуже много кликаня выконово нарочных функцій парсера.
-
-Поволеный ліміт є $2, теперь шак {{PLURAL:$1|ту єдно кликаня є|ту суть $2 кликаня|ту є $2 кликань}}.',
+'expensive-parserfunction-warning' => 'Увага: Тота сторінка обсягує дуже много кликаня выконово тяжкых функцій парсера.
+Дозволеный ліміт є $2, теперь шак {{PLURAL:$1|ту єдно кликаня є|ту суть $2 кликаня|ту є $2 кликань}}.',
'expensive-parserfunction-category' => 'Сторінкы з дуже великым чіслом кликаня функції парсера',
-'post-expand-template-inclusion-warning' => 'Ð\9fозÑ\96Ñ\80Ñ\8c: Ñ\80озмÑ\96Ñ\80 Ñ\88аблон пÑ\80о включіня є барз великый.
+'post-expand-template-inclusion-warning' => 'Ð\9fозÑ\96Ñ\80Ñ\8c: Ñ\80озмÑ\96Ñ\80 Ñ\88аблон на включіня є барз великый.
Дакотры шаблоны не будуть включены.',
'post-expand-template-inclusion-category' => 'Сторінкы з перевышуючов великостёв включеных шаблон',
'post-expand-template-argument-warning' => 'Увага: Тота сторінка обсягує принайменшім єден арґумент шаблоны, котрый є по роспакованю дуже великый.
'expansion-depth-exceeded-warning' => 'Сторінка перевышыла глубку експанзії',
'parser-unstrip-loop-warning' => 'Выявлене заціклїня unstrip',
'parser-unstrip-recursion-limit' => 'Перевышеный ліміт рекурзії unstrip ($1)',
+'converter-manual-rule-error' => 'Найджена хыба в ручнім правилї конверзії языка',
# "Undo" feature
'undo-success' => 'Едітованя може быти зручене.
'revdelete-text' => "'''Змазаны верзії і подїї будуть надале зображены в історії сторінкы і протоколовачіх записах, але дакотры їх части не будуть публікованы.'''
Другы адміністраторы {{GRAMMAR:2sg|{{SITENAME}}}} собі будуть мочі схованый обсяг перезерати і помочов того самого інтерфейсу го будуть мочі обновити,
кідь не были наставлены далшы обмеджіня.",
-'revdelete-confirm' => 'Просиме Вас, підтвердьте же то хочете справды зробити, же собі усвідомуєте резултат і же є то в згодї з [[{{MediaWiki:Policy-url}}|правилами]].',
+'revdelete-confirm' => 'Просиме Вас, потвердьте, же то хочете справды зробити, же собі усвідомлюєте резултат і же є то в згодї з [[{{MediaWiki:Policy-url}}|правилами]].',
'revdelete-suppress-text' => "Затаёваня бы ся мало хосновати ''лем''' в такых припадах:
* Потенціално огварячі інформації
* Непотрібны особны дата
'revdelete-log' => 'Причіна:',
'revdelete-submit' => 'Апліковати на {{PLURAL:$1|зазначену ревізію|зазначены ревізії}}',
'revdelete-success' => "'''Видимость ревізії успішно змінена.'''",
-'revdelete-failure' => "'''Ð\9dе подаÑ\80ило ся змінити видимость ревізії:'''
+'revdelete-failure' => "'''Ð\9dе вдало ся змінити видимость ревізії:'''
$1",
'logdelete-success' => "'''Видимость події успішно наставена.'''",
-'logdelete-failure' => "'''Ð\9dе подаÑ\80ило ся наставити видимость протоколу.'''
+'logdelete-failure' => "'''Ð\9dе вдало ся наставити видимость протоколу.'''
$1",
'revdel-restore' => 'Змінити видимость',
'revdel-restore-deleted' => 'вымазаны ревізії',
# History merging
'mergehistory' => 'Злучованя історії сторінок',
-'mergehistory-header' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка Ð\92ам доволиÑ\82Ñ\8c злÑ\83Ñ\87Ñ\96Ñ\82и Ñ\96Ñ\81Ñ\82оÑ\80Ñ\96Ñ\8e Ñ\94дной здÑ\80ойовой сторінкы з новшов сторінков.
-Пересвідчте ся, же тота зміна утриме цалосность і поступность історії сторінкы.',
+'mergehistory-header' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка Ð\92ам дозволиÑ\82Ñ\8c злÑ\83Ñ\87Ñ\96Ñ\82и Ñ\96Ñ\81Ñ\82оÑ\80Ñ\96Ñ\8e Ñ\94дной жÑ\80Ñ\96дловой сторінкы з новшов сторінков.
+Пересвіджте ся, же тота зміна утриме повязаность і поступность історії сторінкы.',
'mergehistory-box' => 'Злучіти ревізії двох сторінок:',
'mergehistory-from' => 'Здроёва сторінка:',
'mergehistory-into' => 'Цілёва сторінка:',
'editundo' => 'вернути назад',
'diff-multi' => '({{PLURAL:$1|Не є зображена єдна міджілегла верзія|Не суть зображены $1 міджілеглы верзії|Не є зображено $1 міджілеглых верзій}} од {{PLURAL:$2|1 хоснователя|$2 хоснователїв}} .)',
'diff-multi-manyusers' => '(Не є зображено $1 міджілеглых верзій од веце як $2 {{PLURAL:$2|хоснователя|хоснователїв}}.)',
+'difference-missing-revision' => '{{PLURAL:$2|Єдна з ревізій|$2 ревізії|$2 ревізій}} к пожадованому порівнаню ($1) {{PLURAL:$2|не є|не суть|не є}}.
+
+Гевсе є звычайно запрічінене так, же наслїдовали сьте застарїлый одказ історічнов ревізіов сторінкы, котра уж была змазана.
+Детайлы можуть быти найджены в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} книзї змазаный сторінок].',
# Search results
'searchresults' => 'Резултаты гляданя',
-'searchresults-title' => 'Резултаты гляданя про "$1"',
+'searchresults-title' => 'Резултаты гляданя "$1"',
'searchresulttext' => 'Про детайлнїшы інформації про гляданя у проєктї, смоть [[{{MediaWiki:Helppage}}|сторінка помочі]]',
'searchsubtitle' => 'Глядали сьте «[[:$1]]» ([[Special:Prefixindex/$1|вшыткы сторінкы, што ся зачінають на «$1»]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|вшыткы сторінкы, што мають одказ на «$1»]])',
'searchsubtitleinvalid' => "Глядали сте '''$1'''",
'resultsperpage' => 'Чісло резултатів на сторінку:',
'stub-threshold' => 'Поріг про форматованя одказу як <a href="#" class="stub">одказів на "stub"</a> (v bajtech):',
'stub-threshold-disabled' => 'Выпнуте',
-'recentchangesdays' => 'Ð\9dа кілько днїв вказовати новы едітованя',
+'recentchangesdays' => 'Ð\97а кілько днїв вказовати новы едітованя',
'recentchangesdays-max' => '(максімум $1 {{PLURAL:$1|день|днї|днїв}})',
'recentchangescount' => 'Чісло імпліцітно зображованых едітовань:',
'prefs-help-recentchangescount' => 'Тыкать ся послїднїх змін, історії сторінок і протоколовачіх записів.',
'prefs-help-watchlist-token' => 'Кідь до того поля выповните тайный ключ, буде створеный RSS канал вашых слїдованых сторінок.
-Хоцьхто хто знає тот ключ, пак буде мочі ваш список слїдованых сторінок чітати, также не забывайте на безпечность.
-Можете схосновати тоту нагодно выґенеровану годноту: $1',
+Хоцьхто хто знає тот ключ, пак буде мочі ваш список слїдованых сторінок чітати, та же не забывайте на беспечность.
+Можете схосновати тото трафунково выґенероване значіня: $1',
'savedprefs' => 'Ваше наставлїня было уложене.',
'timezonelegend' => 'Часова зона:',
'localtime' => 'Містный час:',
'prefs-common-css-js' => 'Сдїляне CSS/JS про вшыткы штілы:',
'prefs-reset-intro' => 'Помочов той сторінкы можете вшыткы наставлїня вернути на імпліцітны годноты.
Тоту операцію не годен вернути назад.',
-'prefs-emailconfirm-label' => 'Підтверджіня електронічной пошты:',
+'prefs-emailconfirm-label' => 'Потверджіня електронічной пошты:',
'prefs-textboxsize' => 'Розмір окна едітованя',
'youremail' => 'Адреса електронічной пошты:',
'username' => 'Мено хоснователя:',
'uid' => 'Ідентіфікатор хоснователя:',
'prefs-memberingroups' => 'Член {{PLURAL:$1|ґрупы|ґруп}}:',
'prefs-registration' => 'Час реґістрації:',
-'yourrealname' => 'СкÑ\83Ñ\82оÑ\87не мено:',
+'yourrealname' => 'Ð\9fÑ\80авдиве Ñ\96мÑ\8f:',
'yourlanguage' => 'Язык:',
'yourvariant' => 'Варіант языка обсягу:',
'prefs-help-variant' => 'Вами преферованый варіант або правопис, як ся мають на тій вікі зображати обсяговы сторінкы.',
# Groups
'group' => 'Ґрупа:',
'group-user' => 'Хоснователї',
-'group-autoconfirmed' => 'Автопідтверджены хоснователї',
+'group-autoconfirmed' => 'Автопотверджены хоснователї',
'group-bot' => 'Боты',
'group-sysop' => 'Адміністраторы',
'group-bureaucrat' => 'Бірократы',
'group-suppress-member' => '{{GENDER:$1|ревізор|ревізорка|ревізор}}',
'grouppage-user' => '{{ns:project}}:Хоснователї',
-'grouppage-autoconfirmed' => '{{ns:project}}:Автопідтверджены хоснователї',
+'grouppage-autoconfirmed' => '{{ns:project}}:Автопотверджены хоснователї',
'grouppage-bot' => '{{ns:project}}:Боты',
'grouppage-sysop' => '{{ns:project}}:Адміністраторы',
'grouppage-bureaucrat' => '{{ns:project}}:Бірократы',
'right-move-rootuserpages' => 'Переменованя корінёвых сторінок хоснователїв',
'right-movefile' => 'Переменовати файлы',
'right-suppressredirect' => 'Нестворіня напрямлїня про переменоваю сторінкы',
-'right-upload' => 'Ð\9dагÑ\80аваня файлів',
+'right-upload' => 'Ð\97аладовованя файлів',
'right-reupload' => 'Переписованя екзістуючіх файлів',
-'right-reupload-own' => 'Ð\9fеÑ\80епиÑ\81ованÑ\8f Ñ\84айлÑ\96в нагÑ\80анÑ\8bÑ\85 Ñ\81ам Ñ\81обов',
-'right-reupload-shared' => 'Ð\9dагÑ\80аванÑ\8f локалнÑ\8bÑ\85 Ñ\84айлÑ\96в пÑ\80о поÑ\82иÑ\81нÑ\83Ñ\82Ñ\8f Ñ\82Ñ\8bÑ\85 в Ñ\81дÑ\97лÑ\8fнÑ\96м Ñ\83Ñ\81Ñ\85овÑ\96щу',
-'right-upload_by_url' => 'Ð\9dагÑ\80аваня файлів з URL адрес',
-'right-purge' => 'Очіщіня кешу про сторінкы без підтверджовачого діалоґу',
+'right-reupload-own' => 'Ð\9fеÑ\80епиÑ\81ованÑ\8f Ñ\84айлÑ\96в заладованÑ\8bÑ\85 од Ñ\81ебе Ñ\81амого',
+'right-reupload-shared' => 'Ð\97аладованÑ\8f локалнÑ\8bÑ\85 Ñ\84айлÑ\96в жебÑ\8b пеÑ\80екÑ\80Ñ\8bли Ñ\82оÑ\82Ñ\8bÑ\85 в Ñ\81полоÑ\87нÑ\96м Ñ\83Ñ\81Ñ\85овищу',
+'right-upload_by_url' => 'Ð\97аладовованя файлів з URL адрес',
+'right-purge' => 'Очіщіня кешу про сторінкы без потверджовачого діалоґу',
'right-autoconfirmed' => 'Едітованя часточно замкнутых сторінок',
'right-bot' => 'Быти поважованый за автоматічный процес',
'right-nominornewtalk' => 'Невыписованя новых повідомлїнь по малых управах діскузной сторінкы',
'right-writeapi' => 'Хосновати API про писаня',
'right-delete' => 'Змазаня сторінок',
'right-bigdelete' => 'Мазаня сторінок з довгов історіёв',
+'right-deletelogentry' => 'Мазаня тай обновлїня окремых записів лоґів
+,',
'right-deleterevision' => 'Мазаня і обновованя конкретных ревізій сторінок',
'right-deletedhistory' => 'Зображованя змазаных положок в історії без одповідаючого тексту',
'right-deletedtext' => 'перегляд змазаного тексту і роздїлів міджі змазаныма верзіами',
'right-markbotedits' => 'Означованя ревертів як едітованя робота',
'right-noratelimit' => 'Не має обмеджіня в швыдкости',
'right-import' => 'Імпорт сторінок з іншых вікі',
-'right-importupload' => 'Ð\86мпоÑ\80Ñ\82 Ñ\81Ñ\82оÑ\80Ñ\96нок Ñ\87еÑ\80ез нагÑ\80аваня файлів',
+'right-importupload' => 'Ð\86мпоÑ\80Ñ\82 Ñ\81Ñ\82оÑ\80Ñ\96нок Ñ\87еÑ\80ез заладованя файлів',
'right-patrol' => 'Позначованя едітовань як перевіреных',
'right-autopatrol' => 'Автоматічне означованя едітовань як перевіреных',
'right-patrolmarks' => 'Зобразованя патролёваных сторінок в Послїднїх змінах',
'action-move-subpages' => 'переменованя той сторінкы зо вшыткыма єй підсторінками',
'action-move-rootuserpages' => 'переменовати корінёвы сторінкы хостователїв',
'action-movefile' => 'переменовати тот файл',
-'action-upload' => 'нагÑ\80ати тот файл',
+'action-upload' => 'заладовати тот файл',
'action-reupload' => 'переписати тот екзістуючій файл',
'action-reupload-shared' => 'перекрыти тот файл зо сполочного уложыштя',
-'action-upload_by_url' => 'нагÑ\80ати тот файл з URL адресы',
+'action-upload_by_url' => 'заладовати тот файл з URL адресы',
'action-writeapi' => 'хосновати API про писаня',
'action-delete' => 'Вымазати тоту сторінку',
'action-deleterevision' => 'вымазати тоту ревізію сторінкы',
'recentchangeslinked-to' => 'Вказати зміны на сторінках, одказуючіх на задану сторінку',
# Upload
-'upload' => 'Ð\9dагÑ\80ати файл',
-'uploadbtn' => 'Ð\9dаÑ\87Ñ\96Ñ\82ати файл',
-'reuploaddesc' => 'Ð\97Ñ\80Ñ\83Ñ\88Ñ\8bÑ\82и наÑ\87Ñ\96Ñ\82анÑ\8f а веÑ\80нÑ\83Ñ\82и Ñ\81Ñ\8f до Ñ\84оÑ\80мÑ\8b наÑ\87Ñ\96Ñ\82аня',
+'upload' => 'Ð\97аладовати файл',
+'uploadbtn' => 'Ð\97аладовати файл',
+'reuploaddesc' => 'Ð\97Ñ\80Ñ\83Ñ\88Ñ\8bÑ\82и заладовованÑ\8f а веÑ\80нÑ\83Ñ\82и Ñ\81Ñ\8f до Ñ\84оÑ\80мÑ\8b заладовованя',
'upload-tryagain' => 'Уложыти зміненый попис файлу',
'uploadnologin' => 'Не сьте приголошеный(а)',
-'uploadnologintext' => 'Ð\9fÑ\80о наÑ\87Ñ\96Ñ\82анÑ\8f Ñ\84айлÑ\83 Ñ\81Ñ\8f мÑ\83Ñ\81иÑ\82е [[Special:UserLogin|приголосити]].',
-'upload_directory_missing' => 'Ð\90дÑ\80еÑ\81аÑ\80Ñ\8c пÑ\80о нагÑ\80аванÑ\8f Ñ\84айлÑ\96в ($1) Ñ\85Ñ\8bбиÑ\82Ñ\8c Ñ\96 вебовый сервер го не годен створити.',
-'upload_directory_read_only' => 'Ð\94о адÑ\80еÑ\81аÑ\80Ñ\8f пÑ\80о наÑ\87Ñ\96Ñ\82анÑ\8b Ñ\84айлÑ\8b ($1) не маÑ\94 вебовый сервер права запису.',
-'uploaderror' => 'Ð\9fÑ\80и наÑ\87Ñ\96Ñ\82аванÑ\8e дÑ\96Ñ\88ло кÑ\83 Ñ\85Ñ\8bби',
+'uploadnologintext' => 'Ð\96ебÑ\8b заладоваÑ\82и Ñ\84айл, мÑ\83Ñ\81иÑ\82е Ñ\81Ñ\8f [[Special:UserLogin|приголосити]].',
+'upload_directory_missing' => 'Ð\90дÑ\80еÑ\81аÑ\80Ñ\8c пÑ\80о заладовованÑ\8f Ñ\84айлÑ\96в ($1) Ñ\85Ñ\8bбиÑ\82Ñ\8c Ñ\82ай вебовый сервер го не годен створити.',
+'upload_directory_read_only' => 'Ð\94о адÑ\80еÑ\81аÑ\80Ñ\8f заладованÑ\8bÑ\85 Ñ\84айлÑ\96в ($1) не маÑ\82Ñ\8c вебовый сервер права запису.',
+'uploaderror' => 'Ð\9fÑ\96д Ñ\87аÑ\81 заладовованÑ\8f Ñ\81Ñ\8f пÑ\80иÑ\82Ñ\80аÑ\84ила Ñ\85Ñ\8bба',
'upload-recreate-warning' => "'''Увага: Файл з тов назвов быв скоре змазаный ці переменованый.'''
Ту є про перегляд зображеный список мазаня і переменованя той сторінкы:",
-'uploadtext' => "Ниже даный формулар служыть про начітаваня файлів. Уж начітаны файлы собі можете перезерати і глядати помочов [[Special:FileList|списку начітаных файлів]], качде начітаня ся тыж зазначує до [[Special:Log/upload|книгы начітаваня]], змазаня суть в [[Special:Log/delete|книзї змазаных сторінок]].
+'uploadtext' => "Ниже даный формуларь служыть на заладовованя файлів. Уж заладованы файлы собі можете перезерати і глядати помочов [[Special:FileList|списку заладованых файлів]], кажде заладованя ся тыж зазначує до [[Special:Log/upload|книгы заладованя]], змазаня суть в [[Special:Log/delete|книзї змазаных сторінок]].
Про вложіня образку до сторінкы хоснуйте єден з наслїдуючіх способів запису:
-* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Файл.jpg]]</nowiki></code>''' до Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b вложÑ\8bÑ\82Ñ\8c Ñ\86Ñ\96лый образок,
+* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Файл.jpg]]</nowiki></code>''' до Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b вложÑ\8bÑ\82Ñ\8c Ñ\86Ñ\97лый образок,
* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Файл.png|thumb|left|Попис]]</nowiki></code>''' вложыть нагляд в рамику зарівнанім на лівый бік, з пописом „Попис“,
-* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Файл.ogg]]</nowiki></code>''' вложÑ\8bÑ\82Ñ\8c пÑ\80Ñ\8fмÑ\8bй одказ на файл, без того жебы ся зобразив на сторінцї.",
-'upload-permitted' => 'Доволены тіпы файлів: $1.',
+* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Файл.ogg]]</nowiki></code>''' вложÑ\8bÑ\82Ñ\8c дÑ\96Ñ\80екÑ\82 одказ на файл, без того жебы ся зобразив на сторінцї.",
+'upload-permitted' => 'Ð\94озволенÑ\8b Ñ\82Ñ\96пÑ\8b Ñ\84айлÑ\96в: $1.',
'upload-preferred' => 'Преферованы тіпы файлів: $1',
'upload-prohibited' => 'Заказаны тіпы файлів: $1.',
-'uploadlog' => 'книга нагÑ\80аваня',
-'uploadlogpage' => 'Ð\9bоÒ\91 нагÑ\80аных файлів',
+'uploadlog' => 'книга заладованя',
+'uploadlogpage' => 'Ð\9bоÒ\91 заладованых файлів',
'uploadlogpagetext' => 'Ниже найдете список найновшых файлів. Смотьте [[Special:NewFiles|ґалерію новых образків]] про веце візуалного нагляду.',
'filename' => 'Назва файлу:',
'filedesc' => 'Попис',
'filereuploadsummary' => 'Зміны у файлі:',
'filestatus' => 'Авторьскы права:',
'filesource' => 'Жрідло:',
-'uploadedfiles' => 'Ð\9dаÑ\87Ñ\96Ñ\82аны файлы',
-'ignorewarning' => 'Іґноровати варованя тай начітати файл.',
+'uploadedfiles' => 'Ð\97аладованы файлы',
+'ignorewarning' => 'Іґноровати варованя тай уложыти файл.',
'ignorewarnings' => 'Іґноровати вшыткы варованя',
'minlength1' => 'Назва файлу мусить мати холем єдну літеру.',
-'illegalfilename' => 'Ð\9dазва Ñ\84айлÑ\83 "$1" обÑ\81Ñ\8fгÑ\83Ñ\94 бÑ\83квÑ\8b, коÑ\82Ñ\80Ñ\8b не Ñ\81Ñ\83Ñ\82Ñ\8c поволенÑ\8b в назваÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок. Ð\9fÑ\80оÑ\81име, пеÑ\80еменÑ\83йÑ\82е Ñ\84айл Ñ\96 Ñ\81пÑ\80обÑ\83йÑ\82е го нагÑ\80ати зясь.',
+'illegalfilename' => 'Ð\9dазва Ñ\84айлÑ\83 "$1" обÑ\81Ñ\8fгÑ\83Ñ\94 бÑ\83квÑ\8b, коÑ\82Ñ\80Ñ\8b не Ñ\81Ñ\83Ñ\82Ñ\8c дозволенÑ\8b в назваÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок. Ð\9fÑ\80оÑ\81име, пеÑ\80еменÑ\83йÑ\82е Ñ\84айл Ñ\96 Ñ\81пÑ\80обÑ\83йÑ\82е го заладовати зясь.',
'filename-toolong' => 'Назвы файлів не можуть быти довшы, як 240 байтів.',
'badfilename' => 'Назва файлу была змінена на „$1“.',
'filetype-mime-mismatch' => 'Росшырїня файлу ".$1" не одповідать ёго MIME тіпу ($2).',
-'filetype-badmime' => 'Ð\9dе Ñ\94 поволене наÑ\87Ñ\96Ñ\82ати файлы MIME тіп „$1“.',
-'filetype-bad-ie-mime' => 'Ð\9dеможно наÑ\87Ñ\96Ñ\82аÑ\82и Ñ\82оÑ\82 Ñ\84айл, бо Internet Explorer бÑ\8b го поважовав за â\80\9e$1â\80\9c, Ñ\88Ñ\82о Ñ\94 не доволенÑ\8bй Ñ\96 поÑ\82енÑ\86Ñ\96ално небезпечный тіп файлу.',
+'filetype-badmime' => 'Ð\9dе Ñ\94 дозволено заладововати файлы MIME тіп „$1“.',
+'filetype-bad-ie-mime' => 'Ð\9dеможливо заладоваÑ\82и Ñ\82оÑ\82 Ñ\84айл, бо Internet Explorer бÑ\8b го Ñ\82Ñ\80имав за â\80\9e$1â\80\9c, Ñ\88Ñ\82о Ñ\94 не дозволенÑ\8bй Ñ\96 поÑ\82енÑ\86Ñ\96ално небеÑ\81печный тіп файлу.',
'filetype-unwanted-type' => "„.$1“''' є нежеланый формат файлу. {{plural:$3|Желаный формат файлів є|Желаны форматы файлів суть}} $2.",
-'filetype-banned-type' => "'''â\80\9e.$1â\80\9c''' {{PLURAL:$4|Ñ\94 недоволенÑ\8bй Ñ\84оÑ\80маÑ\82 Ñ\84айлÑ\96в|Ñ\81Ñ\83Ñ\82Ñ\8c недоволены форматы файлів}}.
-{{PLURAL:$3|Ð\94оволенÑ\8bй Ñ\84оÑ\80маÑ\82 Ñ\84алÑ\96в|Ð\94оволены форматы файлів суть}} $2.",
-'filetype-missing' => 'Файл не маÑ\94 Ñ\80оÑ\81Ñ\88Ñ\8bÑ\80Ñ\96ня (наприклад, «.jpg»).',
-'empty-file' => 'Ð\9dаÑ\87Ñ\96Ñ\82аный файл є порожнїй.',
-'file-too-large' => 'Ð\9dаÑ\87Ñ\96Ñ\82аный файл є барз великый.',
+'filetype-banned-type' => "'''â\80\9e.$1â\80\9c''' {{PLURAL:$4|Ñ\94 недозволенÑ\8bй Ñ\84оÑ\80маÑ\82 Ñ\84айлÑ\96в|Ñ\81Ñ\83Ñ\82Ñ\8c недозволены форматы файлів}}.
+{{PLURAL:$3|Ð\94озволенÑ\8bй Ñ\84оÑ\80маÑ\82 Ñ\84алÑ\96в|Ð\94озволены форматы файлів суть}} $2.",
+'filetype-missing' => 'Файл не маÑ\94 Ñ\80оÑ\81Ñ\88Ñ\8bÑ\80Ñ\97ня (наприклад, «.jpg»).',
+'empty-file' => 'Ð\97аладованый файл є порожнїй.',
+'file-too-large' => 'Ð\97аладованый файл є барз великый.',
'filename-tooshort' => 'Назва файлу є барз коротка.',
'filetype-banned' => 'Тот тіп файлу є заказаный.',
'verification-error' => 'Тот файл не перешов овіринём файлів.',
'hookaborted' => 'Пожадована вами зміна была одмітнута дакотрым росшырінём.',
-'illegal-filename' => 'ТоÑ\82а назва Ñ\84айлÑ\83 не Ñ\94 поволена.',
-'overwrite' => 'Ð\9dе Ñ\94 доволене переписати екзістуючій файл.',
-'unknown-error' => 'Ð\94Ñ\96Ñ\88ло кÑ\83 незнамÑ\96й Ñ\85Ñ\8bбÑ\96.',
-'tmp-create-error' => 'Ð\9dе подаÑ\80ило ся створити дочасный файл.',
+'illegal-filename' => 'ТоÑ\82а назва Ñ\84айлÑ\83 не Ñ\94 дозволена.',
+'overwrite' => 'Ð\9dе Ñ\81лободно переписати екзістуючій файл.',
+'unknown-error' => 'ТÑ\80аÑ\84ила Ñ\81Ñ\8f незнама Ñ\85Ñ\8bба.',
+'tmp-create-error' => 'Ð\9dе вдало ся створити дочасный файл.',
'tmp-write-error' => 'Хыба запису до дочасного файлу.',
'large-file' => 'Ся рекомендує, жебы довжка файлу непересяговала $1, тот файл має $2.',
'largefileserver' => 'Розмір файлу є векшый як ліміт наставленый на сервері.',
'filepageexists' => "Пописова сторінка про файл з тов назвов уж была на '''<tt>[[:$1]]</tt>''' створена, але одповідаючій файл дотеперь не екзістує.
Згорнутя, котре ту зазначіте, ся на пописовій сторінцї не зобразить.
Кідь там хочете своє згорнутя зобразити, будете мусити дану сторінку едітовати мануално. [[$1|thumb]]",
-'fileexists-extension' => "Уже екзістує файл з подобным меном: [[$2|thumb]]
-* Ð\9dазва наÑ\87Ñ\96Ñ\82аного файлу: '''<tt>[[:$1]]</tt>'''
+'fileexists-extension' => "Уже екзістує файл з подобным іменом: [[$2|thumb]]
+* Ð\9dазва заладованого файлу: '''<tt>[[:$1]]</tt>'''
* Назва екзістуючого файлу: '''<tt>[[:$2]]</tt>'''
Выберте іншу назву.",
-'fileexists-thumbnail-yes' => "Тот файл є асі образок в зменшеній великости ''(нагляд)''. [[$1|thumb]]
+'fileexists-thumbnail-yes' => "Тот файл є асі образчік в зменшеній великости ''(нагляд)''. [[$1|thumb]]
Перевірте файл '''<tt>[[:$1]]</tt>'''.
-Ð\9fокÑ\8b Ñ\94 вказанÑ\8bй Ñ\84айл векÑ\88Ñ\8bй, але Ñ\96накÑ\88е Ñ\80овнакÑ\8bй, не Ñ\94 Ñ\82Ñ\80еба окÑ\80емо наÑ\87Ñ\96Ñ\82ати ёго зменшену верзію.",
+Ð\9aÑ\96дÑ\8c Ñ\94 вказанÑ\8bй Ñ\84айл векÑ\88Ñ\8bй, але Ñ\96накÑ\88е Ñ\94днакÑ\8bй, не Ñ\82Ñ\80еба окÑ\80емо заладовати ёго зменшену верзію.",
'file-thumbnail-no' => "Назва файлу ся зачінать на '''<tt>$1</tt>'''.
-Може є то образок в зменшеній великости ''(нагляд)''.
-Ð\9dаÑ\87Ñ\96Ñ\82айте файл в повнім розлишіню, покы є ку діспозіції, або зміньте назву файлу.",
-'fileexists-forbidden' => 'Файл з Ñ\82аков назвов Ñ\83ж екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\94 Ñ\96 не Ñ\94 поволене го переписати.
-Ð\9aÑ\96дÑ\8c Ñ\85оÑ\87еÑ\82е Ñ\82оÑ\82 Ñ\84айл наÑ\87Ñ\96Ñ\82ати, вернийте ся і звольте іншу назву.
+Може є то образчік в зменшеній великости ''(нагляд)''.
+Ð\97аладÑ\83йте файл в повнім розлишіню, покы є ку діспозіції, або зміньте назву файлу.",
+'fileexists-forbidden' => 'Файл з Ñ\82аков назвов Ñ\83ж екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\94 Ñ\96 не Ñ\94 дозволено го переписати.
+Ð\9aÑ\96дÑ\8c Ñ\85оÑ\87еÑ\82е Ñ\82оÑ\82 Ñ\84айл заладовати, вернийте ся і звольте іншу назву.
[[File:$1|thumb|center|$1]]',
-'fileexists-shared-forbidden' => 'Файл з тов назвов уж екзістує в здїлянім усховіщу. Кідь і наперек тому хочете ваш файл начітати, вернийте ся і звольте іншу назву. [[File:$1|thumb|center|$1]]',
+'fileexists-shared-forbidden' => 'Файл з тов назвов уж екзістує в сполочнім усховищу. Кідь і наперек тому хочете ваш файл заладовати, вернийте ся і звольте іншу назву. [[File:$1|thumb|center|$1]]',
'file-exists-duplicate' => 'Тот файл є дуплікат {{PLURAL:$1|файлу|такых файлів}}:',
-'file-deleted-duplicate' => 'Ð\86денÑ\82Ñ\96Ñ\87нÑ\8bй Ñ\84айл кÑ\83 Ñ\82омÑ\83 ([[:$1]]) бÑ\8bв Ñ\83ж Ñ\81коÑ\80е змазанÑ\8bй. Ð\9fеÑ\80едÑ\82Ñ\8bм Ñ\8fк Ñ\84айл зновÑ\83 нагÑ\80аєте, бы сьте мали перевірити записы о попереднёму змазаню.',
-'uploadwarning' => 'Увага пÑ\80о наÑ\87Ñ\96Ñ\82анÑ\8f',
+'file-deleted-duplicate' => 'Ð\86денÑ\82Ñ\96Ñ\87нÑ\8bй Ñ\84айл кÑ\83 Ñ\82омÑ\83 ([[:$1]]) бÑ\8bв Ñ\83ж Ñ\81коÑ\80е змазанÑ\8bй. Ð\9fеÑ\80едÑ\82Ñ\8bм Ñ\8fк Ñ\84айл зновÑ\83 заладÑ\83єте, бы сьте мали перевірити записы о попереднёму змазаню.',
+'uploadwarning' => 'Ð\9fозÑ\96Ñ\80Ñ\8c к заладованÑ\8e',
'uploadwarning-text' => 'Просиме, зміньте опис файлу ниже і спробуйте то знову.',
'savefile' => 'Уложыти файл',
-'uploadedimage' => 'нагÑ\80ав "[[$1]]"',
-'overwroteimage' => 'наÑ\87Ñ\96Ñ\82ана нова верзія "[[$1]]"',
-'uploaddisabled' => 'Ð\9dаÑ\87Ñ\96Ñ\82анÑ\8f Ñ\84айлÑ\96в забоÑ\80онене',
-'copyuploaddisabled' => 'Ð\9dаÑ\87Ñ\96Ñ\82аня файлів через URL є выпнуте.',
-'uploadfromurl-queued' => 'Ð\92аÑ\88а пожадавка пÑ\80о наÑ\87Ñ\96Ñ\82аня файлу была уложена до фронты.',
-'uploaddisabledtext' => 'Ð\9dаÑ\87Ñ\96Ñ\82аня файлів є выпнуте.',
-'php-uploaddisabledtext' => 'Ð\92 PHP Ñ\94 вÑ\8bпнÑ\83Ñ\82е наÑ\87Ñ\96Ñ\82анÑ\8f Ñ\84айлÑ\96в. Ð\9fÑ\80оÑ\81име, пеÑ\80евÑ\96Ñ\80те наставлїня file_uploads.',
+'uploadedimage' => 'заладовав "[[$1]]"',
+'overwroteimage' => 'заладована нова верзія "[[$1]]"',
+'uploaddisabled' => 'Ð\97аладовованÑ\8f Ñ\84айлÑ\96в забоÑ\80онене.',
+'copyuploaddisabled' => 'Ð\97аладовованя файлів через URL є выпнуте.',
+'uploadfromurl-queued' => 'Ð\92аÑ\88а пожадавка на заладовованя файлу была уложена до фронты.',
+'uploaddisabledtext' => 'Ð\97аладовованя файлів є выпнуте.',
+'php-uploaddisabledtext' => 'Ð\92 PHP Ñ\94 вÑ\8bпнÑ\83Ñ\82е заладовованÑ\8f Ñ\84айлÑ\96в. Ð\9fÑ\80оÑ\81име, пеÑ\80евÑ\96Ñ\80Ñ\8cте наставлїня file_uploads.',
'uploadscripted' => 'Тот файл обсягує HTML-код або скріпт, якый може быти неправилно інтерпретованый вебовым переглядячом.',
'uploadvirus' => 'Файл обсягує вірус! Детайлы: $1',
'uploadjava' => 'Тот файл є ZIP архів, котрый обсягує .class-файл Java.
-Ð\9dаÑ\87Ñ\96Ñ\82анÑ\8f Java-Ñ\84айлÑ\96в не Ñ\94 доволене, бо они можÑ\83Ñ\82Ñ\8c запÑ\80иÑ\87Ñ\96ниÑ\82и обÑ\85од забезпечіня сістемы.',
+Ð\97аладованÑ\8f Java-Ñ\84айлÑ\96в не Ñ\94 дозволене, бо они можÑ\83Ñ\82Ñ\8c запÑ\80Ñ\96Ñ\87Ñ\96ниÑ\82и обÑ\85од забеÑ\81печіня сістемы.',
'upload-source' => 'Жрідловый файл',
'sourcefilename' => 'Назва жрідлового файлу:',
'sourceurl' => 'Жрідлова URL-адреса:',
'destfilename' => 'Назва цілёвого файлу:',
'upload-maxfilesize' => 'Максімалный розмір файлу: $1',
'upload-description' => 'Попис файлу',
-'upload-options' => 'Ð\9fаÑ\80амеÑ\82Ñ\80Ñ\8b наÑ\87Ñ\96Ñ\82аня',
+'upload-options' => 'Ð\9fаÑ\80амеÑ\82Ñ\80Ñ\8b заладовованя',
'watchthisupload' => 'Слїдовати тот файл',
'filewasdeleted' => 'Файл з таков назвов уж екзістовав а быв змазаный. Детайлы обсягує $1.',
-'filename-bad-prefix' => "Назва файлу, котрый начітавате ся зачінать на '''„$1“''', што не є назва звычайно приряджована діґіталным фотоапаратом. Звольте іншу назву, котра ваш файл попише лїпше.",
-'upload-success-subj' => 'Ð\9dаÑ\87Ñ\96Ñ\82аня было успішне',
-'upload-success-msg' => 'Файл вами наÑ\87Ñ\96Ñ\82аный з [$2] є доступный на [[:{{ns:file}}:$1]]',
-'upload-failure-subj' => 'Проблем з начітаным файлом',
-'upload-failure-msg' => 'У вами наÑ\87Ñ\96Ñ\82аного Ñ\84айлÑ\83 взникнÑ\83в проблем з [$2]::
+'filename-bad-prefix' => "Назва заладовованого файлу ся зачінать на '''„$1“''', што не є назва звычайно приряджована діґіталным фотоапаратом. Звольте іншу назву, котра ваш файл попише лїпше.",
+'upload-success-subj' => 'Ð\97аладованя было успішне',
+'upload-success-msg' => 'Файл вами заладованый з [$2] є доступный на [[:{{ns:file}}:$1]]',
+'upload-failure-subj' => 'Проблем із заладованём',
+'upload-failure-msg' => 'У вами заладованого Ñ\84айлÑ\83 вÑ\8bник проблем з [$2]::
$1',
-'upload-warning-subj' => 'Увага пÑ\80о наÑ\87Ñ\96Ñ\82анÑ\8f',
-'upload-warning-msg' => 'Почас вашого начітаваня файлу [$2] ся став проблем. Кідь го хочете вырїшыти, можете ся вернути до [[Special:Upload/stash/$1|формуларя начітаваня]].',
+'upload-warning-subj' => 'Ð\9fозÑ\96Ñ\80Ñ\8c к заладованÑ\8e',
+'upload-warning-msg' => 'Під час вашого заладовованя файлу [$2] ся притрафив проблем. Кідь го хочете вырїшыти, можете ся вернути до [[Special:Upload/stash/$1|формуларя заладовованя]].',
'upload-proto-error' => 'Неплатный протокол',
-'upload-proto-error-text' => 'Ð\9dагÑ\80анÑ\8f вздаленого файлу пожадує зазначіня URLs з початком <code>http://</code> або <code>ftp://</code>.',
+'upload-proto-error-text' => 'Ð\97аладованÑ\8f одлеглого файлу пожадує зазначіня URLs з початком <code>http://</code> або <code>ftp://</code>.',
'upload-file-error' => 'Інтерна хыба',
-'upload-file-error-text' => 'Почас спробы створїня дочасного файлу настала внутрїшня хыба на сервері.
+'upload-file-error-text' => 'При спробі створити дочасный файл настала внутрїшня хыба на серверї.
Просиме контактуйте [[Special:ListUsers/sysop|адміністратора]].',
'upload-misc-error' => 'Незнама хыба',
-'upload-misc-error-text' => 'Ð\9dезнама Ñ\85Ñ\8bба наÑ\81Ñ\82ала поÑ\87аÑ\81 нагÑ\80аванÑ\8f Ñ\84айлÑ\83. Ð\9fеÑ\80евÑ\96Ñ\80Ñ\82е Ñ\86Ñ\96 Ñ\94 URL плаÑ\82на і приступна і спробуйте то знову. Кідь ся хыба обявить знову, контактуйте [[Special:ListUsers/sysop|адміністратора]]. сістемы.',
+'upload-misc-error-text' => 'Ð\9dезнана Ñ\85Ñ\8bба Ñ\81Ñ\8f Ñ\82Ñ\80аÑ\84ила пÑ\96д Ñ\87аÑ\81 заладованÑ\8f Ñ\84айлÑ\83. Ð\9fеÑ\80евÑ\96Ñ\80Ñ\82е Ñ\86Ñ\96 Ñ\94 URL пÑ\80авилна і приступна і спробуйте то знову. Кідь ся хыба обявить знову, контактуйте [[Special:ListUsers/sysop|адміністратора]]. сістемы.',
'upload-too-many-redirects' => 'URL обсягує барз велё напрямлінь',
'upload-unknown-size' => 'Незнамый розмір',
-'upload-http-error' => 'Ð\94Ñ\96Ñ\88ло кÑ\83 Ñ\85Ñ\8bбÑ\96 HTTP: $1',
-'upload-copy-upload-invalid-domain' => 'Ð\9dаÑ\87Ñ\96Ñ\82аня копірованём неможливе з той домены.',
+'upload-http-error' => 'СÑ\82ала Ñ\81Ñ\8f Ñ\85Ñ\8bба HTTP: $1',
+'upload-copy-upload-invalid-domain' => 'Ð\97аладовованя копірованём неможливе з той домены.',
# File backend
'backend-fail-stream' => 'Не вдало ся транслёвати файл $1.',
'backend-fail-readonly' => 'Кінцёва уложна сістема „$1“ моментално лем на чітаня. Прічіна: „$2“',
'backend-fail-synced' => 'Файл "$1" в кінцёвій уложній сістемі в неконзістентнім стані',
'backend-fail-connect' => 'Не вдало ся припоїти до кінцёвой уложной сістемы „$1“.',
-'backend-fail-internal' => 'В кінцёвій уложній сістемі „$1“ дішло к незнаній хыбі.',
-'backend-fail-usable' => 'Не вдало ся записати до файлу $1 про недостаточны права або хыбуючі адресарї/контайнеры.',
+'backend-fail-internal' => 'В кінцёвій уложній сістемі „$1“ ся стала незнама хыба.',
+'backend-fail-contenttype' => 'Не годно было становити тіп обсягу файлу, жебы уложыти го до „$1“.',
+'backend-fail-batchsize' => 'Кінцёве усховище прияло блок з $1 {{PLURAL:файловов операціов|файловыма операціями}};максімум є {{PLURAL:$2|$2}}.',
+'backend-fail-usable' => 'Не вдало ся записати до файлу $1 про брак прав або хыбуючі адресарї/контайнеры.',
+
+# File journal errors
+'filejournal-fail-dbconnect' => 'Не годен ся припоїти к журналовій датабазї усховища «$1».',
+'filejournal-fail-dbquery' => 'Не вдало ся актуалізовати журналову датабазу усховища «$1».',
+
+# Lock manager
+'lockmanager-notlocked' => 'Файл „$1“ не годен одокмнути, бо не є замкнутый.',
+'lockmanager-fail-closelock' => 'Файл із замком про „$1“ не годен заперти.',
+'lockmanager-fail-deletelock' => 'Файл із замком про „$1“ не годен змазати.',
+'lockmanager-fail-acquirelock' => 'Не годен здобыти замок про „$1“.',
+'lockmanager-fail-openlock' => 'Файл із замком про „$1“ не годен отворити.',
+'lockmanager-fail-releaselock' => 'Не годен увольнити замок про „$1“.',
+'lockmanager-fail-db-bucket' => 'Не годен навязати споїня з достаточнов кількостёв датабаз замків в сеґментї $1.',
+'lockmanager-fail-db-release' => 'Замкнутя датабазы $1 не вдало ся увольнити.',
+'lockmanager-fail-svr-acquire' => 'Не вдало ся здобыти замок сервера $1.',
+'lockmanager-fail-svr-release' => 'Замкнутя сервера $1 не вдало ся увольнити.',
# ZipDirectoryReader
'zip-file-open-error' => 'При одкрытю ZIP-архіву про ёго перевірку выникла хыба.',
'zip-wrong-format' => 'Вказаный файл не є ZIP-файлом',
'zip-bad' => 'ZIP-файл є пошкодженый ці в іншый способ непридатный про чітаня.
-Не годен перевірити ёго безпечность.',
+Не годен перевірити ёго беспеку.',
'zip-unsupported' => 'Файл хоснує такы можности ZIP, якы MediaWiki не підпорує.
-Не годен перевірити ёго безпечность.',
+Не годен перевірити ёго беспеку.',
# Special:UploadStash
-'uploadstash' => 'Ð\9dагÑ\80аÑ\82и Ñ\81кÑ\80Ñ\8bÑ\88Ñ\83',
-'uploadstash-summary' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка додаваÑ\82Ñ\8c пÑ\80иÑ\81Ñ\82Ñ\83п кÑ\83 Ñ\84айлом коÑ\82Ñ\80Ñ\8b Ñ\81Ñ\83Ñ\82Ñ\8c нагÑ\80аÑ\82Ñ\8b (або нагÑ\80аванÑ\8f Ñ\96Ñ\89Ñ\96 не Ñ\81кÑ\96нÑ\87Ñ\96ло) але Ñ\96Ñ\89Ñ\96 не бÑ\8bли пÑ\83влÑ\96кованÑ\8b на вÑ\96кÑ\96. ТоÑ\82Ñ\8b Ñ\84айлÑ\8b не видиÑ\82Ñ\8c ниÑ\85Ñ\82о окÑ\80ем Ñ\85оÑ\81новаÑ\82елÑ\8f Ñ\88Ñ\82о Ñ\97Ñ\85 нагÑ\80ав.',
+'uploadstash' => 'СкÑ\80Ñ\8bÑ\88а заладованÑ\8bÑ\85 Ñ\84айлÑ\96в',
+'uploadstash-summary' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка додаваÑ\82Ñ\8c пÑ\80иÑ\81Ñ\82Ñ\83п кÑ\83 Ñ\84айлом коÑ\82Ñ\80Ñ\8b Ñ\81Ñ\83Ñ\82Ñ\8c заладованÑ\8b (або ладованÑ\8f Ñ\96Ñ\89Ñ\96 не Ñ\81кÑ\96нÑ\87Ñ\96ло) але Ñ\96Ñ\89Ñ\96 не бÑ\8bли опÑ\83влÑ\96кованÑ\8b на вÑ\96кÑ\96. ТоÑ\82Ñ\8b Ñ\84айлÑ\8b не видиÑ\82Ñ\8c ниÑ\85Ñ\82о окÑ\80ем Ñ\85оÑ\81новаÑ\82елÑ\8f Ñ\88Ñ\82о Ñ\97Ñ\85 заладовав.',
'uploadstash-clear' => 'Змазати схованы файлы',
'uploadstash-nofiles' => 'Не маєте жадны схованы файлы.',
'uploadstash-badtoken' => 'Выконаня той дїї не было успішне, може зато, же вашы повірїня про едітованя скінчіли. Попробуйте знову.',
'uploadstash-errclear' => 'Змазаня файлів не было успішне.',
'uploadstash-refresh' => 'Обновити список файлів',
+'invalid-chunk-offset' => 'Неприступный посув фраґмента.',
# img_auth script messages
'img-auth-accessdenied' => 'Приступ одопертый',
'img-auth-nopathinfo' => 'Ваш сервер не є наштелёваный так, жебы давав тоту інформацію.
Може фунґує помочов CGI і img_auth на нім не може фунґовати.
Посмотьте https://www.mediawiki.org/wiki/Manual:Image_Authorization.',
-'img-auth-notindir' => 'Пожадована стежка не є в конфіґурованім адресарю з начітаныма файлами.',
+'img-auth-notindir' => 'Пожадована стежка не є в конфіґурованім адресарю із заладованыма файлами.',
'img-auth-badtitle' => 'З „$1“ ся не дасть створити платна назва сторінкы.',
'img-auth-nologinnWL' => 'Не сьте приголошеный і „$1“ не є на білім списку.',
'img-auth-nofile' => 'Файл «$1» не екзістує.',
'img-auth-isdir' => 'Пробуєте приступовати до адресаря „$1“.
-Доволеный є лем приступ к файлам.',
+Ð\94озволенÑ\8bй Ñ\94 лем пÑ\80иÑ\81Ñ\82Ñ\83п к Ñ\84айлам.',
'img-auth-streaming' => 'Переношать ся „$1“.',
-'img-auth-public' => 'Помочов img_auth.php ся поскытують файлы на пріватных вікі.
+'img-auth-public' => 'Помочов img_auth.php ся придавають файлы з пріватных вікі.
Тота вікі є наставлена як публічна.
-З безпечностных причін є img_auth.php выпнуте.',
+З беспечностных прічін є img_auth.php выпнуте.',
'img-auth-noread' => 'Хоснователь не має приступ про чітаня „$1“.',
'img-auth-bad-query-string' => 'URL обсягує неправилный одказ.',
# HTTP errors
'http-invalid-url' => 'Неправилне URL: $1',
'http-invalid-scheme' => 'URL хоснуючі схемы „$1“ не суть підпорованы.',
-'http-request-error' => 'Ð\9dезнама Ñ\85Ñ\8bба поÑ\87аÑ\81 одоÑ\81Ñ\8bланÑ\8f пожадавкы.',
-'http-read-error' => 'Хыба почас чітаня HTTP.',
+'http-request-error' => 'Ð\9dезнана Ñ\85Ñ\8bба пÑ\80и одоÑ\81Ñ\8bланÑ\8e пожадавкы.',
+'http-read-error' => 'Хыба чітаня HTTP.',
'http-timed-out' => 'Час про HTTP пожадавкы уплинув.',
'http-curl-error' => 'Хыба при чітаню з URL: $1',
-'http-host-unreachable' => 'Ð\9dе подаÑ\80ило Ñ\81Ñ\8f конÑ\82акÑ\80овати URL.',
-'http-bad-status' => 'Почас HTTP пожадавкы настав проблем: $1 $2',
+'http-host-unreachable' => 'Ð\9dе вдало Ñ\81Ñ\8f доÑ\81Ñ\8fгнÑ\83ти URL.',
+'http-bad-status' => 'Під час HTTP пожадавкы притрафив ся проблем: $1 $2',
# Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
-'upload-curl-error6' => 'Ð\9dе подаÑ\80ило ся досягнути URL.',
+'upload-curl-error6' => 'Ð\9dе вдало ся досягнути URL.',
'upload-curl-error6-text' => 'Із зазначеной URL ся не дасть чітати. Перевірте ці є URL правилно написана і сервер є доступный.',
-'upload-curl-error28' => 'ЧаÑ\81 пÑ\80о нагÑ\80аванÑ\8f Ñ\83плинÑ\83в',
+'upload-curl-error28' => 'ЧаÑ\81 вÑ\8bдÑ\97ленÑ\8bй на заладованÑ\8f Ñ\83ж вÑ\8bÑ\87еÑ\80Ñ\8cпанÑ\8bй',
'upload-curl-error28-text' => 'Сервер довго не одповідать. Перевірте ці є доступный і кус почекайте і спробуйте то знову.',
'license' => 'Ліценцованя:',
'upload_source_file' => ' (файл на вашім компютерї)',
# Special:ListFiles
-'listfiles-summary' => 'ТоÑ\82а Ñ\88пеÑ\86Ñ\96ална Ñ\81Ñ\82оÑ\80Ñ\96нка зобÑ\80ажÑ\83Ñ\94 вÑ\88Ñ\8bÑ\82кÑ\8b наÑ\87Ñ\96Ñ\82аны файлы.
-Ð\9fÑ\80и Ñ\84Ñ\96лÑ\82Ñ\80ованÑ\8e подлÑ\8f Ñ\85оÑ\81новаÑ\82елÑ\8f Ñ\81Ñ\8f зобÑ\80ажÑ\83Ñ\8eÑ\82Ñ\8c лем Ñ\84айлÑ\8b, Ñ\83 коÑ\82Ñ\80Ñ\8bÑ\85 Ñ\85оÑ\81новаÑ\82елÑ\8c наÑ\87Ñ\96Ñ\82ав актуалну верзію.',
+'listfiles-summary' => 'ТоÑ\82а Ñ\88пеÑ\86Ñ\96ална Ñ\81Ñ\82оÑ\80Ñ\96нка зобÑ\80ажÑ\83Ñ\94 вÑ\88Ñ\8bÑ\82кÑ\8b заладованы файлы.
+Ð\9fÑ\80и Ñ\84Ñ\96лÑ\82Ñ\80ованÑ\8e за Ñ\85оÑ\81новаÑ\82елÑ\91м, зобÑ\80ажÑ\83Ñ\8eÑ\82Ñ\8c Ñ\81Ñ\8f лем Ñ\84айлÑ\8b, Ñ\83 коÑ\82Ñ\80Ñ\8bÑ\85 Ñ\85оÑ\81новаÑ\82елÑ\8c заладовав актуалну верзію.',
'listfiles_search_for' => 'Глядати файл по назві:',
'imgfile' => 'файл',
'listfiles' => 'Список файлів',
'filehist-filesize' => 'Розмір файлу',
'filehist-comment' => 'Коментарь',
'filehist-missing' => 'Файл хыбіть',
-'imagelinks' => 'Ð\92Ñ\8bÑ\83жÑ\8bÑ\82я файлу',
+'imagelinks' => 'ХоÑ\81нованя файлу',
'linkstoimage' => '{{PLURAL:$1|Далша сторінка ся одказує|Далшы сторінкы ся одказують}} на тот файл:',
'linkstoimage-more' => 'На тот файл {{PLURAL:$1|одказує веце сторінок|одказує веце як $1 сторінок|одказує веце як $1 сторінок}}.
Наслїдуючій список зображує лем {{PLURAL:$1|тоту першу|першы $1|першых $1}}.
Веце інформацій обсягує ёго [$2 сторінка з пописом файлу].',
'sharedupload-desc-here' => 'Тот файл походить з {{grammar:2sg|$1}} і можуть го хосновати другы проєкты.
Ниже суть зображены інформації, котры обсягує ёго [$2 сторінка з пописом файлу].',
+'sharedupload-desc-edit' => 'Гевсесь файл походить з {{grammar:2sg|$1}} тай можуть го хосновати другы проєкты.
+Може хочете управити [$2 тамтушню строрінку з пописом файлу].',
+'sharedupload-desc-create' => 'Гевсесь файл походить з {{grammar:2sg|$1}} тай можуть го хосновати другы проєкты.
+Може бы сьте хотїли правити [$2 тамтушню сторінку з пописом файлу].',
'filepage-nofile' => 'Не екзістує файл з таков назвов',
-'filepage-nofile-link' => 'Файл з Ñ\82аков назвов не екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\94, але можеÑ\82е [$1 го наÑ\87Ñ\96Ñ\82ати].',
-'uploadnewversion-linktext' => 'Ð\9dагÑ\80ати нову верзію того файлу',
+'filepage-nofile-link' => 'Файл з Ñ\82аков назвов не екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\94, але можеÑ\82е [$1 го заладовати].',
+'uploadnewversion-linktext' => 'Ð\97аладовати нову верзію того файлу',
'shared-repo-from' => 'з $1',
'shared-repo' => 'здїляного усховіща',
'filerevert-legend' => 'Вернути назад файл',
'filerevert-intro' => "Вертате назад '''[[Media:$1|$1]]''' на [$4 верзію з $3 $2].",
'filerevert-comment' => 'Причіна:',
-'filerevert-defaultcomment' => 'Ð\9dавеÑ\80нÑ\83Ñ\82а веÑ\80зÑ\96Ñ\8f нагÑ\80ана в $2 дня $1.',
+'filerevert-defaultcomment' => 'Ð\9dавеÑ\80нÑ\83Ñ\82а веÑ\80зÑ\96Ñ\97 з $2 дня $1.',
'filerevert-submit' => 'Вернути назад',
'filerevert-success' => "Файл '''[[Media:$1|$1]]''' быв вернутый назад на [$4 верзію з $3 $2].",
'filerevert-badversion' => 'Не є доступна попередня верзія того файлу з одоповідаючов часовов значков.',
# MIME search
'mimesearch' => 'Гляданя по MIME',
-'mimesearch-summary' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка Ñ\83можнÑ\8eÑ\94 Ñ\84Ñ\96лÑ\82Ñ\80оваÑ\82и Ñ\84айлÑ\8b подлÑ\8f Ñ\82Ñ\96пÑ\83 MIME.<br />
+'mimesearch-summary' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка Ñ\83можнÑ\8eÑ\94 Ñ\84Ñ\96лÑ\82Ñ\80оваÑ\82и Ñ\84айлÑ\8b за Ñ\82Ñ\96пом MIME.<br />
Вступ: <code>тіп обсягу/підтіп</code>, наприклад <code>image/jpeg</code>.',
'mimetype' => 'MIME-тіп:',
'download' => 'скачати',
'unusedtemplateswlh' => 'іншы одказы',
# Random page
-'randompage' => 'Ð\9dагодна статя',
+'randompage' => 'ТÑ\80аÑ\84Ñ\83нкова статя',
'randompage-nopages' => 'Не є сторінок в {{PLURAL:$2|просторі назв|просторах назв}} $1.',
# Random redirect
-'randomredirect' => 'Ð\9dагодне напрямлїня',
+'randomredirect' => 'ТÑ\80аÑ\84Ñ\83нкове напрямлїня',
'randomredirect-nopages' => 'Простор назв „$1“ не обсягує жадны напрямлїня.',
# Statistics
'statistics-articles' => 'Обсяговы сторінкы',
'statistics-pages' => 'Сторінкы',
'statistics-pages-desc' => 'Вшыткы сторінкы на вікі враховано діскузій, напрямлїня ітд.',
-'statistics-files' => 'Ð\9dаÑ\87Ñ\96Ñ\82аны файлы',
+'statistics-files' => 'Ð\97аладованы файлы',
'statistics-edits' => 'Чісло едітованя од основаня вікі {{SITENAME}}',
'statistics-edits-average' => 'Середнє чісло едітовань на сторінку',
'statistics-views-total' => 'Вшыткых переглядів',
'disambiguations' => 'Сторінкы одказуючі на богатозначны статї',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "Одказы на наслїдуючіх сторінках ведуть на '''богатозначны сторінкы'''. (сторінкы котры обсягують дакотру з тых шаблон на [[MediaWiki:Disambiguationspage|списку шаблон про богатозначны сторінкы]]) намісто на дану статю.",
+'disambiguations-text' => "Слїдуючі сторінкы включають найменше єден одказ на '''чеперушку'''.
+Асі намісто того мали бы одказовати на конкретнїшу сторінку.<br />
+Сторінка є тримана за чеперушку, кідь хоснує дакотру із шаблон одказованых на [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Двоїты напрямлїня',
'doubleredirectstext' => 'На тій сторінцї є список напрямлїн ведучіх на далшы напрямлїня.
'wantedpages' => 'Пожадованы статї',
'wantedpages-badtitle' => 'Резултаты обсягують неправилну назву: $1',
'wantedfiles' => 'Жаданы файлы',
+'wantedfiletext-cat' => 'Наступны файлы ся хоснують але не існують. Файл з одлеглых усховищ гев можуть быти написасны, бо існують. Такы фалешны позітіва будуть зображены <del>перечаркнути</del>. Сторінкы, котры включають неіснуючі файлы суть іщі к тому написаны на [[:$1]].',
+'wantedfiletext-nocat' => 'Насупны файлы ся хоснують але не існують. Файлы з одлеглых усховищ гев можуть быти написаны, ай наперек тому же існують. Такы фалешны позітіва будуть зображены <del>перечаркнуты</del>.',
'wantedtemplates' => 'Хыблячі шаблоны',
'mostlinked' => 'Найодказованїшы сторінкы',
'mostlinkedcategories' => 'Найхоснованїшы катеґорії',
'mostimages' => 'Найужыванїшы файлы',
'mostrevisions' => 'Сторінкы з найвеце ревізіями',
'prefixindex' => 'Вшыткы сторінкы з початком назв',
+'prefixindex-namespace' => 'Вшыткы сторінкы з префіксом (простор назв $1)',
'shortpages' => 'Курты статї',
'longpages' => 'Найдовшы статті',
'deadendpages' => 'Слїпы сторінкы',
'protectedpagestext' => 'Наслїдуючі сторінкы суть замкнуты або напів замкнуты про едітованя або переменованя',
'protectedpagesempty' => 'Жадна сторінка не є замкнута з тыма параметрами.',
'protectedtitles' => 'Замкнуты назвы сторінок',
-'protectedtitlestext' => 'Наслїдуючі назвы суть замкнуты і не доволены про сторінкы',
+'protectedtitlestext' => 'Ð\9dаÑ\81лÑ\97дÑ\83Ñ\8eÑ\87Ñ\96 назвÑ\8b Ñ\81Ñ\83Ñ\82Ñ\8c замкнÑ\83Ñ\82Ñ\8b Ñ\96 не дозволенÑ\8b пÑ\80о Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b',
'protectedtitlesempty' => 'Жадна назва не є замкнута з тыма параметрами.',
'listusers' => 'Список хоснователїв',
'listusers-editsonly' => 'Вказати лем хоснователїв з едітованями',
-'listusers-creationsort' => 'СоÑ\80Ñ\82оваÑ\82и подлÑ\8f даÑ\82Ñ\83мÑ\83 створїня',
+'listusers-creationsort' => 'СоÑ\80Ñ\82оваÑ\82и за даÑ\82Ñ\83мом створїня',
'usereditcount' => '$1 {{PLURAL:$1|едітованя|едітованя|едітовань}}',
'usercreated' => '{{GENDER:$3|Реґістрованый|Реґістрована|Реґістрованый(а)}} $1 в $2',
'newpages' => 'Новы сторінкы',
Зображіня можете зужыти выбером тіпу запису, мена хоснователя (залежыть на великости букв) або зазначеной сторінкы (тыж залежыть на великости букв).',
'logempty' => 'Протокол не обсягує жаден одповідаючій запис.',
'log-title-wildcard' => 'Глядати назвы зачінаючі ся з тым текстом',
+'showhideselectedlogentries' => 'Вказати/сховати зволены записы лоґу.',
# Special:AllPages
'allpages' => 'Вшыткы сторінкы',
'allpagesnext' => 'Далшы',
'allpagessubmit' => 'Выконати',
'allpagesprefix' => 'Вказати сторінкы што ся зачінають на:',
-'allpagesbadtitle' => 'Задана назва сторінкы не была платна або обсяговала префікс міджіязыкового або міджівікі одказу. Може обсяговав буквы, котры не суть доволены.',
+'allpagesbadtitle' => 'Задана назва сторінкы не была правилна або обсяговала префікс міджіязыкового або міджівікі одказу. Може обсяговав буквы, котры не суть дозволены.',
'allpages-bad-ns' => '{{SITENAME}} не має простору назв «$1».',
+'allpages-hide-redirects' => 'Сховати напрямлїня',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Позерати собі кешовану верзію той сторінкы, котра може быти стара аж $1.',
+'cachedspecial-viewing-cached-ts' => 'Позерати собі кешовану верзію той сторінкы, котра могла стратити актуалность.',
+'cachedspecial-refresh-now' => 'Вказати найновшы.',
# Special:Categories
'categories' => 'Катеґорії',
Невказаны суть то [[Special:UnusedCategories|нехоснованы катеґорії]].
Посмотьте ся тыж на [[Special:WantedCategories|жаданы катеґорії]].',
'categoriesfrom' => 'Вказати сторінкы, што ся зачінають на:',
-'special-categories-sort-count' => 'Ñ\83поÑ\80Ñ\8fдковаÑ\82и подлÑ\8f множеÑ\81Ñ\82ва',
+'special-categories-sort-count' => 'Ñ\83поÑ\80Ñ\8fдковаÑ\82и за кÑ\96лÑ\8cкоÑ\81Ñ\82Ñ\91в',
'special-categories-sort-abc' => 'упорядковати за алфавітом',
# Special:DeletedContributions
# E-mail user
'mailnologin' => 'Без адресы одосланя',
'mailnologintext' => 'Кідь хочете посылати ел. пошту іншым хоснователям, мусите ся [[Special:UserLogin|приголосити]] і мати платну адресу ел. пошты в своїм [[Special:Preferences|наставлїню]].',
-'emailuser' => 'Послати е-маіл тому хоснователёви',
+'emailuser' => 'Послати імейл тому хоснователёви',
'emailpage' => 'Пошлийте е-пошту',
'emailpagetext' => 'Помочов ниже зображеного формуларя можете тому хоснователёви послати повідомлїня ел. поштов.
Адреса ел. пошты, котру мате зазначену в [[Special:Preferences|наставлїня]],ся обявить як адреса одосылателя пошты, жебы вам адресат міг одповісти прямо.',
'usermaildisabledtext' => 'Не маєте право одосылати ел. пошту іншым хоснователям той вікі',
'noemailtitle' => 'Без адресы ел. пошты',
'noemailtext' => 'Тот хоснователь не зазначів платну адресу ел. пошты.',
-'nowikiemailtitle' => 'Ел. пошта не є доволена',
+'nowikiemailtitle' => 'Ð\95л. поÑ\88Ñ\82а не Ñ\94 дозволена',
'nowikiemailtext' => 'Тот хоснователь собі не желать діставати пошту од іншых хоснователїв.',
'emailnotarget' => 'Неекзістуюче або некоректне імя хоснователя.',
'emailtarget' => 'Уведьте імя хоснователя-адресата',
# Displayed when you click the "watch" button and it is in the process of watching
'watching' => 'Придаваня до списку слїдованя...',
'unwatching' => 'Одобратя зо списку слїдованя...',
-'watcherrortext' => 'При змінї слїдованой сторінкы „$1“ дішло ку хыбі.',
+'watcherrortext' => 'При змінї слїдованой сторінкы „$1“ ся стала хыба.',
'enotif_mailer' => 'Засылач нотіфікацій {{grammar:2sg|{{SITENAME}}}}',
'enotif_reset' => 'Означіти вшытко як навщівене',
# Delete
'deletepage' => 'Змазати сторінку',
-'confirm' => 'Підтверджіня',
+'confirm' => 'Потверджіня',
'excontent' => 'обсяг быв: „$1“',
'excontentauthor' => 'обсяг быв: „$1“ (і єдиным приспівателём быв „[[Special:Contributions/$2|$2]]“)',
'exbeforeblank' => 'обсяг перед выпорожнїнём быв: „$1“',
'delete-legend' => 'Вымазати',
'historywarning' => "'''Варованя:''' Сторінка, котру хочете змазати, має історію з приближно $1 {{plural:$1|ревізії|ревізіями}}:",
'confirmdeletetext' => 'Рыхтуєте ся вымазати сторінку і вшыткы єй лоґы едітовань.
-Просиме Вас, підтвердьте, же справды тото хочете зробити, повно розумієте наслїдкы і же робите тото в одповідности з [[{{MediaWiki:Policy-url}}|правилами]].',
+Просиме Вас, потвердьте, же справды тото хочете зробити, повно розумієте наслїдкы і же робите тото в одповідности з [[{{MediaWiki:Policy-url}}|правилами]].',
'actioncomplete' => 'Дїя выконана',
-'actionfailed' => 'Ð\9eпеÑ\80аÑ\86Ñ\96Ñ\8f Ñ\81Ñ\8f не подаÑ\80ила',
+'actionfailed' => 'Ð\9eпеÑ\80аÑ\86Ñ\96Ñ\8f Ñ\81Ñ\8f не вдала',
'deletedtext' => '"$1" было змазане.
Смоть $2 про список послїднїх змазань.',
'dellogpage' => 'Лоґ вымазаня',
** Порушїня авторьскых прав
** Вандалізм',
'delete-edit-reasonlist' => 'Едітовати причіны вымазаня',
-'delete-toobig' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка маÑ\94 великÑ\83 Ñ\96Ñ\81Ñ\82оÑ\80Ñ\96Ñ\8e едÑ\96Ñ\82ованÑ\8f, Ñ\87еÑ\80ез $1 {{plural:$1|веÑ\80зÑ\96Ñ\97|веÑ\80зÑ\96й|веÑ\80зÑ\96й}}. Ð\9cазанÑ\8f Ñ\82акÑ\8bÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок Ñ\94 обмеджено, жебÑ\8b Ñ\81Ñ\8f пеÑ\80едÑ\96Ñ\88ло нехоченому нарушіню {{grammar:2sg|{{SITENAME}}}}.',
+'delete-toobig' => 'ТоÑ\82а Ñ\81Ñ\82оÑ\80Ñ\96нка маÑ\94 великÑ\83 Ñ\96Ñ\81Ñ\82оÑ\80Ñ\96Ñ\8e едÑ\96Ñ\82ованÑ\8f, Ñ\87еÑ\80ез $1 {{plural:$1|веÑ\80зÑ\96Ñ\97|веÑ\80зÑ\96й|веÑ\80зÑ\96й}}. Ð\9cазанÑ\8f Ñ\82акÑ\8bÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок Ñ\94 обмеджено, жебÑ\8b Ñ\81Ñ\8f забоÑ\80онило нехоченому нарушіню {{grammar:2sg|{{SITENAME}}}}.',
'delete-warning-toobig' => 'Тота сторінка має велику історію едітацій, через $1 {{plural:$1|верзії|верзій|верзій}}. Мазаня такых сторінок може нарушыти датабазовы операцім {{grammar:2sg|{{SITENAME}}}}; мерькуйте.',
# Rollback
'rollback' => 'Вернути назад едітованя',
'rollback_short' => 'Вернути назад',
'rollbacklink' => 'вернути назад',
+'rollbacklinkcount' => 'вернутя $1 {{PLURAL:$1|едітованя|едітовань}} назад',
+'rollbacklinkcount-morethan' => 'вернутя бівше як $1 {{PLURAL:$1|едітованя|едітовань}} назад',
'rollbackfailed' => 'Не годно было ся вернути назад',
'cantrollback' => 'Не годен вернути послїднє едітованя, бо послїднїй приспіватель є єдиным автором той сторінкы.',
'alreadyrolled' => 'Не годен вернути послїднє едітованя [[:$1]] од хоснователя [[User:$2|$2]] ([[User talk:$2|діскузія]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), бо дахто друхый уже сторінку едітовав або вернув тоту зміну назад.
Стисните клапку „назад“, обновте сторінку, з котрой сьте пришли і спробуйте то знову;',
# Protect
-'protectlogpage' => 'Лоґ охраны',
-'protectlogtext' => 'Ниже є уведеный список вшыткых замків і сторінок.
-Ð\9fоÑ\81моÑ\82Ñ\8c [[Special:ProtectedPages|Ñ\81пиÑ\81ок Ñ\85Ñ\80аненых сторінок]]',
+'protectlogpage' => 'Лоґ сокочіня',
+'protectlogtext' => 'Ð\9dиже Ñ\94 Ñ\83ведженÑ\8bй Ñ\81пиÑ\81ок вÑ\88Ñ\8bÑ\82кÑ\8bÑ\85 замкÑ\96в Ñ\96 Ñ\81Ñ\82оÑ\80Ñ\96нок.
+Ð\9fоÑ\81моÑ\82Ñ\8c [[Special:ProtectedPages|Ñ\81пиÑ\81ок Ñ\81окоÑ\87еных сторінок]]',
'protectedarticle' => 'замыкать "[[$1]]"',
'modifiedarticleprotection' => 'зміненa рівень охраны сторінкы «[[$1]]»',
'unprotectedarticle' => 'знята охрана з "[[$1]]"',
'prot_1movedto2' => '«[[$1]]» переменована на «[[$2]]»',
'protect-badnamespace-title' => 'Незамыкательный простор назв',
'protect-badnamespace-text' => 'Сторінкы в тім просторї назв не годен замыкати.',
-'protect-legend' => 'Підтвердити замкнутя',
+'protect-legend' => 'Потвердити замкнутя',
'protectcomment' => 'Причіна:',
'protectexpiry' => 'Кінчіть:',
'protect_expiry_invalid' => 'Неправилный час укончіня',
'protect_expiry_old' => 'Час страты платности є в минулости.',
-'protect-unchain-permissions' => 'Ð\97пÑ\80иÑ\81Ñ\82Ñ\83пниÑ\82и далшы наставлїня замку',
+'protect-unchain-permissions' => 'Ð\9eÑ\82воÑ\80иÑ\82и пÑ\80иÑ\81Ñ\82Ñ\83п на далшы наставлїня замку',
'protect-text' => "Ту можете видїти і змінити рівень охраны сторінкы '''$1'''.",
'protect-locked-blocked' => "Не можете мінити наставлїня замків покы сьте заблокованый. Сучасне наставлїня про тоту сторінку є: '''$1''':",
'protect-locked-dblock' => "Наставлїня замків ся не дасть змінити про замкнуту датабазу.
'protect-locked-access' => "Ваше конто немає права мінити рівень охраны сторінкы.
Моменталны наставлїня про сторінку : '''$1''':",
'protect-cascadeon' => 'Тота сторінка є теперь хранена, бо є загорнута {{PLURAL:$1|до зазначеной сторінкы ніже, на яку|до ниже зазначеных сторінок, на якы}} становлена каскадова охрана. Вы можете змінити рівень охраны той сторінкы, але тото не буде впливати на каскадову охрану.',
-'protect-default' => 'Доволити вшыткым хоснователям',
+'protect-default' => 'Ð\94озволиÑ\82и вÑ\88Ñ\8bÑ\82кÑ\8bм Ñ\85оÑ\81новаÑ\82елÑ\8fм',
'protect-fallback' => 'Портрібны "$1" права',
'protect-level-autoconfirmed' => 'Блоковати новых і незареґістрованых хоснователїв',
'protect-level-sysop' => 'Лем адміністраторы',
'protect-summary-cascade' => 'каскадовый',
'protect-expiring' => 'кінчіть $1 (UTC)',
'protect-expiring-local' => 'кінчіть ся $1',
-'protect-expiry-indefinite' => 'до одволанÑ\8f',
+'protect-expiry-indefinite' => 'навÑ\81е (до покликанÑ\8f)',
'protect-cascade' => 'Хранити сторінкы вложены до той сторінкы (каскадова охрана)',
'protect-cantedit' => 'Вы не можете змінити рівень охороны той сторінкы, тому што вы не маєте прав про єй едітованя.',
'protect-othertime' => 'Іншый час:',
'restriction-edit' => 'Едітованя',
'restriction-move' => 'Переменовати',
'restriction-create' => 'Вытвориня',
-'restriction-upload' => 'Ð\9dагÑ\80аваня файлів',
+'restriction-upload' => 'Ð\97аладовованя файлів',
# Restriction levels
'restriction-level-sysop' => 'замкнуте',
'undeleterevision-missing' => 'Неправилна або хыбляча ревізія. Може маєте планый одказ, або ревізія была обновлена ці одстранена з архіву.',
'undelete-nodiff' => 'Не найджена жадна попередня верзія.',
'undeletebtn' => 'Обновити',
-'undeletelink' => 'відїти/обновити',
-'undeleteviewlink' => 'відїти',
+'undeletelink' => 'видїти/обновити',
+'undeleteviewlink' => 'видїти',
'undeletereset' => 'Ресетовати',
'undeleteinvert' => 'Інвертовати селекцію',
'undeletecomment' => 'Причіна:',
'undeletedrevisions' => '{{PLURAL:$1|Обновлена $1 верзія|Обновлены $1 верзії|Обновленых $1 верзій}}',
'undeletedrevisions-files' => '{{PLURAL:$1|Обновлена єдна верзія|Обновлены $1 верзії|Обновленых $1 верзій}} і $2 {{PLURAL:$2|файл|файлы|файлів}}.',
'undeletedfiles' => '{{PLURAL:$1|обновленый $1 файл|обновлены $1 файлы|обновленых $1 файлів}}',
-'cannotundelete' => 'Ð\9eбновлÑ\97нÑ\8f Ñ\81Ñ\8f не подаÑ\80ило; правдоподобно дахто другый обновив сторінку скоре як вы.',
+'cannotundelete' => 'Ð\9eбновлÑ\97нÑ\8f Ñ\81Ñ\8f не вдало; правдоподобно дахто другый обновив сторінку скоре як вы.',
'undeletedpage' => "'''$1 была обновлена'''
Запис о послїднїх мазанях і обновлїнях найдете в [[Special:Log/delete|книзї змазаных сторінок]].",
'undelete-header' => 'Видьте недавно змазаны сторінкы в [[Special:Log/delete|книзї змазаных сторінок]].',
+'undelete-search-title' => 'Гляданя змазаных сторінок',
'undelete-search-box' => 'Гляданя вымазаных сторінок',
'undelete-search-prefix' => 'Вказати сторінкы што ся почінають з',
'undelete-search-submit' => 'Найти',
'undelete-no-results' => 'Пожадавцї жадны змазаны сторінкы не одповідають.',
'undelete-filename-mismatch' => 'Не годен обновити верзію файлу з часовов значков $1: назва файлу не одповідать',
'undelete-bad-store-key' => 'Не годен обновити верзію файлу з часовов значков $1: файл педед змазанём хыбив',
-'undelete-cleanup-error' => 'Хыба почас мазаня нехоснованого архівного файлу „$1“.',
-'undelete-missing-filearchive' => 'Не подарило ся обновити файл архіву з ідентіфікаціёв $1, протоже не є в датабазї. Може уже быв обновленый.',
-'undelete-error-short' => 'Хыба почас обновованя файлу: $1',
-'undelete-error-long' => 'Взникла хыба почас обновованя файлу:
+'undelete-cleanup-error' => 'Хыба мазаня нехоснованого архівного файлу „$1“.',
+'undelete-missing-filearchive' => 'Не вдало ся обновити файл архіву з ідентіфікаціёв $1, бо не є в датабазї. Може же уж быв обновленый.',
+'undelete-error' => 'Хыба обновлїня сторінкы',
+'undelete-error-short' => 'Хыба обновлїня файлу: $1',
+'undelete-error-long' => 'Выникла хыба під час обновлїня файлу:
$1',
'undelete-show-file-confirm' => 'На певно собі хочете посмотрити змазану ревізію файлу „<nowiki>$1</nowiki>“ з $2, $3?',
# Contributions
'contributions' => 'Приспівок хоснователя',
-'contributions-title' => 'Приспевок хоснователя $1',
+'contributions-title' => 'Приспівок хоснователя $1',
'mycontris' => 'Мої приспівкы',
-'contribsub2' => 'Приспевок $1 ($2)',
-'nocontribs' => 'Ð\9dенайдженÑ\8b жаднÑ\8b змÑ\96нÑ\8b подлÑ\8f Ñ\82Ñ\8bÑ\85 кÑ\80Ñ\96Ñ\82еÑ\80Ñ\96й.',
+'contribsub2' => 'Приспівок $1 ($2)',
+'nocontribs' => 'Ð\9dенайдженÑ\8b жаднÑ\8b змÑ\96нÑ\8b за Ñ\82Ñ\8bма кÑ\80Ñ\96Ñ\82еÑ\80Ñ\96Ñ\8fми.',
'uctop' => ' (послїдня)',
'month' => 'Од місяця (і скоре):',
'year' => 'Од року (і скоре):',
-'sp-contributions-newbies' => 'Вказати приспевкы лем новых конт',
+'sp-contributions-newbies' => 'Вказати приспівкы лем новых конт',
'sp-contributions-newbies-sub' => 'Новы хоснователї',
'sp-contributions-newbies-title' => 'Приспівкы новый хоснователїв',
'sp-contributions-blocklog' => 'Лоґ блокованя',
'sp-contributions-deleted' => 'вымазаны приспевкы хоснователя',
-'sp-contributions-uploads' => 'нагÑ\80аванÑ\8f',
+'sp-contributions-uploads' => 'заладованÑ\8b Ñ\84айлÑ\8b',
'sp-contributions-logs' => 'лоґы',
'sp-contributions-talk' => 'діскузія',
'sp-contributions-userrights' => 'Справа хосновательскых прав',
Послїднїй запис в лоґах блоковань є такый:',
'sp-contributions-blocked-notice-anon' => 'Тота IP адреса є теперь блокована.
Послїднїй запис в лоґах блоковань є такый:',
-'sp-contributions-search' => 'Глядати приспевкы',
-'sp-contributions-username' => 'IP-адреса або мено хоснователя:',
+'sp-contributions-search' => 'Глядати приспівкы',
+'sp-contributions-username' => 'IP-адреса або імя хоснователя:',
'sp-contributions-toponly' => 'Вказати лем актуалны ревізії',
'sp-contributions-submit' => 'Найти',
** Знеужываня веце конт
** Невгодне мено хоснователя',
'ipb-hardblock' => 'Заборонити приголошеным хоснователям едітовати з той IP-адресы',
-'ipbcreateaccount' => 'Не доволити реґістрацію новых хоснователїв',
+'ipbcreateaccount' => 'Ð\9dе дозволиÑ\82и Ñ\80еÒ\91Ñ\96Ñ\81Ñ\82Ñ\80аÑ\86Ñ\96Ñ\8e новÑ\8bÑ\85 Ñ\85оÑ\81новаÑ\82елÑ\97в',
'ipbemailban' => 'Заборонити хоснователёви посылати ел. пошту',
'ipbenableautoblock' => 'Автоматічно блоковати IP адресы хоснованы тым хоснователём',
'ipbsubmit' => 'Заблоковати',
'ipblocklist-submit' => 'Глядати',
'ipblocklist-localblock' => 'Локалне блокованя',
'ipblocklist-otherblocks' => '{{PLURAL:$1|Інше блокованя|Іншы блокованя}}',
-'infiniteblock' => 'до одволанÑ\8f',
+'infiniteblock' => 'навÑ\81е (до покликанÑ\8f)',
'expiringblock' => 'до $1, $2',
'anononlyblock' => 'лем анонімы',
'noautoblockblock' => 'без автоблокованя',
-'createaccountblock' => 'вÑ\8bÑ\82ваÑ\80Ñ\8fнÑ\8f конÑ\82 не Ñ\94 поволене',
+'createaccountblock' => 'вÑ\8bÑ\82воÑ\80Ñ\91ванÑ\8f конÑ\82 не дозволене',
'emailblock' => 'е-маіл блокованый',
'blocklist-nousertalk' => 'не може едітовати властну сторінку діскузії',
'ipblocklist-empty' => 'Список блоковань є порожнїй.',
Смотьте тыж [[Special:BlockList|список вшыткых чінных блоковань]].',
'unblocklogentry' => 'одблоковав $1',
'block-log-flags-anononly' => 'лем анонімны хоснователї',
-'block-log-flags-nocreate' => 'вÑ\8bÑ\82ваÑ\80Ñ\8fнÑ\8f конÑ\82 не поволене',
+'block-log-flags-nocreate' => 'вÑ\8bÑ\82воÑ\80Ñ\91ванÑ\8f конÑ\82 не дозволене',
'block-log-flags-noautoblock' => 'автоматічне блокованя выпнуте',
'block-log-flags-noemail' => 'е-маіл блокованый',
'block-log-flags-nousertalk' => 'не може едітовати властну сторінку діскузії',
'block-log-flags-angry-autoblock' => 'росшырене автоматічне блокованя выпнуте',
'block-log-flags-hiddenname' => 'мено хоснователя сховане',
-'range_block_disabled' => 'Ð\91локованÑ\8f Ñ\80оÑ\81Ñ\81Ñ\8fгÑ\96в IP-адÑ\80еÑ\81 не Ñ\94 поволене',
+'range_block_disabled' => 'Ð\91локованÑ\8f Ñ\80оÑ\81Ñ\81Ñ\8fгÑ\96в IP-адÑ\80еÑ\81 не Ñ\94 дозволене.',
'ipb_expiry_invalid' => 'Неплатный час експірації.',
'ipb_expiry_temp' => 'Блокованя схованых мен хоснователїв бы мало быти тырвале.',
'ipb_hide_invalid' => 'Тото конто ся не дасть затаїти; може має дуже много едітацій.',
'ipb_cant_unblock' => 'Хыба: Блокованя з ID $1 не было найджене. Хоснователь уж може быв одблокованый.',
'ipb_blocked_as_range' => 'Хыба: IP-адреса $1 не є блокована прямо а так єй не є можне одблоковати. Є частёв заблокованого россягу $2, котрый може быти одблокованый.',
'ip_range_invalid' => 'Неплатный IP россяг.',
-'ip_range_toolarge' => 'Ð\91локованÑ\8f Ñ\80оÑ\81Ñ\81Ñ\8fгÑ\96в векÑ\88Ñ\8bÑ\85 Ñ\8fк /$1 не Ñ\94 поволене.',
+'ip_range_toolarge' => 'Ð\91локованÑ\8f Ñ\80оÑ\81Ñ\81Ñ\8fгÑ\96в векÑ\88Ñ\8bÑ\85 Ñ\8fк /$1 не Ñ\94 дозволене.',
'blockme' => 'Заблокуй ня',
'proxyblocker' => 'Блокованя проксі',
'proxyblocker-disabled' => 'Тота фунція є выпнута.',
'proxyblockreason' => 'Ваша IP-адреса была заблокована, зато же фунґує як отвореный проксі сервер.
-Ð\9aонÑ\82акÑ\82Ñ\83йÑ\82е Ñ\81вого Ð\86нÑ\82еÑ\80неÑ\82-пÑ\80овайдеÑ\80а або Ñ\82еÑ\85нÑ\96Ñ\87нÑ\83 пÑ\96дпоÑ\80Ñ\83 Ñ\96 Ñ\96нÑ\84оÑ\80мÑ\83йÑ\82е Ñ\97Ñ\85 о Ñ\82Ñ\96м Ñ\81еÑ\80Ñ\91знÑ\96м безпечностнім проблемі.',
+Ð\9aонÑ\82акÑ\82Ñ\83йÑ\82е Ñ\81вого Ð\86нÑ\82еÑ\80неÑ\82-пÑ\80овайдеÑ\80а або Ñ\82еÑ\85нÑ\96Ñ\87нÑ\83 пÑ\96дпоÑ\80Ñ\83 Ñ\96 Ñ\96нÑ\84оÑ\80мÑ\83йÑ\82е Ñ\97Ñ\85 о Ñ\82Ñ\96м Ñ\81еÑ\80Ñ\8cÑ\91знÑ\96м беÑ\81печностнім проблемі.',
'proxyblocksuccess' => 'Готово.',
'sorbsreason' => 'Ваша IP-адреса є веджена як отвореный проксі в DNSBL.',
'sorbs_create_account_reason' => 'Ваша IP-адреса є веджена як одкрытый проксі в DNSBL. З той адресы собі не можете створити конто.',
'cant-block-while-blocked' => 'Не можете блоковати іншых хоснователїв, кідь сьте сам заблокованый(а).',
'cant-see-hidden-user' => 'Хоснователь, котрого хочете заблоковати, уж быв заблокованый і схованый. Кідьже не маєте права hideuser, не можете собі наставлїня блокованя того хоснователя посмотрити ани го змінити.',
'ipbblocked' => 'Не можете блоковати або одблоковати іншых хоснователїв, {{GENDER:|сам|сама|сам}} сьте {{GENDER:|заблокованый|заблокована|заблокованый}}',
-'ipbnounblockself' => 'Не маєте доволене одблоковати {{GENDER:|сам|сама|сам}} себе',
+'ipbnounblockself' => 'Ð\9dе маÑ\94Ñ\82е дозволене одблоковаÑ\82и {{GENDER:|Ñ\81ам|Ñ\81ама|Ñ\81ам}} Ñ\81ебе',
# Developer tools
'lockdb' => 'Замкнути датабазу',
'unlockdb' => 'Одомкнути датабазу',
-'lockdbtext' => 'Кідь замкнете датабазу, знеможните другым едітовати, управляти наставлїня, слїдованы сторінкы ітд. Підтвердьте, же то справды хочете зробити і же одомкнете датабазу такой по оправах.',
-'unlockdbtext' => 'Кідь одомкнете датабазу, уможните другым едітовати, управляти наставлїня, слїдованы сторінкы ітд. Підтвердьте, же то хочете справды зробити.',
+'lockdbtext' => 'Кідь замкнете датабазу, знеможните другым едітовати, управляти наставлїня, слїдованы сторінкы ітд. Потвердьте, же то справды хочете зробити і же одомкнете датабазу такой по оправах.',
+'unlockdbtext' => 'Кідь одомкнете датабазу, уможните другым едітовати, управляти наставлїня, слїдованы сторінкы ітд. Потвердьте, же то хочете справды зробити.',
'lockconfirm' => 'Гей, справды хочу замкнути датабазу.',
'unlockconfirm' => 'Гей, справды хочу одомкнути датабазу.',
'lockbtn' => 'Замкнути датабазу',
'unlockbtn' => 'Одомкнути датабазу',
-'locknoconfirm' => 'Не было означене поличко підтверджіня.',
+'locknoconfirm' => 'Не было означене поличко потверджіня.',
'lockdbsuccesssub' => 'Датабаза замкнута',
'unlockdbsuccesssub' => 'Датабаза одомкнута',
'lockdbsuccesstext' => 'Датабаза {{grammar:2sg|{{SITENAME}}}} была успішно замкнута.
Стара назва стане перенапрямлинём на нову назву.
Можете автоматично обновити напрямлиня на стару назву.
Кідь вы тото не зробите, просиме Вас, перевірте наявність [[Special:DoubleRedirects|подвойных]] ці [[Special:BrokenRedirects|розорваных]] напрямлїнь.
-Ð\92Ñ\8b зодповÑ\96даÑ\94те за то, жебы одказы і надале вказовали там, де мають.
+Ð\92Ñ\8b одповÑ\96дате за то, жебы одказы і надале вказовали там, де мають.
Уважте, же сторінка '''не''' буде переменована, кідь сторінка з новов назвов уж екзістує, окрем того, коли она порожня або є напрямлїнём, а лоґ єй едітовань порожнїй.
То значіть, же вы можете вернути сторінцї стару назву, кідь вы переменовали єй помылково, але вы не можете переписати екзістуючу сторінку.
'movepage-page-exists' => 'Сторінка $1 уж екзістує і не може быти автоматічно переписана.',
'movepage-page-moved' => 'Сторінка $1 была переменована на $2.',
'movepage-page-unmoved' => 'Сторінка $1 не може быти переменована на $2.',
-'movepage-max-pages' => '{{PLURAL:$1|Ð\91Ñ\8bла пеÑ\80еменована макÑ\81Ñ\96мално поволена Ñ\94дна Ñ\81Ñ\82оÑ\80Ñ\96нка|Ð\91Ñ\8bли пеÑ\80еменованÑ\8b макÑ\81Ñ\96мално поволенÑ\8b $1 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b|Ð\91Ñ\8bло пеÑ\80еменоване макÑ\81Ñ\96мално поволеных $1 сторінок}}, веце їх уж автоматічно переменованых не буде.',
+'movepage-max-pages' => '{{PLURAL:$1|Ð\91Ñ\8bла пеÑ\80еменована макÑ\81Ñ\96мално дозволена Ñ\94дна Ñ\81Ñ\82оÑ\80Ñ\96нка|Ð\91Ñ\8bли пеÑ\80еменованÑ\8b макÑ\81Ñ\96мално дозволенÑ\8b $1 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b|Ð\91Ñ\8bло пеÑ\80еменоване макÑ\81Ñ\96мално дозволеных $1 сторінок}}, веце їх уж автоматічно переменованых не буде.',
'movelogpage' => 'Лоґ переменовань',
'movelogpagetext' => 'Тото є список вшыткых переменованый сторінок.',
'movesubpage' => '{{PLURAL:$1|Підсторінка|Підсторінкы}}',
Кідь хочете приспівати ку локалізації софтверу MediaWiki, навщівте [//www.mediawiki.org/wiki/Localisation локалізачну сторінку на mediawiki.org] і [//translatewiki.net сервер server translatewiki.net].',
'allmessagesnotsupportedDB' => '{{ns:special}}:AllMessages не є підпороване, бо wgUseDatabaseMessages є выпнуте.',
'allmessages-filter-legend' => 'Філтер',
-'allmessages-filter' => 'ФÑ\96лÑ\82еÑ\80 подлÑ\8f Ñ\81Ñ\82авÑ\83:',
+'allmessages-filter' => 'ФÑ\96лÑ\82еÑ\80 за Ñ\81Ñ\82аном:',
'allmessages-filter-unmodified' => 'Незмінено',
'allmessages-filter-all' => 'Вшыткы',
'allmessages-filter-modified' => 'Змінено',
-'allmessages-prefix' => 'ФÑ\96лÑ\82еÑ\80 подлÑ\8f пÑ\80еÑ\84Ñ\96кÑ\81Ñ\83:',
+'allmessages-prefix' => 'ФÑ\96лÑ\82еÑ\80 за пÑ\80еÑ\84Ñ\96кÑ\81ом:',
'allmessages-language' => 'Язык:',
'allmessages-filter-submit' => 'Выконати',
'filemissing' => 'Файл хыбить',
'thumbnail_error' => 'Хыба створїня нагляду: $1',
'djvu_page_error' => 'Сторінка DjVu мімо россяг',
-'djvu_no_xml' => 'СÑ\82воÑ\80Ñ\97нÑ\8f XML пÑ\80о Ñ\84айл DjVu Ñ\81Ñ\8f не подаÑ\80ило.',
+'djvu_no_xml' => 'СÑ\82воÑ\80Ñ\97нÑ\8f XML пÑ\80о Ñ\84айл DjVu Ñ\81Ñ\8f не вдало.',
'thumbnail-temp-create' => 'Дочасный файл нагляду негодно было створити',
'thumbnail-dest-create' => 'Нагляд не годно было уложыти на призначене місце',
'thumbnail_invalid_params' => 'Неплатный параметер нагляду',
'import-interwiki-namespace' => 'Цілёвый простор назв:',
'import-upload-filename' => 'Назва файлу:',
'import-comment' => 'Коментарь:',
-'importtext' => 'Ð\9fÑ\80оÑ\81име Ð\92аÑ\81, екÑ\81поÑ\80Ñ\82Ñ\83йÑ\82е Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83 з Ñ\96нÑ\88ой вÑ\96кÑ\96 помоÑ\87ов [[Special:Export|Ñ\96нÑ\88Ñ\82Ñ\80Ñ\83менÑ\82Ñ\83 на екÑ\81поÑ\80Ñ\82]], Ñ\83ложÑ\82е Ñ\84айл на ваÑ\88 дÑ\96Ñ\81к а поÑ\82Ñ\96м го нагÑ\80айÑ\82е Ñ\82Ñ\83.',
+'importtext' => 'Ð\9fÑ\80оÑ\81име Ð\92аÑ\81, екÑ\81поÑ\80Ñ\82Ñ\83йÑ\82е Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83 з Ñ\96нÑ\88ой вÑ\96кÑ\96 помоÑ\87ов [[Special:Export|Ñ\96нÑ\88Ñ\82Ñ\80Ñ\83менÑ\82Ñ\83 на екÑ\81поÑ\80Ñ\82]], Ñ\83ложÑ\82е Ñ\84айл на ваÑ\88 дÑ\96Ñ\81к а поÑ\82Ñ\96м го заладÑ\83йÑ\82е гев.',
'importstart' => 'Імпорт сторінок…',
'import-revision-count' => '$1 {{PLURAL:$1|ревізія|ревізії|ревізій}}',
'importnopages' => 'Не є што імпортовати.',
'imported-log-entries' => '{{PLURAL:$1|Наімпортованый 1 протоколовачій запис|Наімпортованы $1 протоколовачі записы|Наімпортованых $1 протоколовачіх записів}}.',
-'importfailed' => 'Ð\86мпоÑ\80Ñ\82 Ñ\81Ñ\8f не подаÑ\80ив: $1',
+'importfailed' => 'Ð\86мпоÑ\80Ñ\82 Ñ\81Ñ\8f не вдав: $1',
'importunknownsource' => 'Незнамый тіп імпортованой сторінкы',
'importcantopen' => 'Не дало ся отворити файл імпорту',
'importbadinterwiki' => 'Неплатный одказ інтервікі',
'importnotext' => 'Порожнїй або жаден текст',
'importsuccess' => 'Імпорт сконченый!',
'importhistoryconflict' => 'Екзістує конфлікт міджі історіямі верзії (може тота сторінка уж была імпортована скоре)',
-'importnosources' => 'Ð\9dе бÑ\8bло вÑ\8bбÑ\80ане жÑ\80Ñ\96дло Ñ\96мпоÑ\80Ñ\82Ñ\83 мÑ\96джÑ\96 вÑ\96кÑ\96, пÑ\80Ñ\8fме наÑ\87Ñ\96Ñ\82аня історії змін є выпнуте.',
-'importnofile' => 'Ð\9dе бÑ\8bв наÑ\87Ñ\96Ñ\82аный файл імпорту.',
-'importuploaderrorsize' => 'Ð\9dе подаÑ\80ило Ñ\81Ñ\8f наÑ\87Ñ\96Ñ\82ати файл імпорту. Розмір файлу перевышує становлену меджу.',
-'importuploaderrorpartial' => 'Ð\9dе подаÑ\80ило Ñ\81Ñ\8f наÑ\87Ñ\96Ñ\82аÑ\82и Ñ\96мпоÑ\80Ñ\82нÑ\8bй Ñ\84айл. Файл бÑ\8bв наÑ\87Ñ\96Ñ\82анÑ\8bй лем Ñ\87аÑ\81Ñ\82оÑ\87но.',
-'importuploaderrortemp' => 'Ð\9dе подаÑ\80ило Ñ\81Ñ\8f наÑ\87Ñ\96Ñ\82ати імпортный файл. Не є к діспозіції дочасный адресарь.',
-'import-parse-failure' => 'Хыба почас імпорту XML',
+'importnosources' => 'Ð\9dе бÑ\8bло вÑ\8bбÑ\80ане жÑ\80Ñ\96дло Ñ\96мпоÑ\80Ñ\82Ñ\83 мÑ\96джÑ\96 вÑ\96кÑ\96, дÑ\96Ñ\80екÑ\82 заладованя історії змін є выпнуте.',
+'importnofile' => 'Ð\9dе бÑ\8bв заладованый файл імпорту.',
+'importuploaderrorsize' => 'Ð\9dе вдало Ñ\81Ñ\8f заладовати файл імпорту. Розмір файлу перевышує становлену меджу.',
+'importuploaderrorpartial' => 'Ð\9dе вдало Ñ\81Ñ\8f заладоваÑ\82и Ñ\96мпоÑ\80Ñ\82нÑ\8bй Ñ\84айл. Файл бÑ\8bв заладованÑ\8bй лем Ñ\87аÑ\81Ñ\82ково.',
+'importuploaderrortemp' => 'Ð\9dе вдало Ñ\81Ñ\8f заладовати імпортный файл. Не є к діспозіції дочасный адресарь.',
+'import-parse-failure' => 'Хыба під час імпорту XML',
'import-noarticle' => 'Не є сторінка про імпорт!',
'import-nonewrevisions' => 'Вшыткы верзії уж были скоре імпортованы',
'xml-error-string' => '$1 на рядку $2, стовпець $3 (байт $4): $5',
-'import-upload' => 'Ð\9dагÑ\80ати XML дата',
+'import-upload' => 'Ð\97аладовати XML дата',
'import-token-mismatch' => 'Стратили ся дата релації. Спробуйте то знову.',
'import-invalid-interwiki' => 'Із зазначеной вікі ся не дасть імпортовати.',
'import-error-edit' => 'Сторінка „$1“ ся не наімпортовала, бо не мате право єй едітовати.',
# Import log
'importlogpage' => 'Книга імпортів',
'importlogpagetext' => 'На тій сторінцї ся зображують адміністраторскы імпорты сторінок враховано едітовань з іншых вікі.',
-'import-logentry-upload' => 'Ñ\96мпоÑ\80Ñ\82овав [[$1]] наÑ\87Ñ\96Ñ\82анём файлу',
+'import-logentry-upload' => 'Ñ\96мпоÑ\80Ñ\82овав [[$1]] заладованём файлу',
'import-logentry-upload-detail' => '$1 {{PLURAL:$1|ревізія|ревізії|ревізій}}',
'import-logentry-interwiki' => 'міджівікі імпорт $1',
'import-logentry-interwiki-detail' => '$1 {{PLURAL:$1|ревізія|ревізії|ревізій}} з $2',
'javascripttest-pagetext-noframework' => 'Тота сторінка є резервована про тестованя JavaScript.',
'javascripttest-pagetext-unknownframework' => 'Незнаный фреймворк тестованя „$1“.',
'javascripttest-pagetext-frameworks' => 'Просиме, звольте єден з наступных фреймворків тестованя : $1',
+'javascripttest-pagetext-skins' => 'Звольте взгляд під котрым ся мають тесты спустити:',
+'javascripttest-qunit-intro' => 'Смотьте [$1 документацію тестованя] на mediawiki.org',
+'javascripttest-qunit-heading' => 'Сет тестів JavaScript в MediaWiki QUnit',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Ваша сторінка хоснователя',
'tooltip-pt-anonlogin' => 'Рекомендуєме ся приголосити, але не є то повинне.',
'tooltip-pt-logout' => 'Одголошіня',
'tooltip-ca-talk' => 'Діскузія о обсягу сторінкы',
-'tooltip-ca-edit' => 'ТоÑ\82Ñ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83 можно едітовати. Просиме, хоснуйте перегляд перед уложінём.',
+'tooltip-ca-edit' => 'ТоÑ\82Ñ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83 можеÑ\82е едітовати. Просиме, хоснуйте перегляд перед уложінём.',
'tooltip-ca-addsection' => 'Створити нову секцію',
'tooltip-ca-viewsource' => 'Тота сторінка є замкнута.
-Можете відїти єй код.',
+Можете видїти єй код.',
'tooltip-ca-history' => 'Минулы верзії той сторінкы',
-'tooltip-ca-protect' => 'Ð¥Ñ\80анÑ\8c тоту сторінку',
+'tooltip-ca-protect' => 'Ð\92Ñ\81окоÑ\82иÑ\82и тоту сторінку',
'tooltip-ca-unprotect' => 'Змінити замок той сторінкы',
'tooltip-ca-delete' => 'Вымазати тоту сторінку',
'tooltip-ca-undelete' => 'Обновити едітованя той сторінкы выконаны перед єй змазанём',
'tooltip-ca-watch' => 'Придати гевсю сторінку до вашого списку слїдованых сторінок',
'tooltip-ca-unwatch' => 'Одобрати тоту сторінку з вашого списку слїдованых сторінок',
'tooltip-search' => 'Глядати {{SITENAME}}',
-'tooltip-search-go' => 'Ð\9fеÑ\80ейÑ\82и до Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b, Ñ\88Ñ\82о маÑ\94 Ñ\82оÑ\87но таку назву (кідь екзістує)',
-'tooltip-search-fulltext' => 'Ð\9dайÑ\82и Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b пÑ\80о Ñ\82оÑ\82 Ñ\82екÑ\81Ñ\82',
+'tooltip-search-go' => 'Ð\9fеÑ\80ейÑ\82и на Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83, Ñ\88Ñ\82о маÑ\82Ñ\8c акÑ\83Ñ\80аÑ\82 таку назву (кідь екзістує)',
+'tooltip-search-fulltext' => 'Ð\9dайÑ\82и Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b з Ñ\82Ñ\8bм Ñ\82екÑ\81Ñ\82ом',
'tooltip-p-logo' => 'Головна сторінка',
'tooltip-n-mainpage' => 'Перейти на Головну сторінку',
'tooltip-n-mainpage-description' => 'Перейти на головну сторінку',
'tooltip-n-portal' => 'О проєктї, што можете зробити, де ся што находить',
'tooltip-n-currentevents' => 'Актуалны подїї',
'tooltip-n-recentchanges' => 'Список послїднїх змін',
-'tooltip-n-randompage' => 'Ð\97обÑ\80ажÑ\96нÑ\8f нагодной Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b',
+'tooltip-n-randompage' => 'Ð\86Ñ\82и на Ñ\82Ñ\80аÑ\84Ñ\83нковÑ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83',
'tooltip-n-help' => 'Посмотрити поміч',
'tooltip-t-whatlinkshere' => 'Список вшыткых сторінок, што одказують ся на тоту сторінку',
'tooltip-t-recentchangeslinked' => 'Послїднї зміны на сторінках, котры мають одказ на тїй сторінцї',
'tooltip-feed-rss' => 'RSS канал про тоту сторінку',
-'tooltip-feed-atom' => 'Atom канал пÑ\80о гевÑ\81Ñ\8e Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83',
+'tooltip-feed-atom' => 'Atom канал гевÑ\81Ñ\91й Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b',
'tooltip-t-contributions' => 'Перегляд приспевків того хоснователя',
-'tooltip-t-emailuser' => 'Послати е-маіл тому хоснователёви',
-'tooltip-t-upload' => 'Ð\9dагÑ\80ати файлы',
+'tooltip-t-emailuser' => 'Послати імейл тому хоснователёви',
+'tooltip-t-upload' => 'Ð\97аладовати файлы',
'tooltip-t-specialpages' => 'Список вшыткых шпеціалных сторінок',
-'tooltip-t-print' => 'Ð\92еÑ\80зÑ\96Ñ\8f Ñ\82ой Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b пÑ\80о дÑ\80Ñ\83к',
+'tooltip-t-print' => 'Ð\92еÑ\80зÑ\96Ñ\8f Ñ\82ой Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\8b до дÑ\80Ñ\83кÑ\83',
'tooltip-t-permalink' => 'Перманентный одказ на тоту верзію сторінкы',
'tooltip-ca-nstab-main' => 'Обсяг сторінкы',
-'tooltip-ca-nstab-user' => 'Відїти сторінку хоснователя',
+'tooltip-ca-nstab-user' => 'Видїти сторінку хоснователя',
'tooltip-ca-nstab-media' => 'Вказати сторінку файлу',
-'tooltip-ca-nstab-special' => 'Шпеціална сторінка, она є недоступна про едітованя',
+'tooltip-ca-nstab-special' => 'Шпеціална сторінка, тай єй не годен едітовати.',
'tooltip-ca-nstab-project' => 'Сторінка проєкту',
'tooltip-ca-nstab-image' => 'Видїти код сторінкы',
'tooltip-ca-nstab-mediawiki' => 'Вказати повідомлїня сістемы',
-'tooltip-ca-nstab-template' => 'Відїти шаблону',
+'tooltip-ca-nstab-template' => 'Видїти шаблону',
'tooltip-ca-nstab-help' => 'Видїти сторінку помочі',
'tooltip-ca-nstab-category' => 'Сторінка катеґорії',
'tooltip-minoredit' => 'Позначіти тото як незначне едітованя',
'tooltip-save' => 'Уложыти вашы зміны',
'tooltip-preview' => 'Нагляд сторінкы, просиме Вас, хоснуйте перед уложінём!',
'tooltip-diff' => 'Вказати зміны, што были зроблены в тексті.',
-'tooltip-compareselectedversions' => 'Відїти роздїл міджі двома указаныма верзіями той сторінкы.',
+'tooltip-compareselectedversions' => 'Видїти роздїл міджі двома указаныма верзіями той сторінкы.',
'tooltip-watch' => 'Придати тоту сторінку до списку слїдованых',
+'tooltip-watchlistedit-normal-submit' => 'Остранити надписы',
+'tooltip-watchlistedit-raw-submit' => 'Актуалізовати список слїдованых сторінок',
'tooltip-recreate' => 'Обновити сторінку і кідь была змазана',
'tooltip-upload' => 'Почати одосыланя',
'tooltip-rollback' => 'Єдным кликом вернути зміны, зроблены послїдным приспівателём',
-'tooltip-undo' => 'Ð\97Ñ\80Ñ\83Ñ\88Ñ\8bÑ\82и змÑ\96нÑ\8b Ñ\96 вказаÑ\82и попеÑ\80еднÑ\97й пеÑ\80еглÑ\8fд. Ð\94оволÑ\8fÑ\94 пÑ\80идаÑ\82и пÑ\80ичіну до ресуме.',
+'tooltip-undo' => 'Ð\97Ñ\80Ñ\83Ñ\88Ñ\8bÑ\82и змÑ\96нÑ\8b Ñ\96 вказаÑ\82и попеÑ\80еднÑ\97й пеÑ\80еглÑ\8fд. Ð\94озволÑ\8eÑ\94 пÑ\80идаÑ\82и пÑ\80Ñ\96чіну до ресуме.',
'tooltip-preferences-save' => 'Уложыти наставлїня',
'tooltip-summary' => 'Задайте курте згорнутя',
'spambot_username' => 'MediaWiki очістка спаму',
'spam_reverting' => 'Реверт на послїдню верзію необсягуючу одказы на $1',
'spam_blanking' => 'Вшыткы ревізії обсяговали одказы на $1, выпорожнєны',
+'spam_deleting' => 'Вшыткы ревізії обсяговали одказы на $1, змазане',
# Info page
'pageinfo-title' => 'Інформація про "$1"',
'rcpatroldisabledtext' => 'Патролованя послїднїх змін є моментално выпнута.',
'markedaspatrollederror' => 'Не дасть ся означіти як перевірене',
'markedaspatrollederrortext' => 'Мусите зволити ревізію, котра має быти означена як перевірена.',
-'markedaspatrollederror-noautopatrol' => 'Не маєте доволене означовати властны едітованя як перевірены.',
+'markedaspatrollederror-noautopatrol' => 'Ð\9dе маÑ\94Ñ\82е дозволене ознаÑ\87оваÑ\82и влаÑ\81Ñ\82нÑ\8b едÑ\96Ñ\82ованÑ\8f Ñ\8fк пеÑ\80евÑ\96Ñ\80енÑ\8b.',
# Patrol log
'patrol-log-page' => 'Книга перевіреных едітовань',
# Image deletion
'deletedrevision' => 'Змазана стара ревізія $1',
-'filedeleteerror-short' => 'Ð¥Ñ\8bба поÑ\87аÑ\81 мазанÑ\8f Ñ\84айлÑ\83: $1',
-'filedeleteerror-long' => 'Взникла хыба почас мазаня файлу:
+'filedeleteerror-short' => 'Хыба мазаня файлу: $1',
+'filedeleteerror-long' => 'Выникла хыба під час мазаня файлу:
$1',
'filedelete-missing' => 'Файл „$1“ ся не дасть змазати, бо не екзістує.',
# Special:NewFiles
'newimages' => 'Ґалерія новых файлів',
'imagelisttext' => "Ниже є {{plural:$1|єден файл|список '''$1''' файлів сортованых $2|список '''$1''' файлів сортованых $2}}.",
-'newimages-summary' => 'Ð\9dа Ñ\82Ñ\96й Ñ\88пеÑ\86Ñ\96алнÑ\96й Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\97 Ñ\81Ñ\8f зобÑ\80ажÑ\83Ñ\8eÑ\82Ñ\8c оÑ\81Ñ\82аÑ\82нÑ\97 наÑ\87Ñ\96Ñ\82аны файлы.',
+'newimages-summary' => 'Ð\9dа Ñ\82Ñ\96й Ñ\88пеÑ\86Ñ\96алнÑ\96й Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\97 Ñ\81Ñ\8f зобÑ\80ажÑ\83Ñ\8eÑ\82Ñ\8c оÑ\81Ñ\82аÑ\82нÑ\97 заладованы файлы.',
'newimages-legend' => 'Філтер',
'newimages-label' => 'Назва файлу (або єй часть):',
'showhidebots' => '($1 ботів)',
'noimages' => 'Не є што зобразити.',
'ilsubmit' => 'Глядати',
-'bydate' => 'подлÑ\8f даÑ\82Ñ\83мÑ\83',
+'bydate' => 'за даÑ\82Ñ\83мом',
'sp-newimages-showfrom' => 'Вказати новы файлы почінаючі од $2, $1',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
Кідь ся файл зедітовав по вытворїню, даякы параметры можуть не одповідати тому образку.',
'metadata-expand' => 'Вказати додатковы детайлы',
'metadata-collapse' => 'Сховати додатковы детайлы',
-'metadata-fields' => 'Ð\9fоложкÑ\8b меÑ\82адаÑ\82 обÑ\80азÑ\87Ñ\96кÑ\96в зазнаÑ\87енÑ\8b Ñ\83 Ñ\82Ñ\96м повÑ\96домлÑ\97нÑ\8e бÑ\83дÑ\83Ñ\82Ñ\8c на Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\97 з попиÑ\81ом вÑ\8bпиÑ\81анÑ\8b вÑ\81е. Ð\9fÑ\80о зобÑ\80ажÑ\96нÑ\8f дÑ\80Ñ\83гÑ\8bÑ\85 буде треба кликнути на „зобразити детайлы“.
+'metadata-fields' => 'Ð\9fоложкÑ\8b меÑ\82адаÑ\82 обÑ\80азÑ\87Ñ\96кÑ\96в зазнаÑ\87енÑ\8b Ñ\83 Ñ\82Ñ\96м повÑ\96домлÑ\97нÑ\8e бÑ\83дÑ\83Ñ\82Ñ\8c на Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\97 з попиÑ\81ом вÑ\8bпиÑ\81анÑ\8b вÑ\81е. Ð\96ебÑ\8b вказаÑ\82и дÑ\80Ñ\83гÑ\8b, буде треба кликнути на „зобразити детайлы“.
* make
* model
* datetimeoriginal
'exif-imagedescription' => 'Назва образку',
'exif-make' => 'Выробник фотоапарату',
'exif-model' => 'Модел фотоапарату',
-'exif-software' => 'Проґрамове забезпечіня',
+'exif-software' => 'Проґрамове забеспечіня',
'exif-artist' => 'Автор',
'exif-copyright' => 'Властник авторьскых прав',
'exif-exifversion' => 'Верзія Exif',
'exif-gpslongitude' => 'Ґеоґрафічна довжка',
'exif-gpsaltituderef' => 'Над/підморьска вышка/глубка',
'exif-gpsaltitude' => 'Надморьска вышка',
-'exif-gpstimestamp' => 'GPS Ñ\87аÑ\81 (подлÑ\8f аÑ\82омовÑ\8bÑ\85 годин)',
+'exif-gpstimestamp' => 'GPS Ñ\87аÑ\81 (аÑ\82омовÑ\8b годинÑ\8b)',
'exif-gpssatellites' => 'Сателіты хоснованы про міряня',
'exif-gpsstatus' => 'Статус приїмача',
'exif-gpsmeasuremode' => 'Режім міряня',
'exif-datetimemetadata' => 'Датум послїднёй управы метадат',
'exif-nickname' => 'Неформална назва образчіка',
'exif-rating' => 'Рейтінґ (1–5)',
-'exif-rightscertificate' => 'ЦеÑ\80Ñ\82Ñ\96Ñ\84Ñ\96каÑ\82 Ñ\80Ñ\8fджÑ\96ня прав',
+'exif-rightscertificate' => 'ЦеÑ\80Ñ\82Ñ\96Ñ\84Ñ\96каÑ\82 Ñ\81пÑ\80авованя прав',
'exif-copyrighted' => 'Статус авторьскых прав',
'exif-copyrightowner' => 'Властник авторьскых прав',
'exif-usageterms' => 'Условія хоснованя',
'exif-attributionurl' => 'Кідь хоснуєте тото дїло, зазначте одказ',
'exif-preferredattributionname' => 'Кідь хоснуєте тото дїло, зазначте автора',
'exif-pngfilecomment' => 'Позначкы ку файлу PNG',
-'exif-disclaimer' => 'Ð\92Ñ\8bлÑ\83Ñ\87Ñ\96нÑ\8f зодповÑ\96дноÑ\81Ñ\82и',
+'exif-disclaimer' => 'Вылучіня одповідности',
'exif-contentwarning' => 'Упозорнїня ку обсягу',
'exif-giffilecomment' => 'Позначкы ку файлу GIF',
'exif-intellectualgenre' => 'Тіп положкы',
'exif-compression-3' => 'Кодованя факсів CCITT Group 3',
'exif-compression-4' => 'Кодованя факсів CCITT Group 4',
-'exif-copyrighted-true' => 'Ð¥Ñ\80анене авторьскым правом',
+'exif-copyrighted-true' => 'СокоÑ\87ене авторьскым правом',
'exif-copyrighted-false' => 'Вольне дїло',
'exif-unknowndate' => 'Незнамый датум',
'limitall' => 'вшыткы',
# E-mail address confirmation
-'confirmemail' => 'Підтверджіня адресы ел. пошты',
+'confirmemail' => 'Потверджіня адресы ел. пошты',
'confirmemail_noemail' => 'Во своїм [[Special:Preferences|хосновательскім наставлїню]] сьте не зазначіли платну адресу ел. пошты.',
-'confirmemail_text' => 'Тота вікі выжадує, жебы сьте перед хоснованым дакотрых функцій підтвердили свою адресу електронічной пошты. Кликнутём на клапку ниже одошлете підтверджовачій лист на вами зазначену адресу. Тот лист обсягує одказ і код підтверджіня; зображінём одказованой сторінкы во своїм інтернетовім переглядачу підтвердите, же зазначена адреса є платна.',
-'confirmemail_pending' => 'Підтверджовачій код быв посланый ва вашу адресу ел. пошты.
+'confirmemail_text' => 'Тота вікі выжадує, жебы сьте перед хоснованым дакотрых функцій потвердили свою адресу електронічной пошты. Кликнутём на клапку ниже одошлете потверджовачій лист на вами зазначену адресу. Тот лист обсягує одказ і код потверджіня; зображінём одказованой сторінкы во своїм інтернетовім переглядачу потвердите, же зазначена адреса є платна.',
+'confirmemail_pending' => 'Потверджовачій код быв посланый ва вашу адресу ел. пошты.
Кідь сьте собі конто створили перед моментом, спробуйте на доручіня коду пару минут почекати, покы пожадате о новый.',
-'confirmemail_send' => 'Одослати підтверджовачій код',
-'confirmemail_sent' => 'Підтверджовачій лист быв посланый.',
-'confirmemail_oncreate' => 'На вашу адресу ел. пошты быв посланый підтверджовачій код.
+'confirmemail_send' => 'Одослати потверджовачій код',
+'confirmemail_sent' => 'Потверджовачій лист быв посланый.',
+'confirmemail_oncreate' => 'На вашу адресу ел. пошты быв посланый потверджовачій код.
Тот код не треба про приголошіня, але буде го треба про актівацію дакотрых функцій заложеных на хоснованю ел. пошты.',
-'confirmemail_sendfailed' => '{{GRAMMAR:3sg|{{SITENAME}}}} Ñ\81Ñ\8f не подаÑ\80ило одоÑ\81лаÑ\82и пÑ\96дÑ\82веÑ\80джоваÑ\87Ñ\96й лиÑ\81Ñ\82. Ð\9fеÑ\80евÑ\96Ñ\80те ці адреса не обсягує хыбны буквы.
+'confirmemail_sendfailed' => '{{GRAMMAR:3sg|{{SITENAME}}}} Ñ\81Ñ\8f не вдало одоÑ\81лаÑ\82и поÑ\82веÑ\80джоваÑ\87Ñ\96й лиÑ\81Ñ\82. Ð\9fеÑ\80евÑ\96Ñ\80Ñ\8cте ці адреса не обсягує хыбны буквы.
Поштовый проґрам одповив: $1',
-'confirmemail_invalid' => 'Неплатный підтверджовачій код. Може уж уплинула платность коду.',
-'confirmemail_needlogin' => 'Про підтверджіня своёй адрес ел. пошты ся мусите $1.',
-'confirmemail_success' => 'Ваша адреса ел. пошты была підтверджена. Нынї ся можете [[Special:UserLogin|приголосити]] і хосновати вікі.',
-'confirmemail_loggedin' => 'Ваша адреса ел. пошты была підтверджена.',
-'confirmemail_error' => 'Ð\9dе подаÑ\80ило Ñ\81Ñ\8f Ñ\83ложÑ\8bÑ\82и ваÑ\88е пÑ\96дтверджіня.',
-'confirmemail_subject' => 'Підтверджіня адресы ел. пошты про {{grammar:4sg|{{SITENAME}}}}',
+'confirmemail_invalid' => 'Неправильный потверджовачій код. Може уж уплынула платность коду.',
+'confirmemail_needlogin' => 'Про потверджіня своёй адрес ел. пошты ся мусите $1.',
+'confirmemail_success' => 'Ваша адреса ел. пошты была потверджена. Нынї ся можете [[Special:UserLogin|приголосити]] і хосновати вікі.',
+'confirmemail_loggedin' => 'Ваша адреса ел. пошты была потверджена.',
+'confirmemail_error' => 'Ð\9dе вдало Ñ\81Ñ\8f Ñ\83ложÑ\8bÑ\82и ваÑ\88е потверджіня.',
+'confirmemail_subject' => 'Потверджіня адресы ел. пошты про {{grammar:4sg|{{SITENAME}}}}',
'confirmemail_body' => 'Хтось (асі вы, з IP адресы $1) собі реґістровав конто з меном "$2" і тов адресов ел. пошты на {{grammar:6sg|{{SITENAME}}}}.
-Кідь собі желате актівовати функції ел. пошты на {{grammar:6sg|{{SITENAME}}}}, так про підтверджіня, же тота адреса справды належыть вам, перейдите своїм інтернетовым переглядачом на наслїдуючу адресу:
+Кідь собі желате актівовати функції ел. пошты на {{grammar:6sg|{{SITENAME}}}}, так про потверджіня, же тота адреса справды належыть вам, перейдите своїм інтернетовым переглядачом на слїдуючу адресу:
$3
-Кідь сьте о тото підтверджіня *не жадали*, кликните на наслїднїй одказ, котрым підтверджіня зрушыте:
+Кідь сьте о тото потверджіня *не жадали*, кликните на слїдуючій одказ, котрым потверджіня зрушыте:
$5
-Платность того коду підтверджіня експірує $4.',
+Платность того коду потверджіня експірує $4.',
'confirmemail_body_changed' => 'Хтось (асі вы, з IP адресы $1),
змінив адресу ел. пошты ку конту "$2" на {{grammar:6sg|{{SITENAME}}}} на тоту адресу.
-Кідь собі желате актівовати функції ел. пошты на {{grammar:6sg|{{SITENAME}}}}, так про підтверджіня, же тота адреса справды належыть вам, перейдите своїм інтернетовым переглядачом на наслїдуючу адресу:
+Кідь собі желате актівовати функції ел. пошты на {{grammar:6sg|{{SITENAME}}}}, так про потверджіня, же тота адреса справды належыть вам, перейдьте своїм інтернетовым переглядачом на наслїдуючу адресу:
$3
-Кідь сьте о тото підтверджіня *не жадали*, кликните на наслїднїй одказ, котрым підтверджіня зрушыте:
+Кідь сьте о тото потверджіня *не жадали*, кликните на слїдуючій одказ, котрым потверджіня зрушыте:
$5
-Платность того коду підтверджіня експірує $4.',
+Платность того коду потверджіня експірує $4.',
'confirmemail_body_set' => 'Дахто (асі вы, з IP адресы $1) наставив імейлову адресу
конта „$2“ на {{grammar:6sg|{{SITENAME}}}} на тоту адресу.
Кідь хочете знову актівовати імейловы функції на
-{{grammar:6sg|{{SITENAME}}}}, так про підтверджіня, же тота адреса справды
+{{grammar:6sg|{{SITENAME}}}}, та жебы потвердити, же тота адреса справды
належыть вам, ідьте своїм інтернетовым перезерачом на адресу ниже:
$3
Кідь вам тото конто *не належыть*, кликните на наступный
-одказ з чім підтверджіня зрушыте:
+одказ з чім потверджіня зрушыте:
$5
Платность того коду кінчіть $4.',
-'confirmemail_invalidated' => 'Підтверджіня адресы електронічной пошты было зрушене',
-'invalidateemail' => 'Зрушыти підтверджіня адресы електронічной пошты',
+'confirmemail_invalidated' => 'Потверджіня адресы електронічной пошты было зрушене',
+'invalidateemail' => 'Зрушыти потверджіня адресы електронічной пошты',
# Scary transclusion
'scarytranscludedisabled' => '[Вкладаня шаблон міджі вікі є выпнуте]',
-'scarytranscludefailed' => '[Ð\9dе подаÑ\80ило Ñ\81Ñ\8f наÑ\87Ñ\96Ñ\82ати шаблону про $1]',
+'scarytranscludefailed' => '[Ð\9dе вдало Ñ\81Ñ\8f наÑ\82Ñ\8fгнÑ\83ти шаблону про $1]',
'scarytranscludetoolong' => '[URL дуже довгый]',
# Delete conflict
-'deletedwhileediting' => "'''Увага:''' поÑ\87аÑ\81 вашой едітації была тота сторінка змазана!",
+'deletedwhileediting' => "'''Увага:''' бÑ\96гом вашой едітації была тота сторінка змазана!",
'confirmrecreate' => 'Хоснователь [[User:$1|$1]] ([[User talk:$1|діскузія]]) тоту сторінку змазав потім, як сьте зачали едітовати з причінов:
: „$2“
Справды собі хочете знову тоту сторінку створити?',
'autosumm-new' => 'Створена сторінка: $1',
# Live preview
-'livepreview-loading' => 'Ð\9dагÑ\80аваня...',
-'livepreview-ready' => 'Ð\9dагÑ\80аваня… Готово!',
+'livepreview-loading' => 'Ð\97аладовованя...',
+'livepreview-ready' => 'Ð\97аладовованя… Готово!',
'livepreview-failed' => 'Швыдкый нагляд не є доступный! Спробуйте хосновати звычайный нагляд.',
'livepreview-error' => 'Не успішне споїня: $1 "$2". Хоснуйте звычайный нагляд.',
'version-license' => 'Ліценція',
'version-poweredby-credits' => "Тота вікі біжыть на '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001–$1 $2.",
'version-poweredby-others' => 'іншы',
-'version-license-info' => 'MediaWiki Ñ\94 Ñ\81лободнÑ\8bй Ñ\81оÑ\84Ñ\82веÑ\80; можеÑ\82е го Ñ\88Ñ\8bÑ\80иÑ\82и або Ñ\83пÑ\80авлÑ\8fÑ\82и подлÑ\8f Ñ\83Ñ\81ловÑ\96й GNU General Public License, вÑ\8bдаваной Free Software Foundation; бÑ\83дÑ\8c веÑ\80зÑ\96Ñ\8f 2 Ñ\82ой лÑ\96Ñ\86енÑ\86Ñ\96Ñ\97 або (подлÑ\8f ваÑ\88ого Ñ\83важÑ\96нÑ\8f) будьяка пізнїша верзія.
+'version-license-info' => 'MediaWiki Ñ\94 Ñ\81лободнÑ\8bй Ñ\81оÑ\84Ñ\82веÑ\80; можеÑ\82е го Ñ\88Ñ\8bÑ\80иÑ\82и або Ñ\83пÑ\80авлÑ\8fÑ\82и в згодÑ\97 з Ñ\83Ñ\81ловÑ\96Ñ\8fми GNU General Public License, вÑ\8bдаваной Free Software Foundation; бÑ\83дÑ\8c веÑ\80зÑ\96Ñ\8f 2 Ñ\82ой лÑ\96Ñ\86енÑ\86Ñ\96Ñ\97 або (Ñ\8fк Ñ\83важÑ\8bÑ\82е) будьяка пізнїша верзія.
MediaWiki є дістрібуована в надїї, же буде хосновна, але БЕЗ БУДЬЯКОЙ ЗАРУКЫ; не давають ся ани зарукы ПРОДАЙНОСТИ або ВАЛУШНОСТИ ПРО СТАНОВЛЕНЫЙ ЦІЛЬ. Детайлы ся дочітате в текстї GNU General Public License.
'version-software' => 'Іншталованый софтвер',
'version-software-product' => 'Продукт',
'version-software-version' => 'Верзія',
+'version-entrypoints' => 'URL вступных пунктів',
+'version-entrypoints-header-entrypoint' => 'Вступный пункт',
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'Стежка ку файлу',
'htmlform-select-badoption' => 'Вами зазначена величіна не є доступна можность.',
'htmlform-int-invalid' => 'Зазначена величіна не є ціле чісло.',
'htmlform-float-invalid' => 'Зазначена величіна не є чісло.',
-'htmlform-int-toolow' => 'Ð\92ами зазнаÑ\87ена велиÑ\87Ñ\96на Ñ\94 менÑ\88а Ñ\8fк доволене мінімум $1',
-'htmlform-int-toohigh' => 'Ð\92ами зазнаÑ\87ена велиÑ\87Ñ\96на Ñ\94 менÑ\88а Ñ\8fк доволене максімум $1',
+'htmlform-int-toolow' => 'Ð\92ами зазнаÑ\87ене знаÑ\87Ñ\96нÑ\8f Ñ\94 менÑ\88е Ñ\8fк дозволене мінімум $1',
+'htmlform-int-toohigh' => 'Ð\92ами зазнаÑ\87ене знаÑ\87Ñ\96нÑ\8f Ñ\94 менÑ\88а Ñ\8fк дозволене максімум $1',
'htmlform-required' => 'Тота величіна є повинна',
'htmlform-submit' => 'Одослати',
'htmlform-reset' => 'Вернути зміны',
# New logging system
'logentry-delete-delete' => '$1 змазав сторінку $3',
'logentry-delete-restore' => '$1 обновив сторінку $3',
+'logentry-delete-event' => '$1 змінив відимость {{PLURAL:$5|протоколового запису|$5 протоколовых записів}} к сторінцї $3: $4',
+'logentry-delete-revision' => '$1 змінив відимость {{PLURAL:$5|ревізії|$5 ревізій}} на сторінцї $3: $4',
+'logentry-delete-event-legacy' => '$1 змінив відимость протоколовых записів к сторінцї $3',
+'logentry-delete-revision-legacy' => '$1 змінив відимость ревізій на сторінцї $3',
+'logentry-suppress-delete' => '$1 утаїв сторінку $3',
+'logentry-suppress-event' => '$1 тайком змінив відимость {{PLURAL:$5|протоколового запису|$5 протоколовых записів}} к сторінцї $3: $4',
+'logentry-suppress-revision' => '$1 тайком змінив відимость {{PLURAL:$5|ревізії|$5 ревізій}} на сторінцї $3: $4',
+'logentry-suppress-event-legacy' => '$1 тайком змінив відимость протоколовых записів к сторінцї $3',
+'logentry-suppress-revision-legacy' => '$1 тайком змінив відимость ревізій на сторінцї $3',
+'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-noredirect' => '$1 переменовав сторінку $3 на $4 без створїня напрямлїня',
'logentry-move-move_redir' => '$1 переменовав сторінку $3 на $4 з вычерянём напрямлїнём',
'logentry-move-move_redir-noredirect' => '$1 переменовав сторінку $3 на $4 місце напрямлїня без створїня напрямлїня',
+'logentry-patrol-patrol' => '$1 означів ревізію $4 сторінкы $3 як перевірену',
+'logentry-patrol-patrol-auto' => '$1 автоматічно означів ревізію $4 сторінкы $3 як перевірену',
+'logentry-newusers-newusers' => '$1 створив конто хоснователя',
+'logentry-newusers-create' => '$1 створив конто хоснователя',
+'logentry-newusers-create2' => '$1 створив конто хоснователя $3',
+'logentry-newusers-autocreate' => 'Автоматічно было створене конто $1',
'newuserlog-byemail' => 'гело послане електронічнов поштов',
# Feedback
+'feedback-bugornote' => 'Кідь сьте прирыхтованый подробно описати технічный проблем, можете [$1 наголосити хыбу].
+Інакше можете схносновати простый формулать ниже. Ваш коментарь буде приданый на сторінку „[$3 $2]“ разом з вашым іменом хоснователя тай інформаціов о тім, якый бровсер хоснуєте.',
'feedback-subject' => 'Предмет:',
'feedback-message' => 'Повідомлїня:',
'feedback-cancel' => 'Сторно',
'feedback-submit' => 'Одослати одозву',
'feedback-adding' => 'Коментарь ся придавать на сторінку…',
'feedback-error1' => 'Хыба: Нерозознаый резултат з API',
-'feedback-error2' => 'Ð¥Ñ\8bба: Ð\95дÑ\96Ñ\82ованÑ\8f Ñ\81Ñ\8f не подаÑ\80ило',
+'feedback-error2' => 'Ð¥Ñ\8bба: Ð\95дÑ\96Ñ\82ованÑ\8f Ñ\81Ñ\8f не вдало',
'feedback-error3' => 'Хыба: API не вернуло жадну одповідь',
+'feedback-thanks' => 'Дякуєме! Ваш коментарь быв приданый на сторінку „[$2 $1]“.',
'feedback-close' => 'Готово',
+'feedback-bugcheck' => 'Герешнї! Лем перевірьте, ці то не єдна з [$1 уж знамых хыб].',
+'feedback-bugnew' => 'Перевірив(а) єм то. Хочу повідомити нову хыбу.',
# API errors
-'api-error-badaccess-groups' => 'Ð\9dе маÑ\82е доволено нагÑ\80авати файлы на тоту вікі.',
+'api-error-badaccess-groups' => 'Ð\9dе маÑ\82е дозволено заладововати файлы на тоту вікі.',
'api-error-badtoken' => 'Внутрїшня хыба: планый знак.',
-'api-error-copyuploaddisabled' => 'Ð\9dаÑ\87Ñ\96Ñ\82аваня з URL є на тім сервері заказане.',
+'api-error-copyuploaddisabled' => 'Ð\97аладовованя з URL є на тім сервері заказане.',
'api-error-duplicate' => 'На тій вікі уж {{PLURAL:$1|екзістує [$2 другый файл]|екзістують [$2 іншы файлы]}} з такым самым обсягом.',
'api-error-duplicate-archive' => '{{PLURAL:$1|быв [$2 другый файл]|были [$2 даякы другы файлы]}} з такым самым обсягом уж гев оперед {{PLURAL:$1|быв|были}}, але {{PLURAL:$1|быв змазаный|были змазаны}}.',
'api-error-duplicate-archive-popup-title' => '{{PLURAL:$1|дупліцітный файл, якый быв|дупліцітны файл, як были}} змазаны',
'api-error-duplicate-popup-title' => 'Дупліцітны {{PLURAL:$1|файл|файлы}}',
-'api-error-empty-file' => 'Начітаный файл є порожнїй.',
-'api-error-fetchfileerror' => 'Внутрїшня хыба: дішло ку хыбі почас обтриманя файлу.',
-'api-error-file-too-large' => 'Начітаный файл є барз великый.',
+'api-error-empty-file' => 'Заладованый файл є порожнїй.',
+'api-error-emptypage' => 'Створїня новых, порожнїх сторінк неслободно.',
+'api-error-fetchfileerror' => 'Внутрїшня хыба: трафила ся хыба під час обтриманя файлу.',
+'api-error-fileexists-forbidden' => 'Файл з назвов „$1“ уж екзістує тай не годен го переписати.',
+'api-error-fileexists-shared-forbidden' => 'файл з назвов „$1“ уж екзістує в сполочнім усховищу тай не годен го переписати.',
+'api-error-file-too-large' => 'Заладованый файл є барз великый.',
'api-error-filename-tooshort' => 'Назва файлу є барз курта.',
'api-error-filetype-banned' => 'Тот тіп файлу є заказаный.',
'api-error-filetype-missing' => 'Тот файл не мать росшырїня.',
'api-error-hookaborted' => 'Пожадована вами зміна была одмітнута дакотрым росшырінём.',
-'api-error-http' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: не подаÑ\80ило Ñ\81Ñ\8f пÑ\80ипоÑ\97Ñ\82и кÑ\83 серверу.',
-'api-error-illegal-filename' => 'ТоÑ\82а назва Ñ\84айлÑ\83 не Ñ\94 поволена.',
-'api-error-internal-error' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: дÑ\96Ñ\88ло кÑ\83 Ñ\85Ñ\8bбÑ\96 поÑ\87аÑ\81 Ñ\81пÑ\80аÑ\86ованÑ\8f ваÑ\88ого наÑ\87Ñ\96Ñ\82аного файлу.',
+'api-error-http' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: не вдало Ñ\81Ñ\8f пÑ\80ипоÑ\97Ñ\82и к серверу.',
+'api-error-illegal-filename' => 'ТоÑ\82а назва Ñ\84айлÑ\83 не Ñ\94 дозволена.',
+'api-error-internal-error' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: пÑ\80иÑ\82Ñ\80аÑ\84ила Ñ\81Ñ\8f Ñ\85Ñ\8bба пÑ\96д Ñ\87аÑ\81 Ñ\81пÑ\80аÑ\86ованÑ\8f ваÑ\88ого заладованого файлу.',
'api-error-invalid-file-key' => 'Внутрїшня хыба: файл ся не нашов в дочаснім усховіщі.',
'api-error-missingparam' => 'Внутрїшня хыба: хыбують параметры пожадавкы.',
'api-error-missingresult' => 'Внутрїшня хыба: не годен становити, ці копірованя было успішне.',
-'api-error-mustbeloggedin' => 'Ð\9fÑ\80о наÑ\87Ñ\96Ñ\82аванÑ\8f Ñ\84айлÑ\96в мусите быти приголошеный.',
+'api-error-mustbeloggedin' => 'Ð\96ебÑ\8b заладоваÑ\82и Ñ\84айлÑ\8b, мусите быти приголошеный.',
'api-error-mustbeposted' => 'Внутрїшня хыба: пожадавка мусить быти через HTTP POST.',
-'api-error-noimageinfo' => 'Ð\9dаÑ\87Ñ\96Ñ\82аня было успішне, але сервер не додав о файлї жадны інформації.',
-'api-error-nomodule' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: не Ñ\94 наÑ\81Ñ\82авленÑ\8bй модÑ\83л наÑ\87Ñ\96Ñ\82аваня.',
+'api-error-noimageinfo' => 'Ð\97аладованя было успішне, але сервер не додав о файлї жадны інформації.',
+'api-error-nomodule' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: не Ñ\94 наÑ\81Ñ\82авленÑ\8bй модÑ\83л заладовованя.',
'api-error-ok-but-empty' => 'Внутрїшня хыба: сервер не одповідать.',
-'api-error-overwrite' => 'Не є доволене переписати екзістуючій файл.',
-'api-error-stashfailed' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: Ñ\81еÑ\80веÑ\80Ñ\83 Ñ\81Ñ\8f не подаÑ\80ило уложыти дочасный файл.',
+'api-error-overwrite' => 'Ð\9dе Ñ\94 дозволене пеÑ\80епиÑ\81аÑ\82и екзÑ\96Ñ\81Ñ\82Ñ\83Ñ\8eÑ\87Ñ\96й Ñ\84айл.',
+'api-error-stashfailed' => 'Ð\92нÑ\83Ñ\82Ñ\80Ñ\97Ñ\88нÑ\8f Ñ\85Ñ\8bба: Ñ\81еÑ\80веÑ\80Ñ\83 Ñ\81Ñ\8f не вдало уложыти дочасный файл.',
'api-error-timeout' => 'Сервер не одповідав в очекаванім часї.',
-'api-error-unclassified' => 'Ð\94Ñ\96Ñ\88ло кÑ\83 незнамÑ\96й Ñ\85Ñ\8bбÑ\96.',
+'api-error-unclassified' => 'ТÑ\80аÑ\84ила Ñ\81Ñ\8f незнама Ñ\85Ñ\8bба.',
'api-error-unknown-code' => 'Незнама хыба: „$1“',
-'api-error-unknown-error' => 'Внутрїшня хыба: дішло ку хыбі при спробі о начітаня файлу.',
+'api-error-unknown-error' => 'Внутрїшня хыба: трафила ся хыба при спробі о заладованя файлу.',
'api-error-unknown-warning' => 'Незнаме варованя: $1',
+'api-error-unknownerror' => 'Незнама хыба: „$1“',
'api-error-uploaddisabled' => 'Начітаваня файлів є на тій вікі выпнуте.',
'api-error-verification-error' => 'Файл є може пошкодженый, або мать плане росшырїня.',
'duration-years' => '$1 {{PLURAL:$1|рік|рокы|років}}',
'duration-decades' => '$1 {{PLURAL:$1|декада|декады|декад}}',
'duration-centuries' => '$1 {{PLURAL:$1|стороча|стороча|стороч}}',
+'duration-millennia' => '$1 {{PLURAL:$1|тісячроча|тісячроча|тісячроч}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|є недозволеный формат файлів|суть недозволены форматы файлів}}. {{PLURAL:$3|Дозволеный формат фалів є|Дозволены форматы файлів суть}} $2.',
);
'youhavenewmessages' => 'भवदर्थम् $1 सन्ति। ($2).',
'newmessageslink' => 'नूतनाः सन्देशाः',
'newmessagesdifflink' => 'अन्तिमं परिवर्तनम्',
+'youhavenewmessagesfromusers' => '{{PLURAL:$3|अन्ययोजकः|$3 योजकाः}} ($2) इत्यस्मात् भवतः $1 अस्ति ।',
+'youhavenewmessagesmanyusers' => 'अनैकेभ्यः योजकेभ्यः ते $1 सन्ति $2 ।',
+'newmessageslinkplural' => '{{PLURAL:$1|नूतनः सन्देशः|नूतनसन्देशाः}}',
+'newmessagesdifflinkplural' => 'सद्यः {{PLURAL:$1|परिवर्तनम्|परिवर्तनानि}}',
'youhavenewmessagesmulti' => 'भवतः कृते $1 मध्ये नूतनः सन्देशः विद्यते',
'editsection' => 'सम्पाद्यताम्',
'editold' => 'सम्पाद्यताम्',
'enterlockreason' => 'तन्त्रितीकरणस्य कारणं ददातु, अपि च आकलितं ददातु यत् तन्त्रणं कदा उद्घाट्यिष्यते।',
'readonlytext' => 'समंकाधारं वर्तमानकाले तन्त्रितमस्ति नूतनान् प्रविष्टीन् विरुध्य तथा च अन्यानि परिवर्तनानि विरुध्य। इदं नियमिततया समंकाधार परिचर्याऽर्थं तथा स्यात्। तत्पश्चादिदं सामान्यतां संप्राप्स्यति।
तन्त्रितीकारकेन प्रबन्धकेन इदं कारणं प्रदत्तम्: $1',
-'missing-article' => 'दतà¥\8dताधारà¥\87ण(डाà¤\9fाबà¥\87सà¥\8d à¤\87तà¥\8dयनà¥\87न) "$1" $2 à¤\87तिनामà¤\95à¤\82 पà¥\8dरापà¥\8dतवà¥\8dयà¤\82 यतà¥\8d पà¥\83षà¥\8dठà¤\82 ततà¥\8d नà¥\88व पà¥\8dरापà¥\8dतमà¥\8d।
+'missing-article' => 'त्ताधारेण(डाटाबेस् इत्यनेन) "$1" $2 इतिनामकं प्राप्तव्यं यत् पृष्ठं तत् नैव प्राप्तम्।
प्रायः कालातीतस्य अथवा अपाकृतस्य इतिहाससम्पर्कतन्तोः कारणेन एवं भवति।
यदि नैवं तर्हि भवता तन्त्रांशकीटकं प्राप्तं स्यात्।
-कृपया कोऽपि [[Special:ListUsers//sysop|administrator]]अस्य पृष्ठस्य सङ्केतज्ञापनपूर्वकं सूच्यताम्।',
+कृपया कोऽपि [[Special:ListUsers/sysop|administrator]]अस्य पृष्ठस्य सङ्केतज्ञापनपूर्वकं सूच्यताम्।',
'missingarticle-rev' => '(आवृत्तिः# :$1)',
'missingarticle-diff' => '(व्यतिरेक: $1, $2)',
'readonly_lag' => 'मुख्य-समंकाधार-परिवेशकं उपमुख्य-समंकाधार-परिवेशकस्य संप्रापणात् पूर्वे एव स्वतः तन्त्रितम् अस्ति।',
'badtitle' => 'दुष्टं शिरोनाम',
'badtitletext' => 'प्रार्थितं पृष्ठशीर्षकम् अमान्यं रिक्तम् अशुद्धतया सम्बद्धम् आन्तर्भाषिकम्, आन्तर्विकीयं वा शीर्षकमस्ति । अस्मिन् एकं एकाधिकानि वा एतादृशानि अक्षराणि विद्यन्ते येषां प्रयोगं शीर्षकेषु कर्तुम् अशक्यम्।',
'perfcached' => 'अनुपदोक्तं लेखः कैश् इत्येतस्माद् अस्ति, अतः अद्यतनं न स्यात्। {{PLURAL:$1|one result is|$1 results are}}',
-'perfcachedts' => 'अधोनिदेशितलेखः सञ्चितः । पूर्वपदोन्नतिः $1 । $4 अधिकाधिकपरिणामः सञ्चये उपलब्धः ।',
+'perfcachedts' => 'अधोनिदेशितलेखः सञ्चितः । पूर्वपदोन्नतिः $1 । $4 {{PLURAL:}} अधिकाधिकपरिणामः सञ्चये उपलब्धः ।',
'querypage-no-updates' => 'अस्य पृष्ठस्य परिशोधनं विफलीकृतमस्ति ।
सद्यः अत्रत्यः विषयः न नवीक्रियते ।',
'wrong_wfQuery_params' => 'wfQuery() इत्येतस्य अशुद्धः मानदण्डः दत्तः अस्ति<br />
'remembermypassword' => 'अस्मिन् सङ्गणके मम प्रवेशः स्मर्यताम् (अधिकतमम् $1 {{PLURAL:$1|दिनम्|दिनानि}})',
'securelogin-stick-https' => 'प्रवेशोपरान्तं एचटीटीपीएस(HTTPS) इत्यनेन सह संबद्धः तिष्ठतु।',
'yourdomainname' => 'भवतः प्रक्षेत्रम्:',
+'password-change-forbidden' => 'अस्यां विक्यां निकुञ्चं परिवर्तयितुं न शक्नोति ।',
'externaldberror' => 'तत्र प्रमाणीकरण समंकाधारे त्रुटिर्जाता, अथवा भवान् स्वकीयां बाह्य-लेखां अद्यतनीकर्तुं अनुमतिं न धारयति।',
'login' => 'प्रविश्यताम्',
'nav-login-createaccount' => 'प्रविश्यताम्/ सदस्यता प्राप्यताम्',
अल्पकालिकः कूटशब्दः : $2',
'passwordreset-emailsent' => 'एकः स्मारकः विद्युत्सन्देशः प्रेषितोऽस्ति।',
'passwordreset-emailsent-capture' => 'अधो दर्शितस्य विद्युन्मानसङ्केतस्य अनुस्मारकं प्रेषितम् ।',
-'passwordreset-emailerror-capture' => 'अधो निर्दिष्टानुस्मारकः विद्युन्मानसन्देशः रचितः । किन्तुः योजकसम्प्रेषणं विपन्नम् ।',
+'passwordreset-emailerror-capture' => 'अधो निर्दिष्टानुस्मारकः विद्युन्मानसन्देशः रचितः । किन्तुः योजकसम्प्रेषणं विपन्नम् ।$1',
# Special:ChangeEmail
'changeemail' => 'विद्युन्मानपत्रादेशं परिवर्तयतु',
'noarticletext-nopermission' => 'अस्मिन् पृष्ठे अधुना किमपि न विद्यते। भवान् विकिपीडियावर्तिषु अन्येषु पृष्ठेषु इदं [[Special:Search/{{PAGENAME}}|शीर्षकम् अन्वेष्टुम् अर्हति]]
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} related logs अन्वेष्टुम् अर्हति],
अथवा [{{fullurl:{{FULLPAGENAME}}|action=edit}} इदं पृष्ठं स्रष्टुम् अर्हति]</span>.',
+'missing-revision' => '{{PAGENAME}} इति नामाङ्कितपुटस्य #$1 इति पुनरावृत्तिः अत्र नाश्ति ।
+पुटेन सह कालातीतानुबन्धकारणेन एतत् अभवत् ।
+विवरणम् अत्र दृश्यते ।[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].',
'userpage-userdoesnotexist' => '"$1" इति प्रयोक्तृलेखा पञ्जीकृता नास्ति।
चेद्भवान् एतत्पृष्ठं स्रष्टुमिच्छति सम्पादयितुमिच्छति वा तदा कृपया पुनरीक्षताम्।',
'userpage-userdoesnotexist-view' => '"$1" इति प्रयोक्तृलेखा पञ्जीकृता नास्ति।',
'session_fail_preview' => "'''क्षम्यताम्! अस्माभिः भवतः सम्पादनस्य संसाधनं न कर्तुं शक्तम् यस्माद्धि सत्रस्य सूचनाः लुप्ताः।'''
कृपया पुनः चेष्टताम्।
चेदेतत् अधुनाऽपि न कार्यशीलं स्यात्, [[Special:UserLogout|सत्राद्बहिः गत्वा]] पुनः प्रवेशं करोतु।",
-'session_fail_preview_html' => 'लेखभागाभावात् ते परिचर्यां समापयितुं न शक्यते ।',
+'session_fail_preview_html' => 'लेखभागाभावात् ते परिचर्यां समापयितुं न शक्यते ।[[Special:UserLogout|logging out]]',
'token_suffix_mismatch' => "'''ते सम्पादनं तिर्स्कृतम् । यतः ते ग्राहकः सम्पादनप्रतीके लेखानचिह्नानि क्षतविक्षतानि अकरोत्। '''
पाठ्यपुटस्य संरक्षणार्थं सम्पादनावकाशः पिहितः । अनामिकानाम् उपयोगकाले कदाचित् एवं सम्भवति ।",
'edit_form_incomplete' => "'''सम्पादनस्य कतिचनांशाः वितारकं न प्राप्ताः ; सम्पादनं द्विवरं परिशीलयतु । ते सम्पादनानि अनाहतानि, पुनः यतताम् '''",
भवान् एतदपि प्रमाणीकरोति यत् एतद् भवता स्वतः लिखितमस्ति अथवा कस्माच्चत् जनार्पितात् वा मुक्तात् वा स्रोतसः प्रतिलिपीकृतमस्ति।
'''प्रतिलिप्यधिकारयुतान् लेखान्, अनुज्ञां विना, माऽत्र प्रददातु!'''",
-'copyrightwarning2' => "कृपया संस्मर्तव्यं यत् {{SITENAME}} इत्येतद् प्रति कृतानि सर्वाणि योगदानानि $2 इत्यस्य प्रतिबंधांतर्गतानि सन्ति (अधिकाय ज्ञानाय $1 इत्येतद् पश्यतु)।
+'copyrightwarning2' => "कृपया संस्मर्तव्यं यत् {{SITENAME}} इत्येतद् प्रति कृतानि सर्वाणि योगदानानि इत्यस्य प्रतिबंधांतर्गतानि सन्ति (अधिकाय ज्ञानाय $1 इत्येतद् पश्यतु)।
यदि भवान् स्वकीयानि लिखितानि परिवर्तमन्तश्च, पुनः वितर्यमन्तश्च न द्रष्टुमिच्छति तदा मा कृपया माऽत्र योगदानं करोतु। <br />
भवान् एतदपि प्रमाणीकरोति यत् एतद् भवता स्वतः लिखितमस्ति अथवा कस्माच्चत् जनार्पितात् वा मुक्तात् वा स्रोतसः प्रतिलिपीकृतमस्ति।
'''प्रतिलिप्यधिकारयुतान् लेखान्, अनुज्ञां विना, माऽत्र प्रददातु!'''",
-'longpageerror' => "तà¥\8dरà¥\81à¤\9fिà¤\83: à¤à¤µà¤¤à¤¾ पà¥\8dरदतà¥\8dतà¤\83 पाठà¤\83 $1 किलोबैटमितः दीर्घः, अतः एषः अधिकतमानुज्ञातात् $2 मितात् दीर्घतरः अस्ति। एषः रक्षितुं न शक्यते।'''",
-'readonlywarning' => "'''पूर्वसूचना ''' निर्वहणार्थं पाठः पिहितः । अधुना भवान् सम्पादनं रक्षितुं नैव शक्नोति । पाठसञ्चिकायां संश्लेष्य कार्यफलं रक्षतु । एतद्विवरणं प्रतिबन्धकः प्रशासकः विरतरि ।",
+'longpageerror' => "रà¥\81à¤\9fिà¤\83: à¤à¤µà¤¤à¤¾ पà¥\8dरदतà¥\8dतà¤\83 पाठà¤\83 {{PLURAL:}} $1 किलोबैटमितः दीर्घः, अतः एषः अधिकतमानुज्ञातात् $2 मितात् दीर्घतरः अस्ति। एषः रक्षितुं न शक्यते।'''",
+'readonlywarning' => "पूर्वसूचना ''' निर्वहणार्थं पाठः पिहितः । अधुना भवान् सम्पादनं रक्षितुं नैव शक्नोति । पाठसञ्चिकायां संश्लेष्य कार्यफलं रक्षतु । एतद्विवरणं प्रतिबन्धकः प्रशासकः विरतरि ।$1",
'protectedpagewarning' => "'''पूर्वसूचना ''' प्रशासकपदयुक्ताः योजकाः एव सम्पादनं कर्तुमर्हन्ति । अतः एतत्पुटं सुरक्षितम् । निदेशार्थम् अधः जघन्यप्रवेशः सूचितः ।",
'semiprotectedpagewarning' => "'''सूचना ''' पञ्जीकृतयोजकानां उपयोगार्थ केवलम् एतत्पुटम् अभिरक्षितम् । जघन्यप्रवेशस्य सूचना आनुकूल्यार्थम् अधोनिदेशिता ।",
'cascadeprotectedwarning' => "'''पूर्वसूचना ''' प्रशासकसौकर्ययुक्तानां योजकानाम् सम्पादनार्थम् एतत् पुटम् अभिरक्षितमस्ति । यतः अधोनिदेशितनिर्झरे एतदन्तर्गतम् । {{PLURAL:$1|page|pages}}:",
'expansion-depth-exceeded-category' => 'पुटविस्तारस्य गाहः अतिक्रान्तः ।',
'expansion-depth-exceeded-warning' => 'विस्तारगाहातिक्रान्तपुटम् ।',
'parser-unstrip-loop-warning' => 'अपट्टरन्धः दृष्टः ।',
-'parser-unstrip-recursion-limit' => 'अपट्टपुनरवतरणमितिः अतिक्रान्ता ।',
+'parser-unstrip-recursion-limit' => 'अपट्टपुनरवतरणमितिः अतिक्रान्ता ।$1',
+'converter-manual-rule-error' => 'मानवीये भाषापरिवर्तने दोषः दृष्टः ।',
# "Undo" feature
'undo-success' => 'सम्पादनमिदं विपरीतीकर्तुं शक्यते।
'revdelete-show-file-submit' => 'आम्',
'revdelete-selected' => "'''{{PLURAL:$2|Selected revision|Selected revisions}} of [[:$1]]:'''",
'logdelete-selected' => "'''{{PLURAL:$1|Selected log event|Selected log events}}:'''",
-'revdelete-text' => "'''परिमार्जितानि अवतरणानि पुटेतिहासे अद्यापि दृश्यन्ते । तस्य कश्चन भागः सार्वजनिकः न भवति । '''
+'revdelete-text' => "'''परिमार्जितानि अवतरणानि पुटेतिहासे अद्यापि दृश्यन्ते । तस्य कश्चन भागः सार्वजनिकः न भवति । '''
{{SITENAME}} इत्यस्य अन्यप्रशसासकः गुप्तसामग्रीः प्राप्नुवन्ति । अपि च अन्तरापुटेन अस्य अपरिमार्जनं कर्तुं शक्नुवन्ति । यावत् अतिरिक्तप्रतिबन्धकाः न स्थापिताः ।",
'revdelete-confirm' => 'भवान् एतत् कार्यं करोति इति दढयतु । भवान् अस्य परिणामं जानाति । [[{{MediaWiki:Policy-url}}|the policy]] भवान् एतदनुसारं करोति ।',
'revdelete-suppress-text' => 'अधोनिदेशितपरिस्थितिषु केवलं निग्रहः कार्यः ।
'revdelete-log' => 'कारणम् :',
'revdelete-submit' => '{{PLURAL:$1|चितायां आवृत्त्यां|चितासु आवृत्तिषु}} अनुप्रयोजयतु।',
'revdelete-success' => 'अवतरणदृश्यता साफल्येन उन्नतीकृता ।',
-'revdelete-failure' => 'à¤\85वतणदà¥\83शà¥\8dयता à¤\89नà¥\8dनतà¥\80à¤\95रणà¤\82 न शà¤\95à¥\8dयतà¥\87 ।',
+'revdelete-failure' => 'à¤\85वतरणदà¥\83शà¥\8dयता à¤\89नà¥\8dनतà¥\80à¤\95रणà¤\82 न शà¤\95à¥\8dयतà¥\87 ।$1',
'logdelete-success' => 'नामाङ्कनदृश्यता साफल्येन योजिता ।',
'logdelete-failure' => 'नामाभिलेखदृश्यता सपला नाभवत् । $1',
'revdel-restore' => 'दृष्टिविषयः परिवर्त्यताम्',
# Suppression log
'suppressionlog' => 'निग्रहनामाभिलेखः ।',
'suppressionlogtext' => 'अधोनिदेशितप्रशासकैः सङ्गुप्तस्य विभागस्य निष्कासितपुटानां सूची ।
-निषिद्धपिहितपुटानि [Special:BlockList|block list]] पश्यतु ।',
+निषिद्धपिहितपुटानि [[Special:BlockList|block list]] पश्यतु ।',
# History merging
'mergehistory' => 'संलीनपुटेतिहासाः ।',
+'mergehistory-header' => 'एतत्पुटं कस्यचित् स्रोतपुटस्य इतिहासस्य संयोजनार्थमस्ति ।
+एतत्परिवर्तनं पूर्वतनपुटैः नैरन्तर्यं रक्षति इति दृढयतु ।',
+'mergehistory-box' => 'पुटद्वयस्य अवतरणं व्यलीयताम् ।',
+'mergehistory-from' => 'मूलपुटम् ।',
+'mergehistory-into' => 'लक्षितपुटम् ।',
+'mergehistory-list' => 'विलीनयोग्यसम्पादनस्य इतिहासः ।',
+'mergehistory-merge' => '[[:$1]] इत्यस्य निम्नावतरणं । [[:$2]] इत्यनेन संयोजयितुं शक्यते । निर्दिष्टकाले सर्जितानि संयोजयितुं रेडियोपिञ्जस्तम्भम् उपयोजयतु ।
+सञ्चलनस्य अनुबन्धाः स्तम्भमेतं पुनःस्थापयति ।',
+'mergehistory-go' => 'विलीनयोग्यसम्पादनानि दर्शयतु ।',
+'mergehistory-submit' => 'अवतरणं योजयतु ।',
+'mergehistory-empty' => 'अवतरणानि संयोजयितुं न शक्यते ।',
+'mergehistory-success' => '$3 {{PLURAL:$3|revision|revisions}} of [[:$1]] successfully merged into [[:$2]].',
+'mergehistory-fail' => 'इतिहासविलीनता नैव शक्यते । पुटं कालं व्याप्तिं च परिशीलयतु ।',
+'mergehistory-no-source' => 'पूलपुटं $1 अस्तित्वं नास्ति ।',
+'mergehistory-no-destination' => 'लक्षितपुटं $1 अस्तित्वे नास्ति ।',
+'mergehistory-invalid-source' => 'मूलपुटस्य मान्यशीर्षिका स्यात् ।',
+'mergehistory-invalid-destination' => 'लक्षितपुटं मानितशीर्षिकायुतं भवेत् ।',
+'mergehistory-autocomment' => 'लीनं [[:$1]] into [[:$2]]',
+'mergehistory-comment' => 'लीनं [[:$1]] [[:$2]] : $3',
+'mergehistory-same-destination' => 'मूलपुटं लक्षितपुटं च समानं न भवेत् ।',
'mergehistory-reason' => 'कारणम् :',
# Merge log
+'mergelog' => 'नामाभिलेखं योजयतु ।',
+'pagemerge-logentry' => '[[$1]] तु [[$2]] मध्ये विलीनम् (अवतरणं $3 पर्यन्तम् ) ।',
'revertmerge' => 'पृथक्क्रियताम्',
+'mergelogpagetext' => 'अतिनूतनविलीनस्य आवली अधो दत्ता यस्य इतिहासः अन्यस्मिन् अस्ति ।',
# Diffs
'history-title' => '"$1" इत्येतस्य आवर्तनेतिहासः :',
+'difference-title' => '"$1" इत्यस्य अवतरणमध्ये व्यत्यासः ।',
+'difference-title-multipage' => '"$1" तथा "$2" पुटयोः मध्ये व्यत्यासः ।',
+'difference-multipage' => 'पुटेषु व्यत्यासः ।',
'lineno' => 'पंक्तिः $1:',
'compareselectedversions' => 'चितानाम् आवृत्तीनां तोलनं क्रियताम्',
+'showhideselectedversions' => 'चितावतरणानि दर्शयतु/गोपयतु ।',
'editundo' => 'निष्क्रियताम्',
'diff-multi' => '({{PLURAL:$2|योजकेन|$2 योजकैः}} कृता {{PLURAL:$1|मध्यमा आवृत्तिः|$1 मध्यमा आवृत्तयः}} न दर्शिताः ।)',
+'diff-multi-manyusers' => '({{PLURAL:$2|योजकेन|$2 योजकैः}} कृता {{PLURAL:$1|मध्यमा आवृत्तिः|$1 मध्यमा आवृत्तयः}} न दर्शिताः ।)',
+'difference-missing-revision' => '{{PLURAL:$2|One revision|$2 पुनरावृत्तेः }} व्यत्यासः ($1) {{PLURAL:$2|was|were}} न दृष्टः ।
+कारणम् अत्र दृश्यते । [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].',
# Search results
'searchresults' => 'अन्वेषणस्य फलितानि',
'searchresulttext' => '{{SITENAME}} इत्यस्मिन् अन्वेषणे सहाय्यार्थम् [[{{MediaWiki:Helppage}}|{{int:help}}]] इत्येतत् पश्यतु ।',
'searchsubtitle' => 'भवान् \'\'\'[[:$1]]\'\'\'([[Special:Prefixindex/$1|सर्वाणि "$1" इत्यस्माद् आरभमन्तः पृष्ठाणि]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|सर्वाणि "$1" इत्येतत्प्रति संबद्धानि पृष्ठाणि]]) इत्यस्य कृते अन्विष्टवान्।',
'searchsubtitleinvalid' => "भवता '''$1''' इत्यस्य कृते अन्वेषणं कृतम्",
+'toomanymatches' => 'अत्यधिकाः मेलाः प्रत्यागताः । अन्यप्रश्नेन यतताम् ।',
+'titlematches' => 'पुटशीर्षिकामेलाः ।',
'notitlematches' => 'न कस्यापि पृष्ठस्य शीर्षकम् अस्य समम्।',
+'textmatches' => 'पुटपाठस्य मेलाः',
'notextmatches' => 'न कस्यापि पृष्ठस्य पाठः अस्य सममस्ति',
'prevn' => 'प्राक्तनानि {{PLURAL:$1|$1}}',
'nextn' => 'अग्रिमाणि {{PLURAL:$1|$1}}',
'nextn-title' => 'प्राक्तन-{{PLURAL:$1|फलितम्| फलितानि}}',
'shown-title' => 'प्रत्येकस्मिन् पृष्ठे $1 {{PLURAL:$1|फलितम्|फलितानि}} दर्श्यताम्',
'viewprevnext' => 'दर्श्यताम् ($1 {{int:pipe-separator}} $2) ($3)',
+'searchmenu-legend' => 'अन्वेषणस्य विकल्पाः ।',
'searchmenu-exists' => 'अस्मिन् विकिमध्ये "[[:$1]]"नामकं पृष्ठं विद्यते।',
'searchmenu-new' => "'''अस्यां विक्यां \"[[:\$1]]\" इति पृष्ठं सृज्यताम्!'''",
+'searchhelp-url' => 'Help: साहाय्यम् : आधेयाः ।',
+'searchmenu-prefix' => '[[Special:PrefixIndex/$1|एतदुपसर्गयुक्तपुटं पश्यतु ]]',
'searchprofile-articles' => 'आन्तर्यम्',
'searchprofile-project' => 'सहायता प्रकल्पपृष्ठानि च',
'searchprofile-images' => 'बहुमाध्यमः',
'searchprofile-advanced-tooltip' => 'विशेषनामस्थानेषु अन्विष्यताम्',
'search-result-size' => '$1 ({{PLURAL:$2|1 शब्दः|$2 शब्दाः}})',
'search-result-category-size' => '{{PLURAL:$1|1 सदस्यः|$1 सदस्याः}} ({{PLURAL:$2|1 उपवर्गः|$2 उपर्गाः}}, {{PLURAL:$3|1 सञ्चिका|$3 सञ्चिकाः}})',
+'search-result-score' => 'सम्बन्धः $1% ।',
'search-redirect' => '($1 इत्यत्र अनुप्रेषितम्)',
'search-section' => '(विभागः $1)',
'search-suggest' => 'किं भवतः आशयः एवमस्ति : $1',
'search-interwiki-more' => '(अधिकानि)',
'search-mwsuggest-enabled' => 'उपक्षेपेभ्यः सह',
'search-mwsuggest-disabled' => 'नात्र उपक्षेपाः',
+'search-relatedarticle' => 'सम्बद्धानि ।',
+'mwsuggest-disable' => 'निष्क्रियाः AJAX सूचनाः ।',
+'searcheverything-enable' => 'सर्वनामावकाशे अन्विषतु ।',
'searchrelated' => 'सम्बद्धानि',
'searchall' => 'सर्वाणि',
+'showingresults' => "निम्नगतक्रमाङ्कस्य '''$2''' तः आरभ्य अधिकतमं परिणामः'''$1''' {{PLURAL:$1| दर्शितः}}।",
+'showingresultsnum' => "निम्नगतक्रमाङ्क'''$2'''तः आरभ्य अधिकतमः '''$3''' परिणामः {{PLURAL:$3|दर्शितः}}।",
'showingresultsheader' => "'''$4''' इत्येतस्य {{PLURAL:$5|'''$3'''स्य '''$1''' फलितम्|'''$3'''स्य '''$1 - $2''' फलितानि}}",
'nonefound' => "'''सूचना''': स्वतः अत्र केषुचिदेव नामाकाशेषु अन्वेषणं क्रियते।
'qbsettings-none' => 'नास्ति',
'qbsettings-fixedleft' => 'बामे स्थापितः',
'qbsettings-fixedright' => 'दक्षिणे स्थापितः',
+'qbsettings-floatingleft' => 'वामप्लवनम् ।',
+'qbsettings-floatingright' => 'दक्षिणे प्लवनम् ।',
+'qbsettings-directionality' => 'निश्चितम् । ते भाषालिप्याः दिशात्मकतानुसारं भवति ।',
# Preferences page
'preferences' => 'इष्टतमानि',
'mypreferences' => 'मम इष्टतमानि',
'prefs-edits' => 'सम्पादनानां सख्याः',
'prefsnologin' => 'नैव प्रविष्ट',
+'prefsnologintext' => 'वरीयतां परिवर्तयितुं भवता <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}}नामाभिलेखः]</span> करणियः।',
'changepassword' => 'कूटशब्दः परिवर्त्यताम्',
'prefs-skin' => 'त्वक्',
'skin-preview' => 'प्राग्दृश्यम्',
'datedefault' => 'वरीयांसि नास्ति',
+'prefs-beta' => 'आवर्णलक्षणानि ।',
'prefs-datetime' => 'दिनांक तथा समय',
+'prefs-labs' => 'प्रयोगशालालक्षणानि ।',
+'prefs-user-pages' => 'योजकपुटानि ।',
'prefs-personal' => 'योजकः व्यक्तिरेखा',
'prefs-rc' => 'सद्योजातानि परिवर्तनानि',
'prefs-watchlist' => 'दृष्टि सूची',
'prefs-watchlist-days' => 'दृष्टि सूची दर्शनार्थे दिवसानि',
-'prefs-watchlist-days-max' => 'Maximum $1 {{PLURAL:$1|day|days}}',
+'prefs-watchlist-days-max' => 'अधिकतमानि $1 {{PLURAL:$1|दिनानि}}',
+'prefs-watchlist-edits' => 'विस्तृतावलोकनावल्यां प्रदर्शयितुम् अत्यधिकपरिवर्तनानि ।',
'prefs-watchlist-edits-max' => 'अधिकतम संख्या: १०००',
+'prefs-watchlist-token' => 'अवलोकनावल्याः प्रतीकः ।',
'prefs-misc' => 'विविधः',
'prefs-resetpass' => 'कूटशब्दः परिवर्त्यताम्',
+'prefs-changeemail' => 'विद्युन्मानपत्रसङ्केतं परिवर्तयतु ।',
+'prefs-setemail' => 'विद्युन्मानपत्रसङ्केतं योजयतु ।',
'prefs-email' => 'इमेल वैकल्पिकाः',
'prefs-rendering' => 'स्वरुपः',
'saveprefs' => 'संरक्ष्यताम्',
'resetprefs' => 'असंरक्षितानि परिवर्तनानि विलुप्यन्ताम्',
'restoreprefs' => 'समग्राः व्यवस्थादय व्यवस्थानुसारं पुनः संरक्ष्यताम्',
'prefs-editing' => 'सम्पादनम्',
+'prefs-edit-boxsize' => 'सम्पादनकोष्ठस्य आकारः ।',
'rows' => 'पंक्ति',
'columns' => 'अध: पंक्त्याः',
'searchresultshead' => 'अन्वेषणम्',
'resultsperpage' => 'प्रति पृष्ट हिट्स:',
+'stub-threshold' => '<a href="#" class="stub">आधारानुबन्धानां </a>अधिकतमाकारः ।',
'stub-threshold-disabled' => 'निष्क्रियः',
'recentchangesdays' => 'दिवसानि पर्यन्तो सद्यावधि-परिवर्तनानि दृश्यतु:',
'recentchangesdays-max' => 'अधिकतम $1 {{PLURAL:$1|दिवसः|दिवसानि}}',
'recentchangescount' => 'सम्पादन संख्यकानि व्यवस्थानुसारेण दृश्यतु:',
+'prefs-help-recentchangescount' => 'अत्र सद्यः परिवर्तनानि, पुटेतिहासाः, प्रवेशाः च अन्तर्गताः ।',
+'prefs-help-watchlist-token' => 'अत्र रहस्यकुञ्चिकया पूरणेन भवतः नीरीक्षावल्यां RSS पूरितं भवति । रहस्यकुञ्चिकां यः जानाति तेन भवतः निरीक्षावली दृष्टुं शक्यते । अतः कृपया सुरक्षमौल्यं चिनोतु । अत्र यादृच्छया निर्मितं मौल्यं भवान् $1 द्वारा पश्यति ।',
+'savedprefs' => 'आद्यताः संरक्षिताः ।',
'timezonelegend' => 'समय मण्डल:',
'localtime' => 'स्थानीय समय:',
+'timezoneuseserverdefault' => 'विकिनिश्चितं ($1) उपयुज्यताम् ।',
+'timezoneuseoffset' => 'अन्ये (समयान्तरं निर्दिशतु )',
+'timezoneoffset' => 'समयान्तरम् ¹',
+'servertime' => 'वितारकसमयः ।',
+'guesstimezone' => 'जालदर्शिकातः पूरयतु ।',
'timezoneregion-africa' => 'कालद्वीप',
'timezoneregion-america' => 'अमेरिका',
'timezoneregion-antarctica' => 'अंटार्कटिका',
'timezoneregion-europe' => 'यूरोप',
'timezoneregion-indian' => 'हिंद महासागर',
'timezoneregion-pacific' => 'प्रशांत महासागर',
+'allowemail' => 'अन्योपयोजकानां विद्युन्मानसङ्केतं निष्कियं करोतु ।',
+'prefs-searchoptions' => 'अन्वेषणविकल्पाः ।',
+'prefs-namespaces' => 'नामाकाशः :',
+'defaultns' => 'अन्यथा एतेषु नामाकाशेषु अन्विषतु ।',
'default' => 'यदभावे',
'prefs-files' => 'सञ्चिका',
+'prefs-custom-css' => 'सि.एस्.एस्.रचयतु ।',
+'prefs-custom-js' => 'जावालिपिं रचयतु ।',
+'prefs-common-css-js' => 'सर्वावरणानां कृते विभक्त सि.एस्.एस्./ जावालिपिः ।',
+'prefs-reset-intro' => 'आद्यतानां पुनर्निदेशार्थम् एतत्पुटम् उपयोक्तुं शकोति । एतत् अकृतं न भवति ।',
+'prefs-emailconfirm-label' => 'विद्युन्मानसङ्केतस्य दृढीकरणम् ।',
+'prefs-textboxsize' => 'सम्पादनकोष्ठस्य आकारः ।',
'youremail' => 'ईपत्रसङ्केतः',
'username' => 'योजकनामन्:',
'uid' => 'प्रयोक्तृ-क्रमांकः :',
'prefs-registration' => 'पंजीकरण कालः:',
'yourrealname' => 'वास्तविकं नाम:',
'yourlanguage' => 'भाषा:',
+'yourvariant' => 'भाषासामग्रीणां संस्करणम् ।',
+'prefs-help-variant' => ' विक्यां प्रदर्शितुं भवति ।',
'yournick' => 'नूतनाः हस्ताक्षराः:',
'prefs-help-signature' => 'संभाषणपृष्ठगताः संवादाः "<nowiki>~~~~</nowiki>" इति लिखित्वा हस्ताक्षरोपेताः कर्त्तव्याः। एतानि चिह्नानि पृष्ठरक्षणपश्चात् भवतः हस्ताक्षरान् समयमुद्रां च प्रदर्शयिष्यन्ति।',
'badsig' => 'अमान्याः (त्रुटिपूर्णाः) हि एते अपक्वाः हस्ताक्षराः।
'gender-unknown' => 'अनिर्दिष्टम्',
'gender-male' => 'पुरुष',
'gender-female' => 'स्त्री',
+'prefs-help-gender' => 'वैकल्पिकः : अयं तन्त्रांशः लिङ्गानुसारसम्बोधनस्य उपयोजकः ।',
'email' => 'विद्युत्पत्रव्यवस्था',
+'prefs-help-realname' => 'निजनामधेयस्य उल्लेखः आवश्यकः नास्ति ।
+यदि ददाति तर्हि अस्य प्रयोगः भवतः योगदानार्थं भवते श्रेयं दातुम् उपयुक्तः भवति ।',
'prefs-help-email' => 'ईपत्रसङ्केतः अनिवार्यः नास्ति । किन्तु कूटशब्दः विस्मर्यते चेत् तस्य परिवर्तनाय अवश्यकः भवति ।',
'prefs-help-email-others' => 'अन्ये योजकाः ईपत्रमाध्यमेन भवतः सम्पर्कं यथा कुर्युः तथा भवदीये योजकपृष्ठे सम्भाषणपृष्ठे वा सम्पर्कतन्तुः योजयितुं शक्यः ।
भवतः सम्पर्कं कृतवद्भिः योजकैः भवदीयः ईपत्रसङ्केतः अभिज्ञातः न भवति ।',
+'prefs-help-email-required' => 'विद्युन्मानपत्रसङ्केतः आवश्यकः ।',
+'prefs-info' => 'मूलसूचनाः ।',
+'prefs-i18n' => 'अन्ताराष्ट्रिकरणम् ।',
'prefs-signature' => 'हस्ताक्षर',
+'prefs-dateformat' => 'दिनाङ्कस्य प्रारूपः',
+'prefs-timeoffset' => 'समयान्तरम् ।',
+'prefs-advancedediting' => 'उन्नतविकल्पाः',
+'prefs-advancedrc' => 'उन्नतविकल्पाः',
+'prefs-advancedrendering' => 'उन्नतविकल्पाः',
+'prefs-advancedsearchoptions' => 'उन्नतविकल्पाः',
+'prefs-advancedwatchlist' => 'उन्नतविकल्पाः',
+'prefs-displayrc' => 'प्रदर्शनविकल्पाः',
+'prefs-displaysearchoptions' => 'प्रदर्शनविकल्पाः',
+'prefs-displaywatchlist' => 'प्रदर्शनविकल्पाः',
+'prefs-diffs' => 'अन्तरम्',
+
+# User preference: e-mail validation using jQuery
+'email-address-validity-valid' => 'प्रयुक्तः विद्युन्मानपत्रसङ्केतः मानितः ।',
+'email-address-validity-invalid' => 'मान्यः विद्युन्मानपत्रसङ्केतः योजनीयः ।',
# User rights
+'userrights' => 'योजकाधिकारस्य प्रबन्धनम् ।',
+'userrights-lookup-user' => 'योजकसमूहं प्रबन्धयतु ।',
+'userrights-user-editname' => 'योजकनाम योजयतु ।',
+'editusergroup' => 'योजकसमूहं सम्पादयतु ।',
+'editinguser' => "'''[[User:$1|$1]]''' $2 इति योजकस्य योजकाधिकारः परिवर्त्यते ।",
+'userrights-editusergroup' => 'योजकसमूहं सम्पादयतु ।',
+'saveusergroups' => 'योजकसमूहं संरक्षतु ।',
+'userrights-groupsmember' => 'अस्य सदस्यः ।',
+'userrights-groupsmember-auto' => 'अस्य निश्चितसदस्यः ।',
+'userrights-groups-help' => 'अस्य सदस्यस्य समूहसदस्यत्वं परिवर्तयितुं शक्यते ।
+* मञ्जूषा अङ्किता चेत् योजकः अस्य समूहस्य सदस्यः अस्ति ।
+* मञ्जूषा अनङ्किता चेत् योजकः अस्य समूहस्य सदस्यः न
+* कदाचित् भवता समूहः योजितः चेत् अपनेतुं नैव शक्नोति इति * चिह्नं सूचयति ।',
'userrights-reason' => 'कारणम् :',
+'userrights-no-interwiki' => 'अन्यविकिषु योजकाधिकारं सम्पादयितुं ते अनुमतिः नास्ति ।',
+'userrights-nodatabase' => '$1 मूलपाठाः न सन्ति अथवा स्थानीयाः ।',
+'userrights-nologin' => '[[Special:UserLogin|log in]] प्रशासकस्थानेन प्रविश्य योजकाधिकारान् निर्देष्टुं शक्नोति ।',
+'userrights-notallowed' => 'योजकाधिकारान् अपनेतुं ते स्थानस्य अनुमतिः नास्ति ।',
+'userrights-changeable-col' => 'परिवर्तनार्हाः समूहाः ।',
+'userrights-unchangeable-col' => 'परिवर्तनार्हाः समूहाः ।',
# Groups
+'group' => 'समूहः :',
'group-user' => 'योजकः',
+'group-autoconfirmed' => 'स्वदृढितयोजकाः ।',
+'group-bot' => 'स्वयं सक्रियाः ।',
'group-sysop' => 'प्रबंधकाः',
+'group-bureaucrat' => 'स्वयम् अधिकारिणः ।',
+'group-suppress' => 'अलक्ष्यम् ।',
'group-all' => '(सर्वे)',
'group-user-member' => '{{GENDER:$1|योजक}}',
+'group-autoconfirmed-member' => '{{GENDER:$1|स्वस्थानदृढितः योजकः}}',
+'group-bot-member' => '{{GENDER:$1|स्वयं सक्रियः}}',
+'group-sysop-member' => '{{GENDER:$1|प्रशासकः}}',
+'group-bureaucrat-member' => '{{GENDER:$1|स्वयम् अधिकारी}}',
+'group-suppress-member' => '{{GENDER:$1|अलक्ष्यम्}}',
'grouppage-user' => '{{ns:project}}:योजक',
+'grouppage-autoconfirmed' => '{{ns:project}}: स्वयंदृढितयोजकाः ।',
+'grouppage-bot' => '{{ns:project}}: स्वयंसक्रियाः।',
'grouppage-sysop' => '{{ns:project}}:प्रचालकाः',
+'grouppage-bureaucrat' => '{{ns:project}}: स्वयम् अधिकारिणः ।',
+'grouppage-suppress' => '{{ns:project}}: अक्ष्यम् ।',
+
+# Rights
+'right-read' => 'पुटानि पठतु ।',
+'right-edit' => 'पुटसम्पादनं करोतु ।',
+'right-createpage' => 'पुटनिर्माणं करोतु ।(यानि चर्च्यानि न सन्ति)',
+'right-createtalk' => 'चर्च्यपुटानां निर्माणं करोतु ।',
+'right-createaccount' => 'नूतनयोजकस्थानं निर्मातु ।',
+'right-minoredit' => 'सम्पादनं लघुचिह्नया निर्दिशतु ।',
+'right-move' => 'पुटं चालयतु ।',
+'right-move-subpages' => 'उपपुटैः सह पुटं चालयतु ।',
+'right-move-rootuserpages' => 'मूलयोजकपुटानि चालयतु ।',
+'right-movefile' => 'सञ्चिकाः चालयतु ।',
+'right-suppressredirect' => 'पुटचालनावसरे मूलपुटेभ्यः पुनर्निदेशं न सृजतु ।',
+'right-upload' => 'सञ्चिकाः उत्तारयतु ।',
+'right-reupload' => 'स्थितसञ्चिकाः पुनर्लिखतु ।',
+'right-reupload-own' => 'एकेन उत्तारितसञ्चिकाः पुनर्लिखतु ।',
+'right-reupload-shared' => 'विभक्तमाध्यमकोशगतसञ्चिकाः अतिसञ्चरतु ।',
+'right-upload_by_url' => 'अन्तर्जालस्थानात् सञ्चिकाः उत्तारयतु ।',
+'right-purge' => 'दृढतारहितपुटस्य क्षेत्राधारं पुनातु ।',
+'right-autoconfirmed' => 'अल्परक्षितपुटनि सम्पादयतु ।',
+'right-bot' => 'स्वचालितप्रक्रियाः इव उपचारितः भवतु ।',
+'right-nominornewtalk' => 'चर्चापुटानां लघुसम्पादनं न भवतु । नूतनसन्देशान् चोदयतु ।',
+'right-apihighlimits' => 'API प्रश्नेषु उन्नतसीमम् उपयोजयतु ।',
+'right-writeapi' => 'श्वेतं API उपयोगः ।',
+'right-delete' => 'पुटानि परिमार्जयतु ।',
+'right-bigdelete' => 'दीर्घेतिहासयुक्तपुटानि परिमार्जयतु ।',
+'right-deletelogentry' => 'निर्दिष्टनामाभिलेकप्रवेशं परिमार्जयतु अपरिमार्जयतु च ।',
+'right-deleterevision' => 'निर्दिष्टावरतरणस्य पुटानि अपमर्जतु, अनपमर्जतु ।',
+'right-deletedhistory' => ' तत्सम्बद्धपाठैः विनाअपमर्जितेतिहासप्रवेशस्य दर्शनम् ।',
+'right-deletedtext' => 'अपमर्जितावतरणेषु परिवर्तनं, अपमर्जितपाठान् च अवलोकयतु ।',
+'right-browsearchive' => 'अपमर्जितपुटानि अन्विषतु ।',
+'right-undelete' => 'पुटम् अनपमर्जतु ।',
+'right-suppressrevision' => 'प्रशासकेभ्यः सङ्गुप्तावतरणानि पुनरालोक्य पुनरानयतु ।',
+'right-suppressionlog' => 'स्वायत्तनामाबिलेखं पश्यतु ।',
+'right-block' => 'अन्ययोजकान् सम्पादनेन अवरोधतु ।',
+'right-blockemail' => 'योजकस्य विद्युन्मानसन्देशप्रेषणम् अवरोधतु ।',
+'right-hideuser' => 'योजकनाम अवरोधतु । तेन सर्वजनोपयोगात् गोपयतु ।',
+'right-ipblock-exempt' => 'IP अवरोधं मार्गयतु, स्वयम् अवरोधः, निर्दिष्टावरोधः ।',
+'right-proxyunbannable' => 'अन्येषां स्वयंचालितावरोधं परिहरतु ।',
+'right-unblockself' => 'स्वयम् अनवरोधं करोतु ।',
+'right-protect' => 'सुरक्षास्तरान् परिवर्तयतु । सुरक्षितपुटानि सम्पादयतु ।',
+'right-editprotected' => 'सुरक्षितपुटानि सम्पादयतु ।',
+'right-editinterface' => 'योजकमाध्यमं सम्पादयतु ।',
+'right-editusercssjs' => 'अन्ययोजकान् सम्पादयतु । सि.एस्.एस्. जावलालिपिसञ्चिकाः च ।',
+'right-editusercss' => 'अन्ययोजकान् सम्पादयतु सि.एस्.एस्. सञ्चिकाः ।',
+'right-edituserjs' => 'अन्ययोजकान सम्पादयतु जावालिपिसञ्चिकाः ।',
+'right-rollback' => 'अन्तिमयोजकस्य सम्पादनं शीघ्रं प्रचालयतु यः निर्दिष्टपुटं सम्पादितवान् ।',
+'right-markbotedits' => 'प्रतिचालितसम्पादनानि स्वचालितसम्पदनं इव अङ्कितानिकरोतु ।',
+'right-noratelimit' => 'मूल्यनियत्या प्रभावितं नस्यात् ।',
+'right-import' => 'अन्यविकितः पुटानाम् आयातं करोतु ।',
+'right-importupload' => 'उत्तारितसञ्चिकातः पुटानि आयातानि करोतु ।',
+'right-patrol' => 'अन्येषां सम्पादनम् आरक्षितमिव अङ्कयतु ।',
+'right-autopatrol' => 'कस्यचित् स्वस्य सम्पादनानि आरक्षितमिव स्वयम् अङ्कयतु ।',
+'right-patrolmarks' => 'आरक्षणाङ्कितानां सद्यः परिवर्तनानि अवलोकयतु ।',
+'right-unwatchedpages' => 'अपरीक्षितपुटानाम् आवलीम् अवलोकयतु ।',
+'right-mergehistory' => 'पुटेतिहासं विलीनं करोतु ।',
+'right-userrights' => 'सर्वयोजकाधिकारं सम्पादयतु ।',
+'right-userrights-interwiki' => 'योजकाधिकारान् अन्यविकिषु सम्पादयतु ।',
+'right-siteadmin' => 'पाठमूलस्य निशेधनम् अनिशेधनं च ।',
+'right-override-export-depth' => 'पञ्चस्तरपर्यन्तं संलग्नपुटानि निर्यातानि करोतु ।',
+'right-sendemail' => 'अन्ययोजकेभ्यः विद्युन्मानपत्राणि प्रेषयतु ।',
+'right-passwordreset' => 'निकुञ्चपुनारचितानां विद्युन्मानपत्राणाम् अवलोकनम् ।',
# User rights log
'rightslog' => 'प्रयोक्तृ-अधिकार-सूचिका',
+'rightslogtext' => 'अयं योजकाधिकारस्य परिवर्तनकुञ्चः ।',
+'rightslogentry' => '$2 - $3 तः $1 सामूहिकसदस्यत्वं परिवर्तितम् ।',
+'rightslogentry-autopromote' => '$2 तः $3 स्वयम् उन्नतीकृतम् ।',
'rightsnone' => '(कतम)',
# Associated actions - in the sentence "You do not have permission to X"
+'action-read' => 'एतत्पुटं पठतु ।',
'action-edit' => 'इदं पृष्ठं सम्पाद्यताम्',
+'action-createpage' => 'पुटानि सृजतु ।',
+'action-createtalk' => 'चर्चापुटानि सृजतु ।',
+'action-createaccount' => 'नूतनयोजकस्थानं निर्मातु ।',
+'action-minoredit' => 'एतत्सम्पादनं लघु इति अङ्कयतु ।',
+'action-move' => 'एतत्पुटं चालयतु ।',
+'action-move-subpages' => 'एतत्पुटम् अस्य उपपुटानि च चालयतु ।',
+'action-move-rootuserpages' => 'मूलयोजकपुटानि चालयतु ।',
+'action-movefile' => 'एतां सञ्चिकां चालयतु ।',
+'action-upload' => 'एतां सञ्चिकाम् उत्तारयतु ।',
+'action-reupload' => 'स्थितसञ्चिकां पुनर्लिखतु ।',
+'action-reupload-shared' => 'विभक्तकोशे एतां सञ्चिकां पुनर्लिखतु ।',
+'action-upload_by_url' => 'अन्तर्जालस्थानतः एतां सञ्चिकाम् उत्तारयतु ।',
+'action-writeapi' => 'श्वेतं API उपयोगः ।',
+'action-delete' => 'एतत्पुटं अपमर्जयतु ।',
+'action-deleterevision' => 'एतदवतरणम् अपमर्जतु ।',
+'action-deletedhistory' => 'अस्य पुटस्य अपमर्जितेतिहासम् अवलोकयतु ।',
+'action-browsearchive' => 'अपमर्जितपुटानि अन्विषतु ।',
+'action-undelete' => 'एतत्पुटम् अनपमर्जयतु ।',
+'action-suppressrevision' => 'सङ्गुप्तावतरणं पुनःपश्यतु पुनर्नयतु च ।',
+'action-suppressionlog' => 'एतत् स्वायत्तपुटम् अवलोकयतु ।',
+'action-block' => 'अन्ययोजकान् सम्पादनेन अवरोधतु ।',
+'action-protect' => 'अस्य पुटस्य सुरक्षास्तरं परिवर्तयतु ।',
+'action-rollback' => 'अन्तिमयोजकस्य सम्पादनं शीघ्रं प्रचालयतु यः निर्दिष्टपुटं सम्पादितवान् ।',
+'action-import' => 'अन्यविकितः एतत्पुटम् आयातयतु ।',
+'action-importupload' => 'उत्तारितसञ्चिकातः पुटानि आयातानि करोतु ।',
+'action-patrol' => 'अन्येषां सम्पादनम् आरक्षितमिव अङ्कयतु ।',
+'action-autopatrol' => 'भवतः सम्पादनम् आरक्षितम् इति अङ्कयतु ।',
+'action-unwatchedpages' => 'अपरीक्षितपुटानाम् आवलीम् अवलोकयतु ।',
+'action-mergehistory' => 'पुटेतिहासं विलीनं करोतु ।',
+'action-userrights' => 'सर्वयोजकाधिकारं सम्पादयतु ।',
+'action-userrights-interwiki' => 'योजकाधिकारान् अन्यविकिषु सम्पादयतु ।',
+'action-siteadmin' => 'पाठमूलस्य निशेधनम् अनिशेधनं च ।',
+'action-sendemail' => 'विद्युन्मानपत्राणि प्रेषयतु ।',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|परिवर्तनम्|परिवर्तनानि}}',
'minoreditletter' => '(लघु)',
'newpageletter' => '(नवीनम्)',
'boteditletter' => '(बोट्)',
+'number_of_watching_users_pageview' => '[$1 अवलोकयति {{PLURAL:$1|योजकः|योजकाः}}]',
+'rc_categories' => 'वर्गान् नियतीकरोतु ।',
'rc_categories_any' => 'कश्चित्',
+'rc-change-size-new' => '$1 {{PLURAL:$1|byte|bytes}} परिवर्तनपश्चात् ।',
'newsectionsummary' => '/* $1 */ नवीन विभागः',
'rc-enhanced-expand' => 'विवरणानि दर्श्यन्ताम् (जावालिपिः अपेक्ष्यते)',
'rc-enhanced-hide' => 'विवरणानि गोप्यन्ताम्',
+'rc-old-title' => 'मूलरूपेण $1 इति रचितम् ।',
# Recent changes linked
'recentchangeslinked' => 'पृष्ठसम्बद्धानि परिवर्तनानि',
# Upload
'upload' => 'सञ्चिका आरोप्यताम्',
'uploadbtn' => 'सञ्चिका आरोप्यताम्',
+'reuploaddesc' => 'उत्तारणम् अपकर्षतु उत्तरणप्रपत्रम् आगच्छतु च ।',
+'upload-tryagain' => 'उन्नतीकृतं सञ्चिकाविवरणं समर्पयतु ।',
+'uploadnologin' => 'न प्रविष्टम्',
+'uploadnologintext' => 'सञ्चिकारोपणाय [[Special:UserLogin|अन्तःप्रवेशः]] अपेक्षितः ।',
+'upload_directory_missing' => 'उत्तारणनिदेशनं ($1) नष्टम्, जालवितारकेन सर्जितुं न शक्यते ।',
+'upload_directory_read_only' => 'उत्तारणनिदेशनं ($1) तु जालवितारकेन लेखनयोग्यं नास्ति ।',
+'uploaderror' => 'उत्तरणदोषः ।',
+'upload-recreate-warning' => "''' पूर्वसूचना ''' तन्नामयुक्ता सञ्चिका अपमर्जिता अथवा चालिता ।",
+'uploadtext' => "सञ्चिकाः उत्तर्तुम् अधः सूचितरूपणि उपयोजयतु ।
+To view or search previously uploaded files go to the [[Special:FileList|list of uploaded files]], (re)uploads are also logged in the [[Special:Log/upload|upload log]], deletions in the [[Special:Log/delete|deletion log]].
+
+To include a file in a page, use a link in one of the following forms:
+* '''<tt><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></tt>''' to use the full version of the file
+* '''<tt><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|alt text]]</nowiki></tt>''' to use a 200 pixel wide rendition in a box in the left margin with 'alt text' as description
+* '''<tt><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></tt>''' for directly linking to the file without displaying the file",
+'upload-permitted' => 'अनुमतसञ्चिकाभेदाः $1.',
+'upload-preferred' => 'अनुमतसञ्चिकाभेदाः $1.',
+'upload-prohibited' => 'अनुमतसञ्चिकाभेदाः $1.',
+'uploadlog' => 'उत्तरणस्य सूची ।',
'uploadlogpage' => 'आरोपितानां सूची',
+'uploadlogpagetext' => 'अधः सद्यः काले उत्तारितसञ्चिकानाम् आवली अस्ति ।
+अधिकदृश्यविवरणार्थम् एतत् पश्यतु [[Special:NewFiles|gallery of new files]]',
'filename' => 'सञ्चिकानाम',
'filedesc' => 'सारांशः :',
'fileuploadsummary' => 'संग्रहः :',
+'filereuploadsummary' => 'सञ्चिकापरिवर्तनानि ।',
+'filestatus' => 'प्रतिकृत्यधिकारस्य स्थितिः ।',
'filesource' => 'मूल:',
'uploadedfiles' => 'आरोपिताः सञ्चिकाः',
+'ignorewarning' => 'पूर्वसूचनां निर्लक्ष्य सञ्चिकाः कथञ्चित् संरक्षतु ।',
+'ignorewarnings' => 'पूर्वसूचनाः निर्लक्षतु ।',
+'minlength1' => 'सञ्चिकानाम न्यूनतिन्यूनम् एकाक्षरं भवेत् ।',
+'illegalfilename' => 'अस्यां "$1" सञ्चिकानाम्नि सङ्ख्या अस्ति । अत्र सा निषिद्धा । सञ्चिकां पुनः नामाङ्कयतु ।',
+'filename-toolong' => 'सञ्चिकानाम २४०बैट्स्तः अधिकदीर्घं न भवेत् ।',
+'badfilename' => '"$1" इति सञ्चिकानाम परिवर्तितम् ।',
+'filetype-mime-mismatch' => '".$1" इति सञ्चिकाविस्तारः अपमर्जितया MIME ($2) प्रकारस्य सञ्चिका मेलं न करोति ।',
+'filetype-badmime' => 'MIME प्रकारस्य "$1" सञ्चिकाः उत्तारयितुं नार्हन्ति ।',
+'filetype-bad-ie-mime' => 'जालदर्शिकया सूचितं यत् "$1" सञ्चिका अपायकरिणीसञ्चिका इति । अतः एताम् उत्तारयितुं नैव शक्यते ।',
+'filetype-unwanted-type' => "'''\".\$1\"''' काचित् अनपक्षिता सञ्चिका अस्ति ।
+अपेक्षिता सञ्चिका एषा {{PLURAL:\$3|अस्ति}} \$2।",
+'filetype-banned-type' => '\'\'\'".$1"\'\'\'सञ्चिका {{PLURAL:$4|प्रकारस्य }} अनुमतिः नास्ति ।
+प्रकारसञ्चिकायाः{{PLURAL:$3|}} अनुमतिरस्ति $2।',
+'filetype-missing' => 'अस्याः सञ्चिकायाः विस्तारः नास्ति । (उदाहरणम् ".jpg")।',
+'empty-file' => 'समर्पिता सञ्चिका रिक्ता अस्ति ।',
+'file-too-large' => 'संयोजिता शीर्षिका सुदीर्घा अस्ति ।',
+'filename-tooshort' => 'सञ्चिकानाम अतीव ह्रस्वम् अस्ति ।',
+'filetype-banned' => 'ईदृशी सञ्चिका प्रतिबन्धिता ।',
+'verification-error' => 'सञ्चिकापरीक्षायाम् इयं सञ्चिका अनुत्तीर्णा ।',
+'hookaborted' => 'भवतः संस्करणप्रयत्नः विस्तारेण अपसारितः ।',
+'illegal-filename' => 'सञ्चिकानामलेखनं नानुमतः ।',
+'overwrite' => 'वर्तमानसञ्चिकायाः पुनर्लेखनं नानुमतम् ।',
+'unknown-error' => 'अज्ञातदोषः उपगतः ।',
+'tmp-create-error' => 'तत्कालिकसञ्चिकां सृष्टुं नैव शक्यते ।',
+'tmp-write-error' => 'तात्कालिकसञ्चिकायाः दोषसम्पादनम् ।',
+'large-file' => '$1; इयं सञ्चिका $2. तः अधिका दीर्घा न स्यात् इति सूचितम् ।',
+'largefileserver' => 'इयं सञ्चिका वितारकस्य निदेशनात् अधिका दीर्घा अस्ति ।',
+'emptyfile' => 'उत्तारितसञ्चिका रिक्ता इति भाति ।
+सञिकानामाङ्कनकारणं स्यात् ।
+एतां सञ्चिकाम् उत्तारयितुमिच्छति वा इति परिशीलयतु ।',
+'windows-nonascii-filename' => 'एषा विकि विशेषाक्षरयुक्तं सञ्चिकानाम न अनुमन्यते ।',
+'fileexists' => "अनेन सञ्चिकानाम्ना काचित् सञ्चिकास्ति । यदि निश्चयेन न जानाति परिवर्तयितुम् इच्छति तर्हि '''<tt>[[:$1]]</tt>''' एतत् परिशीलयतु । : [[$1|thumb]]",
+'filepageexists' => "अस्याः सञ्चिकायाः विवरणपुटम् तावत् निर्मितम् एव । '''<tt>[[:$1]]</tt>''', अनेन नाम्ना सद्यः कापि सञ्चिका वर्तते ।
+लिखितसारांशः विवरणपुटे न आगमिष्यति ।
+ते सारांशः तत्रागन्तुं स्वयं सम्पादयतु । [[$1|thumb]]",
+'fileexists-extension' => "अनेन नाम्ना सदृनामाङ्किता सञ्चिका पूर्वमेव अस्ति । [[$2|thumb]]
+* उत्तर्यमानसञ्चिकायाः नाम '''<tt>[[:$1]]</tt>'''
+* वर्तमानसञिकायाः नाम '''<tt>[[:$2]]</tt>'''
+* अन्यनाम चिनोतु ।",
+'fileexists-thumbnail-yes' => "एषा सञ्चिका बृहच्चित्रस्य क्षीणाकारा इति भाति । ''(उङ्गुष्टाकारः)'' [[$1|thumb]]
+'''<tt>[[:$1]]</tt>''' सञ्चिकां पश्यतु ।
+यदि परिक्षिता सञ्चिका एतादृशाकरस्य भवति तर्हि उत्तारणस्य आवश्यकता नास्ति ।",
+'file-thumbnail-no' => "सञ्चिकानाम आरभते '''<tt>$1</tt>'''एतस्मात् ।
+न्यूनीकृताकारस्य चित्रम् इति भाति
+यदि एतच्चित्रं मूलाकारेण अस्ति तर्हि उत्तारयतु अन्यथा न ।",
+'fileexists-forbidden' => 'एदादृशनाम्नः सञ्चिका तावत् पूर्वमेवोपस्थिता । अस्य स्थाने अन्यां नोत्तारयितुं शक्यते ।
+तथापि यदि एतां सञ्चिकाम् उत्तारयितुम् इच्छति तर्हि सञ्चिकायाः नाम परिवर्तयतु ।
+[[File:$1|thumb|center|$1]]',
+'fileexists-shared-forbidden' => 'एतस्य नाम्नः सञ्चिका विभक्तभाण्डारे तावत् अस्ति एव ।
+तथापि यदि एतां सञ्चिकाम् उत्तारयितुम् इच्छति तर्हि अस्याः नामपरिवर्तनं करोतु ।
+[[File:$1|thumb|center|$1]]',
+'file-exists-duplicate' => 'एषा सञ्चिका तु {{PLURAL:$1|file|files}}: इत्यस्य प्रतिकृतिः ।',
+'file-deleted-duplicate' => 'अस्याः सञ्चिकायाः ([[:$1]]) सादृश्ययुक्ता सञ्चिकातु अपमर्जिता ।
+एतस्याः उत्तारणात् पूर्वं प्राचीनसञ्चिकायाः इतिहासः अवलोकनीयः ।',
+'uploadwarning' => 'उत्तारणस्य पूर्वसूचना ।',
+'uploadwarning-text' => 'अधो दत्तं सञ्चिकाविवरणं संस्कृत्य पुनः यतताम् ।',
+'savefile' => 'सञ्चिकां संरक्षतु ।',
'uploadedimage' => '"[[$1]]" इत्येतद् आरोपितमस्ति',
-
+'overwroteimage' => '"[[$1]]" इत्यस्य नूतनावतरणम् उत्तारयतु ।',
+'uploaddisabled' => 'सक्रियम् उत्तारयतु ।',
+'copyuploaddisabled' => 'निष्क्रियतः यु.आर्.एल् तः उत्तारयतु ।',
+'uploadfromurl-queued' => 'ते उत्तारणम् अनुपङ्कौ अस्ति ।',
+'uploaddisabledtext' => 'उत्तारितसञ्चिकाः निष्क्रियाः ।',
+'php-uploaddisabledtext' => 'PHP मध्ये उत्तारितसञ्चिकाः निष्क्रियाः ।',
+'uploadscripted' => 'HTMLयुक्ताः अथवा लिपिसङ्केतयुक्ताः सञ्चिकाः जालदर्शिकया बाधिताः ।',
+'uploadvirus' => 'अस्यां सञ्चिकायां वैराणुः अस्ति । विवरणम् $1',
+'uploadjava' => 'इयं ZIP सञ्चिका अस्यां जावावर्गस्य सञ्चिकाः सन्ति ।
+जावासञ्चिकाः उत्तरणं निषिद्धम् । यतः अनेन सुरक्षाबन्धाः शिथिलाः भवन्ति ।',
+'upload-source' => 'मूलसञ्चिका ।',
+'sourcefilename' => 'मूलसञ्चिकायाः नाम ।',
+'sourceurl' => 'मूलं URL:',
+'destfilename' => 'लक्षितसञ्चिकायाः नाम ।',
+'upload-maxfilesize' => 'सञ्चिकायाः गरिष्ठाकारः ।$1',
+'upload-description' => 'सञ्चिकाविवरणम् ।',
+'upload-options' => 'उत्तारणविकल्पाः ।',
+'watchthisupload' => 'इमां सञ्चिकाम् अवलोकयतु ।',
+'filewasdeleted' => 'अनेन नाम्ना उत्तारिता काचित् सञ्चिका पूर्वमेव अपमर्जिता ।
+ $1 परिशील्य उत्तरणं पुनः उत्तारयतु ।',
+'filename-bad-prefix' => "यस्याः सञ्चिकायाः उत्तारणं कुर्वाणः अस्ति तस्य नाम '''\"\$1\"''' तः आरभते । यत् डिज़िटल् क्यामरा द्वारा दत्तम् अस्ति ।
+अस्याः अधिकज्ञानप्रपकं किमपि अन्यत् नाम योजयतु ।",
+'upload-success-subj' => 'सफलम् उत्तारणम् ।',
+'upload-success-msg' => '[$2] तः उत्तारणं सफलम् । तदत्र अस्ति । [[:{{ns:file}}:$1]]',
+'upload-failure-subj' => 'उत्तारणसमस्या ।',
+'upload-failure-msg' => '[$2]तः उत्तारणे कापिसमस्या आसीत् ।
+$1',
+'upload-warning-subj' => 'उत्तारणस्य पूर्वसूचना ।',
+'upload-warning-msg' => ' [$2] तः उत्तारणे समस्या आसीत् । अस्याः समस्यायाः परिहारार्थम् अत्र गच्छतु [[Special:Upload/stash/$1|उत्तारणप्रपत्रम्]]',
+
+'upload-proto-error' => 'सदोषः क्रमः ।',
+'upload-proto-error-text' => 'स्वयम् उत्तरणं <code>http://</code> or <code>ftp://</code>. इत्यनेन सह आरब्धः भवति ।',
+'upload-file-error' => 'आन्तरिकः दोषः',
+'upload-file-error-text' => 'वितारके तात्कालिकसञ्चिकानिर्माणावसरे उपगतः आन्तरिकदोषः ।
+सम्पर्कयतु एतम् [[Special:ListUsers/sysop|administrator]]',
+'upload-misc-error' => 'अज्ञातः उत्तारणदोषः ।',
+'upload-misc-error-text' => 'उत्तारणावसरे कश्चन अज्ञातदोषः उपगतः ।
+URL मान्यम् अभिगम्यं वेति परिशील्य पुनः यतताम् ।[[Special:ListUsers/sysop|administrator]]',
+'upload-too-many-redirects' => 'URL अधिकपुनर्निदेशान् अन्तर्गतम् ।',
'upload-unknown-size' => 'अज्ञात आकार',
+'upload-http-error' => 'कश्चन HTTP दोषः उपगतः $1',
+'upload-copy-upload-invalid-domain' => 'अस्मिन् कोशे प्रतिकृत्युत्तारणम् उपलब्धं नास्ति ।',
+
+# File backend
+'backend-fail-stream' => 'सञ्चिका क्रमगता न $1.',
+'backend-fail-backup' => 'सञ्चिकां प्रतिचयनम् अशक्तम् $1.',
+'backend-fail-notexists' => '$1 सञ्चिका न वर्तते ।',
+'backend-fail-hashes' => 'सञ्चिकादोषः तोलनार्थं न मिलति ।',
+'backend-fail-notsame' => '$1 मध्ये काचित् अज्ञातसञ्चिका पूर्वमेवास्ति ।',
+'backend-fail-invalidpath' => '$1 मान्यः सङ्ग्रहपथः न ।',
+'backend-fail-delete' => '$1 सञ्चिकां परिमर्जितुं नैव शक्यते ।',
+'backend-fail-alreadyexists' => '$1 इति सञ्चिक पूर्वमेव वर्तते ।',
+'backend-fail-store' => '$1 सञ्चिकां $2 मध्ये सङ्ग्रहितुं नैव शक्यते ।',
+'backend-fail-copy' => '$1 सञ्चिकां $2 मध्ये प्रतिकृतिः कर्तुं नैव शक्यते ।',
+'backend-fail-move' => '$1 सञ्चिकां $2 प्रति चालयितुं न शक्यते ।',
+'backend-fail-opentemp' => 'तात्कालिकसञ्चिकाः उद्घाटयितुं नैव शक्यते ।',
+'backend-fail-writetemp' => 'तात्कालिकसञ्चिकायां लेखितुं न शक्यते ।',
+'backend-fail-closetemp' => 'तात्कालिकसञ्चिकां पिधातुं नैव शक्यते ।',
+'backend-fail-read' => '$1 इति सञ्चिकां पठितुं नैव शक्यते ।',
+'backend-fail-create' => '$1 इति सञ्चिकां लेखितुं नैव शक्यते ।',
+'backend-fail-maxsize' => '{{PLURAL:$2|one byte|$2 bytes}}तः अधिकम् अस्ति अतः $1 सञ्चिकां लेखितुं नैव शक्यते ।',
+'backend-fail-readonly' => '"$1" सङ्ग्रहागारान्तः तु सद्यः केवलं पठनार्हः कारणं दत्तं तु "\'\'$2\'\'" ।',
+'backend-fail-synced' => '"$1" सञ्चिका आन्तरिकसङ्ग्रहागारन्ते उपयोगायोग्यस्थितौ न अस्ति ।',
+'backend-fail-connect' => '"$1" सङ्ग्राहागारन्ते सम्पर्कयितुं नैव शक्यते ।',
+'backend-fail-internal' => '"$1"सङ्ग्रहागारन्ते अज्ञातदोषः उपगतः ।',
+'backend-fail-contenttype' => '"$1"मध्ये सङ्ग्रहितुं सञ्चिकायाः प्रकारं निश्चिनोतुं नैव शक्यते ।',
+'backend-fail-batchsize' => '$1 संचिकायाः गणस्य निक्षेपावकाशः प्रदत्तः । {{PLURAL:$1|operation|operations}}; समयनिर्बन्धः $2 {{PLURAL:$2|operation|operations}}.',
+'backend-fail-usable' => 'अपर्यापानुमतिकारणेन अथवा निदेशिकायाः /आधानस्य अभावात् $1 सञ्चिकां लेखितुं न शक्यते ।',
+
+# File journal errors
+'filejournal-fail-dbconnect' => '"$1" निक्षेपार्थं मूलपाठपत्रिकां सम्पर्कयितुं न शक्यते ।',
+'filejournal-fail-dbquery' => '"$1"निक्षेपस्य कृते पत्रिकामूलपाठम् उन्नतीकर्तुं नैव शक्यते ।',
+
+# Lock manager
+'lockmanager-notlocked' => '"$1" इत्येतत् उद्घाटयितुं न शक्यते यतः एतत् कीलितं न ।',
+'lockmanager-fail-closelock' => '"$1" निमित्तं सञ्चिकाम् उद्घाटयितुं न शक्यते ।',
+'lockmanager-fail-deletelock' => '"$1"कृते कपाटितसञ्चिकाम् अपमर्जितुं न शक्यते ।',
+'lockmanager-fail-acquirelock' => '"$1"कपाटितुं न शक्यते ।',
+'lockmanager-fail-openlock' => '"$1" निमित्तं सञ्चिकाम् उद्घाटयितुं न शक्यते ।',
+'lockmanager-fail-releaselock' => '"$1"कपाटितुं न शक्यते ।',
+'lockmanager-fail-db-bucket' => ' $1 द्रेणपात्रे कपाटनीयमूलपाठाः अपर्याप्ताः सन्ति ।',
+'lockmanager-fail-db-release' => '$1 मूलपाठेषु कपाटिकाविमोचनं नैव शक्यते ।',
+'lockmanager-fail-svr-acquire' => '$1 वितारके कपाटिकायोजनं न शक्यते ।',
+'lockmanager-fail-svr-release' => '$1 मूलपाठेषु कपाटिकाविमोचनं नैव शक्यते ।',
+
+# ZipDirectoryReader
+'zip-file-open-error' => 'ZIP परिशीलनार्थम् उद्घाटनावसरे कश्चन दोषः सङ्गतः ।',
+'zip-wrong-format' => 'निश्चितसञ्चिका तु सञ्चिका ZIP नैव ।',
+'zip-bad' => 'ZIP सञ्चिका तु दूषिता अथवा अपठनीया अस्ति ।
+सुरक्षार्थं परिशीलयितुं न शक्यते ।',
+'zip-unsupported' => 'एषा सञिका तु मीडियाविकिना अननुमोदिता ZIP सञ्चिका अस्ति ।
+सुरक्षर्थं सम्यक् परिशील्या न भवति ।',
+
+# Special:UploadStash
+'uploadstash' => 'राशीः उत्तारयतु ।',
+'uploadstash-summary' => 'एतत्पुटम् उत्तारितसञ्चिकानां सम्पर्कं साधयति । विक्याम् एतानि प्रकाशितानि न । योजकः उत्तारितवानपि एताः सञ्चिकाः अदृश्याः सन्ति ।',
+'uploadstash-clear' => 'राशीकृतसञ्चिकाः विशदयतु ।',
+'uploadstash-nofiles' => 'भवान् सञ्चिकाः न राशीकृतवान् ।',
+'uploadstash-badtoken' => 'प्रक्रियाचरणं सफलम् । किन्तु प्रायः ते सम्पादनाधिकारः विनष्टः । पुनः यतताम् ।',
+'uploadstash-errclear' => 'सञ्चिकविशदनं सफलम् ।',
+'uploadstash-refresh' => 'सञ्चिकावलीं संस्करोतु ।',
+'invalid-chunk-offset' => 'अमान्यं चङ्क् आफ्सेट्',
+
+# img_auth script messages
+'img-auth-accessdenied' => 'अभिगमनम् अपलपितम् ।',
+'img-auth-nopathinfo' => 'पथसूची विनष्टा ।
+ते वितारकः सूचनाः प्रेषयितुं संसिद्धः न ।
+एतत् CGI अवलम्बितं स्यात् अपि च img_auth अनुमोदनं न करोति ।
+See https://www.mediawiki.org/wiki/Manual:Image_Authorization.',
+'img-auth-notindir' => 'सुदृढितायाम् उत्तारणनिदेशिकायाम् अभ्यर्थितपथः नास्ति ।',
+'img-auth-badtitle' => '"$1"तः मान्यशीर्षिकां निर्मातुं न शक्यते ।',
+'img-auth-nologinnWL' => 'नामाभिलेखेन न प्रविष्टः अपिच $1 तु श्वेतावली न ।',
+'img-auth-nofile' => '"$1" इति सञ्चिका न वर्तते ।',
+'img-auth-isdir' => 'भवान् "$1"निदेशिकाम् अभिगन्तुं यतते ।
+सञ्चिकाभिगमनम् एव अनुमतम् ।',
+'img-auth-streaming' => '"$1"इत्यस्य प्रवाहिनी ।',
+'img-auth-public' => 'स्वायत्तविकितः सञ्चिकाः नेतुम् अयं कार्यक्रमः img_auth.php उपयुज्यते ।
+एषा विकिः सार्वजनिकविकिः इति दृढिता ।
+वैकल्पिकसुरक्षार्थं img_auth.php अपलपितः । ।',
+'img-auth-noread' => '"$1"पठने योजकस्य अभिगमनं नास्ति ।',
+'img-auth-bad-query-string' => ' URL मध्ये अमान्यं प्रश्नतन्तुः अस्ति ।',
+
+# HTTP errors
+'http-invalid-url' => ' $1 इति अमान्यम् URL ।',
+'http-invalid-scheme' => '"$1"योजनायुक्तं URLs नानुमोदितानि ।',
+'http-request-error' => ' अज्ञातदोषात् HTTP अभ्यर्थनं निष्पलम् ।',
+'http-read-error' => 'HTTP पठनदोषः।',
+'http-timed-out' => 'HTTP अभ्यर्थनं कालातीतम् ।',
+'http-curl-error' => 'दोषाहरणस्य URL: $1',
+'http-host-unreachable' => 'URL प्राप्तुं न शक्यते ।',
+'http-bad-status' => 'HTTP : $1 $2अभ्यर्थने समस्या आसीत् ।',
+
+# Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
+'upload-curl-error6' => 'URL प्राप्तुं न शक्यते ।',
+'upload-curl-error6-text' => 'उपपन्नं URL न प्राप्नोति ।
+द्विटङ्कनेन URLअदोषत्वं क्षेत्रं च परिशीलयतु ।',
+'upload-curl-error28' => 'उत्तारणस्य समयातीतः ।',
+'upload-curl-error28-text' => 'जालक्षेत्रेण प्रतिस्पन्दितुं दीर्घकालः आश्रितः ।
+जालक्षेत्रस्य जीवितं परिशीलयतु । अथवा कञ्चित्कालान्तरेण प्रयतताम् ।
+भवान् न्यूनकार्यव्यस्तकाले प्रयत्नं करोतु ।',
'license' => 'अनुमतिदानम्',
'license-header' => 'अनुमतिदानम्',
+'nolicense' => 'चियनं नास्ति ।',
+'license-nopreview' => 'पूर्वावलोकनं न मिलति ।',
+'upload_source_url' => '(मान्यं, प्रचारात्मकाभिगमनयुतं URL)',
+'upload_source_file' => ' (ते सङ्गणकस्य सञ्चिका)',
# Special:ListFiles
+'listfiles-summary' => 'एतद्विशेषपुटम् उत्तारितसञ्चिकाः प्रदर्शयति ।
+योजकेन शुद्धाः अतिनूतनं सञ्चिकाः केवलम् अत्र प्रदर्शयति ।',
+'listfiles_search_for' => 'माध्यमनामधेयार्थम् अन्विषतु ।',
'imgfile' => 'संचिका',
+'listfiles' => 'सञ्चिकावली ।',
+'listfiles_thumb' => 'अंगुष्ठनखाकारम् ।',
'listfiles_date' => 'दिनाङ्क',
'listfiles_name' => 'नामन्',
'listfiles_user' => 'योजक',
'file-anchor-link' => 'सञ्चिका',
'filehist' => 'सञ्चिकायाः इतिहासः',
'filehist-help' => 'सञ्चिका तत्समये कीदृशी आसीदिति द्रष्टुं दिनांकः/समयः नुद्यताम् ।',
+'filehist-deleteall' => 'सर्वान् परिमर्जतु ।',
'filehist-deleteone' => 'विलोप',
'filehist-revert' => 'प्रतिनिवर्त्यताम्',
'filehist-current' => 'सद्योजातम्',
'filehist-datetime' => 'दिनाङ्कः/समयः',
'filehist-thumb' => 'अंगुष्ठनखाकारम्',
'filehist-thumbtext' => '$1 समये विद्यमत्याः आवृत्तेः अंगुष्ठनखाकारम्',
+'filehist-nothumb' => 'अङ्गुष्टनखाकारकं नाश्ति ।',
'filehist-user' => 'योजकः',
'filehist-dimensions' => 'आयामाः',
+'filehist-filesize' => 'सञ्चिकाकारः ।',
'filehist-comment' => 'टिप्पणी',
+'filehist-missing' => 'सञ्चिका विनष्टा ।',
'imagelinks' => 'संचिका यत्र उपयुक्ता',
'linkstoimage' => '{{PLURAL:$1|अधोलिखितं पृष्ठं| अधोलिखितानि $1 पृष्ठाणि}} इदं संचिकां प्रति संबंधनं {{PLURAL:$1|करोति| कुर्वन्ति}}।',
+'linkstoimage-more' => '{{PLURAL:$1|$1}} तः अधिकपुटानि अस्यां सञ्चिकायां योज्यन्ते ।
+अधोनिदेशितसूची सञ्चिकाभिः योजनीयपुटानि पश्यति ।{{PLURAL:$1|$1 पृष्ठ|$1 पृष्ठ}}
+[[Special:WhatLinksHere/$2|पूर्णसूची]] अपि लभ्यते ।',
'nolinkstoimage' => 'एतद चित्रात् न पृष्ठा सम्बद्धं करोन्ति।',
+'morelinkstoimage' => ' [[Special:WhatLinksHere/$1|more links]] मध्ये सञ्चिकामवलोकयतु ।',
+'linkstoimage-redirect' => '$1 (सञ्चिका पुनर्निदेशिता) $2',
+'duplicatesoffile' => 'अधो निदेशितसञ्चिका द्विप्रतिः । {{PLURAL:$1|}} विशेषविवरणार्थम् अत्र प्रविशतु । [[Special:FileDuplicateSearch/$2|more details]]',
'sharedupload' => 'इयं संचिका $1 इत्यस्मादस्ति, एषा खलु अन्येष्वपि प्रकल्पेषु प्रयोक्तुं शक्यते।',
+'sharedupload-desc-there' => 'एषा सञ्चिका $1 तथा अन्यप्रकल्पेन च उपयुक्ता ।
+इत्योप्यतिशयसूचनार्थं $2 सञ्चिकाविवरणपुटं पश्यतु ।',
'sharedupload-desc-here' => 'एषा सञ्चिका $1 इत्यतः उद्धृता अन्यासु योजनासु उपयोगार्हा ।
अस्याः सञ्चिकायाः [$2 सञ्चिकाविवरणपृष्ठम्] इत्यत्र उपलभ्यमानं विवरणम् अधोलिखितं यथा ।',
+'sharedupload-desc-edit' => ' एषा सञ्चिका $1 इत्यतः उद्धृता अन्यासु योजनासु उपयोगार्हा ।
+अस्याः सञ्चिकायाः [$2 सञ्चिकाविवरणपृष्ठम्] इत्यत्र उपलभ्यमानं विवरणम् अधोलिखितं यथा ।',
+'sharedupload-desc-create' => 'एषा सञ्चिका $1 इत्यतः उद्धृता अन्यासु योजनासु उपयोगार्हा ।
+अस्याः सञ्चिकायाः [$2 सञ्चिकाविवरणपृष्ठम्] इत्यत्र उपलभ्यमानं विवरणम् अधोलिखितं यथा ।',
+'filepage-nofile' => 'अनेन नाम्ना कापि सञ्चिका न वर्तते ।',
+'filepage-nofile-link' => 'अनेन नाम्ना कापि सञ्चिका न वर्तते । $1 इत्येतत् उत्तारयितुं शक्नोति ।',
'uploadnewversion-linktext' => 'अस्य पृष्ठस्य नूतनाम् आवृत्तिं उद्भारयतु',
+'shared-repo-from' => '$1 इत्यस्मात् ।',
+'shared-repo' => 'विभक्तः कोशः ।',
# File reversion
+'filerevert' => '$1 अनुवर्तताम् ।',
+'filerevert-legend' => 'सञ्चिकाम् अनुवर्तताम् ।',
+'filerevert-intro' => "भवान् '''[[Media:$1|$1]]''' इति सञ्चिकायाः $4 इत्यवतरणं $3, $2 इति अनुवर्तमानः अस्ति ।",
'filerevert-comment' => 'कारणम् :',
+'filerevert-defaultcomment' => '$2 इत्येनं $1 समयस्य अवतरणम् अनुवृत्तम् ।',
+'filerevert-submit' => 'अनुवर्तताम् ।',
+'filerevert-success' => "'''[[Media:$1|$1]]''' इत्येनं $4 $2 को $3 समयावतरणम् अनुवृत्तम् ।",
+'filerevert-badversion' => 'दत्तसमये सन्देशदायिका सञ्चिका प्राचीनावतरणं नास्ति ।',
# File deletion
+'filedelete' => '$1 इत्येतत् अपमर्जतु ।',
+'filedelete-legend' => 'सञ्चिकाम् अपमर्जतु ।',
+'filedelete-intro' => "'''[[Media:$1|$1]]''' इति सञ्चिकायाः इतिहाससहितम् अपमर्जयन् अस्ति ।",
+'filedelete-intro-old' => "भवान्'''[[Media:$1|$1]]''' इत्यस्य [$4 $2 इत्येतयोः $3 कालस्य अवतरणम्] अपमार्जयन् अस्ति ।",
'filedelete-comment' => 'कारणम् :',
'filedelete-submit' => 'विलुप्यताम्',
+'filedelete-success' => "'''$1''' अपमर्जितम् ।",
+'filedelete-success-old' => "'''[[Media:$1|$1]]''' इत्यस्य $2 इत्येतत् $3 समयस्यावतरणम् अपमर्जितम् ।",
+'filedelete-nofile' => "'''$1''' न वर्तते ।",
+'filedelete-nofile-old' => "'''$1''' इत्यस्य भवता वर्णितविशेषतायुतम् अवतरणम् अत्र न वर्तते ।",
+'filedelete-otherreason' => 'अपरम्/अतिरिक्तं कारणम् :',
'filedelete-reason-otherlist' => 'अन्य कारणम्',
+'filedelete-reason-dropdown' => '* अपमर्जनस्य सामान्यं कारणम् ।
+** कृतिस्वाम्यस्य उल्लङ्घनम् ।
+** प्रतिकृता सञ्चिका ।',
+'filedelete-edit-reasonlist' => 'अपमार्जनकारणानि सम्पादयतु ।',
+'filedelete-maintenance' => 'सञ्चिकानाम् अपमर्जनम् अनमपमर्जनं च निर्वहणकाले तात्कालिकतया निष्क्रियौ ।',
+'filedelete-maintenance-title' => 'सञ्चिकाम् अपमर्जितुं न शक्यते ।',
# MIME search
+'mimesearch' => 'MIME अन्वेषणम् ।',
+'mimesearch-summary' => 'MIME-प्रकारानुसारं सञ्चिकान्वेषणार्थम् एतत्पुटम् उपयोक्तुं शाक्नोति ।
+इनपुट: सञ्चिकायाः प्रकारः/उपप्रकारः, उदाहरणम्. <tt>image/jpeg</tt>.',
+'mimetype' => 'MIME प्रकारः :',
'download' => 'डाउनलोड',
+# Unwatched pages
+'unwatchedpages' => 'अनवलोकितपुटानि ।',
+
+# List redirects
+'listredirects' => 'चालितानाम् अवली ।',
+
+# Unused templates
+'unusedtemplates' => 'अनुपयुक्ताः प्राकृतयः ।',
+'unusedtemplatestext' => 'अस्मिन् पुटे {{ns:template}} नामस्थानयुतानि सर्वपुटानि अन्तर्गतानि । यानि अन्यपुटेषु न सन्ति ।
+अस्य अपमर्जनात् पूर्वं सञ्चिकायाः अन्यानुबन्धान् परिशीलयतु ।',
+'unusedtemplateswlh' => 'अन्यानुबन्धाः ।',
+
# Random page
'randompage' => 'यादृच्छिकपृष्ठम्',
+'randompage-nopages' => 'अधोनिदेशितनामस्थाने पुटानि न सन्ति । {{PLURAL:$2| एतन्नमस्थाने}} नास्ति : $1।',
+
+# Random redirect
+'randomredirect' => 'यादृच्छिकचालनम् ।',
+'randomredirect-nopages' => '$1नामस्थाने चालनानि न सन्ति ।',
# Statistics
'statistics' => 'स्थितिगणितम्',
+'statistics-header-pages' => 'पुटसाङ्ख्यिकाः ।',
+'statistics-header-edits' => 'सङ्ख्यिकाः सम्पादयतु ।',
+'statistics-header-views' => 'साङ्ख्यिकाः अवलोकयतु ।',
+'statistics-header-users' => 'योजकसाङ्ख्यिकाः ।',
+'statistics-header-hooks' => 'अन्यसाङ्ख्यिकाः ।',
+'statistics-articles' => 'आधेयपुटानि ।',
'statistics-pages' => 'पृष्ठानि',
+'statistics-pages-desc' => 'अस्यां विक्यां तु सम्भाषाणपुटसहितानि अन्यसर्वपुटानि चालितानि ।',
'statistics-files' => 'उद्भारितसञ्चिकाः',
+'statistics-edits' => '{{SITENAME}} व्यवस्थापनपर्यन्तं पुटसम्पादनानि ।',
+'statistics-edits-average' => 'प्रतिपुटं माध्यसम्पादनानि ।',
+'statistics-views-total' => 'अवलोकनयोगः ।',
+'statistics-views-total-desc' => 'असंवृत्तपुटानाम् अवलोकनानि । अपि च विशेषपुटानि नान्तर्गतानि ।',
+'statistics-views-peredit' => 'प्रतिसम्पादनम् अवलोकनम् ।',
+'statistics-users' => 'पञ्जीकृतः [[Special:ListUsers|योजकः]]',
'statistics-users-active' => 'सक्रियाः सदस्याः',
+'statistics-users-active-desc' => 'गतेषु {{PLURAL:$1|day|$1 दिनेषु}} सक्रियाः योजकाः ।',
+'statistics-mostpopular' => 'अत्यवलोकितपुटानि ।',
+'disambiguations' => 'द्वैधीभावरहितपुटानाम् अनुबन्धितपुटानि ।',
'disambiguationspage' => 'Template:असन्दिग्धम्',
+'disambiguations-text' => 'अधो निदेशितपुटानि असन्धिग्धपुटेन अनुबन्धितानि ।
+एतानि यथार्थविषैः योजनीयानि । <br />
+यदि कोऽपि पुटेन प्रकृतिं प्रयोजयति यः [[MediaWiki:Disambiguationspage]] इत्यनेन अनुबद्धः ससन्दिग्धपुटम् इति उच्यते ।',
'doubleredirects' => 'दुगुनी-अनुप्रेषिते',
-
+'doubleredirectstext' => 'एतत्पुटं तेषां पुटानां सूची अस्ति यानि अन्यपुनर्निदेशितपुटानि प्रति पुनरिदेशितानि सन्ति ।
+प्रत्येकं पङ्क्तिः प्रथमद्वितीयपुनर्निदेशम् अन्तर्गता । द्वितीयपुनर्निदेशः लक्ष्यं यत् वास्तवं लक्ष्यपुटं प्रथमं प्रदर्शितम् ।
+अपि च प्रथमपुनर्निदेशः वास्तवेन एतदेवलक्षितं स्यात् । <del>काटी गई</del> प्रविष्टयः परिहृताः ।',
+'double-redirect-fixed-move' => '[[$1]] इत्यस्य स्थानं परिवर्तितम् ।
+इदानीम् [[$2]] इत्यस्य दिशि पुनर्निदिष्टम् अस्ति ।',
+'double-redirect-fixed-maintenance' => '[[$1]] तः [[$2]] पुनर्निदेशद्वयं निश्चिनोति ।',
+'double-redirect-fixer' => 'पुनर्निदेशस्य बन्धकः ।',
+
+'brokenredirects' => 'भग्नपुनर्निदेशाः ।',
+'brokenredirectstext' => 'अधो दत्तपुनर्निदेशाः अवृत्तपुटैः सह अनुबन्दं रक्षन्ति ।',
'brokenredirects-edit' => 'सम्पाद्यताम्',
'brokenredirects-delete' => 'विलुप्यताम्',
+'withoutinterwiki' => 'भाषानुबन्धरिहातानि पुटानि ।',
+'withoutinterwiki-summary' => 'अधस्थपुटानि अन्यभाषावतरणैः अनुबन्धं न कुर्वन्ति ।',
'withoutinterwiki-legend' => 'पूर्वोऽपपदम्',
'withoutinterwiki-submit' => 'दर्श्यताम्',
+'fewestrevisions' => 'न्यूनतमालोकनयुक्तपुटानि ।',
+
# Miscellaneous special pages
'nbytes' => '$1 {{PLURAL:$1|बैट्|बैट्स्}}',
+'ncategories' => '{{PLURAL:$1|वर्गः|वर्गाः }}',
+'nlinks' => '$1 {{PLURAL:$1|अनुबन्धः|अनुबन्धाः}}',
'nmembers' => '$1 {{PLURAL:$1|सदस्यः|सदस्याः}}',
+'nrevisions' => '$1 {{PLURAL:$1|पुनरावृत्तिः}}',
+'nviews' => '$1 {{PLURAL:$1|अनुबन्धः|अनुबन्धाः}}',
+'nimagelinks' => '$1 {{PLURAL:$1|पुटम्|पुटानि}} प्रयुक्तानि ।',
+'ntransclusions' => '$1 {{PLURAL:$1|पुटम्|पुटानि}} प्रयुक्तानि ।',
+'specialpage-empty' => 'अस्य वृत्तस्य परिणामः नास्ति ।',
+'lonelypages' => 'अनाथपुटानि ।',
+'lonelypagestext' => '{{SITENAME}} इत्यस्मिन् अधो निदेशितपुटानि नानुबद्धानि अथवा अन्तर्गतानि अन्यपुटेषु ।',
+'uncategorizedpages' => 'अवर्गीकृतपुटानि ।',
+'uncategorizedcategories' => 'अवर्गीकृताः वर्गाः ।',
+'uncategorizedimages' => 'अवर्गीकृताः सञ्चिकाः ।',
+'uncategorizedtemplates' => 'अवर्गीकृताः प्रकृतयः ।',
+'unusedcategories' => 'अनुपयुक्ताः वर्गाः ।',
+'unusedimages' => 'अनुपयुक्तानि पुटाणी ।',
+'popularpages' => 'प्रसिद्धानि पुटानि ।',
+'wantedcategories' => 'आवश्यकाः वर्गाः ।',
+'wantedpages' => 'आवश्यकपुटानि ।',
+'wantedpages-badtitle' => '$1 परिणामनिरूपणे अमान्यशीर्षकम् ।',
+'wantedfiles' => 'आवश्यकाः सञ्चिकाः ।',
+'wantedfiletext-cat' => 'अधो दत्तसञ्चिकाः उपयुक्ताः किन्तु न वर्तन्ते । बाह्यकोशानां सञ्चिकाः उपस्थिताः इति एताः सूच्यां स्युः । एतादृशः कोपि सदोषप्रवेशः<del> अवरुद्धः</del> भवति । अपि च यत्पुटं तादृश्याः अनुपस्थितसञ्चिकायाः प्रयोगं कुर्वन्ति तासं सूची [[:$1]] मध्ये अस्ति ।',
+'wantedfiletext-nocat' => 'अधो दत्ताः सञ्चिकाः उपयुक्ताः किन्तु न वर्तन्ते । बाह्यकोशस्य सञ्चिकाः उपस्थिताः इति एताः सूच्यां स्युः । तदृशः कोऽपि सदोषप्रवेशः<del>struck out</del>. अत्र स्यात् ।',
+'wantedtemplates' => 'आवश्यकाः प्राकृतयः ।',
+'mostlinked' => 'अत्यनुबद्धानि पुटानि ।',
+'mostlinkedcategories' => 'वर्गैः सह अत्यनुबद्धाः ।',
+'mostlinkedtemplates' => 'प्राकृतिभिः अत्यनुबद्धाः ।',
+'mostcategories' => 'बहुवर्गयुक्तपुटानि ।',
+'mostimages' => 'अत्यनुबद्धानि पुटानि ।',
+'mostrevisions' => 'सर्वाधिकपुनरावृत्तियुक्तानि पुटानि ।',
'prefixindex' => 'उपसर्गयुक्तानि सर्वाणि पृष्ठानि',
+'prefixindex-namespace' => 'उपसर्गैः युक्तानि सर्वपुटानि । ($1 नामस्थानम्)',
+'shortpages' => 'ह्रस्वपुटानि',
'longpages' => 'दीर्घाणि पृष्ठानि',
+'deadendpages' => 'अन्तिमपुटानि ।',
+'deadendpagestext' => 'अधो निदेशितपुटानि {{SITENAME}} इत्यस्मिन् अन्यपुटैः अनुबद्धानि न ।',
+'protectedpages' => 'सुरक्षितानि पुतानि ।',
+'protectedpages-indef' => 'अनिर्दिष्टसुरक्षा केवलम् ।',
+'protectedpages-cascade' => 'प्रपातसंरक्षणं केवलम् ।',
+'protectedpagestext' => 'अधोसूचितपुटानि चालनात् सम्पादनात् वा सुरक्षितानि ।',
+'protectedpagesempty' => 'अनेन विस्तारेण न किमपि पुटं सद्यः न सुरक्षितम् ।',
+'protectedtitles' => 'सुरक्षितानि शीर्षकानि ।',
+'protectedtitlestext' => 'अधो दत्तशीर्षकाणि सर्जनात् रक्षितानि ।',
+'protectedtitlesempty' => 'एतैः विस्तारैः न किमपि शीर्षकं सद्यः परिरक्षितानि ।',
'listusers' => 'योजक सूचि',
+'listusers-editsonly' => 'केवलं सम्पादनसहितयोजकान् दर्शयतु ।',
+'listusers-creationsort' => 'सर्जनदिनाङ्कैः वर्गीकरोतु ।',
+'usereditcount' => '$1 {{PLURAL:$1|दिनम्|दिनानि}}',
'usercreated' => '$1 दिने $2 समये रचितम् योजकनाम $3',
'newpages' => 'नवीनपृष्ठम्',
'newpages-username' => 'योजकनामन्:',
'ancientpages' => 'प्राचीनतमानि पृष्ठानि',
'move' => 'चाल्यताम्',
'movethispage' => 'इदं पृष्ठं चाल्यताम्',
+'unusedimagestext' => 'अधो दत्तसञ्चिकाः सन्ति किन्तु कस्मिंश्चिदपि पुटे न न्यस्ताः ।',
+'unusedcategoriestext' => 'निम्नलिखितवर्गाः सन्ति तथापि अन्यपुटं वर्गः वा न उपयुङ्क्ते ।',
+'notargettitle' => 'लक्ष्यं नास्ति ।',
+'notargettext' => 'एतत्कार्यं समाचरितुं भवान् लक्षितपुटं योजकं वा न निर्दिष्टवान् ।',
+'nopagetitle' => 'तादृशलक्षितपुटं नास्ति ।',
+'nopagetext' => 'भवता निर्दिष्टं लक्षितपुटं नास्ति ।',
'pager-newer-n' => '{{PLURAL:$1|नूतनतरम् 1|नूतनतराणि $1}}',
'pager-older-n' => '{{PLURAL:$1|पुरातनतरम् 1|पुरातनतराणि $1}}',
+'suppress' => 'अलक्ष्यम् ।',
+'querypage-disabled' => 'समाचरणकारणेन एतद्विशेषपुटं निष्क्रियम् ।',
# Book sources
'booksources' => 'ग्रन्थानां स्रोतः',
'booksources-search-legend' => 'ग्रन्थस्रोतः अन्विष्यताम्',
'booksources-go' => 'गम्यताम्',
+'booksources-text' => 'अधस्था आवली नूतनप्राचीनपुस्तकानां विक्रयकेन्द्रस्य अनुबन्धान् सूचयति । यत्र ते आवश्यकाः अन्यविषयाः अपि उपलभ्याः ।',
+'booksources-invalid-isbn' => 'दत्तं ISBN मान्यम् इति न भाति । मूलस्रोततः प्रतिकृतीः कर्तुं परिशीलयतु ।',
# Special:Log
+'specialloguserlabel' => 'आचारी :',
+'speciallogtitlelabel' => 'लक्ष्यम् (शीर्षकम् / योजकः)',
'log' => 'लॉग् इत्येतानि',
+'all-logs-page' => 'सर्वसार्वजनिकप्रवेशः ।',
+'alllogstext' => '{{SITENAME}}इत्यस्य उबलब्धप्रवेशानां संयुक्तप्रदर्शनम् ।
+प्रवेशप्रकारं चित्वा भवान् दृश्यं क्षाययितुं शक्नोति । योजकनाम, सदस्य नाम (ह्रस्वदीर्घाक्षरसंवादी) प्रभावितपुटम् ।',
+'logempty' => 'प्रवेशे मेलयुक्तपुटं नास्ति ।',
+'log-title-wildcard' => 'अनेन पाठेन आरब्धानि शीर्षकानि अन्विषतु ।',
+'showhideselectedlogentries' => 'चितप्रवेशावलीः प्रदर्शयतु/गोपयतु ।',
# Special:AllPages
'allpages' => 'सर्वाणि पृष्ठानि',
'alphaindexline' => '$1 तः $2 पर्यन्तम्',
+'nextpage' => '($1)अग्रिमपुटम् ।',
'prevpage' => 'पूर्वपृष्ठम् ($1)',
'allpagesfrom' => 'इत्यस्मात् आरभ्यमाणानि पृष्ठानि दर्श्यन्ताम्:',
'allpagesto' => 'तानि पृष्ठानि दर्श्यन्तां येषाम् अन्त्यम् एवम् :',
'allarticles' => 'सर्वाणि पृष्ठानि',
+'allinnamespace' => 'सर्वपुटानि ($1 नामस्थानम्)',
+'allnotinnamespace' => 'सर्वपुटानि ($1 नामस्थानं विना)',
'allpagesprev' => 'पूर्वतन',
'allpagesnext' => 'अग्रिम',
'allpagessubmit' => 'गम्यताम्',
+'allpagesprefix' => 'उपसर्गयुक्तपुटानि दर्शयतु ।',
+'allpagesbadtitle' => 'दत्तपुटशीर्षकम् अमान्यम् अथवा आन्तर्भाषिकम्, आन्तर्विकीयं वा अस्ति ।
+अस्मिन् एकं नैकं वा अक्षराणि सन्ति येषां प्रयोगं शीर्षकेषु कर्तुम् अशक्यम् ।',
+'allpages-bad-ns' => '{{SITENAME}} इत्यस्मिन् "$1" नामस्थानं नास्ति ।',
+'allpages-hide-redirects' => 'पुनर्निदेशान् गोपयतु ।',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => ' भवान् अस्यपुटास्य निगूढावृत्तिं पश्यन् अस्ति । यत् $1 कालिकम् अस्ति ।',
+'cachedspecial-viewing-cached-ts' => 'भवान् निगूढावृत्तेः पुटम् अवलोकयन् अस्ति । यत् परिपूर्णतया वास्तवं न ।',
+'cachedspecial-refresh-now' => 'जघन्यम् अवलोकयतु ।',
# Special:Categories
'categories' => 'वर्गाः',
+'categoriespagetext' => 'निम्नोक्ताः {{PLURAL:$1|श्रेणी|श्रेणयः}} पुटानि माध्यमान् वा युक्ताः ।
+यस्याः श्रेण्याः [[Special:UnusedCategories|अप्रयुक्तश्रेण्यः]] अत्र न सन्ति ।
+[[Special:WantedCategories|अपेक्षितश्रेण्यः]] अपि पश्यतु ।',
+'categoriesfrom' => 'इत्यस्मात् आरभ्यमाणानि पृष्ठानि दर्श्यन्ताम्:',
+'special-categories-sort-count' => 'गणनानुगुणं वर्गीकरोतु ।',
+'special-categories-sort-abc' => 'अकारदिक्रमेण वर्गीकरोतु ।',
+
+# Special:DeletedContributions
+'deletedcontributions' => 'अपमर्जितानि योजकयोगदानानि ।',
+'deletedcontributions-title' => 'अपमर्जितानि योजकयोगदानानि ।',
+'sp-deletedcontributions-contribs' => 'योगदानानि ।',
# Special:LinkSearch
'linksearch' => 'बाह्यसम्पर्कतन्तूनाम् अन्वेषणम्',
+'linksearch-pat' => 'अन्वेषणस्य क्रमः ।',
+'linksearch-ns' => 'नामस्थानम् :',
'linksearch-ok' => 'अन्वेषणम्',
+'linksearch-text' => '"*.wikipedia.org" सदृशानि वन्यपत्राणि योजयितुं शक्यते ।
+न्यूनातिन्यूनं ".org" सदृशः अत्युन्नतस्तरस्य डोमेन आवश्यकम् अस्ति <br />
+अनुमोदितक्रमागतिः <tt>$1</tt> (एतेषु कतममपि अन्वेषणे न योजयतु )',
'linksearch-line' => '$2 पृष्ठं $1 तः सम्पृक्तम् अस्ति।',
+'linksearch-error' => 'वन्यपत्राणि आतिथेयस्य नाम्ना समं केवलं प्रभान्ति ।',
# Special:ListUsers
+'listusersfrom' => 'एतस्मात् आरभमाणान् योजकान् दर्शयतु ।',
'listusers-submit' => 'दर्श्यताम्',
+'listusers-noresult' => 'योजकः न प्राप्तः ।',
+'listusers-blocked' => 'अवरुद्धम् ।',
+
+# Special:ActiveUsers
+'activeusers' => 'सक्रिययोजकानाम् आवली ।',
+'activeusers-intro' => 'एषा तु गतेषु $1 {{PLURAL:$1|दिनेषु}} कृतकार्याणां योजकाना आवली ।',
+'activeusers-count' => '$1 {{PLURAL:$1|सम्पादनानि}} गतेषु $3 {{PLURAL:$3|दिनेषु}} कृतानि ।',
+'activeusers-from' => 'एतस्मात् आरभमाणान् योजकान् दर्शयतु ।',
+'activeusers-hidebots' => 'स्वयं चालकान् गोपयतु ।',
+'activeusers-hidesysops' => 'प्रशासकान् गोपयतु ।',
+'activeusers-noresult' => 'योजकः न प्राप्तः ।',
# Special:Log/newusers
'newuserlogpage' => 'प्रयोक्तृ-सृजन-सूचिका',
+'newuserlogpagetext' => 'अयं योजकनिर्माणास्य प्रवेशः ।',
# Special:ListGroupRights
+'listgrouprights' => 'योजकसमूहाधिकाराः ।',
+'listgrouprights-summary' => 'अधोदत्ता विकिपरिभाषितस्य सङ्गताभिगम्यताधिकारैः सहिता योजकसमूहस्य आवली । [[{{MediaWiki:Listgrouprights-helppage}}|additional information]]',
+'listgrouprights-key' => '* <span class="listgrouprights-granted">दत्ताधिकाराः</span>
+* <span class="listgrouprights-revoked">हृताधिकाराः</span>',
'listgrouprights-group' => 'वर्ग',
+'listgrouprights-rights' => 'अधिकाराः ।',
+'listgrouprights-helppage' => 'Help: समूहाधिकाराः ।',
'listgrouprights-members' => '(सदस्यानां सूची)',
+'listgrouprights-addgroup' => '{{PLURAL:$2|समूहः}} योज्यताम् $1',
+'listgrouprights-removegroup' => 'समूहः{{PLURAL:$2|विलोपयतु}}: $1',
+'listgrouprights-addgroup-all' => 'सर्वसमूहान् योजयतु ।',
+'listgrouprights-removegroup-all' => 'सर्वसमूहान् अपनयतु ।',
+'listgrouprights-addgroup-self' => 'स्वस्थाने {{PLURAL:$2|समूहम्}} योजयतु $1',
+'listgrouprights-removegroup-self' => 'स्वस्थाने {{PLURAL:$2|समूहम्}} अपनयतु $1',
+'listgrouprights-addgroup-self-all' => 'स्वस्थाने सर्वसमूहान योजयतु ।',
+'listgrouprights-removegroup-self-all' => 'स्वस्थानात् सर्वसमूहान् अपनयतु ।',
# E-mail user
+'mailnologin' => 'सम्प्रेषणस्य सङ्केतः नास्ति ।',
+'mailnologintext' => 'अस्य योजकेभ्यः विद्युन्मानपत्रप्रेषणार्थम् [[Special:UserLogin|नामाभिलेखनम्]] आवश्यकम् [[Special:Preferences|आद्यता]]यां प्रेषयितुं विद्युन्मानपत्रसङ्केतः आवश्यकः ।',
'emailuser' => 'एतस्मै योजकाय ईपत्रं प्रेष्यताम्',
'emailpage' => 'ई-मेल योजक',
+'emailpagetext' => 'अस्मै योजकाय विद्युन्मानपत्रं प्रेषयितुम् अधो दत्तप्रपत्रम् उपयोक्तुं शक्नोति ।
+[[Special:Preferences|your user preferences]] अत्र भवता विनिवेशितः वि-पत्रसङ्केतः सकाशात् इति स्थाने प्रतिभाति । अनेन स्वीकर्ता साक्षात् प्रत्युत्तरं दातुं प्रभविष्यति ।',
+'usermailererror' => 'पत्राचारपदार्थस्य प्रत्यागतदोषः ।',
+'defemailsubject' => '{{SITENAME}}"$1" इति योजकात् विद्युन्मानपत्रम् ।',
+'usermaildisabled' => 'योजकस्य विद्युन्मानपत्रं निष्क्रियम् ।',
+'usermaildisabledtext' => 'अस्यां विक्याम् अन्ययोजकेभ्यः विद्युन्मानपत्रं प्रेषयितुं नै शक्नोति ।',
+'noemailtitle' => 'विद्युन्मानपत्रसङ्केतः नास्ति ।',
+'noemailtext' => 'अस्य योजकस्य निरिदिष्टः विद्युन्मानपत्रसङ्केतः नास्ति ।',
+'nowikiemailtitle' => 'विद्युन्मानपत्रम् अननुमतम् ।',
+'nowikiemailtext' => 'अयं योजकः अन्ययोजकेभ्यः विद्युन्मानपत्राणि स्वीकार्तुं नेच्छति ।',
+'emailnotarget' => 'स्वीकर्तुः अस्तित्वविहीनम् अथवा अमान्यं योजकनाम ।',
+'emailtarget' => 'स्वीकर्तुः योजकनाम लिखतु ।',
'emailusername' => 'योजकनामन्:',
+'emailusernamesubmit' => 'उपस्थाप्यताम्',
+'email-legend' => '{{SITENAME}} इति अन्ययोजकाय विद्युन्मानपत्रं प्रेषयतु ।',
'emailfrom' => 'सकाशात्',
'emailto' => 'सविधे:',
'emailsubject' => 'विषयः',
'emailmessage' => 'सन्देशः :',
'emailsend' => 'प्रेषति',
+'emailccme' => 'सन्देशस्य प्रतिकृतिः मे विद्युन्मानपत्रसङ्केताय अपि प्रेषयतु ।',
+'emailccsubject' => '$1: $2 कृते अपि भवतः सन्देशस्य प्रकृतीः ।',
+'emailsent' => 'विद्युन्मानपत्रं प्रेषितम् ।',
+'emailsenttext' => 'भवतः विद्युन्मानपत्रसन्देशः प्रेषिताः ।',
+'emailuserfooter' => 'एतद्विद्युन्मानपत्रं {{SITENAME}} इत्यस्य योजपत्राचरव्यवस्थाद्वारा $1 इत्यनेन $2 कृते प्रेषितम् ।',
+
+# User Messenger
+'usermessage-summary' => 'तान्त्रिकसन्देशानां त्यागः ।',
+'usermessage-editor' => 'तान्त्रिकसन्देशवाहकः ।',
# Watchlist
'watchlist' => 'मम अवेक्षणसूची',
'mywatchlist' => 'मम अवेक्षणसूची',
'watchlistfor2' => 'हि $1 $2',
+'nowatchlist' => 'अवलोकनावल्यां पदार्थः नास्ति ।',
+'watchlistanontext' => 'अवलोकनपट्टिकायां पुटं दृष्टुं सम्पादयितुं वा $1 करोतु ।',
+'watchnologin' => 'न नामाभिलितम्',
+'watchnologintext' => 'अवलोकनावलीं परिवर्तयितुं भवता नामाभिलेखनं करणीयम् ।[[Special:UserLogin|logged in]]',
+'addwatch' => 'अवलोकनावलीं योजयतु ।',
'addedwatchtext' => 'भवतः [[Special:Watchlist|ध्यानसूचिकायां]] "[[:$1]]" इत्येतत् योजितमस्ति।
इदानींप्रभृति अस्मिन् पृष्ठे तथा अस्य चर्चापृष्ठे सन्तः परिवर्तनानि भवतः निरीक्षासूचिकायां द्रक्ष्यन्ते तथा च [[Special:RecentChanges|सद्यःपरिवर्तितानां सूचिकायां]] इदं पृष्ठं स्थूलाक्षरैः द्रक्ष्यते, यस्मात् भवान् सरलतया इदं पश्यतु <p>निरीक्षासूचिकातः निराकर्तुमिच्छति चेत्, "मा निरीक्षताम्" इत्यसमिन् नोदयतु।',
+'removewatch' => 'अवलोकनावलीतः अपनयतु ।',
'removedwatchtext' => '"[[:$1]]" इति पृष्ठं [[Special:Watchlist|भवतः निरीक्षासूचिकातः]] निराकृतमस्ति।',
'watch' => 'निरीक्षताम्',
'watchthispage' => 'इदं पृष्ठं निरीक्षताम्',
'unwatch' => 'मा निरीक्षताम्',
+'unwatchthispage' => 'अवलोकनेन अलम् ।',
+'notanarticle' => 'न आधेयं पुटम् ।',
+'notvisiblerev' => 'अन्ययोजकेन कृतम् अवतरणम् अपमर्जितम् ।',
+'watchnochange' => 'दर्शितावधौ अवलोकितपदार्थाः न सम्पादिताः ।',
'watchlist-details' => '{{PLURAL:$1|$1 पृष्ठं|$1 पृष्ठानि}} भवतः अवेक्षणसूच्यां सन्ति, सम्भाषणपृष्ठानि नात्र गणितानि।',
+'wlheader-enotif' => '* विद्युन्मानपत्रस्य सूचनाः सक्रियाः ।',
+'wlheader-showupdated' => '* भवतः सन्दर्शनस्य पश्चात् परिवर्तितानि पुटानि स्थूलाक्षरैः निर्दिष्टानि ।',
+'watchmethod-recent' => 'अवलोकितपुटानां सद्यः सम्पादनस्य परीक्षणम् ।',
+'watchmethod-list' => 'सद्यः सम्पादनार्थम् अवलोकितपुटानां परीक्षणम् ।',
+'watchlistcontains' => 'भवतः अवलोकनावली $1 युक्तास्ति ।{{PLURAL:$1|page|pages}}.',
+'iteminvalidname' => "समस्या '$1' इत्यनेन अस्ति । अमान्यं नाम ।",
+'wlnote' => "अधस्तात् {{PLURAL:$1|'''1''' परिवर्तनमस्ति|अन्तिमानि '''$1''' परिवर्तनानि सन्ति}},{{PLURAL:$2|गते दिवसे|'''$2''' गतेषु दिवसेषु}}, , $3, $4. इति",
'wlshowlast' => 'अन्तिमानि ($1 होराः $2 दिनानि) $3 इति दर्श्यन्ताम्',
'watchlist-options' => 'अवेक्षणसूच्याः विकल्पाः',
# Displayed when you click the "watch" button and it is in the process of watching
'watching' => 'निरीक्षते...',
'unwatching' => 'निरीक्षाम् अपाकरोति...',
+'watcherrortext' => ' "$1" कृते अवलोकनावल्याः व्यवस्थापरिवर्तनावसरे दोषः संविधितः ।',
+'enotif_mailer' => '{{SITENAME}} सूचितः विद्युन्मानपत्रप्रेषकः ।',
+'enotif_reset' => 'सन्दर्शितानि इति सर्वपुटानि अङ्कयतु ।',
'enotif_newpagetext' => 'इदम् एकं नवीनपृष्ठम्',
'enotif_impersonal_salutation' => '{{SITENAME}} योजक',
+'changed' => 'परिवर्तितम् ।',
+'created' => 'सृष्टम् ।',
+'enotif_subject' => '{{SITENAME}} $ पुटशीर्षकं $ परिवर्तितम्$ इत्यनेन ।',
+'enotif_lastvisited' => 'भवतः पूवसन्दर्शनस्य पश्चात् सवृत्तपरिवर्तनार्थं $1 पश्यतु ।',
+'enotif_lastdiff' => 'एतत्परिवर्तनं दृष्टुं $1 पश्यतु ।',
+'enotif_anon_editor' => 'अनामकः योजकः $1',
+'enotif_body' => 'आत्मीय $ अवलोकनबन्धो',
# Delete
'deletepage' => 'पृष्ठं निराकरोतु।',
'confirm' => 'स्थिरीकरोतु',
+'excontent' => '"$1" आधेयः आसीत् ।',
+'excontentauthor' => 'आधेयः $1आसीत् । अपि च योगदाता तु "[[Special:Contributions/$2|$2]]" आसीत् ।',
+'exbeforeblank' => 'रिक्तीकरणात् पूर्वम् आधेयः "$1" आसीत् ।',
+'exblank' => 'पुटं रिक्तमासीत् ।',
'delete-confirm' => 'विलुप्यताम् "$1"',
'delete-legend' => 'विलुप्यताम्',
+'historywarning' => "' पूर्वसूचना ''' भवता अपमर्जनसिद्धपुटे बहुशः $1 इतिहासयुक्तः अस्ति ।{{PLURAL:$1|revision|revisions}}:",
'confirmdeletetext' => 'भवान् एकं पृष्ठं तस्य अखिलेन इतिहासेन सहितं अपाकर्तुं प्रवृत्तोऽस्ति। कृपया सुपुष्टीकरोतु यत् भवतः एतदेव आशयः, यद् भवता अस्य परिणामाः सुविज्ञाताः सन्ति तथा च भवता क्रियैषा [[{{MediaWiki:Policy-url}}| यथानीति]] सम्पाद्यते।',
'actioncomplete' => 'कार्यं सम्पन्नम्',
'actionfailed' => 'कर्मन् रिष्ट',
'deletedtext' => '"$1" इत्येतद् अपाकृतमस्ति।
सद्यःकृतानां अपाकरणानाम् अभिलेखः $2 इत्यस्मिन् पश्यतु।',
'dellogpage' => 'अपाकरणानां सूचिका',
+'dellogpagetext' => 'सद्यः कालीनापमर्जितपुटानाम् आवली अधः अस्ति ।',
+'deletionlog' => 'अपमर्जनसूचिका ।',
+'reverted' => 'प्राचीनपुनरावृत्तिः पूर्ववत् कृता ।',
'deletecomment' => 'कारणम् :',
'deleteotherreason' => 'अपरं/अतिरिक्तं कारणम् :',
'deletereasonotherlist' => 'इतर कारणम्',
+'deletereason-dropdown' => '*अपमर्जनस्य सामान्यकारणानि ।
+** लेखकस्य निवेदनम् ।
+** कृतिस्वाम्यस्य उल्लङ्घनम् ।
+** नाशकत्वम् ।',
+'delete-edit-reasonlist' => 'अपमार्जनकारणानि सम्पादयतु ।',
+'delete-toobig' => 'अस्य पुटास्य सम्पादनेतिहासः$1तः अधिकः {{PLURAL:$1|पुनरावृत्तिः}} इति कारणेन बृहत् अस्ति ।
+{{SITENAME}} इत्यस्य अकस्मात् प्रविदारणम् अवरोद्धुं तादृशपुटस्य अपमर्जनं निषिद्धम् ।',
+'delete-warning-toobig' => ' $1 {{PLURAL:$1|पुनरावृत्तिः|पुनरावृत्तयः}} अस्मिन् पुटे विसृतः सम्पादनेतिहासः ।',
# Rollback
+'rollback' => 'सम्पादनं निर्वर्तयतु ।',
+'rollback_short' => 'प्रत्याहरणम् ।',
'rollbacklink' => 'प्रतिनिवर्त्यताम्',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|सम्पादनम्|सम्पादनानि}} प्रत्याहरतु ।',
+'rollbacklinkcount-morethan' => '$1 {{PLURAL:$1|सम्पादनम्|सम्पादनानि}} अधिकं प्रत्याहरतु ।',
+'rollbackfailed' => 'प्रत्यहरणम् असफलम् ।',
+'cantrollback' => 'सम्पादनं पूर्ववत् प्रत्यानेतुं न शक्यते ।
+गतयोजकः केवलम् अस्यपुटस्य कर्ता ।',
+'alreadyrolled' => '[[User:$2|$2]] ([[User talk:$2|वार्ता]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) द्वारा कृतम् [[:$1]] इत्यस्य गतसम्पादनं पूर्वतनस्थितौ प्रत्याहरणं न शक्यते । अत्रान्तरे कोऽप्यन्यः एतत्पुटं पुनस्सम्पादितवान् अथवा पूर्वमेव प्राचीनस्थितौ आनीतम् अस्ति ।
+अस्य पुटास्य अन्तिमसम्पादनं [[User:$3|$3]] ([[User talk:$3|वार्ता]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) इत्यनेन कृतम् ।',
+'editcomment' => "\"''\$1''\" इति सम्पादनसारः आसीत् ।",
+'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]])इत्यस्य सम्पादनम् अपमर्ज्य [[User:$1|$1]] इति अन्तिमपुनरावृत्तिः ।',
+'revertpage-nouser' => '(योजकस्य नाम अपनीतम्) द्वारा कृतसम्पादनं पूर्वस्थितौ प्रत्याहृत्य तत्पूर्वतनस्य [[User:$1|$1]] द्वारा कृतपुनरावृत्तेः नूतनावृत्तिः कृता ।',
+'rollback-success' => '$1 इत्यस्य सम्पादनम् अपनयतु ।
+$2 द्वारा सम्पादितां अन्तिमावृत्तिं पुनस्थापयतु ।',
+
+# Edit tokens
+'sessionfailure-title' => 'सत्रस्य वैफल्यम् ।',
+'sessionfailure' => 'भवतः प्रवेशत्रेण सह कापि समस्या अस्ति इति भाति ।
+सत्रापहरणात् रक्षणस्य सावधानार्थं भवतः क्रियाकलापः अपनीतः ।
+निर्गत्य पूर्वपुटं गत्वा पुनः गत्वा प्रयत्नं करोतु ।',
# Protect
'protectlogpage' => 'सुरक्षासूची',
+'protectlogtext' => 'अधो दत्ता सुरक्षार्थं कृतपरिवर्ननानां सूचिका अस्ति ।
+वरतमानस्य सुरक्षितपुटानां सूचिकार्थम् अत्र [[Special:ProtectedPages|सुरक्षितपुटानां सूचिका]] पश्यतु ।',
'protectedarticle' => '"[[$1]]" इत्येतद् संरक्षितमस्ति',
'modifiedarticleprotection' => '"[[$1]]" इत्येतदर्थं सुरक्षा-स्तरः परिवर्तित: :',
+'unprotectedarticle' => '"[[$1]]"तः सुरक्षा अपमर्जिता ।',
+'movedarticleprotection' => 'सुरक्षणस्य स्तरः "[[$2]]" तः परिवर्त्य "[[$1]]" कृतः अस्ति ।',
+'protect-title' => '"$1" इत्यस्य सुरक्षास्तरं पश्यतु ।',
+'protect-title-notallowed' => '"$1" इत्यस्य सुरक्षास्तरं पश्यतु ।',
+'prot_1movedto2' => '[[$1]] इत्यस्य नामपरिवर्तनं कृत्वा [[$2]] इति कृतम् ।',
+'protect-badnamespace-title' => 'असुरक्षितं नामस्थानम् ।',
+'protect-badnamespace-text' => 'अस्मिन् नामस्थाने पुटानि सुरक्षितानि न भवन्ति ।',
+'protect-legend' => 'सुरक्षां दृढयतु ।',
'protectcomment' => 'कारणम् :',
'protectexpiry' => 'अवसानम् :',
'protect_expiry_invalid' => 'अवसान-समयः अमान्योऽस्ति।',
'protect_expiry_old' => 'अवसान-समयः अतीतोऽस्ति।',
+'protect-unchain-permissions' => 'अग्रिमान् सुरक्षाविकल्पान् निर्तालयतु ।',
'protect-text' => "'''$1''' इति पृष्ठस्य कृते सुरक्षा-स्तरं भवान् अत्र दृष्टुं शक्नोति, तथा च तं परिवर्तयितुं शक्नोति।",
+'protect-locked-blocked' => "भवान् सुरक्शणस्य स्तरं परिवर्तयितुं नैव शक्नोति ।
+'''$1'' इति पुटस्य वर्तमाना स्थितिः एषा अस्ति ।",
+'protect-locked-dblock' => "सक्रियेन दत्तपाठतालनेन सुरक्षापत्राणि परिवर्तयितुं न शक्यते ।
+'''$1''' इत्यस्य वर्तमाना स्थितिः एषा अस्ति ।",
'protect-locked-access' => "भवान् अस्य पृष्ठस्य सुरक्षा-स्तरं परिवर्तयितुम् अनुज्ञां न धारयति। '''$1''' इति पृष्ठस्य अधुनातनः सुरक्षा-स्तरः :",
'protect-cascadeon' => 'इदं पृष्ठं वर्तमत्काले सुरक्षितमस्ति, यत इदं {{PLURAL:$1|निम्नलिखिते पृष्ठे |निम्नलिखितेषु पृष्ठेषु}} समाहितमस्ति {{PLURAL:$1|यस्मिन्|येषु}} सोपानात्मिका सुरक्षा प्रभाविनी अस्ति। भवान् अस्य पृष्ठस्य सुरक्षा-स्तरं परिवर्तयितुं शक्नोति, परं तेन सोपानात्मिका-सुरक्षा न परिवर्तयिष्यति।',
'protect-default' => 'सर्वान् प्रयोक्तॄन् अनुज्ञापयतु।',
'protect-level-sysop' => 'प्रबंधकाः केवलाः',
'protect-summary-cascade' => 'सोपानात्मकम्',
'protect-expiring' => 'अवसानम् $1 (UTC)',
+'protect-expiring-local' => '$1 अपनीतम् ।',
'protect-expiry-indefinite' => 'अनिश्चितकालः',
'protect-cascade' => 'अस्मिन् पृष्ठे समाहितानि पृष्ठाणि सुरक्षितानि करोतु (सोपानात्मिका सुरक्षा)।',
'protect-cantedit' => 'भवान् अस्य पृष्ठस्य सुरक्षा-स्तरं परिवर्तयितुं न शक्नोति, यतो भवान् इदं पृष्ठं संपादयितुं अनुज्ञां न धारयति।',
+'protect-othertime' => 'अन्यः समयः ।',
+'protect-othertime-op' => 'अन्यः समयः :',
+'protect-existing-expiry' => 'विद्यमानः समाप्तिसमयः $3, $2',
+'protect-otherreason' => 'अपरं/अतिरिक्तं कारणम् :',
'protect-otherreason-op' => 'इतर कारणम्',
+'protect-dropdown' => '*सुरक्षायाः सामान्यकारणानि ।
+** अत्यधिकं नाशकत्वम् ।
+** अत्यधिकं शुष्कसन्देशाः ।
+** अफलदायि सम्पादनयुद्धम्
+** अधिकसञ्चारयुक्तपुटानि ।',
+'protect-edit-reasonlist' => 'सुरक्षाकारणानि सम्पादयतु ।',
+'protect-expiry-options' => '१ होरा :1 hour,१दिनम्:1 day,१ सप्ताहः:1 week,सप्ताहद्वयम्:2 weeks,१मासः:1 month,३मासाः:3 months,६मासाः:6 months,१ वर्षम् :1 year, अनन्तम् :infinite',
'restriction-type' => 'अनुमतिः:',
'restriction-level' => 'सुरक्षा-स्तरः :',
+'minimum-size' => 'कनिष्टाकारः ।',
+'maximum-size' => 'गरिष्टाकारः ।',
+'pagesize' => 'बैट्स् ।',
# Restrictions (nouns)
'restriction-edit' => 'सम्पाद्यताम्',
+'restriction-move' => 'चलनम् ।',
'restriction-create' => 'सृज्यताम्',
'restriction-upload' => 'आरोप्यताम्',
# Restriction levels
'restriction-level-sysop' => 'पूर्णतया संरक्षितम्',
'restriction-level-autoconfirmed' => 'अर्धसंरक्षितम्',
+'restriction-level-all' => 'कोऽपि स्तरः ।',
# Undelete
+'undelete' => 'अपमर्जितपुटानि अवलोकयतु ।',
+'undeletepage' => 'अपमर्जितपुटानि दृष्ट्वा पुनस्थापयतु ।',
+'undeletepagetitle' => "'''अधः [[:$1|$1]] इत्येतेषाम् अपनीतावृत्तीनां दर्शनं भवति ।",
+'viewdeletedpage' => 'अपमर्जितपुटानि अवलोकयतु ।',
+'undeletepagetext' => '{{PLURAL:$1|$1पुटं|$1 पुटानि}} इत्येतानि अपनीतानि किन्तु एतानि लेखागारे सन्ति अपि च पुनस्थापितानि कर्तुं शक्यते ।',
+'undelete-fieldset-title' => 'पुनरावृत्तीः पुनस्थापयतु ।',
+'undeleteextrahelp' => "पुटानाम् इतिहासं प्रत्याहर्तुं चिह्नितमञ्जूषाः अवचिताः कृत्वा '''''{{int:undeletebtn}}''''' इत्येतत् तुदतु ।
+विचितेतिहासं प्रत्याहर्तुं तद्वृत्तीनां पार्श्वगतचिह्नमञ्जूषासु चयनचिह्नानि विनिवेशयतु । पश्चात्'''''{{int:undeletebtn}}''''' एतत् तुदतु ।",
+'undeleterevisions' => '$1 {{PLURAL:$1|पुनरावृत्तिः}}',
+'undeletehistory' => 'यदि भवान् पुटानि पुनस्थापयितुमिच्छति तर्हि पुनरवृत्तीनां सर्वेतिहासाः पुनस्थापितानि भवन्ति ।
+अपनयनात् परं यदि तस्मिन् एव नाम्नि नूतनपुटनिर्माणं करोति चेत् तस्य पुनस्थापितावृत्तिः पूर्वेतिहासे एव दृश्यते ।',
+'undeleterevdel' => 'यदि पुनस्थापनस्य फलस्वरूपशीर्षकपुटं, सञ्चिकां, पुनरावृत्तिं वा आंशिकरूपेण नाशयति चेत् एतत् न क्रियते ।
+एतादस्थितौ नूतनापनीताः पुनरावृत्तीनाम् अपचयनं असङ्गोपनं वा कुर्याट् ।',
+'undeletehistorynoadmin' => 'एतत्पुटम् अपमर्जितम् ।
+अधः अपमर्जनस्य कारणं दर्शितम् । अपमर्जनात् पूर्वं ये योजकाः सम्पादनं कृतवन्तः तेषां विषयः अपि दर्शिताः ।
+अपमर्जितपुनरावृत्तीनां वास्तवपाठः केवलं प्रशासकै दृष्टुं शक्यते ।',
+'undelete-revision' => '$1 ($4 इत्येतं $5 समये $3 द्वारा निर्मितम्) इत्येतेषाम् अपमर्जितपुनरावृत्तयः ।',
+'undeleterevision-missing' => 'अमान्या अथवा विलुप्ता पुनरावृत्तिः । भवान् प्रदुष्टानुबन्धयुक्तः अथवा पुनरावृत्तिः पुनस्थापिता अथवा लेखागारात् अपनीता ।',
+'undelete-nodiff' => 'पूर्वतनपुनरावृत्तिः न दृष्टा ।',
+'undeletebtn' => 'पुन्थापयतु ।',
'undeletelink' => 'दृश्यताम्/प्रत्यानीयताम्',
'undeleteviewlink' => 'दृश्यताम्',
+'undeletereset' => 'पुनर्योजयतु ।',
+'undeleteinvert' => 'चयनं परिवर्तयतु ।',
'undeletecomment' => 'कारणम् :',
+'undeletedrevisions' => '{{PLURAL:$1| पुनरावृत्तिः पुनस्थापिता|$1 पुनरावृत्तयः पुनस्थापिताः}} अस्ति|सन्ति ।',
+'undeletedrevisions-files' => '{{PLURAL:$1|1 पुनरावृत्तिः|$1 पुनरावृत्तयः}} अपि च {{PLURAL:$2|१सञ्चिका|$2 सञ्चिकाः}} पुनस्थापिताः सन्ति ।',
+'undeletedfiles' => '{{PLURAL:$1|१सञ्चिका|$1 सञ्चिकाः}} पुनस्थापिताः ।',
+'cannotundelete' => 'अनपमर्जनम् असफलम् ।
+प्रथमं कोऽप्यन्यः पुटम् अपमर्जितवान् स्यात् ।',
+'undeletedpage' => "'''$1 इत्येतत् पुनस्थापितम् अस्ति ।
+सद्यः अपनीतानि पुनस्थापितानि च पुटाणि ज्ञातुम् अत्र पश्यतु । [[Special:Log/delete|अपनयनप्रवेशः]] ।",
+'undelete-header' => 'सद्यः एव अपनीतानां पुटानां दर्शनार्थं अत्र प्रविशतु । [[Special:Log/delete|अपनीतावली]] ।',
+'undelete-search-title' => 'अपमर्जितपुटानि अन्विषतु ।',
+'undelete-search-box' => 'अपमर्जितपुटानि अन्विषतु ।',
+'undelete-search-prefix' => 'इत्यनेन आरब्धपुटानि दर्शयतु ।',
'undelete-search-submit' => 'अन्वेषणम्',
+'undelete-no-results' => 'अपमर्जनलेखागारे अमेलपुटानि लब्धानि ।',
+'undelete-filename-mismatch' => 'समयमुद्रया सञ्चिकापुनरावृत्तिः अनपमर्जितुं शक्यते । यतः $1 इति सञिकानाम अननुरूपम् ।',
+'undelete-bad-store-key' => 'समयमुद्रया सञ्चिकापुनरावृत्तिः अनपमर्जनं नैव शक्नोति । ।$1: यतः अपमर्जनात् पूर्वमेव विलुप्तम् ।',
+'undelete-cleanup-error' => 'दोषापमारजनस्य अनुपयुक्ता लेखागारसञ्चिका "$1" ।',
+'undelete-missing-filearchive' => '$1 इति सञ्चिकालेखागारस्य अभिज्ञापकं पुनस्थापितुं नैव शक्यते । यतः एतत् दत्तपाठे नास्ति ।
+एतत् पूर्वमेव अनपमर्जितं स्यात् ।',
+'undelete-error' => 'पुटापमर्जने दोषः ।',
+'undelete-error-short' => 'सञ्चिकानपमर्जने दोषः : $1',
+'undelete-error-long' => '!!सञ्चिकानपमर्जने आगता समस्या ।$1',
+'undelete-show-file-confirm' => '$2 तः $3 मध्ये "<nowiki>$1</nowiki>" इति सञ्चिकायाः निरस्तं परिष्करणं भवान् नूनं द्रष्टुम् इच्छति ?',
'undelete-show-file-submit' => 'आम्',
# Namespace form on various pages
'namespace' => 'नामाकाशः :',
'invert' => 'चयनं विपरीतीकरोतु',
+'tooltip-invert' => 'चितनामस्थाने परिवर्तनं गोपयितुं मञ्जूषाम् अर्गलयतु ।',
+'namespace_association' => 'सम्बद्धं नामस्थानम् ।',
+'tooltip-namespace_association' => 'चितनामस्थानेन सह सम्बद्धं विषयनामस्थानम् अथवा सम्भाषणम् अपि उपादातुम् इमां मञ्जूषाम् अर्गलयतु ।',
'blanknamespace' => '(मुख्यः)',
# Contributions
'contributions-title' => '$1 इत्येतस्य कृते योजकानां योगदानानि',
'mycontris' => 'मम योगदानानि',
'contribsub2' => '$1 इत्येतदर्थम् ($2)',
+'nocontribs' => 'एतादृशयोग्यताभिः समं परिवर्तनानि न दृष्टानि ।',
'uctop' => '(शीर्षम्)',
'month' => 'अस्मात् मासात् (प्राक्तनानि च):',
'year' => 'अस्मात् वर्षात् (प्राक्तनानि च):',
'sp-contributions-newbies' => 'नूतनयोजकानां केवलं योगदानानि दर्श्यन्ताम्',
+'sp-contributions-newbies-sub' => 'नूतनलेखार्थम् ।',
+'sp-contributions-newbies-title' => 'नूतनलेखार्थं योजकयोगदानम् ।',
'sp-contributions-blocklog' => 'अवरुद्धा सूची',
+'sp-contributions-deleted' => 'योजकयोगदानम् अपमर्जतु ।',
'sp-contributions-uploads' => 'आरोप्यताम्',
'sp-contributions-logs' => 'लोग्स',
'sp-contributions-talk' => 'सम्भाषणम्',
+'sp-contributions-userrights' => 'योजकाधिकारस्य व्यवस्थापनम् ।',
+'sp-contributions-blocked-notice' => 'अयं प्रयोक्ता सम्प्रति अवरुद्धः वर्तते।
+नूतनतमा अवरोधाभिलेख-प्रविष्टिः सन्दर्भार्थम् अधस्तात् प्रदत्ताऽस्ति:',
+'sp-contributions-blocked-notice-anon' => 'अयं प्रयोक्ता सम्प्रति अवरुद्धः वर्तते।
+नूतनतमा अवरोधाभिलेख-प्रविष्टिः सन्दर्भार्थम् अधस्तात् प्रदत्ताऽस्ति:',
'sp-contributions-search' => 'योगदानानि अन्विष्यन्ताम्',
'sp-contributions-username' => 'आइ.पी.सङ्केतः अथवा योजकनाम :',
'sp-contributions-toponly' => 'सम्पादनानां नूतनावृत्तिमात्रं दर्श्यताम्',
'whatlinkshere-page' => 'पृष्ठम् :',
'linkshere' => "अधोलिखितानि पृष्ठाणि '''[[:$1]]''' इत्येतद् प्रति संबंधनं कुर्वन्ति :",
'nolinkshere' => "'''[[:$1]]'''इत्येतेन न किञ्चित् पृष्ठं संयुक्तम्",
+'nolinkshere-ns' => "चितनामस्थानात् '''[[:$1]]''' इत्येनं योजनयोग्यं पुटं नास्ति ।",
'isredirect' => 'अनुप्रेषण-पृष्ठम्',
'istemplate' => 'मिलापयतु',
'isimage' => 'सञ्चिकासंबन्ध',
'whatlinkshere-filters' => 'निस्यन्दनानि',
# Block/unblock
+'autoblockid' => 'स्वयं पिहितम् । $1',
+'block' => 'योजकम् अवरुणद्धु ।',
+'unblock' => 'योजकम् अनवरुणद्धु ।',
'blockip' => 'प्रयोक्तारं निरुध्नातु',
+'blockip-title' => 'योजकम् अवरुणद्धु ।',
+'blockip-legend' => 'योजकम् अवरुणद्धु ।',
+'blockiptext' => 'विशिष्टं IP सङ्केतम् अथवा योजकनाम लेखानाधिकारस्य प्राप्तये निम्नदत्तपत्रस्य उपयोगं करोतु ।
+केवलं नाशकत्वम् अवरोद्धुं एतस्य उपयोगं करोतु । [[{{MediaWiki:Policy-url}}|नीतिः]] इत्यानुसारं करणीयम् ।
+अधः विशिष्टं कारणमपि लिखतु ।',
+'ipadressorusername' => 'आइ.पी.सङ्केतः अथवा योजकनाम :',
+'ipbexpiry' => 'समाप्तिः :',
'ipbreason' => 'कारणम् :',
+'ipbreasonotherlist' => 'अन्यत् कारणम्',
+'ipbreason-dropdown' => '* अवरोधस्य सामान्यानि कारणानि ।
+** मिथ्या योजकनाम ।
+** एकाधिकयोजकस्थानं निर्मीय तेषां दुरुपयोगः ।
+** असत्यविषयानाम् उत्तारणम् ।
+** पुटेषु अवकरपूरणम् ।
+** पुटेभ्यः पदार्थान् अपनयनम् ।
+** बाह्यजालस्थानाम् असम्बद्धानुबन्धानाम् संयोजनम् ।
+** योजकानां पीडनम् ।',
+'ipb-hardblock' => 'नामाभिलेखितयोजकान् अनेन ऐपि सङ्केतेन सम्पादनं निवारयतु ।',
+'ipbcreateaccount' => 'योजकस्थानस्य निर्माणं निवारयतु ।',
+'ipbemailban' => 'योजकस्य विद्युन्मानसन्देशप्रेषणम् अवरुणद्धु ।',
+'ipbenableautoblock' => 'अनेन योजकेन उपयुक्तम् ऐपिसङ्केतम्, अग्रे अनेन योजकेन सम्पादयितुं प्रयतमानम् ऐपिसङ्केतं च स्वयम् अवरुद्धं करोतु ।',
+'ipbsubmit' => 'एतं योजकम् अवरुणद्धु ।',
+'ipbother' => 'अन्यः समयः ।',
'ipboptions' => '२ होराः:2 hours,१ दिनम्:1 day,३ दिनानि:3 days,१ सप्ताहः:1 week,२ सप्ताहौ:2 weeks,१ मासः:1 month,३ मासाः:3 months,६ मासाः:6 months,१ वर्षः:1 year,अनन्तम्:infinite',
'ipbotheroption' => 'अन्य',
+'ipbotherreason' => 'अपरं/अतिरिक्तं कारणम् :',
+'ipbhidename' => 'सम्पादनेभ्यः आवलीभ्यः च योजकनाम सङ्गोपयतु ।',
+'ipbwatchuser' => 'अस्य योजकस्य योजकपुटानि सम्भाषणपुटानि च अवलोकयतु ।',
+'ipb-disableusertalk' => 'एतं योजकम् अवरोधकाले स्वस्य सम्भाषणपुटस्य सम्पानात् निवारयतु ।',
+'ipb-change-block' => 'एतैः विन्यासैः योजकं पुनः अवरुणद्धु ।',
+'ipb-confirm' => 'अवरोधं दृढयतु ।',
+'badipaddress' => 'अमान्यः ऐपिसङ्केतः ।',
+'blockipsuccesssub' => 'अवरोधः सफलः ।',
+'blockipsuccesstext' => '[[Special:Contributions/$1|$1]]इत्येतत् अवरुद्धम् । <br />
+अवरोधानां समीक्षां करोतु । [[Special:BlockList|IP अवरोधसूचिका]]',
+'ipb-blockingself' => 'भवान् स्वयम् अवरोधने निरतः । निश्चयेन स्वावरोधनम् इच्छति वा ?',
+'ipb-confirmhideuser' => 'योजकगोपनस्य पिञ्जं निपीडयन् भवान् योजकावरुद्धिं यतते । एतत् सर्वावलीषु सर्वप्रवेशसूचिकासु च योजकनाम निग्रहति । भवान् निश्चयेन एतत् कर्तुमिच्छति वा ?',
+'ipb-edit-dropdown' => 'अवरोधकारणानि सम्पादयतु ।',
+'ipb-unblock-addr' => '$1 अनवरोधनं करोतु ।',
+'ipb-unblock' => 'योजकनाम अथवा ऐपिसङ्केतम् अवरुणद्धु ।',
+'ipb-blocklist' => 'वर्तमानावरोधान् अवलोकयतु ।',
+'ipb-blocklist-contribs' => '$1कृते योगदानम् ।',
+'unblockip' => 'योजकसु अवरोधं परिहरतु ।',
+'unblockiptext' => 'सद्यः अवरुद्धान् ऐपिसङ्केतान् अथवा अवरुद्धानि योजकनामानि पुनस्संस्थाप्य लिखनावकाशं प्राप्तुम् अधो दत्तप्रपत्रस्य उपयोगं करोतु ।',
+'ipusubmit' => 'अवरोधम् अपनयतु ।',
+'unblocked' => '[[User:$1|$1]] इति योजकस्य अवरोधम् अपनयतु ।',
+'unblocked-range' => '$1 इत्येतस्य अवरोधः कृतः ।',
+'unblocked-id' => '$1 इत्यस्य अवरोधः अपनीतः ।',
+'blocklist' => 'अवरुद्धाः योजकाः ।',
'ipblocklist' => 'अवरुद्धाः योजकाः',
+'ipblocklist-legend' => 'अवरुद्धयोजकं पश्यतु ।',
+'blocklist-userblocks' => 'योजकस्थानावरुद्धिं गोपयतु ।',
+'blocklist-tempblocks' => 'तात्कालिकावरुद्धिं गोपयतु ।',
+'blocklist-addressblocks' => 'एकाकिनम् ऐपि अवरोधं गोपयतु ।',
+'blocklist-rangeblocks' => 'प्रान्तीयावरोधान् गोपयतु ।',
+'blocklist-timestamp' => 'समयमुद्रा',
+'blocklist-target' => 'लक्ष्यम्',
+'blocklist-expiry' => 'नश्यति',
+'blocklist-by' => 'वरोधनस्य प्रशसनम् ।',
+'blocklist-params' => 'विस्तारान् अवरुणद्धु ।',
'blocklist-reason' => 'कारणम्',
'ipblocklist-submit' => 'अन्वेषणम्',
+'ipblocklist-localblock' => 'स्थानीयावरोधः ।',
+'ipblocklist-otherblocks' => 'अन्याः{{PLURAL:$1|अवरोधाः}}',
+'infiniteblock' => 'अनन्तम् ।',
+'expiringblock' => '$1 इत्यस्य $2समये समाप्तिः भवति ।',
+'anononlyblock' => 'अनामकः केवलम् ।',
+'noautoblockblock' => 'स्वयमवरोधः निष्क्रियः ।',
+'createaccountblock' => 'योजकस्थाननिर्माणं निष्क्रियम् ।',
+'emailblock' => 'विद्युन्मानपत्रं निष्क्रियम् ।',
+'blocklist-nousertalk' => 'स्वस्य सम्भाषणपुटं सम्पादयितुं न शक्यते ।',
+'ipblocklist-empty' => 'अवरोधावली रिक्ता अस्ति ।',
+'ipblocklist-no-results' => 'अभ्यर्थितः ऐपिसङ्केतः अथवा अभ्यर्थितः योजकनाम अवरुद्धं न ।',
'blocklink' => 'अवरोधः क्रियताम्',
'unblocklink' => 'निरोधः अपास्यताम्',
'change-blocklink' => 'विभागः परिवर्त्यताम्',
'contribslink' => 'योगदानम्',
+'emaillink' => 'विद्युन्मानपत्रं प्रेषयतु ।',
+'autoblocker' => 'भवतः ऐपि सङ्केतः स्वयम् अवरुद्धः यः सद्यः काले एव [[User:$1|$1]]" इत्यनेन उपयुक्तः ।
+$1 इत्यस्य अवरोधस्य कारणं तु "$2" अस्ति ।',
'blocklogpage' => 'अवरोधानां सूची',
+'blocklog-showlog' => 'अयम् एपि सङ्केतः पूर्वमेव अवरुद्धः ।
+अवरोधसूची आधाराय अधः दत्तः अस्ति :',
+'blocklog-showsuppresslog' => 'अयं योजकः पूर्वमेव अवरुद्धः सङ्गुप्तः च ।
+निग्रहकरणं तु अधः उल्लिखितम् ।',
'blocklogentry' => '[[$1]] इत्येतद् अवरुद्धम्, $2 $3 इति अवसान-समयेन सह',
+'reblock-logentry' => '[[$1]] इत्यस्य अवरोधस्य विन्यासः परिवर्तितः अयं $2 $3 समये विनश्येत् ।',
+'blocklogtext' => 'इयम् अवरुद्धानवरुद्धप्रक्रियायाः अवलोकनस्य सूचिका ।
+स्वयम् अवरुद्धानाम् ऐपिसङ्केतानाम् आवली न कृता ।
+सद्यः उपयोगनिषेधस्य अवरोधानाम् आवलीप्राप्तये [[Special:BlockList|block list]] अवलोकयतु ।',
'unblocklogentry' => 'अनिरुद्धम् $1',
+'block-log-flags-anononly' => 'अनामकाः योजकाः केवलम् ।',
'block-log-flags-nocreate' => 'सदस्यता प्राप्तिः अवरुद्धा अस्ति',
+'block-log-flags-noautoblock' => 'स्वयमवरोधः निष्क्रियः ।',
+'block-log-flags-noemail' => 'विद्युन्मानपत्रं निष्क्रियम् ।',
+'block-log-flags-nousertalk' => 'स्वस्य सम्भाषणपुटं सम्पादयितुं न शक्यते ।',
+'block-log-flags-angry-autoblock' => ' उन्नतीकृतः स्वयमवरोधः सक्रियः ।',
+'block-log-flags-hiddenname' => 'योजकस्य नाम सङ्गुप्तम् ।',
+'range_block_disabled' => ' प्रादेशिकावरोधस्य प्रशासकस्य सामर्थ्यं निष्क्रियम् ।',
+'ipb_expiry_invalid' => 'अवसानसमयः अमान्योऽस्ति।',
+'ipb_expiry_temp' => 'सङ्गुप्तयोजकनामावरोधः शश्वतः भवेत् ।',
+'ipb_hide_invalid' => 'एतस्य योजकस्थानस्य निग्रहः असाध्यः । अस्मिन् अनेकानि सम्पादनानि स्युः ।',
+'ipb_already_blocked' => '"$1" इत्येषः पूर्वमेव अवरुद्धः ।',
+'ipb-needreblock' => '$1 इत्येषः पूर्वमेव अवरुद्धः विन्यासं परिवर्तयितुमिच्छति वा ?',
+'ipb-otherblocks-header' => 'अन्याः {{PLURAL:$1|अवरोधः |अवरोधाः}}',
+'unblock-hideuser' => 'एतं योजकम् अवरोधात् विमोचयितुं न शक्यते । यतः अस्य योजकनाम सङ्गुप्तम् ।',
+'ipb_cant_unblock' => ' दोषः : $1 इति अवरुद्धः पत्रसङ्केतः न दृष्टः । प्रायः तावत् पूर्वमेव उत्तारितम् ।',
+'ipb_blocked_as_range' => 'दोषः : $1 इति ऐपिसङ्केतः साक्षात् अवरुद्धः न अपि च विमोचनं न शक्यते ।
+$2 इति प्रकारस्य अवरोधं कर्तुं शक्यते यत् अनवरोधमिच्छति ।',
+'ip_range_invalid' => 'अमान्यः ऐपिप्रकारः',
+'ip_range_toolarge' => '/$1 तः अधिकं वृहत्प्रकारकः अवरोधः नानुमतः ।',
+'blockme' => 'माम् अवरुणद्धु ।',
+'proxyblocker' => 'प्रतिहस्तकः अवरोधकः ।',
+'proxyblocker-disabled' => 'अयं कार्यकलापः निष्क्रियः ।',
+'proxyblockreason' => 'भवतः ऐपि सङ्केतः अवरुद्धः यतः अयं कश्चन मुक्तप्रतिहस्तकः ।
+अन्तर्जालसेवादायकं सम्पर्कयतु गभीरायाः सुरक्षासमस्यायाः विषये सूचयतु च',
'proxyblocksuccess' => 'समापित ।',
+'sorbsreason' => 'DNSBL उपयोगः {{SITENAME}} कृतस्य भवतः ऐपिसङ्केतः मुक्तप्रतिहस्तकः इति आवलीगतः',
+'sorbs_create_account_reason' => 'DNSBL उपयुक्तः {{SITENAME}} अतः भवतः ऐपिसङ्केतः अवरुद्धः यतः अयं मुक्तप्रतिहस्तकः इति आवलीगतः । अतः भवान् योजकस्थानं निर्मातुं न शक्नोति ।',
+'cant-block-while-blocked' => 'अन्ययोजकान् अवरोद्धुं भवान् नैव शक्नोति यतः भवान् अवरुद्धः ।',
+'cant-see-hidden-user' => 'यं योजकः अवरोद्धं भवान् प्रयतमानः सः पूर्वमेव अवरुद्धः सङ्गुप्तः च ।
+भवान् तु योजकसङ्गोपनाधिकारयुक्तः न । अतः भवान् योजकावरोधं दृष्टुं सम्पादयितुं वा न शक्नोति ।',
+'ipbblocked' => 'भवान् अन्ययोजकान् अवरोद्धुम् विमोचयितुं वा न शक्नोति । यतः भवान् तु अवरुद्धः अस्ति ।',
+'ipbnounblockself' => 'भवान् भवन्तं मोचयितुं नैव शक्नोति ।',
+
+# Developer tools
+'lockdb' => 'दत्तपाठान् अवरुणद्धु ।',
+'unlockdb' => 'दत्तपाठान् अनवरुणद्धु ।',
+'lockdbtext' => 'दत्तपाठानाम् अवरोधः सर्वयोजकानां सम्पादनसामर्थ्यं लुम्पति । तेषाम् आद्यतां परिवर्तयतु । तेषाम् अवलोकनावलीं सम्पादयतु । परिवर्तनावश्यकदतापाठान् अपि परिवर्तयतु । भवान् एतदेव कर्तुकामः इति दृढयतु । यदा भवतः निर्वहणं भविष्यति तदा दत्तपाठाः अनवरुद्धाः भविष्यन्ति ।',
+'unlockdbtext' => 'दत्तपाठानाम् अवरोधः सर्वयोजकानां सम्पादनसामर्थ्यं लुम्पति । तेषाम् आद्यतां परिवर्तयतु । तेषाम् अवलोकनावलीं सम्पादयतु । परिवर्तनावश्यकदतापाठान् अपि परिवर्तयतु । भवान् एतदेव कर्तुकामः इति दृढयतु ।',
+'lockconfirm' => 'आम्, अहं निश्चयेन दत्तपाठान् अवरोद्धुम् इच्छामि ।',
+'unlockconfirm' => 'आम्, अहं निश्चयेन दत्तपाठान् अनवरोद्धुम् इच्छामि ।',
+'lockbtn' => 'दत्तपाठान् अवरुणद्धु ।',
+'unlockbtn' => 'दत्तपाठान् विमोचयतु ।',
+'locknoconfirm' => 'दृढीकरणमञ्जूषां भवान् न अर्गलितवान् ।',
+'lockdbsuccesssub' => 'दत्तपाठावरोधः सफलः ।',
+'unlockdbsuccesssub' => 'दत्तपाठावरोधः विमुक्तः ।',
+'lockdbsuccesstext' => 'दत्तपाठाः तालिताः । ।<br />
+भवतः निर्वहणस्य पश्चात् वितालनं स्मरतु [[Special:UnlockDB|वितालनम्]] ।',
+'unlockdbsuccesstext' => 'दत्तपाठाः वितालिताः ।',
+'lockfilenotwritable' => 'दत्तपाठः कपाटनस्य सञ्चिका लेखनार्हा न ।
+दत्तपाठान् कपाटयितुम् अकापाटयितुं वा जालवितारकेन लेखनार्हः आवश्यक्तः ।',
+'databasenotlocked' => 'दत्तपाठाः कपाटिताः न ।',
+'lockedbyandtime' => '(द्वारा {{GENDER:$1|$1}} इत्यस्मिन् $2 अत्र $3)',
# Move page
+'move-page' => ' $1 चालयतु ।',
'move-page-legend' => 'पृष्ठं रक्ष्यताम्',
+'movepagetalktext' => 'सम्बद्धसम्भाषणपुटानि अनेन सह स्थानान्तरितानि भवन्ति अन्यथा
+* भवान् पुटं अन्यस्थानान्तरं कुर्वन् अस्ति ।
+* अस्मिन् नाम्नि सम्भाषणपुटं पूर्वनिर्मितमस्ति अस्थवा
+* अधोदत्ताम् अर्गलनमञ्चूषाम् उत्पाटितवान् ।
+अस्मिन् विषये यदि इच्छति तर्हि भवता पुटानि चालनीयानि अथवा संयोजनीयानि ।',
'movearticle' => 'पृष्ठं चाल्यताम्',
+'moveuserpage-warning' => 'पूर्वसूचा : योजकपुटं चालयितुम् उद्युक्तः । स्मरतु केवलं पुटं स्थानान्तरितं भवति न तु योजकनाम परिवर्तनं न भविष्यति ।',
+'movenologin' => 'न नामाभिलितम्',
+'movenologintext' => ' [[Special:UserLogin|logged in]] पञ्जीकृतयोजकः भवता नामाभिलेखनं करणीयं भवति ।',
+'movenotallowed' => 'पुटानि स्थानान्तरियितुम् अनुमतिः नाश्ति ।',
+'movenotallowedfile' => 'सञ्चिकाः स्थानान्तरयितुम् अनुमतिः नास्ति ।',
+'cant-move-user-page' => 'योजकपुटानि स्थानन्तरितुम् अनुमतिः ते नास्ति । (उपपुटानि विना)',
+'cant-move-to-user-page' => 'किञ्चिनपुटं योजकपुटं स्थानान्तरितुं ते अनुमतिः नास्ति । (योजकपुटं विना)',
'newtitle' => 'नूतनं शीर्षकं प्रति :',
'move-watch' => 'इदं पृष्ठं निरीक्षताम्।',
'movepagebtn' => 'पृष्ठं चालयतु।',
'pagemovedsub' => 'चालनं सिद्धम्।',
'movepage-moved' => '\'\'\'"$1" इत्येतद् "$2" इत्येतद् प्रति चालितमस्ति \'\'\'',
+'movepage-moved-redirect' => 'पुनर्निदेशः सृष्टः ।',
+'movepage-moved-noredirect' => 'पुनर्निदेशनसृष्टिः निग्रहितः ।',
'articleexists' => 'अनेन नाम्ना पृष्ठमेकं पूर्वेऽपि विद्यते, अथवा भवता चितं नाम तु अमान्यमस्ति। कृपया इतरं किमपि नाम चिनोतु।',
+'cantmove-titleprotected' => 'अस्मिन् स्थाने पुटस्थानान्तरणं न भवति । यतः नूतनशीर्षकं सर्जनात् सुरक्षितम् ।',
'talkexists' => "'''पृष्ठं साफल्येन चालितमस्ति, परं चर्चापृष्ठं चालयितुं न शक्यम्, यतो नवेऽपि पृष्ठे चर्चापृष्ठं विद्यते। कृपया तं स्वयमेव चालयतु।'''",
'movedto' => 'इदं प्रति चालितम्।',
'movetalk' => 'सहगामिनं चर्चापृष्ठं चालयतु।',
+'move-subpages' => 'उपपुटनि चालयतु । ($1 पर्यन्तम्)',
+'move-talk-subpages' => 'सम्भाषणपुटानाम् उपपुटानि चालयतु ।($1 पर्यन्तम्)',
+'movepage-page-exists' => '$1 इत्येतत् पुटं पूर्वमेव विद्यते । तदुपरि लेखनम् अशक्यम् ।',
+'movepage-page-moved' => '$1 पुटं $2 प्रति चालितम् अस्ति ।',
+'movepage-page-unmoved' => '$1 पुटं $2 प्रति चालनम् अशक्यम् ।',
+'movepage-max-pages' => '$1 इत्यस्य {{PLURAL:$1|page|pages}} गरष्टपुटानि चालितानि अतः इतोप्यधिकपुटानि स्वयं चालितानि न भवन्ति ।',
'movelogpage' => 'लॉग् इत्येतद् चाल्यताम्',
+'movelogpagetext' => 'पुटचालनस्य आवली अधः अस्ति ।',
+'movesubpage' => '{{PLURAL:$1|उपपुटः|उपपुटानि}}',
+'movesubpagetext' => '$1 {{PLURAL:$1|उपपुटम्|उपपुटानि }}अस्य पुटस्य उपपुटानि अधः दर्शितानि ।',
+'movenosubpage' => 'अस्य पुटस्य उपपुटानि न सन्ति ।',
'movereason' => 'कारणम् :',
'revertmove' => 'प्रतिनिवर्त्यताम्',
+'delete_and_move' => 'अपमर्जनं चालनं च ।',
+'delete_and_move_text' => '==अपमर्जनम् आवश्यकम्==
+लक्षितपुटं "[[:$1]]" पूर्वमेव अस्ति ।
+चालनपथं सृष्टुम् एतत् अपमर्जितुम् इच्छति वा ?',
+'delete_and_move_confirm' => 'आम्, पुटम् अपमर्जतु ।',
+'delete_and_move_reason' => '"[[$1]]" तः स्थानान्तरणं कर्तुं पथनिर्माणार्थम् अपमर्जितम् ।',
+'selfmove' => 'स्रोतः लक्ष्यशीर्षकं च समाने ।
+पुटं स्वस्थानान् स्थानान्तरं न शक्यते ।',
+'immobile-source-namespace' => '$1 इति नामस्थाने पुटस्थानान्तरं न शक्यते ।',
+'immobile-target-namespace' => '"$1" इति नामस्थाने पुटानां स्थानान्तरं न शक्यते ।',
+'immobile-target-namespace-iw' => 'पुटचालनार्थम् अन्तर्विक्यानुबन्धः मान्यं लक्ष्यं न ।',
+'immobile-source-page' => 'एतत्पुटं चालनयोग्यं न ।',
+'immobile-target-page' => 'तत् लक्षितशीर्षकं प्रति चालयितुं न शक्यते ।',
+'imagenocrossnamespace' => 'सञ्चिकां अनामस्थाने स्थानान्तरितं कर्तुं नैव शक्यते ।',
+'nonfile-cannot-move-to-file' => 'असञ्चिकायाः सञ्चिकानामस्थाने स्थानान्तरं न शक्यते ।',
+'imagetypemismatch' => 'नूतपुटविस्तारः तस्य प्रकाण सह मेलं न प्राप्नोति ।',
+'imageinvalidfilename' => 'लक्षितसञ्चिकानाम अमान्यम् ।',
+'fix-double-redirects' => 'यङ्कमपि पुनर्निदेशं उन्नतीकरोतु यः मूलशीर्षकं निदेशति ।',
+'move-leave-redirect' => 'कञ्चित् पुनर्निदेशं पूर्वमेव त्यजतु ।',
+'protectedpagemovewarning' => "'''पूर्वसूचना ''' प्रशासकपदयुक्ताः योजकाः एव सम्पादनं कर्तुमर्हन्ति । अतः एतत्पुटं सुरक्षितम् । निदेशार्थम् अधः जघन्यप्रवेशः सूचितः ।",
+'semiprotectedpagemovewarning' => "'''सूचना ''' पञ्जीकृतयोजकानां उपयोगार्थ केवलम् एतत्पुटम् अभिरक्षितम् । जघन्यप्रवेशस्य सूचना आनुकूल्यार्थम् अधोनिदेशिता ।",
+'move-over-sharedrepo' => '==वर्तमानसञ्चिकाः==
+ [[:$1]] विभक्तकोशे सञ्चिकास्ति । अस्यां शीर्षकं स्थानान्तरणेन विभक्तसञ्चिका विकृता भवति ।',
+'file-exists-sharedrepo' => 'विभक्तकोशे चितसञ्चिकानाम प्रथममेव उपयोगे अस्ति । अन्यं नाम चिनोतु ।',
# Export
'export' => 'पृष्ठानां निर्यातं करोतु',
+'exportall' => 'सर्वपुटानि निर्यातानि करोतु ।',
+'exportcuronly' => 'सद्यः पुनरावृत्तिं केवलं सङ्गृह्णातु समूर्णम् इतिहासं न ।',
+'exportnohistory' => 'सुचना : अनुष्टानस्य कारणेन पुटनिर्यातस्य सम्पूर्णेतिहासः एतत्पुटाद्वारा निष्क्रियाः ।',
+'exportlistauthors' => 'प्रत्येकं पुटाय योगदातॄणां पूर्णावलीम् अन्तर्भावयतु ।',
'export-submit' => 'निर्हरति',
+'export-addcattext' => 'वर्गतः पटानि योजयतु ।',
'export-addcat' => 'संयोजयति',
+'export-addnstext' => 'नामस्थानात् पुटानि योजयतु ।',
'export-addns' => 'संयोजयति',
+'export-download' => 'सञ्चिका इव रक्षतु ।',
+'export-templates' => 'प्राकृतीः अनर्भावयतु ।',
+'export-pagelinks' => '...इत्यस्य गाहाय अनुबद्धपुटानि अन्तरभावयतु ।',
# Namespace 8 related
'allmessages' => 'व्यवस्था सन्देशाः',
'allmessagesname' => 'नाम',
'allmessagesdefault' => 'डिफॉल्टसन्देशपाठ',
+'allmessagescurrent' => 'सद्यः सन्देशपाठः ।',
+'allmessagestext' => 'एषा मीडियाविकिनामस्थाने उपलब्धा काचित् तन्त्रसन्देशस्य सूचिका अस्ति । यदि भवान् सामान्यमीडियाविकि क्षेत्रीयकरणे योगदानं कर्तुमिच्छति तर्हि[//www.mediawiki.org/wiki/Localisation मीडियाविकि क्षेत्रीयकरणम्] अथवा [//translatewiki.net translatewiki.net] इत्यत्र गच्छतु ।',
+'allmessagesnotsupportedDB' => "अस्य पुटस्य उपयोगः नैव शक्यते यतः '''\$wgUseDatabaseMessages''' तटास्थम् अस्ति ।",
+'allmessages-filter-legend' => 'शोधनी ।',
+'allmessages-filter' => 'ग्राहकीकरणस्य स्थितौ शोधनी ।',
+'allmessages-filter-unmodified' => 'अपरिष्कृतम् ।',
'allmessages-filter-all' => 'अखिलम्',
'allmessages-filter-modified' => 'परिवर्तितम्',
+'allmessages-prefix' => 'उपसर्गानुगुणं शोधनी ।',
'allmessages-language' => 'भाषा:',
'allmessages-filter-submit' => 'गम्यताम्',
# Thumbnails
'thumbnail-more' => 'विस्तीर्यताम्',
+'filemissing' => 'सञ्चिका विनष्टा ।',
'thumbnail_error' => 'सङ्कुचितचित्रनिर्माणे दोषः: $1',
+'djvu_page_error' => 'DjVu पुटं पृष्ठ परिधेः बहिः ।',
+'djvu_no_xml' => 'DjVu पुटार्थं XMLप्राप्तुं न शक्तम् ।',
+'thumbnail-temp-create' => 'अनित्यां सङ्कुचितसञ्चिकां निर्मातुं न शक्यते ।',
+'thumbnail-dest-create' => 'लक्ष्ये सङ्कुचितं रक्षितुं न शक्यते ।',
+'thumbnail_invalid_params' => 'सङ्कुचितस्य विस्तारः अमान्यः ।',
+'thumbnail_dest_directory' => 'लक्षस्य निदेशिकां सृष्टुं नैव शक्यते ।',
+'thumbnail_image-type' => 'चित्रस्य प्रकारः नानुमोदितः ।',
+'thumbnail_gd-library' => 'अपूर्णं जि.जि.ग्रन्थालयानुन्यासः : विनष्टः कार्यकलापः $1',
+'thumbnail_image-missing' => 'सञ्चिका विनष्टा इति भाति : $1',
# Special:Import
+'import' => 'पृष्ठानां निर्यातं करोतु',
+'importinterwiki' => 'ट्रान्स् विकि आयातकाः',
+'import-interwiki-text' => 'आयातं कर्तुं एकां विकिं एकं पुटं चिनोतु ।
+पुनरावृत्तीनां दिनाङ्कानि, सम्पादनानि च सुरक्षितानि भविष्यन्ति।
+सर्वाः ट्रान्सविक्यायातक्रियाः नामाभिलेखिताः [[Special:Log/import|आयातसूचिकासु]] स्थापिताः ।',
+'import-interwiki-source' => 'स्रोतविकि/पुटम्',
+'import-interwiki-history' => 'एतत्पुटार्थं सर्वेतिहासान् पुनरावृत्तीः च प्रकृतीः करोतु ।',
+'import-interwiki-templates' => 'प्राकृतीः अनर्भावयतु ।',
+'import-interwiki-submit' => 'आयातं करोतु ।',
+'import-interwiki-namespace' => 'लक्षितनामस्थानानि ।',
+'import-upload-filename' => 'सञ्चिकानाम',
'import-comment' => 'टिप्पणी:',
+'importtext' => '[[Special:Export|export utility]] एतेनानुबन्धेन स्रोतविकितः सञ्चिकानां निर्यातं करोतु । भवदीयसङ्गणके सुरक्ष्य अत्र उत्तारयतु ।',
+'importstart' => 'पुटानाम् आयातः....',
+'import-revision-count' => '$1 {{PLURAL:$1|पुनरावृत्तिः}}',
+'importnopages' => 'आयातं कर्तुं पुटानि न सन्ति ।',
+'imported-log-entries' => 'आयातकृतम्$1{{PLURAL:$1|log entry|प्रवेशसूचिकाः}}.',
+'importfailed' => 'असफलायाताः : $1',
+'importunknownsource' => 'अज्ञातायातस्रोतप्रकारः ।',
+'importcantopen' => 'आयातसञ्चिकाः उद्घाटयितुं न शक्यते ।',
+'importbadinterwiki' => 'प्रदुष्टः अन्तर्विक्यनुबन्धः ।',
+'importnotext' => 'रिक्तम् अथवा पाठः नास्ति ।',
+'importsuccess' => 'आयातः समाप्तः ।',
+'importhistoryconflict' => 'उपलब्धाः इतिहासपुनरावृत्तयः परस्परं विपरीताः। (एतत्पुटं पूर्वमेव आयातम् इति भाति ।)',
+'importnosources' => 'कोऽपि ट्रान्स्विकि आयातः नोपलब्धः अपि च प्रत्यक्षेतिहासस्य उत्तारणं निष्कियम् ।',
+'importnofile' => 'कापि आयातसञ्चिका उत्तारिता ।',
+'importuploaderrorsize' => 'आयातसञ्चिकाः अनुत्तारिताः। अस्याः आकारः अधिकतरः अस्ति ।',
+'importuploaderrorpartial' => 'आयातसञ्चिकाः अनुत्तारिताः । सञ्चिकाः अपूर्णोत्तारिताः ।',
+'importuploaderrortemp' => 'अयातसञ्चिकानाम् उत्तारणम् असफलम् ।
+अनित्यः सम्पुटः विनष्टः ।',
+'import-parse-failure' => 'XML आयातस्य व्यवस्थायाः वैफल्यम् ।',
+'import-noarticle' => 'आयातं कर्तुं पुटानि न सन्ति ।',
+'import-nonewrevisions' => 'सर्वाः पुनरावृत्तयः पूर्वमेव आयाताः ।',
+'xml-error-string' => '$1 पङ्किः $2 इत्यस्मिन् , स्तम्भः $3 (बैट्स् $4): $5',
+'import-upload' => 'XML पाठान् उत्तारयतु ।',
+'import-token-mismatch' => 'सत्रस्य पाठानां नाशः ।
+पुनः प्रयतताम् ।',
+'import-invalid-interwiki' => 'निर्दिष्टविकितः आयातः न सम्भवति ।',
+'import-error-edit' => '" $1 "पुटस्य आयातः न शक्यते यतः तस्य सम्पादनुमति ते नास्ति ।',
+'import-error-create' => '" $1 "पुटस्य आयातः न शक्यते यतः ते सम्पादनस्य अनुमतिः नास्ति ।',
+'import-error-interwiki' => '"$1" पुटम् आयातं न यतः अस्य नाम बाह्यानुबन्धार्थं सुरक्षितम् । (अन्तर्विकि)',
+'import-error-special' => '"$1" पुटम् आयातं नैव यतः एतत् विशेषनामस्थानेन सम्बद्धं यत् अन्यपुटानि नानुमन्यते ।',
+'import-error-invalid' => '"$1" पुटं न आयातं यतः अस्य नाम अमान्यम् ।',
+
+# Import log
+'importlogpage' => 'आयातसूचिका ।',
+'importlogpagetext' => 'अन्यविकितः सम्पादितेतिहाससहितानि प्रशासकानाम् आयातपुटानि ।',
+'import-logentry-upload' => 'सञ्चिकाम् उत्तारयित्वा [[$1]] इत्यस्य आयातः कृतः ।',
+'import-logentry-upload-detail' => '$1 {{PLURAL:$1|पुनरावृत्तिः}}',
+'import-logentry-interwiki' => 'ट्रान्स्विकिकृतम् ।$1',
+'import-logentry-interwiki-detail' => '$1 {{PLURAL:$1|पुनरावृत्तिः}} $2 इत्येतस्मात् ।',
+
+# JavaScriptTest
+'javascripttest' => 'जावालिपिपरीक्षणम् ।',
+'javascripttest-disabled' => 'विक्याम् अयं क्रियाकलापः निष्क्रियः ।',
+'javascripttest-title' => '$1 परीक्षाप्रचलति ।',
+'javascripttest-pagetext-noframework' => 'जावलिपिचालनपरीक्षार्थम् एतत्पुटम् आरक्षितम् ।',
+'javascripttest-pagetext-unknownframework' => 'अज्ञातपरीक्षाप्रक्रिया $1',
+'javascripttest-pagetext-frameworks' => 'अधो दत्तेषु कञ्चिदेकां परीक्षाप्रक्रियां चिनोतु : $1',
+'javascripttest-pagetext-skins' => 'अनेन सह परीक्षां सञ्चालयितुं काचित् त्वक् चिनोतु ।',
+'javascripttest-qunit-intro' => 'mediawiki.org. [$1 अभिलेखपरीक्षा] इत्यत्र पश्यतु ।',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'भवतः योजकपृष्ठम्',
+'tooltip-pt-anonuserpage' => 'ऐपिसङ्केतार्थं योजकपुटं भवान् सम्पादयति एवम्..',
'tooltip-pt-mytalk' => 'भवतः सम्भाषणपृष्ठम्',
+'tooltip-pt-anontalk' => 'एतस्मात् ऐपिसङ्केतात् सम्पादनस्य परिचर्चा ।',
'tooltip-pt-preferences' => 'भवतः इष्टतमानि',
'tooltip-pt-watchlist' => 'भवद्भिः परिवर्तनानि निरीक्ष्यमाणानां पृष्ठानां सूची',
'tooltip-pt-mycontris' => 'भवतः योगदानानाम् आवली',
'tooltip-pt-login' => 'भवान् न प्रविष्टः। प्रवेशः अनिवार्यः न।',
+'tooltip-pt-anonlogin' => 'भवतः नामाभिलेखः उत्साहयते । किन्तु नामाभिलेखः ऐच्छिकः ।',
'tooltip-pt-logout' => 'निर्गमनम्',
'tooltip-ca-talk' => 'पृष्ठान्तर्गतविषये चर्चा',
'tooltip-ca-edit' => 'भवान् इदं पृष्ठं सम्पादयितुम् अर्हति। रक्षणात्पूर्वं कृपया प्राग्दृश्यं पश्यतु।',
'tooltip-ca-viewsource' => 'इदं पृष्ठं संरक्षितं विद्यते। भवान् अस्य स्रोतः द्रष्टुम् अर्हति।',
'tooltip-ca-history' => 'अस्य पृष्ठस्य पुरातन्यः आवृत्तयः',
'tooltip-ca-protect' => 'इदं पृष्ठं संरक्ष्यताम्',
+'tooltip-ca-unprotect' => 'अस्य पुटास्य सुरक्षां परिवर्तयतु ।',
'tooltip-ca-delete' => 'इदं पृष्ठम् अपाक्रियताम्',
+'tooltip-ca-undelete' => 'अस्य पुटस्य अपमर्जनात् पूर्वम् अस्य सम्पादनानि पुनस्थापयतु ।',
'tooltip-ca-move' => 'इदं पृष्ठं चाल्यताम्',
'tooltip-ca-watch' => 'इदं पृष्ठं भवतः अवेक्षणसूच्यां योज्यताम्',
'tooltip-ca-unwatch' => 'इदं पृष्ठं भवतः अवेक्षणसूच्याः निष्कास्यताम्',
'tooltip-t-permalink' => 'पृष्ठस्य अस्याः आवृत्तेः स्थिरसम्पर्कतन्तुः',
'tooltip-ca-nstab-main' => 'आन्तर्यं दृश्यताम्',
'tooltip-ca-nstab-user' => 'योजकपृष्ठं दृश्यताम्',
+'tooltip-ca-nstab-media' => 'माध्यमपुटम् अवलोकयतु ।',
'tooltip-ca-nstab-special' => 'इदमेकं विशिष्टं पृष्ठम्, भवान् इदं पृष्ठं सम्पादयितुं न अर्हति।',
'tooltip-ca-nstab-project' => 'प्रकल्पपृष्ठं दृश्यताम्',
'tooltip-ca-nstab-image' => 'सञ्चिकापृष्ठं दृश्यताम्',
+'tooltip-ca-nstab-mediawiki' => 'तन्त्रसन्देशान् अवलोकयतु ।',
'tooltip-ca-nstab-template' => 'फलकं दृश्यताम्',
+'tooltip-ca-nstab-help' => 'साहाय्यपुटम् अवलोकयतु ।',
'tooltip-ca-nstab-category' => 'वर्गाणां पृष्ठं दृश्यताम्',
'tooltip-minoredit' => 'इदं परिवर्तनं लघुपरिवर्तनरूपेण अङ्क्यताम्',
'tooltip-save' => 'परिवर्तनानि रक्ष्यन्ताम्',
'tooltip-diff' => 'पाठे भवता कृतानि परिवर्तनानि दृश्यन्ताम्।',
'tooltip-compareselectedversions' => 'पृष्ठस्य द्वयोः चितयोः आवृत्त्योः भेदः दृश्यताम्',
'tooltip-watch' => 'इदं पृष्ठं भवतः अवेक्षणसूच्यां योज्यताम्',
+'tooltip-watchlistedit-normal-submit' => 'शीर्षकानि अपनयतु ।',
+'tooltip-watchlistedit-raw-submit' => 'अवलोकनावलीं समुद्धरतु ।',
+'tooltip-recreate' => 'एतत्पुटं पूर्वमेव अपमर्जितः अतः पुन सृजतु ।',
+'tooltip-upload' => 'उत्तारणम् आरभताम्',
'tooltip-rollback' => '"पूर्ण-प्रतिगमनं(रोलबैक् इत्येतद्)" अस्य पृष्ठस्य संपादनानि अंतिम-योगदातृकृतानि विपरीतीकरोति एकेन क्लिक्कारेण',
'tooltip-undo' => '"निष्क्रियताम्" इत्येतद् इदं सम्पादनं विपरीतीकरोति, तथा च सम्पादनप्रारूपं प्राग्दृश्यरूपेण उद्घाटयति।
अस्य सारांशे कारणमपि लेखितुं शक्यते।',
+'tooltip-preferences-save' => 'आद्यताः रक्षतु ।',
'tooltip-summary' => 'संक्षिप्तः सारांशः योज्यताम्',
+# Metadata
+'notacceptable' => 'भवतः ग्रहकस्य पठनेच्छारूपेण विकिवितारकः दत्तपाठं प्रकल्पितुं नैव शक्नोति ।',
+
# Attribution
+'anonymous' => '{{SITENAME}} इत्यस्य {{PLURAL:$1||}} अनामकयोजकः ।',
'siteuser' => '{{SITENAME}} योजक $1',
'anonuser' => '{{SITENAME}} अज्ञात योजक $1',
+'lastmodifiedatby' => 'एतस्य पुटस्य अन्तिमपरिवर्तनं $1 दिनाङ्के $2 समये कृतम् ।',
+'othercontribs' => '$1 इत्यस्य कार्यस्य अनुसारम् ।',
'others' => 'अन्य',
'siteusers' => '{{SITENAME}} {{PLURAL:$2|योजक|योजक}} $1',
'anonusers' => '{{SITENAME}} अज्ञात {{PLURAL:$2|योजक|योजक}} $1',
+# Spam protection
+'spambot_username' => 'मिडियाविकिअवकरशुद्धीकरणम् ।',
+'spam_reverting' => '$1 इत्यनेन नानुबद्धनां प्राचीनपुनरावृत्तीनां पुनस्थापनं कुर्वन्ति ।',
+'spam_blanking' => 'सर्वाः पुनरावृत्तयः $1 इत्यस्य अनुबन्धाः पूर्णपाठाः अपनीयन्ते ।',
+'spam_deleting' => 'सर्वाः पुनरावृत्तयः $1 इत्यस्य अनुबन्धाः । पूर्णपाठाः अपनीयन्ते ।',
+
# Info page
+'pageinfo-title' => '"$1" कृते सूचनाः ।',
'pageinfo-header-edits' => 'सम्पादयति',
+'pageinfo-header-watchlist' => 'अवलोकनावली ।',
+'pageinfo-header-views' => 'अवलोकनानि ।',
'pageinfo-subjectpage' => 'पृष्ठम्',
'pageinfo-talkpage' => 'कथा पृष्ठम्',
+'pageinfo-watchers' => 'अवलोकनानां सङ्ख्या ।',
+'pageinfo-edits' => 'सम्पादननां सङ्ख्या ।',
+'pageinfo-authors' => 'स्पष्टानां कर्तॄणां सङ्ख्या ।',
+'pageinfo-views' => 'अवलोकनानां सङ्ख्या ।',
+'pageinfo-viewsperedit' => 'प्रत्येकं सम्पादनस्य अवलोकनानि ।',
# Skin names
'skinname-standard' => 'पूर्व',
'skinname-myskin' => 'मे चर्मन्',
'skinname-chick' => 'Chick',
+# Patrolling
+'markaspatrolleddiff' => 'आरक्षितमिति अङ्कयतु ।',
+'markaspatrolledtext' => 'एतपुटम् आरक्षितमिति अङ्कयतु ।',
+'markedaspatrolled' => 'आरक्षितमिति अङ्कयतु ।',
+'markedaspatrolledtext' => '[[:$1]] इत्यस्य चितपुनरावृत्तिः आरक्षणचिह्निता अस्ति ।',
+'rcpatroldisabled' => 'सद्यः परिवर्तननानि आरक्षणं निष्क्रियम् ।',
+'rcpatroldisabledtext' => 'नूतनपरिवर्ननानाम् आरक्षणव्यवस्था सद्यः निष्क्रिया ।',
+'markedaspatrollederror' => 'आरक्षितमिति अङ्कितं न भवति ।',
+'markedaspatrollederrortext' => 'आरक्षितमिति सूचयितुं पुनरावृत्तिं विशेषयतु ।',
+'markedaspatrollederror-noautopatrol' => 'स्वस्य परिवर्तनानि आरक्षितं कर्तुं भवान् नानुमतः ।',
+
+# Patrol log
+'patrol-log-page' => 'आरक्षणसूचिका ।',
+'patrol-log-header' => 'इयम् आरक्षितपुनरावृत्तीनां सूचिका अस्ति ।',
+'log-show-hide-patrol' => '$1 इत्यस्य आरक्षणसूचिका ।',
+
+# Image deletion
+'deletedrevision' => 'अपमर्जितप्राचीनपुनरावृत्तिः $1',
+'filedeleteerror-short' => 'सञ्चिकानपमर्जने दोषः : $1',
+'filedeleteerror-long' => ' सञ्चिकानामपमर्जने आगता समस्या $1',
+'filedelete-missing' => '"$1" सञ्चिका अनपमर्जनीया यतः एषा न वर्तते एव ।',
+'filedelete-old-unregistered' => 'दत्तपाठे $1 इति विशिष्टा पुनरावृत्तिः नास्ति ।',
+'filedelete-current-unregistered' => '"$1" विशिष्टा सञ्चिका दत्तपाठे नास्ति ।',
+'filedelete-archive-read-only' => '"$1" लेखागारास्य निदेशिका जालवितारकेन अलेख्या अस्ति ।',
+
# Browsing diffs
'previousdiff' => '← पुरातनतरं सम्पादनम्',
'nextdiff' => 'नवतरं सम्पादनम् →',
# Media information
+'mediawarning' => 'पूर्वसूचना : प्रायः एतादृशपुटे अनर्थसङ्केतः भवति ।
+एतदनुष्टाने अनीय भवतः तन्त्रव्यवस्था सदोषा स्यात् ।',
+'imagemaxsize' => "चित्राकरस् परिमितिः :<br />''(सञ्चिकाविविरणपुटार्थम्)''",
+'thumbsize' => 'सङ्कुचितास्य आकारः ।',
+'widthheightpage' => '$1 × $2, $3 {{PLURAL:$1|पुटम्|पुटानि}} प्रयुक्तानि ।',
+'file-info' => 'सञ्चिकाकारः : $1, MIME प्रकारः $2',
'file-info-size' => '$1 × $2 पिक्सेलानि, संचिकायाः आकारः: $3, MIME-प्रकारः: $4',
+'file-info-size-pages' => '$1 × $2 पिक्सेल्, सञ्चिकायाः आकारः : $3 , MIME प्रकारः : $4 , $5 {{PLURAL:$5|पुटम्|पुटानि}}',
'file-nohires' => 'उच्चतरं विभेदनं नोपलब्धम्',
'svg-long-desc' => 'SVG संचिका, साधारणतया $1 × $2 पिक्सेलानि, संचिकायाः आकारः : $3',
'show-big-image' => 'पूर्णं विभेदनम्',
+'show-big-image-preview' => 'अस्य पूर्वावलोकनस्य आकारः : $1',
+'show-big-image-other' => 'अन्याः {{PLURAL:$2| प्रस्तवः|प्रस्तावाः}}: $1 ।',
+'show-big-image-size' => '$1 × $2 पिक्सेल्',
+'file-info-gif-looped' => 'चक्रितम्',
+'file-info-gif-frames' => '$1 {{PLURAL:$1|पृष्ठम्|पृष्ठानि}}',
+'file-info-png-looped' => 'चक्रितम्',
+'file-info-png-repeat' => 'विलसितम् $1 {{PLURAL:$1|समयः|समयाः}}',
+'file-info-png-frames' => '$1 {{PLURAL:$1|पृष्ठम्|पृष्ठानि}}',
# Special:NewFiles
'newimages' => 'नूतन-संचिकानां वीथिका',
+'imagelisttext' => "अधः $2 इत्यनुसारं '''$1''' {{PLURAL:$1|सञ्चिका दत्ता|सञ्चिकाः प्रदत्ता।}}",
+'newimages-summary' => 'एतत् विशेषपुटम् सद्यः उत्तारितसञ्चिकाः दर्शयति ।',
+'newimages-legend' => 'शोधनी ।',
+'newimages-label' => 'सञ्चिकानाम (अथवा अस्य भागः)',
+'showhidebots' => '(स्वयं चालकः $1)',
+'noimages' => 'शून्यदर्शनम् ।',
+'ilsubmit' => 'अन्वेषणम्',
+'bydate' => 'दिनाङ्कानुगुणम्',
+'sp-newimages-showfrom' => ' $2 $1 तः आरब्धाः सञ्चिकाः दर्शयतु ।',
+
+# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
+'seconds' => '{{PLURAL:$1|$1 second|$1 seconds}}',
+'minutes' => '{{PLURAL:$1|$1 निमेषः|$1 निमेषाः}}',
+'hours' => '{{PLURAL:$1|$1होरा|$1 होराः}}',
+'days' => '{{PLURAL:$1|$1 दिनम्|$1 दिनानि}}',
+'ago' => '$1 पूर्वम्',
# Bad image list
'bad_image_list' => 'रूपम् एवम् अस्ति -
# EXIF tags
'exif-imagewidth' => 'विस्तारः',
'exif-imagelength' => 'औन्नत्यम्',
+'exif-bitspersample' => 'प्रत्येकं भागस्य अंशः ।',
+'exif-compression' => 'तुलनायाः योजना ।',
+'exif-photometricinterpretation' => 'पिक्सेल् रचनम् ।',
+'exif-orientation' => 'अभिस्थापनम्',
+'exif-samplesperpixel' => 'भागानां सङ्ख्या ।',
+'exif-planarconfiguration' => 'दत्तांशस्य व्यवस्था ।',
+'exif-ycbcrsubsampling' => 'Y इत्यस्य C इत्यनेन सह सबसॅम्पलींग प्रमाणम् ।',
+'exif-ycbcrpositioning' => 'Y तथा C पोज़िशनिंग',
+'exif-xresolution' => 'तिर्यक् प्रस्तावः ।',
+'exif-yresolution' => 'लम्बप्रस्तावः ।',
+'exif-stripoffsets' => 'चित्रदत्तांशस्य स्थानम् ।',
+'exif-rowsperstrip' => 'प्रतिपट्टं स्तम्भानां सङ्ख्या ।',
+'exif-stripbytecounts' => 'सङ्कोचितप्रपट्टं बैट्स् ।',
+'exif-jpeginterchangeformat' => 'Offset to JPEG SOI',
+'exif-jpeginterchangeformatlength' => 'जेपिइजि दत्तांशस्य बैट्स् ।',
+'exif-whitepoint' => 'श्वेतबिन्दुवर्णगुणः ।',
+'exif-primarychromaticities' => 'प्राथमिकस्य वर्णगुणः ।',
+'exif-ycbcrcoefficients' => 'वर्णाकाशस्य वर्गान्तरम् मॅट्रीक्स कोएफिशीयंट्स्',
+'exif-referenceblackwhite' => 'उल्लेखमौल्यस्य श्वेतकृष्णयुगम् ।',
+'exif-datetime' => 'सञ्चिकापरिवर्तनस्य दिनाङ्कः समयः च ।',
+'exif-imagedescription' => 'चित्रशीर्षकम् ।',
+'exif-make' => 'चित्रग्राहिण्याः उत्पादकः ।',
+'exif-model' => 'चित्रग्राहिण्याः स्वरूपम् ।',
'exif-artist' => 'लेखक',
'exif-flash' => 'स्फुरणम्',
'exif-gpsspeedref' => 'गती एकक',
'feedback-cancel' => 'निवर्तयते',
'feedback-close' => 'समापित',
+# API errors
+'api-error-unclassified' => 'कश्चन अज्ञातः दोषः जातः ।',
+'api-error-unknown-code' => 'अज्ञातः दोषः " $1 "',
+'api-error-unknown-error' => 'आन्तरिकदोषः : सञ्चिकायाः आरोपणावसरे कश्चन दोषः जातः ।',
+'api-error-unknown-warning' => 'अज्ञातः प्रबोधः "$1"',
+'api-error-unknownerror' => 'अज्ञातः दोषः " $1 "',
+'api-error-uploaddisabled' => 'अस्यां वीक्याम् आरोपणं निष्क्रिया कृता अस्ति ।',
+'api-error-verification-error' => 'इयं सञ्चिका सदोषा स्यात् अथवा विस्तारः दोषयुक्तः स्यात् ।',
+
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|क्षणम्|क्षणानि}}',
+'duration-minutes' => '$1 {{PLURAL:$1|निमेषः|निमेषाः}}',
+'duration-hours' => '$1 {{PLURAL:$1|घण्टा|घण्टाः}}',
+'duration-days' => '$1 {{PLURAL:$1|दिनम्|दिनानि}}',
+'duration-weeks' => '$1 {{PLURAL:$1|सप्ताहः|सप्ताहाः}}',
+'duration-years' => '$1 {{PLURAL:$1|वर्षम्|वर्षाणि}}',
+'duration-decades' => '$1 {{PLURAL:$1|दशकम्|दशकानि}}',
+'duration-centuries' => '$1 {{PLURAL:$1|शतकम्|शतकानि}}',
+'duration-millennia' => '$1 {{PLURAL:$1|सहस्राब्धः|सहस्राब्धाः}}',
+
);
'upload-too-many-redirects' => 'URL наһаа элбэх утаарыылаах',
'upload-unknown-size' => 'Биллибэт кээмэй',
'upload-http-error' => 'HTTP алҕаһа таҕыста: $1',
+'upload-copy-upload-invalid-domain' => 'Бу домеҥҥа хачайдааһыны хатылыыр табыллыбат.',
# File backend
'backend-fail-stream' => '$1 билэни ыытар табыллыбата.',
'backend-fail-closetemp' => 'Быстах кэмнээх билэни сабар табыллыбата.',
'backend-fail-read' => '$1 билэни ааҕар табыллыбата.',
'backend-fail-create' => '$1 билэни суруттарар табыллыбата.',
+'backend-fail-maxsize' => '$1 билэни суруттарар табыллыбата, тоҕо диэтэххэ кини кээмэйэ $2 баайты куоһарар.',
'backend-fail-readonly' => '«$1» сиэрбэр «ааҕыы эрэ» эрэсиимҥэ турар. Төрүөтэ: «$2»',
'backend-fail-synced' => '«$1» билэ сөпсөһүллүбэтэх туруктаах эбит',
'backend-fail-connect' => 'Маны кытта «$1» холбонор табыллыбата.',
'backend-fail-internal' => 'Манна «$1» биллибэт алҕас таҕыста.',
'backend-fail-contenttype' => 'Билэ иһинээҕитин көрүҥүн араарар сатамматаҕын түмүгэр манна «$1» угар табыллыбата.',
+'backend-fail-usable' => '$1 билэни суруттарар табыллыбата, тоҕо диэтэххэ быраабыҥ тиийбэтэ эбэтэр анаммыт паапка суох буолан биэрдэ.',
# Lock manager
'lockmanager-notlocked' => 'Маны "$1" хааччаҕын устар табыллыбата; кини хааччахтамматах.',
'lockmanager-fail-releaselock' => '"$1" хааччаҕын устар сатаммата.',
'lockmanager-fail-db-bucket' => '$1 сегмеҥҥа хааччах баазаларын кытта ситими ситиһэр табыллыбата.',
'lockmanager-fail-db-release' => '$1 билии олоҕун хааччахтааһынын устар табыллыбата.',
+'lockmanager-fail-svr-acquire' => '$1 сиэрбэр хааччахтааһынын ылар табыллыбата.',
'lockmanager-fail-svr-release' => '$1 сиэрбэр хааччахтааһынын устар табыллыбата.',
# ZipDirectoryReader
[$2 туһунан сирэй]тэн ылыллыбыт тиэкис аллара көрдөрүлүннэ.',
'sharedupload-desc-edit' => 'Бу билэ манна сытар $1, хас да ситим-сиргэ туттуллар кыахтаах.
[$2 билэ туһунан] сирэйи уларытыахха сөп.',
+'sharedupload-desc-create' => 'Бу билэ манна сытар $1, хас да ситим-сиргэ туттуллар кыахтаах.
+[$2 билэ туһунан] сирэйи уларытыахха сөп.',
'filepage-nofile' => 'Маннык ааттаах билэ суох.',
'filepage-nofile-link' => 'Маннык ааттаах билэ суох. Ол гынан баран эн [$1 суруттарыаххын] сөп.',
'uploadnewversion-linktext' => 'Бу билэ саҥа барылын суруттар',
Сурунаал көрүҥүнэн, кыттааччы аатынан (улахан-кыра буукубата учуоттанар) эбэтэр сирэй аатынан (эмиэ улахана-кырата учуоттанар) наардыаххытын сөп.',
'logempty' => 'Сурунаалга сөп түбэһэр элэмиэннэр суохтар.',
'log-title-wildcard' => 'Бу сурук бэлиэлэриттэн (буукубалартан) саҕаланар ааттары бул',
+'showhideselectedlogentries' => 'Талыллыбыт суруктары кистээ/көрдөр',
# Special:AllPages
'allpages' => 'Сирэйдэр барыта',
'allpagesprefix' => 'Мантан саҕаланар сирэйдэри бул:',
'allpagesbadtitle' => 'Сирэй маннык ааттанара сатаммат: аакка туттуллуо суохтаах бэлиэлэрдээх эбэтэр тыллар ыккардыларыгар туһаныллар ыйынньыктаах.',
'allpages-bad-ns' => '{{SITENAME}} не содержит пространства имён «$1».',
+'allpages-hide-redirects' => 'Утаарыылары кистээ',
# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Сирэй кээстэммит барылын көрөҕүн, $1 ынараа өттүгэр саҥардыллыбыт буолуон сөп.',
+'cachedspecial-viewing-cached-ts' => 'Сирэй кээскэ киирбит барылын көрөҕүн, дьиҥнээх сирэй чыҥха атын буолуон сөп.',
'cachedspecial-refresh-now' => 'Бүтэһик барылы көр.',
# Special:Categories
'ipb-confirm' => 'Хааччаҕы бигэргэт',
'badipaddress' => 'IP аадырыһа сыыһа',
'blockipsuccesssub' => 'Тохтотулунна',
-'blockipsuccesstext' => '[[Special:Contributions/$1|«$1»]] бобÑ\83ллÑ\83бÑ\83Ñ\82/Ñ\82оÑ\85Ñ\82оÑ\82Ñ\83ллÑ\83бÑ\83т.<br />
-[[Special:BlockList|Бобуллубут IP-лар испииһэктэрин]] көр.',
+'blockipsuccesstext' => '[[Special:Contributions/$1|«$1»]] бобÑ\83ллÑ\83бÑ\83Ñ\82/Ñ\85ааÑ\87Ñ\87аÑ\85Ñ\82аммÑ\8bт.<br />
+[[Special:BlockList|Бобуллубут IP-лар тиһиктэрин]] көр.',
'ipb-blockingself' => 'Эн бэйэҕин хааччахтаан эрэҕин! Ону өйдүүгүн дуо?',
'ipb-confirmhideuser' => 'Кытааччыны хааччахтаан уонна аатын кистээн эрэҕин. Аата мантан инньэ тиһиктэргэ уонна сурунаалларга көстүбэт буолуо. Бигэргэтэҕин дуо?',
'ipb-edit-dropdown' => 'Бобуу биричиинэтин уларыт',
'reblock-logentry' => 'манна [[$1]] аналлаах хааччахтааһын туруоруулара уларыйда, болдьоҕо $2 $3',
'blocklogtext' => 'Кыттааччылары хааччахтааһын уонна ол хааччахтааһыннарын суох гыныы сурунаала.
Аптамаатынан хааччахтаммыт IP-лар манна көстүбэттэр.
-[[Special:BlockList|Билигин баар хааччахтаныылар испииһэктэрин]] көр.',
+[[Special:BlockList|Билигин баар хааччахтаныылар тиһиктэрин]] көр.',
'unblocklogentry' => '$1 хааччахтааһыны уһулла',
'block-log-flags-anononly' => 'ааттамматах кыттааччылар эрэ',
'block-log-flags-nocreate' => 'саҥа бэлиэтэнии бобуллубут',
'duration-centuries' => '$1 үйэ',
'duration-millennia' => '$1 тыһыынча сыл',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 — {{PLURAL:$4|билэ бобуллубут көрүҥэ|билэ бобуллубут көрүҥнэрэ}}.. Көҥүллэммит билэ {{PLURAL:$3|көрүҥэ маннык|көрүҥнэрэ манныктар}}: $2.',
);
);
/**
- * Aliases from the fallback language 'lt' to avoid breakage of links
- */
-
+ * Aliases from the fallback language 'lt' to avoid breakage of links
+ */
$namespaceAliases = array(
'Specialus' => NS_SPECIAL,
'Aptarimas' => NS_TALK,
'Kategorijos_aptarimas' => NS_CATEGORY_TALK,
);
+$namespaceGenderAliases = array();
+
$messages = array(
# User preference toggles
'tog-underline' => 'Pabrauktė nūruodas:',
'duration-centuries' => '$1 {{PLURAL:$1|vijek|vijekova}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenijum|milenijuma}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|nije dopušten tip datoteke|nisu dopušteni tipovi datoteka}}. {{PLURAL:$3|Dopuštena vrsta datoteke je|Dopuštene vrste datoteka su}} $2.',
);
'youhavenewmessages' => 'ඔබ හට $1 ($2)',
'newmessageslink' => 'නව පණිවුඩ',
'newmessagesdifflink' => 'අවසාන වෙනස',
+'youhavenewmessagesfromusers' => '{{PLURAL:$3|තවත් එක් පරිශීලකයෙකුගෙන්|පරිශීලකයන් $3 දෙනෙකුගෙන්}} ඔබ හට $1 ඇත ($2).',
+'youhavenewmessagesmanyusers' => 'බොහෝ පරිශීලකයන් වෙතින් ඔබ හට $1 ඇත ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|නව පණිවුඩයක්|නව පණිවුඩ}}',
+'newmessagesdifflinkplural' => 'අවසන් {{PLURAL:$1|වෙනස්වීම|වෙනස්වීම්}}',
'youhavenewmessagesmulti' => 'ඔබ හට $1 හි නව පණිවුඩ ඇත',
'editsection' => 'සංස්කරණය',
'editsection-brackets' => '[$1]',
'cannotdelete' => '"$1" පිටුව හෝ ගොනුව හෝ මකා දැමිය නොහැකි විය.
අනෙකෙකු විසින් දැනටමත් මකා දැමීම සිදු කර ඇතිවා විය හැක.',
'cannotdelete-title' => '"$1" පිටුව මැකිය නොහැක',
+'delete-hook-aborted' => 'හසුර මගින් මකාදැමුම රෝධනය කෙරිණි.
+එයට පැහැදිලි කිරීමක් ලබා නොදුනි.',
'badtitle' => 'නුසුදුසු ශීර්ෂයක්',
'badtitletext' => 'අයැද ඇති පිටු ශීර්ෂය අනීතික, හිස් හෝ වැරදි ලෙස සබැඳි අන්තර්-භාෂා/අන්තර්-විකී ශීර්ෂයකි.
ශීර්ෂයන්හි භාවිතා කල නොහැකි අක්ෂර එකක් හෝ කිහිපයක් හෝ එහි අඩංගු වී ඇතිවා විය හැක.',
'ns-specialprotected' => 'විශේෂ පිටු සංස්කරණය කිරීම සිදු කල නොහැක.',
'titleprotected' => "මෙම ශීර්ෂ-නාමය තැනීම [[User:$1|$1]] විසින් වාරණය කොට ඇත.
මේ සඳහා ''$2'' හේතුව දක්වා ඇත.",
+'exception-nologin' => 'ප්රවිෂ්ට වී නොමැත',
# Virus scanner
'virus-badscanner' => "අයෝග්ය වික්යාසයකි: අඥාත වයිරස සුපිරික්සකයකි: ''$1''",
'allpagesbadtitle' => 'සපයා ඇති පිටු ශීර්ෂය අනීතික විය නැතහොත් එහි අන්තර්-භාෂා හෝ අන්තර් විකී උපසර්ගයක් අඩංගු විය.
ශීර්ෂයන්හි අඩංගු විය නොහැකි අක්ෂර එකක් හෝ කිහිපයක් හෝ එහි අඩංගු වී තිබිය හැක.',
'allpages-bad-ns' => '{{SITENAME}} හි "$1" නාමඅවකාශය නොමැත.',
+'allpages-hide-redirects' => 'යළි-යොමු සඟවන්න',
+
+# SpecialCachedPage
+'cachedspecial-refresh-now' => 'නවතමය නරඹන්න.',
# Special:Categories
'categories' => 'ප්රවර්ග',
'version-software' => 'ස්ථාපිත මෘදුකාංග',
'version-software-product' => 'නිෂ්පාදනය',
'version-software-version' => 'අනුවාදය',
+'version-entrypoints' => 'නිවේශන ලක්ෂ්ය URL',
+'version-entrypoints-header-entrypoint' => 'නිවේශන ලක්ෂ්යය',
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'ගොනු පෙත',
'api-error-duplicate-archive-popup-title' => 'දැනටමත් මකා දමා ඇති එක වගේ {{PLURAL:$1|ගොනුවක්|ගොනු}}',
'api-error-duplicate-popup-title' => 'අනුපිටපත් {{PLURAL:$1|ගොනු|ගොනුව}}',
'api-error-empty-file' => 'ඔබ ඉදිරිපත්කල ගොනුව හිස් එකකි.',
+'api-error-emptypage' => 'නවතම එකක් තනමින්, හිස් පිටුවලට ඉඩ නොදේ.',
'api-error-fetchfileerror' => 'අභ්යන්තර දෝෂය: ගොනුව පැමිණවීම අතරතුරදී කුමක්දෝ වැරදුණා.',
'api-error-file-too-large' => 'ඔබ විසින් යොමන ලද ගොනුව පමණට වඩා විශෘලය.',
'api-error-filename-tooshort' => 'ගොනු නාමය කෙටි වැඩියි.',
'api-error-uploaddisabled' => 'මෙම විකියෙහි උඩුගතකිරීම අක්රිය කොට ඇත.',
'api-error-verification-error' => 'මෙම ගොනුව පළුදුවී හෝ එයට වැරදි විස්තිර්ණයක් (extension) ඇතුවා වියහැක.',
+# Durations
+'duration-seconds' => '{{PLURAL:$1|තත්පර|තත්පර}} $1 ක්',
+'duration-minutes' => '{{PLURAL:$1|මිනිත්තු|මිනිත්තු}} $1 ක්',
+'duration-hours' => '{{PLURAL:$1|පැය|පැය}} $1 ක්',
+'duration-days' => '{{PLURAL:$1|දින|දින}} $1 ක්',
+'duration-weeks' => '{{PLURAL: $1|සති|සති}} $1 ක්',
+'duration-years' => '{{PLURAL:$1|වසර|වසර}} $1 ක්',
+'duration-decades' => '{{PLURAL:$1|දශක|දශක}} $1 ක්',
+'duration-centuries' => '{{PLURAL:$1|ශතවර්ෂ|ශතවර්ෂ}} $1 ක්',
+'duration-millennia' => '{{PLURAL:$1|සහස්රවර්ෂ|සහස්රවර්ෂ}} $1 ක්',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 යනු {{PLURAL:$4|අවසරලත් ගොනු වර්ගයක්|අවසරලත් ගොනු වර්ගයන්}} නොවේ. අවසරලත් {{PLURAL:$3|ගොනු වර්ගය|ගොනු වර්ගයන්}} වන්නේ $2.',
);
Správca, ktorý nariadil uzamknutie, uvádza tento dôvod: $1',
'missing-article' => 'Text stránky s názvom „$1” $2, ktorú ste požadovali, nebol nájdený v databáze.
-To sa zvyčajne stane, keď kliknete na zastaralý odkaz na rozdiel alebo do histórie stránky, ktorá bola zmazaná.
+To sa zvyčajne stane, keď kliknete na zastaraný odkaz na rozdiel alebo do histórie stránky, ktorá bola zmazaná.
Ak to tak nie je, je možné, že ste našli chybu v softvéri.
Oznámte to prosím [[Special:ListUsers/sysop|správcovi]] a uveďte URL.',
'remembermypassword' => 'Pamätať si prihlásenie na tomto počítači (naviac $1 {{PLURAL:$1|deň|dni|dní}})',
'securelogin-stick-https' => 'Zostať pripojený cez HTTPS po prihlásení',
'yourdomainname' => 'Vaša doména:',
+'password-change-forbidden' => 'Na tejto wiki si nemôžete zmeniť heslo.',
'externaldberror' => 'Buď nastala chyba externej autentifikačnej databázy alebo vám nie je povolené aktualizovať váš externý účet.',
'login' => 'Prihlásiť',
'nav-login-createaccount' => 'Vytvorenie konta / prihlásenie',
'noarticletext-nopermission' => 'Táto stránka momentálne neobsahuje žiadny text.
Môžete [[Special:Search/{{PAGENAME}}|hľadať názov tejto stránky]] v texte iných stránok
alebo <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} si pozrieť súvisiace záznamy]</span>.',
+'missing-revision' => 'Revízia #$1 stránky s názvom „{{PAGENAME}}“ neexistuje.
+
+Pravdepodobne ste nasledovali zastaraný odkaz na historickú verziu stránky, ktorá bola medzičasom odstránená.
+Podrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname zmazaní].',
'userpage-userdoesnotexist' => 'Používateľský účet „<nowiki>$1</nowiki>“ nie je registrovaný. Prosím, skontrolujte, či naozaj chcete vytvoriť/upravovať túto stránku.',
'userpage-userdoesnotexist-view' => 'Používateľský účet „$1“ nie je registrovaný.',
'blocked-notice-logextract' => 'Tento používateľ je momentálne zablokovaný.
'editundo' => 'vrátiť',
'diff-multi' => '{{PLURAL:$1|Jedna medziľahlá revízia|$1 medziľahlé revízie|$1 medziľahlých revízií}} od {{PLURAL:$2|jedného používateľa|$2 používateľov}} {{PLURAL:$1|nie je zobrazená|nie sú zobrazené|nie je zobrazených}}.',
'diff-multi-manyusers' => '({{PLURAL:$1|$1 medziľahlá revízia|$1 medziľahlé revízie|$1 medziľahlých revízií}} od viac ako {{PLURAL:$2|$2 používateľa|$2 používateľov}} {{PLURAL:$1|nie je zobrazená|nie sú zobrazené|nie je zobrazených}})',
+'difference-missing-revision' => '{{PLURAL:$2|$2 revízia|$2 revízie|$2 revízií}} pre požadovaný rozdiel ($1) {{PLURAL:$2|neexistuje|neexistujú|neexistuje}}.
+
+Pravdepodobne ste nasledovali zastaraný odkaz na rozdiel revízií, z ktorých niektorá bola medzičasom odstránená.
+Podrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname zmazaní].',
# Search results
'searchresults' => 'Výsledky vyhľadávania',
'right-writeapi' => 'Použitie API na zápis',
'right-delete' => 'Mazať stránky',
'right-bigdelete' => 'Mazať stránky s veľkou históriou',
+'right-deletelogentry' => 'Odstrániť a obnoviť špecifické položky',
'right-deleterevision' => 'Mazať a obnovovať konkrétne revízie stránok',
'right-deletedhistory' => 'Zobrazovať zmazané položky histórie bez ich plného textu',
'right-deletedtext' => 'Zobrazovať zmazané texty a zmeny medzi zmazanými verziami',
'lockmanager-fail-releaselock' => 'Nepodarilo sa uvoľniť zámok „$1“.',
'lockmanager-fail-db-bucket' => 'Nepodarilo sa kontaktovať dostatok databáz zámkov v buckete $1.',
'lockmanager-fail-db-release' => 'Nepodarilo sa uvoľniť zámky na databáze $1.',
+'lockmanager-fail-svr-acquire' => 'Nepodarilo sa získať zámky na serveri $1.',
'lockmanager-fail-svr-release' => 'Nepodarilo sa uvoľniť zámky na serveri $1.',
# ZipDirectoryReader
'disambiguationspage' => 'Template:Rozlišovacia stránka',
'disambiguations-text' => "Nasledovné stránky odkazujú na '''rozlišovaciu stránku'''.
Mali by však odkazovať priamo na príslušnú tému.<br />
-Stránka sa považuje za rozlišovaciu, keď používa šablónu, na ktorú odkazuje [[MediaWiki:Disambiguationspage]]",
+Stránka sa považuje za rozlišovaciu, keď používa šablónu, na ktorú odkazuje [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dvojité presmerovania',
'doubleredirectstext' => 'Táto stránka obsahuje zoznam stránok, ktoré presmerovávajú na iné presmerovacie stránky.
'delete-warning-toobig' => 'Táto stránka má veľkú históriu úprav, viac ako $1 {{PLURAL:$1|revíziu|revízie|revízií}}. Jej zmazanie by mohlo narušiť databázové operácie {{GRAMMAR:genitív|{{SITENAME}}}}; postupujte opatrne.',
# Rollback
-'rollback' => 'Rollback úprav',
-'rollback_short' => 'Rollback',
+'rollback' => 'Vrátiť späť úpravy',
+'rollback_short' => 'Vrátiť',
'rollbacklink' => 'rollback',
+'rollbacklinkcount' => 'vrátenie $1 {{PLURAL:$1|úpravy|úprav}}',
+'rollbacklinkcount-morethan' => 'vrátiť viac ako $1 {{PLURAL:$1|úpravu|úprav}}',
'rollbackfailed' => 'Rollback neúspešný',
'cantrollback' => 'Nie je možné úpravu vrátiť späť, posledný autor je jediný autor tejto stránky.',
'alreadyrolled' => 'Nemožno vrátiť späť poslednú úpravu [[:$1]] od [[User:$2|$2]] ([[User talk:$2|Diskusia]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); niekto iný buď upravoval stránku alebo už vrátil úpravy späť.
'duration-millennia' => '$1 {{PLURAL:$1|tisícročie|tisícročia|tisícročí}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Nepodarilo sa získať zámky na serveri $1.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|nie je povolený typ súboru|nie sú povolené typy súboru}}. {{PLURAL:$3|Povolený typ súborov je|Povolené typy súborov sú}} $2.',
);
'remembermypassword' => 'Zapomni si me na tem računalniku (za največ $1 {{PLURAL:$1|dan|dneva|dni}})',
'securelogin-stick-https' => 'Po prijavi ostani povezan preko HTTPS',
'yourdomainname' => 'Domena',
+'password-change-forbidden' => 'Na tem wikiju ne morete spreminjati gesel.',
'externaldberror' => 'Pri potrjevanju istovetnosti je prišlo do notranje napake ali pa za osveževanje zunanjega računa nimate dovoljenja.',
'login' => 'Prijava',
'nav-login-createaccount' => 'Prijavite se / registrirajte se',
'right-writeapi' => 'Uporaba napisanega API-ja',
'right-delete' => 'Brisanje strani',
'right-bigdelete' => 'Brisanje strani z obsežno zgodovino',
+'right-deletelogentry' => 'Brisanje in obnavljanje izbranih dnevniških vnosov',
'right-deleterevision' => 'Brisanje in obnova posebnih redakcij strani',
'right-deletedhistory' => 'Ogled zgodovine brisanja, brez besedila izbrisanih strani',
'right-deletedtext' => 'Ogled izbrisanega besedila in primerjava med izbrisanimi redakcijami',
'lockmanager-fail-releaselock' => 'Ne morem sprostiti zaklepa »$1«.',
'lockmanager-fail-db-bucket' => 'Ne morem kontaktirati zadostnega števila zaklenitvenih zbirk podatkov v vedru $1.',
'lockmanager-fail-db-release' => 'Ne morem sprostiti zaklepov zbirke podatkov $1.',
+'lockmanager-fail-svr-acquire' => 'Ne morem pridobiti zaklepov na strežniku $1.',
'lockmanager-fail-svr-release' => 'Ne morem sprostiti zaklepov strežnika $1.',
# ZipDirectoryReader
'disambiguations' => 'Strani s povezavami na razločitvene strani',
'disambiguationspage' => 'Template:Razločitev',
-'disambiguations-text' => "Naslednje strani se povezujejo na '''razločitvene strani'''.
-Namesto tega bi se naj povezovale na primerno temo.<br />
-Stran se obravnava kot razločitvena, če uporablja predloge povezane iz [[MediaWiki:Disambiguationspage]]",
+'disambiguations-text' => "Naslednje strani vsebujejo vsaj eno povezavo na '''razločitvene strani'''.
+Namesto tega bi morda bilo bolje, da se povezujejo na primernejše strani.<br />
+Stran se obravnava kot razločitvena, če uporablja predloge, povezane z [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dvojne preusmeritve',
'doubleredirectstext' => 'Ta stran navaja strani, ki se preusmerjajo na druge preusmeritvene strani.
'rollback' => 'Vrni spremembe',
'rollback_short' => 'Vrni',
'rollbacklink' => 'vrni',
+'rollbacklinkcount' => 'vrni $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}',
+'rollbacklinkcount-morethan' => 'vrni več kot $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}',
'rollbackfailed' => 'Vrnitev ni uspela',
'cantrollback' => 'Urejanja ne morem vrniti; zadnji urejevalec je hkrati edini.',
'alreadyrolled' => 'Zadnje spremembe [[:$1]] uporabnika [[User:$2|$2]] ([[User talk:$2|pogovor]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ne morem vrniti;
* <span class="mw-specialpagecached">Predpomnjene posebne strani (morda so zastarele).</span>',
'specialpages-group-maintenance' => 'Vzdrževalna poročila',
'specialpages-group-other' => 'Ostale posebne strani',
-'specialpages-group-login' => 'Prijavite se / registrirajte se',
+'specialpages-group-login' => 'Prijavite se / ustvarite račun',
'specialpages-group-changes' => 'Zadnje spremembe in dnevniki',
'specialpages-group-media' => 'Poročila o datotekah in nalaganja',
'specialpages-group-users' => 'Uporabniki in pravice',
'duration-millennia' => '$1 {{PLURAL:$1|tisočletje|tisočletji|tisočletja|tisočletij}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Ne morem pridobiti zaklepov na strežniku $1.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|ni dovoljena datotečna vrsta|nista dovoljeni datotečni vrsti|niso dovoljene datotečne vrste}}. {{PLURAL:$3|Dovoljena datotečna vrsta je|Dovoljeni datotečni vrsti sta|Dovoljene datotečne vrste so}} $2.',
);
* @author Eagleal
* @author Ergon
* @author Euriditi
+ * @author FatosMorina
* @author Kaganer
* @author Marinari
* @author Mdupont
'youhavenewmessages' => 'Ju keni $1 ($2).',
'newmessageslink' => 'mesazhe të reja',
'newmessagesdifflink' => 'ndryshimi i fundit',
+'youhavenewmessagesfromusers' => 'Ju keni $1 nga {{Shumës:$3|përdorues tjetër|përdoruesit $3}} ($2).',
+'youhavenewmessagesmanyusers' => 'Ju keni 1$ nga shumë përdorues (2$).',
+'newmessageslinkplural' => '{{SHUMËS:1$|një porosi e re|porosi të reja}}',
+'newmessagesdifflinkplural' => 'i fundit {{SHUMËS:$1|ndryshimi|ndryshimet}}',
'youhavenewmessagesmulti' => 'Ju keni mesazhe të reja në $1',
'editsection' => 'redakto',
'editold' => 'redaktoni',
'cannotdelete' => 'Faqja ose skeda $1 nuk mund të fshihej.
Mund të jetë fshirë nga dikush tjetër.',
'cannotdelete-title' => 'Faqja "$1" nuk mund të fshihet',
+'delete-hook-aborted' => 'Fshirja u anulua nga togëza.
+Nuk jipet shpjegim.',
'badtitle' => 'Titull i pasaktë',
'badtitletext' => 'Titulli i faqes që kërkuat nuk ishte i saktë, ishte bosh, ose ishte një titull ndër-gjuhësor/inter-wiki me lidhje të pasaktë.
Mund të përmbajë një ose më shumë germa, të cilat nuk mund të përdoren në tituj.',
'ns-specialprotected' => 'Faqet speciale nuk mund të redaktohen.',
'titleprotected' => "Ky titull është mbrojtur nga [[User:$1|$1]] dhe nuk mund të krijohet.
Arsyeja e dhënë është ''$2''.",
+'filereadonlyerror' => 'Nuk është në gjendje që të ndryshojë skedarin "$1" sepse depoja e skedarit "$2" është në formën vetëm-lexim.
+
+Administratori i cili e mbylli atë e dha këtë shpjegim: "$3".',
+'invalidtitle-knownnamespace' => 'Titull jo i vlefshëm me hapësirën "$2" dhe teksti "$3"',
+'invalidtitle-unknownnamespace' => 'Titull jo i vlefshëm me numrin e panjohur të hapësirës së emrit $1 dhe tekstit "$2"',
+'exception-nologin' => 'I paqasur',
+'exception-nologin-text' => 'Kjo faqe ose ky veprim ju kërkon që të qaseni në këtë wiki.',
# Virus scanner
'virus-badscanner' => "Konfiguracion i parregullt: Skaner i panjohur virusesh: ''$1''",
'remembermypassword' => 'Mbaj mënd fjalëkalimin tim për tërë vizitat e ardhshme (për një kohë maksimale prej $1 {{PLURAL:$1|dite|ditësh}})',
'securelogin-stick-https' => 'Qëndro i lidhur me HTTPS pas hyrjes me emrin përkatës',
'yourdomainname' => 'Faqja juaj',
+'password-change-forbidden' => 'Ju nuk mund të ndryshoni fjalëkalimet në këtë wiki.',
'externaldberror' => 'Ose kishte një gabim tek regjistri i identifikimit të jashtëm, ose nuk ju lejohet të përtërini llogarinë tuaje të jashtme.',
'login' => 'Hyni',
'nav-login-createaccount' => 'Hyni ose hapni një llogari',
'emailconfirmlink' => 'Vërtetoni adresën tuaj',
'invalidemailaddress' => 'Posta elektronike nuk mund të pranohet kështu si është pasi ka format jo valid. Ju lutemi, vendoni një postë mirë të formatuar, ose zbrazeni fushën.',
'cannotchangeemail' => 'Adresat e-mail të llogarive nuk mund të ndryshohen në këtë wiki.',
+'emaildisabled' => 'Kjo faqe nuk mund të dërgojë e-maila.',
'accountcreated' => 'Llogarija e Përdoruesit u krijua',
'accountcreatedtext' => 'Llogarija e Përdoruesit për $1 u krijua',
'createaccount-title' => 'Hapja e llogarive për {{SITENAME}}',
Ju mundeni [[Special:Search/{{PAGENAME}}|me kërku këtë titull]] në faqe tjera,
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} me kërku në regjistrat tematikisht të afërm],
apo [{{fullurl:{{FULLPAGENAME}}|action=edit}} me redaktu këtë faqe]</span>.',
+'missing-revision' => 'Inspektimi #$1 i faqes me emrin "{{PAGENAME}}" nuk ekziston.
+
+Kjo zakonisht shkaktuar duke ndjekur një lidhje të vjetër tek një faqe që është fshirë. Hollësitë mund të gjenden në [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} regjistrin e fshirjeve].',
'userpage-userdoesnotexist' => 'Llogaria e përdoruesit "<nowiki>$1</nowiki>" nuk është e regjistruar.
Ju lutem kontrolloni nëse dëshironi të krijoni/redaktoni këtë faqe.',
'userpage-userdoesnotexist-view' => 'Llogaria i përdoruesit "$1" nuk është e regjistruar.',
'note' => "'''Shënim:'''",
'previewnote' => "'''Vini re! Kjo faqe është vetëm për shqyrtim.'''
Ndryshimet tuaja nuk janë ruajtur ende!",
+'continue-editing' => 'Vazhdo ndryshimin',
'previewconflict' => 'Kjo parapamje reflekton tekstin sipër kutisë së redaktimit siç do të duket kur të kryeni ndryshimin.',
'session_fail_preview' => "'''Ju kërkojmë ndjesë! Redaktimi juaj nuk mund të perpunohej për shkak të humbjes së të dhënave të seancës.'''
Ju lutemi, provojeni përsëri.
Kjo ndodh ndonjëherë kur përdoret server anonim dytësor me gabime.",
'edit_form_incomplete' => "'''Disa pjesë të formularit të redaktimit nuk arritën në server; kontrolloni edhe një herë nëse redaktimet tuaja janë të paprekura dhe provojeni përsëri.'''",
'editing' => 'Duke redaktuar $1',
+'creating' => 'Duke krijuar $1',
'editingsection' => 'Duke redaktuar $1 (paragraf)',
'editingcomment' => 'Duke redaktuar (paragraf i ri) $1',
'editconflict' => 'Konflikt redaktimi: $1',
'edit-no-change' => 'Redaktimi juaj është anashkaluar pasi që asnjë ndryshim nuk u bë në tekst.',
'edit-already-exists' => 'Faqja nuk mundej të hapet.
Ajo tanimë ekziston.',
+'defaultmessagetext' => 'Teksti i porosisë së parazgjedhur',
# Parser/template warnings
'expensive-parserfunction-warning' => 'Kujdes: Kjo faqe ka shumë kërkesa që kërkojnë analizë gramatikore të kushtueshme për sistemin.
# Suppression log
'suppressionlog' => 'Regjistri i ndalimeve',
-'suppressionlogtext' => 'Më poshtë është një listë e grisjeve dhe bllokimeve duke përfshirë përmnajtjen e fshehur nga administratorët.
+'suppressionlogtext' => 'Më poshtë është një listë e grisjeve dhe bllokimeve duke përfshirë përmbajtjen e fshehur nga administratorët.
Shiko [[Special:BlockList|listën e bllokimeve IP]] për listën e përjashtimeve operacionale dhe bllokimeve aktuale.',
# History merging
# Diffs
'history-title' => 'Historiku i redaktimeve te "$1"',
+'difference-title' => 'Ndryshimi mes inspektimeve të "$1"',
+'difference-title-multipage' => 'Ndryshimi mes faqeve "$1" dhe "$2"',
'difference-multipage' => '(Ndryshimi midis faqeve)',
'lineno' => 'Rreshti $1:',
'compareselectedversions' => 'Krahasoni versionet e zgjedhura',
'prefs-beta' => 'Karakteristikat Beta',
'prefs-datetime' => 'Data dhe Ora',
'prefs-labs' => 'Karakteristikat laboratorik',
+'prefs-user-pages' => 'Faqet e përdoruesit',
'prefs-personal' => 'Përdoruesi',
'prefs-rc' => 'Ndryshime së fundmi',
'prefs-watchlist' => 'Lista mbikqyrëse',
'right-writeapi' => 'Përdorimi i shkrimit API',
'right-delete' => 'Gris faqet',
'right-bigdelete' => 'Gris faqet me histori të gjata',
+'right-deletelogentry' => 'Fshij dhe mos i fshij shënimet në regjistrat e veçantë',
'right-deleterevision' => 'Grisi dhe riktheji revizionet specifike të faqeve',
'right-deletedhistory' => 'Shiko shënimet e grisura të historikut, pa tekstet e tyre të shoqëruara',
'right-deletedtext' => 'Shiko tekstin dhe ndryshimet e grisura ndërmjet versioneve të grisura',
'number_of_watching_users_pageview' => '[$1 duke u mbikqyrur nga {{PLURAL:$1|përdorues|përdorues}}]',
'rc_categories' => 'Kufizimi i kategorive (të ndara me "|")',
'rc_categories_any' => 'Të gjitha',
+'rc-change-size-new' => '$1 {{PLURAL:$1|bajt|bajtë}} pas ndryshimit',
'newsectionsummary' => '/* $1 */ seksion i ri',
'rc-enhanced-expand' => 'Trego detajet (kërkon JavaScript)',
'rc-enhanced-hide' => 'Fshih detajet',
+'rc-old-title' => 'fillimisht i krijuar si "$1"',
# Recent changes linked
'recentchangeslinked' => 'Ndryshime të ndërvarura',
'upload-too-many-redirects' => 'Adresa URL përmbante shumë përcjellime.',
'upload-unknown-size' => 'Madhësia e panjohur',
'upload-http-error' => 'Ndodhi një gabim HTTP: $1',
+'upload-copy-upload-invalid-domain' => 'Ngarkesat e kopjimit nuk janë në dispozicion nga ky domein.',
# File backend
'backend-fail-stream' => 'Nuk mund të kalojë skedën $1.',
'backend-fail-closetemp' => 'Nuk mund të mbyllë skedën e përkohshme.',
'backend-fail-read' => 'Nuk mund të lexojë skedën $1.',
'backend-fail-create' => 'Nuk mund të krijojë skedën $1.',
+'backend-fail-maxsize' => 'Nuk mund të shkruante skedarin "$1" sepse ai është më i madh se {{SHUMËS:$2|një bajt|$2 bajtë}}',
+'backend-fail-readonly' => 'Shërbimi i depos "$1" është për momentin vetëm-për-lexim. Arsyeja e dhënë është: "\'\'$2\'\'"',
+'backend-fail-synced' => 'Skedari "$1" është në një gjendje të parregullt brenda proceseve të depos së brendshme',
+'backend-fail-connect' => 'Nuk u arrit lidhja me shërbimin e depos "$1".',
+'backend-fail-internal' => 'Një problem i panjohur ndodhi në shërbimin e depos "$1".',
+'backend-fail-contenttype' => 'Nuk mundi të përcaktojë llojin e përmbajtjes së skedarit për ta ruajtur në "$1".',
# Lock manager
'lockmanager-notlocked' => 'Nuk mund të zhbllokojë "$1"; nuk është e bllokuar.',
'wantedpages' => 'Artikuj më të dëshiruar',
'wantedpages-badtitle' => 'Titull i pavlefshëm në vendosjen e rezultateve: $1',
'wantedfiles' => 'Skedat e dëshiruara',
+'wantedfiletext-cat' => 'Skedarët vijues janë përdorur por nuk ekzistojnë. Skedarët nga depot e panjohura mund të listohen megjithëse nuk ekzistojnë. Ndonjë gjë pozitive e pavërtetë e tillë do të <del>largohet</del>. Për më tepër, faqet që vendosin skedarë që nuk ekzistojnë janë listuar në [[:$1]].',
+'wantedfiletext-nocat' => 'Skedarët vijues janë përdorur por nuk ekzistojnë. Skedarët nga depot e panjohura mund të listohen megjithëse nuk ekzistojnë. Ndonjë gjë pozitive e pavërtetë e tillë do të <del>largohet</del>.',
'wantedtemplates' => 'Stampat e dëshiruara',
'mostlinked' => 'Artikuj më të lidhur',
'mostlinkedcategories' => 'Kategori më të lidhura',
Ju mund të kufizoni pamje sipas tipit të regjistrit, emrit të përdoruesit (shumë i ndjeshëm), dhe faqes në çështje (edhe rastet e ndjeshme)',
'logempty' => 'Nuk ka asnjë përputhje në regjistër.',
'log-title-wildcard' => 'Kërko tituj që fillojnë me këtë tekst',
+'showhideselectedlogentries' => 'Paraqit/fshih shënimet e përzgjedhura të regjistruara.',
# Special:AllPages
'allpages' => 'Të gjitha faqet',
'allpagesbadtitle' => 'Titulli i dhënë ishte i pavlefshë ose kishte një parashtesë ndër-gjuhe ose ndër-wiki.
Mund të përmbajë një ose më shumë karktere të cilat nuk mund të përdoren në tituj.',
'allpages-bad-ns' => '{{SITENAME}} nuk ka hapësirë "$1".',
+'allpages-hide-redirects' => 'Fshih përcjelljet',
+
+# SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'Ju jeni duke e parë një version të ruajtur të kësaj faqe, që mund të jetë deri $1 e vjetër',
+'cachedspecial-viewing-cached-ts' => 'Ju jeni duke e parë një version të ruajtur të kësaj faqe, që mund të mos jetë tërësisht e pranishme.',
+'cachedspecial-refresh-now' => 'Shikoni të fundit.',
# Special:Categories
'categories' => 'Kategori',
'rollback' => 'Riktheji mbrapsh redaktimet',
'rollback_short' => 'Riktheje',
'rollbacklink' => 'riktheje',
+'rollbacklinkcount' => 'riktheni $1 {{PLURAL:$1|ndryshimin|ndryshiemt}}',
+'rollbacklinkcount-morethan' => 'riktheni më tepër $1 {{PLURAL:$1|ndryshim|ndryshime}}',
'rollbackfailed' => 'Rikthimi dështoi',
'cantrollback' => 'Redaktimi nuk mund të kthehej;
redaktori i fundit është i vetmi autor i këtij artikulli.',
'exporttext' => 'Mund të eksportoni tekstin dhe historinë e redaktimit e një faqeje ose disa faqesh të mbështjesha në XML; kjo mund të importohet në një wiki tjetër që përdor softuerin MediaWiki (tani për tani, ky opsion nuk është përfshirë tek {{SITENAME}}).
Për të eksportuar faqe, thjesht shtypni një emër për çdo rresht, ose krijoni lidhje të tipit [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] si [[{{MediaWiki:Mainpage}}]].',
+'exportall' => 'Eksportoni të gjitha faqet',
'exportcuronly' => 'Përfshi vetëm versionin e fundit, jo të gjithë historinë',
'exportnohistory' => "'''Shënim:''' Eksportimi i historisë së faqes për shkaqe të rendimentit nuk është e mundshme.",
'exportlistauthors' => 'Përfshij një listë të plotë të kontribuesve për secilën faqe',
'import-invalid-interwiki' => 'Nuk mund të importohet nga wiki i specifikuar.',
'import-error-edit' => 'Faqja "$1" nuk është importuar sepse ju nuk lejoheni ta redaktoni atë.',
'import-error-create' => 'Faqja "$1" nuk është importuar sepse ju nuk lejoheni ta krijoni atë.',
+'import-error-interwiki' => 'Faqja "$1" nuk është importuar sepse emri i saj është rezervuar për lidhje të jashtme (interwiki)',
+'import-error-special' => 'Faqja "$1" nuk është importuar sepse ajo i përket një hapësire të veçantë që nuk i lejon faqet.',
+'import-error-invalid' => 'Faqja "$1" nuk është importuar sepse emri i saj është i palejueshëm.',
# Import log
'importlogpage' => 'Regjistri i importeve',
'import-logentry-interwiki-detail' => '$1 {{PLURAL:$!1|version|versione}} nga $2',
# JavaScriptTest
+'javascripttest' => 'Duke testuar JavaScript',
+'javascripttest-disabled' => 'Ky funksion nuk është mundësuar në këtë wiki.',
+'javascripttest-title' => 'Duke kryer testet $1',
+'javascripttest-pagetext-noframework' => 'Kjo faqe është rezervuar për kryerjen e testimeve JavaScript.',
+'javascripttest-pagetext-unknownframework' => 'Kornizë pune e panjohur testuese "$1".',
+'javascripttest-pagetext-frameworks' => 'Ju lutemi zgjidhni njërën nga kornizat vijuese punuese të testimit: $1',
+'javascripttest-pagetext-skins' => "Zgjidhni një mostër për t'i kryer testimet:",
'javascripttest-qunit-intro' => 'Shiko [$1 dokumentacionin e testimit] në mediawiki.org.',
+'javascripttest-qunit-heading' => 'Platforma testuese JavaScript QUnit',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Faqja juaj e përdoruesit',
'spambot_username' => 'MediaWiki spam-pastrues',
'spam_reverting' => "U kthye tek versioni i fundit që s'ka lidhje tek $1",
'spam_blanking' => 'U boshatis sepse të gjitha versionet kanë lidhje tek $1',
+'spam_deleting' => 'Të gjitha inspektimet përmbanin lidhje në $1, duke fshirë',
# Info page
'pageinfo-title' => 'Informacion për " $1 "',
'version-software' => 'Softuerët e instaluar',
'version-software-product' => 'Produkti',
'version-software-version' => 'Versioni',
+'version-entrypoints-header-url' => 'URL',
# Special:FilePath
'filepath' => 'Vendndodhja e skedave',
'api-error-empty-file' => 'Skeda që paraqitët ishte bosh.',
'api-error-emptypage' => 'Nuk lejohet krijimi i faqeve të reja bosh.',
'api-error-fetchfileerror' => 'Gabim i brendshëm: Diçka shkoi keq gjatë marrjes së skedës.',
+'api-error-fileexists-forbidden' => 'Një skedar me emrin "$1" tashmë ekziston dhe nuk mund të mbishkruhet.',
+'api-error-fileexists-shared-forbidden' => 'Një skedar me emrin "$1" tashmë ekziston në depon për skedarët e shpërndarë dhe nuk mund të mbishkruhet.',
'api-error-file-too-large' => 'Skeda që paraqitët ishte shumë e madhe.',
'api-error-filename-tooshort' => 'Emri i skedës është shumë i shkurtër.',
'api-error-filetype-banned' => 'Ky lloj i skedës është përjashtuar.',
'api-error-uploaddisabled' => 'Ngarkimi është i çaktivizuar në këte wiki.',
'api-error-verification-error' => 'Skeda mund të jetë e korruptuar ose ka shtesë të gabuar.',
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|sekondë|sekonda}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minutë|minuta}}',
+'duration-hours' => '$1 {{PLURAL:$1|orë|orë}}',
+'duration-days' => '$1 {{PLURAL:$1|ditë|ditë}}',
+'duration-weeks' => '$1 {{PLURAL:$1|javë|javë}}',
+'duration-years' => '$1 {{PLURAL:$1|vit|vite}}',
+'duration-decades' => '$1 {{PLURAL:$1|dekadë|dekada}}',
+'duration-centuries' => '$1 {{PLURAL:$1|shekull|shekuj}}',
+'duration-millennia' => '$1 {{PLURAL:$1|milennium|mileniume}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|nuk është një lloj i skedës së lejuar|nuk janë lloje të lejuara të skedave}}. {{PLURAL:$3|Lloji i lejuar i skedës është|Llojet e lejuara të skedave janë}} $2.',
);
'tog-editsectiononrightclick' => 'Уређивање одељака десним кликом на њихове наслове (јаваскрипт)',
'tog-showtoc' => 'Прикажи садржај страница које имају више од три поднаслова',
'tog-rememberpassword' => 'Запамти ме на овом прегледачу (најдуже $1 {{PLURAL:$1|дан|дана|дана}})',
-'tog-watchcreations' => 'Додај странице које направим у списак надгледања',
-'tog-watchdefault' => 'Додај странице које изменим у списак надгледања',
-'tog-watchmoves' => 'Додај странице које преместим у списак надгледања',
-'tog-watchdeletion' => 'Додај странице које обришем у списак надгледања',
+'tog-watchcreations' => 'Додај странице које направим и датотеке које пошаљем у списак надгледања',
+'tog-watchdefault' => 'Ð\94одаÑ\98 Ñ\81Ñ\82Ñ\80аниÑ\86е и даÑ\82оÑ\82еке коÑ\98е изменим Ñ\83 Ñ\81пиÑ\81ак надгледаÑ\9aа',
+'tog-watchmoves' => 'Ð\94одаÑ\98 Ñ\81Ñ\82Ñ\80аниÑ\86е и даÑ\82оÑ\82еке коÑ\98е пÑ\80емеÑ\81Ñ\82им Ñ\83 Ñ\81пиÑ\81ак надгледаÑ\9aа',
+'tog-watchdeletion' => 'Ð\94одаÑ\98 Ñ\81Ñ\82Ñ\80аниÑ\86е и даÑ\82оÑ\82еке коÑ\98е обÑ\80иÑ\88ем Ñ\83 Ñ\81пиÑ\81ак надгледаÑ\9aа',
'tog-minordefault' => 'Означавај све измене као мање',
'tog-previewontop' => 'Прикажи преглед пре оквира за уређивање',
'tog-previewonfirst' => 'Прикажи преглед на првој измени',
'tog-nocache' => 'Онемогући привремено меморисање страница',
-'tog-enotifwatchlistpages' => 'Пошаљи ми е-поруку када се промени страница коју надгледам',
+'tog-enotifwatchlistpages' => 'Ð\9fоÑ\88аÑ\99и ми е-поÑ\80Ñ\83кÑ\83 када Ñ\81е пÑ\80омени Ñ\81Ñ\82Ñ\80аниÑ\86а или даÑ\82оÑ\82ека коÑ\98Ñ\83 надгледам',
'tog-enotifusertalkpages' => 'Пошаљи ми е-поруку када се промени моја страница за разговор',
-'tog-enotifminoredits' => 'Пошаљи ми е-поруку и за мање измене',
+'tog-enotifminoredits' => 'Пошаљи ми е-поруку и за мање измене у страницама и датотекама',
'tog-enotifrevealaddr' => 'Откриј моју е-адресу у порукама обавештења',
'tog-shownumberswatching' => 'Прикажи број корисника који надгледају',
'tog-oldsig' => 'Текући потпис:',
'retrievedfrom' => 'Преузето из „$1“',
'youhavenewmessages' => 'Имате $1 ($2).',
'newmessageslink' => 'нових порука',
-'newmessagesdifflink' => 'последња измена',
+'newmessagesdifflink' => 'последњу измену',
+'youhavenewmessagesfromusers' => 'Имате $1 од {{PLURAL:$3|другог корисника|$3 корисника|$3 корисника}} ($2).',
+'youhavenewmessagesmanyusers' => 'Имате $1 од много корисника ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|нову поруку|нове поруке}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|последњу измену|последње измене}}',
'youhavenewmessagesmulti' => 'Имате нових порука на $1',
'editsection' => 'уреди',
'editsection-brackets' => '[$1]',
'remembermypassword' => 'Запамти ме на овом прегледачу (најдуже $1 {{PLURAL:$1|дан|дана|дана}})',
'securelogin-stick-https' => 'Останите повезани са HTTPS након пријаве',
'yourdomainname' => 'Домен:',
+'password-change-forbidden' => 'Не можете да промените лозинку на овом викију.',
'externaldberror' => 'Дошло је до грешке при препознавању базе података или немате овлашћења да ажурирате свој спољни налог.',
'login' => 'Пријави ме',
'nav-login-createaccount' => 'Пријава/регистрација',
'watchthis' => 'надгледај ову страницу',
'savearticle' => 'Сачувај страницу',
'preview' => 'Преглед',
-'showpreview' => 'Ð\9fÑ\80икажи пÑ\80еÑ\82пÑ\80еглед',
+'showpreview' => 'Ð\9fÑ\80егледаÑ\98',
'showlivepreview' => 'Тренутни преглед',
'showdiff' => 'Прикажи измене',
'anoneditwarning' => "'''Упозорење:''' нисте пријављени.
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне извештаје] или [{{fullurl:{{FULLPAGENAME}}|action=edit}} уредити страницу]</span>.',
'noarticletext-nopermission' => 'На овој страници тренутно нема садржаја.
Можете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама или <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне извештаје]</span>.',
+'missing-revision' => 'Не могу да пронађем измену бр. $1 на страници под називом „{{PAGENAME}}“.
+
+Ово се обично дешава када пратите застарелу везу до странице која је обрисана.
+Више информација можете пронаћи у [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневнику брисања].',
'userpage-userdoesnotexist' => 'Кориснички налог „<nowiki>$1</nowiki>“ није отворен.
Размислите да ли заиста желите да направите или уредите ову страницу.',
'userpage-userdoesnotexist-view' => 'Кориснички налог „$1“ није отворен.',
'sectioneditnotsupported-text' => 'Уређивање одељка није подржано на овој страници.',
'permissionserrors' => 'Грешке у дозволама',
'permissionserrorstext' => 'Немате овлашћење за ту радњу из {{PLURAL:$1|следећег|следећих}} разлога:',
-'permissionserrorstext-withaction' => 'Ð\9dемаÑ\82е овлаÑ\88Ñ\9bеÑ\9aа за $2 због {{PLURAL:$1|следећег|следећих}} разлога:',
+'permissionserrorstext-withaction' => 'Ð\9dемаÑ\82е дозволÑ\83 да $2 из {{PLURAL:$1|следећег|следећих}} разлога:',
'recreate-moveddeleted-warn' => "'''Упозорење: поново правите страницу која је претходно обрисана.'''
Размотрите да ли је прикладно да наставите с уређивањем ове странице.
'expansion-depth-exceeded-warning' => 'Страница у којој је прекорачена дубина проширења',
'parser-unstrip-loop-warning' => 'Утврђена је петља',
'parser-unstrip-recursion-limit' => 'Прекорачено је ограничење рекурзије ($1)',
+'converter-manual-rule-error' => 'Пронађена је грешка у правилу за ручно претварање језика',
# "Undo" feature
'undo-success' => 'Измена се може вратити.
'editundo' => 'поништи',
'diff-multi' => '({{PLURAL:$1|није приказана међуизмена|нису приказане $1 међуизмене|није приказано $1 међуизмена}} {{PLURAL:$2|једног|$2|$2}} корисника)',
'diff-multi-manyusers' => '({{PLURAL:$1|Није приказана међуизмена|Нису приказане $1 међуизмене|Није приказано $1 међуизмена}} од више од $2 корисника)',
+'difference-missing-revision' => 'Не могу да пронађем {{PLURAL:$2|једну измену|$2 измене|$2 измена}} од ове разлике ($1).
+
+Ово се обично дешава када пратите застарелу везу до странице која је обрисана.
+Више информација можете пронаћи у [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневнику брисања].',
# Search results
'searchresults' => 'Резултати претраге',
'right-writeapi' => 'писање АПИ-ја',
'right-delete' => 'брисање страница',
'right-bigdelete' => 'брисање страница с великом историјом',
+'right-deletelogentry' => 'Брисање и враћање одређених ставки у дневнику',
'right-deleterevision' => 'брисање и враћање одређених измена страница',
'right-deletedhistory' => 'прегледање обрисаних ставки историје без повезаног текста',
'right-deletedtext' => 'прегледање обрисаног текста и измена између обрисаних измена',
'right-suppressrevision' => 'прегледање и враћање измена које су сакривене од стране администратора',
'right-suppressionlog' => 'гледање приватних дневника',
'right-block' => 'блокирање даљих измена других корисника',
-'right-blockemail' => 'блокиÑ\80аÑ\9aе коÑ\80иÑ\81ника да шаљу е-поруке',
+'right-blockemail' => 'онемогÑ\83Ñ\9bаваÑ\9aе коÑ\80иÑ\81ниÑ\86има да шаљу е-поруке',
'right-hideuser' => 'блокирање корисничког имена и његово сакривање од јавности',
'right-ipblock-exempt' => 'заобилажење блокирања IP адресе, самоблокирања и блокирања опсега',
'right-proxyunbannable' => 'заобилажење самоблокирања посредника',
'lockmanager-fail-releaselock' => 'Не могу да ослободим катанац за „$1“.',
'lockmanager-fail-db-bucket' => 'Не могу да контактирам с довољно катанаца у канти $1.',
'lockmanager-fail-db-release' => 'Не могу да ослободим катанце у бази $1.',
+'lockmanager-fail-svr-acquire' => 'Не могу да добијем катанце на серверу $1.',
'lockmanager-fail-svr-release' => 'Не могу да ослободим катанце на серверу $1.',
# ZipDirectoryReader
'disambiguations' => 'Странице до вишезначних одредница',
'disambiguationspage' => 'Template:Вишезначна одредница',
-'disambiguations-text' => "Следеће странице су повезане с '''вишезначном одредницом'''.
-Ð\9eне би Ñ\82Ñ\80ебало биÑ\82и Ñ\83пÑ\83Ñ\9bене ка одговаÑ\80аÑ\98Ñ\83Ñ\9bем Ñ\87ланкÑ\83.
-Страница се сматра вишезначном одредницом ако користи шаблон који је повезан са списком [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Следеће странице садрже бар једну везу до '''вишезначне одреднице'''.
+УмеÑ\81Ñ\82о Ñ\82ога, ваÑ\99ало би да воде до одговаÑ\80аÑ\98Ñ\83Ñ\9bе Ñ\82еме.
+Страница се сматра вишезначном одредницом ако користи шаблон који води од [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Двострука преусмерења',
'doubleredirectstext' => 'Ова страница приказује странице које преусмеравају на друга преусмерења.
Погледајте ''$2'' за више детаља.",
'dellogpage' => 'Дневник брисања',
'dellogpagetext' => 'Испод је списак последњих брисања.',
-'deletionlog' => 'Ð\94невник брисања',
+'deletionlog' => 'дневник брисања',
'reverted' => 'Враћено на ранију измену',
'deletecomment' => 'Разлог:',
'deleteotherreason' => 'Други/додатни разлог:',
'rollback' => 'Врати измене',
'rollback_short' => 'Врати',
'rollbacklink' => 'врати',
+'rollbacklinkcount' => 'врати $1 {{PLURAL:$1|измену|измене|измена}}',
+'rollbacklinkcount-morethan' => 'врати више од $1 {{PLURAL:$1|измене|измене|измена}}',
'rollbackfailed' => 'Неуспешно враћање',
'cantrollback' => 'Не могу да вратим измену.
Последњи аутор је уједно и једини.',
# Block/unblock
'autoblockid' => 'Самоблокирање #$1',
'block' => 'Блокирај корисника',
-'unblock' => 'Ð\9eдблокирај корисника',
+'unblock' => 'Ð\94еблокирај корисника',
'blockip' => 'Блокирај корисника',
'blockip-title' => 'Блокирање корисника',
'blockip-legend' => 'Блокирај корисника',
** Неприхватљиво корисничко име',
'ipb-hardblock' => 'Забрани пријављеним корисницима да уређују с ове ИП адресе',
'ipbcreateaccount' => 'Онемогући отварање налога',
-'ipbemailban' => 'Ð\97абÑ\80ани коÑ\80иÑ\81никÑ\83 Ñ\81лаÑ\9aе е-поÑ\80Ñ\83ка',
+'ipbemailban' => 'Ð\9eнемогÑ\83Ñ\9bи коÑ\80иÑ\81никÑ\83 да Ñ\88аÑ\99е е-поÑ\80Ñ\83ке',
'ipbenableautoblock' => 'Аутоматски блокирај последњу ИП адресу овог корисника и све даљње адресе с којих покуша да уређује',
'ipbsubmit' => 'Блокирај овог корисника',
'ipbother' => 'Друго време:',
'ipb-blockingself' => 'Овом радњом ћете блокирати себе! Јесте ли сигурни да то желите?',
'ipb-confirmhideuser' => 'Управо ћете блокирати корисника с укљученом могућношћу „сакриј корисника“. Овим ће корисничко име бити сакривено у свим списковима и извештајима. Желите ли то да урадите?',
'ipb-edit-dropdown' => 'Уреди разлоге блокирања',
-'ipb-unblock-addr' => 'Ð\9eдблокирај $1',
-'ipb-unblock' => 'Ð\9eдблокирај корисничко име или ИП адресу',
+'ipb-unblock-addr' => 'Ð\94еблокирај $1',
+'ipb-unblock' => 'Ð\94еблокирај корисничко име или ИП адресу',
'ipb-blocklist' => 'Погледај постојећа блокирања',
'ipb-blocklist-contribs' => 'Доприноси за $1',
-'unblockip' => 'Ð\9eдблокирај корисника',
+'unblockip' => 'Ð\94еблокирај корисника',
'unblockiptext' => 'Користите образац испод да бисте вратили право писања блокираној IP адреси или корисничком имену.',
'ipusubmit' => 'Уклони ову блокаду',
'unblocked' => '[[User:$1|$1]] је деблокиран',
'ipblocklist-empty' => 'Списак блокирања је празан.',
'ipblocklist-no-results' => 'Тражена ИП адреса или корисничко име није блокирано.',
'blocklink' => 'блокирај',
-'unblocklink' => 'одблокирај',
+'unblocklink' => 'деблокирај',
'change-blocklink' => 'промени блокирање',
'contribslink' => 'доприноси',
'emaillink' => 'пошаљи е-поруку',
'blocklogtext' => 'Ово је дневник блокирања и деблокирања корисника.
Аутоматски блокиране ИП адресе нису наведене.
Текуће забране и блокирања можете наћи [[Special:BlockList|овде]].',
-'unblocklogentry' => '{{GENDER:|Ñ\98е одблокиÑ\80ао|Ñ\98е одблокиÑ\80ала|Ñ\98е одблокирао}} „$1“',
+'unblocklogentry' => '{{GENDER:|Ñ\98е деблокиÑ\80ао|Ñ\98е деблокиÑ\80ала|Ñ\98е деблокирао}} „$1“',
'block-log-flags-anononly' => 'само анонимни корисници',
'block-log-flags-nocreate' => 'онемогућено отварање налога',
'block-log-flags-noautoblock' => 'аутоматско блокирање је онемогућено',
'tooltip-watchlistedit-raw-submit' => 'Ажурирај списак',
'tooltip-recreate' => 'Поново направите страницу иако је обрисана',
'tooltip-upload' => 'Започните отпремање',
-'tooltip-rollback' => '„Врати“ поништава последњу измену ове странице једним кликом',
+'tooltip-rollback' => 'Опција „Врати“ враћа измене последњег корисника',
'tooltip-undo' => 'Враћа ову измену и отвара образац за уређивање.',
'tooltip-preferences-save' => 'Сачувај поставке',
'tooltip-summary' => 'Унесите кратак опис',
* <span class="mw-specialpagecached">привремено меморисане посебне странице</span>',
'specialpages-group-maintenance' => 'Извештаји одржавања',
'specialpages-group-other' => 'Остале посебне странице',
-'specialpages-group-login' => 'Ð\9eÑ\82ваÑ\80аÑ\9aе налога и пÑ\80иÑ\98авÑ\99иваÑ\9aе',
+'specialpages-group-login' => 'Ð\9fÑ\80иÑ\98ава/Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86иÑ\98а',
'specialpages-group-changes' => 'Скорашње измене и дневници',
'specialpages-group-media' => 'Извештаји о мултимедијалном садржају и отпремања',
'specialpages-group-users' => 'Корисници и корисничка права',
# New logging system
'logentry-delete-delete' => '$1 {{GENDER:|је обрисао|је обрисала|је обрисао}} $3',
'logentry-delete-restore' => '$1 {{GENDER:|је вратио|је вратила|је вратио}} страницу $3',
-'logentry-delete-event' => '$1 {{GENDER:|је променио|је променила|је променио}} видљивост {{PLURAL:$5|догађаја|$5 догађаја|$5 догађаја}} у дневнику на $3: $4',
+'logentry-delete-event' => '$1 {{GENDER:$2|је променио|је променила|је променио}} видљивост {{PLURAL:$5|догађаја|$5 догађаја|$5 догађаја}} у дневнику на $3: $4',
'logentry-delete-revision' => '$1 {{GENDER:|је променио|је променила|је променио}} видљивост {{PLURAL:$5|измене|$5 измене|$5 измена}} на страници $3: $4',
'logentry-delete-event-legacy' => '$1 {{GENDER:|је променио|је променила|је променио}} видљивост догађајâ у дневнику на $3',
'logentry-delete-revision-legacy' => '$1 {{GENDER:|је променио|је променила|је променио}} видљивост изменâ на страници $3',
'duration-millennia' => '$1 {{PLURAL:$1|миленијум|миленијума|миленијума}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Не могу да добијем катанце на серверу $1.',
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|је забрањена врста датотеке|су забрањене врсте датотека}}. {{PLURAL:$3|Дозвољена је|Дозвољене су}} $2.',
);
'tog-hidepatrolled' => 'Sakrij pregledane izmene u spisku skorašnjih izmena',
'tog-newpageshidepatrolled' => 'Sakrij pregledane stranice sa spiska novih stranica',
'tog-extendwatchlist' => 'Proširi spisak nadgledanja za prikaz svih izmena, ne samo skorašnjih',
-'tog-usenewrc' => 'Poboljšani spisak skorašnjih izmena (javaskript)',
+'tog-usenewrc' => 'Promene u grupi po stranici u spisku skorašnjih izmena i nadgledanih stranica (zahteva javaskript)',
'tog-numberheadings' => 'Samostalno numeriši podnaslove',
'tog-showtoolbar' => 'Traka s alatkama za uređivanje (javaskript)',
'tog-editondblclick' => 'Uređivanje stranica dvostrukim klikom (javaskript)',
'tog-editsectiononrightclick' => 'Uređivanje odeljaka desnim klikom na njihove naslove (javaskript)',
'tog-showtoc' => 'Prikaži sadržaj stranica koje imaju više od tri podnaslova',
'tog-rememberpassword' => 'Zapamti me na ovom pregledaču (najduže $1 {{PLURAL:$1|dan|dana|dana}})',
-'tog-watchcreations' => 'Dodaj stranice koje napravim u spisak nadgledanja',
-'tog-watchdefault' => 'Dodaj stranice koje izmenim u spisak nadgledanja',
-'tog-watchmoves' => 'Dodaj stranice koje premestim u spisak nadgledanja',
-'tog-watchdeletion' => 'Dodaj stranice koje obrišem u spisak nadgledanja',
+'tog-watchcreations' => 'Dodaj stranice koje napravim i datoteke koje pošaljem u spisak nadgledanja',
+'tog-watchdefault' => 'Dodaj stranice i datoteke koje izmenim u spisak nadgledanja',
+'tog-watchmoves' => 'Dodaj stranice i datoteke koje premestim u spisak nadgledanja',
+'tog-watchdeletion' => 'Dodaj stranice i datoteke koje obrišem u spisak nadgledanja',
'tog-minordefault' => 'Označavaj sve izmene kao manje',
'tog-previewontop' => 'Prikaži pregled pre okvira za uređivanje',
'tog-previewonfirst' => 'Prikaži pregled na prvoj izmeni',
'tog-nocache' => 'Onemogući privremeno memorisanje stranica',
-'tog-enotifwatchlistpages' => 'Pošalji mi e-poruku kada se promeni stranica koju nadgledam',
+'tog-enotifwatchlistpages' => 'Pošalji mi e-poruku kada se promeni stranica ili datoteka koju nadgledam',
'tog-enotifusertalkpages' => 'Pošalji mi e-poruku kada se promeni moja stranica za razgovor',
-'tog-enotifminoredits' => 'Pošalji mi e-poruku i za manje izmene',
+'tog-enotifminoredits' => 'Pošalji mi e-poruku i za manje izmene u stranicama i datotekama',
'tog-enotifrevealaddr' => 'Otkrij moju e-adresu u porukama obaveštenja',
'tog-shownumberswatching' => 'Prikaži broj korisnika koji nadgledaju',
'tog-oldsig' => 'Tekući potpis:',
'history' => 'Istorija stranice',
'history_short' => 'Istorija',
'updatedmarker' => 'ažurirano od moje poslednje posete',
-'printableversion' => 'Izdanje za štampu',
+'printableversion' => 'Verzija za štampu',
'permalink' => 'Trajna veza',
'print' => 'Štampaj',
'view' => 'Pogledaj',
'retrievedfrom' => 'Preuzeto iz „$1“',
'youhavenewmessages' => 'Imate $1 ($2).',
'newmessageslink' => 'novih poruka',
-'newmessagesdifflink' => 'poslednja izmena',
+'newmessagesdifflink' => 'poslednju izmenu',
+'youhavenewmessagesfromusers' => 'Imate $1 od {{PLURAL:$3|drugog korisnika|$3 korisnika|$3 korisnika}} ($2).',
+'youhavenewmessagesmanyusers' => 'Imate $1 od mnogo korisnika ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|novu poruku|nove poruke}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|poslednju izmenu|poslednje izmene}}',
'youhavenewmessagesmulti' => 'Imate novih poruka na $1',
'editsection' => 'uredi',
'editsection-brackets' => '[$1]',
# General errors
'error' => 'Greška',
'databaseerror' => 'Greška u bazi podataka',
-'dberrortext' => 'Došlo je do sintaksne greške u bazi.
+'dberrortext' => 'Došlo je do sintaktičke greške u bazi.
Možda se radi o grešci u softveru.
Poslednji pokušaj upita je glasio:
<blockquote><tt>$1</tt></blockquote>
unutar funkcije „<tt>$2</tt>“.
Baza podataka je prijavila grešku „<tt>$3: $4</tt>“.',
-'dberrortextcl' => 'Došlo je do sintaksne greške u bazi.
+'dberrortextcl' => 'Došlo je do sintaktičke greške u bazi.
Poslednji pokušaj upita je glasio:
„$1“
unutar funkcije „$2“.
'cannotdelete' => 'Ne mogu da obrišem stranicu ili datoteku „$1“.
Verovatno ju je neko drugi obrisao.',
'cannotdelete-title' => 'Ne mogu da obrišem stranicu „$1“',
+'delete-hook-aborted' => 'Brisanje je prekinula kuka.
+Nije dato nikakvo obrazloženje.',
'badtitle' => 'Neispravan naslov',
'badtitletext' => 'Naslov stranice je neispravan, prazan ili je međujezički ili međuviki naslov pogrešno povezan.
Možda sadrži znakove koji se ne mogu koristiti u naslovima.',
Administrator koji ju je zaključao ponudio je sledeće objašnjenje: „$3“.',
'invalidtitle-knownnamespace' => 'Neispravan naslov s imenskim prostorom „$2“ i tekstom „$3“',
'invalidtitle-unknownnamespace' => 'Neispravan naslov s imenskim prostorom br. $1 i tekstom „$2“',
+'exception-nologin' => 'Niste prijavljeni',
+'exception-nologin-text' => 'Ova stranica ili radnja zahteva da budete prijavljeni na viki.',
# Virus scanner
'virus-badscanner' => "Neispravna postavka: nepoznati skener za viruse: ''$1''",
'remembermypassword' => 'Zapamti me na ovom pregledaču (najduže $1 {{PLURAL:$1|dan|dana|dana}})',
'securelogin-stick-https' => 'Ostanite povezani sa HTTPS nakon prijave',
'yourdomainname' => 'Domen:',
+'password-change-forbidden' => 'Ne možete da promenite lozinku na ovom vikiju.',
'externaldberror' => 'Došlo je do greške pri prepoznavanju baze podataka ili nemate ovlašćenja da ažurirate svoj spoljni nalog.',
'login' => 'Prijavi me',
'nav-login-createaccount' => 'Prijava/registracija',
'watchthis' => 'nadgledaj ovu stranicu',
'savearticle' => 'Sačuvaj stranicu',
'preview' => 'Pregled',
-'showpreview' => 'Prikaži pregled',
+'showpreview' => 'Pregledaj',
'showlivepreview' => 'Trenutni pregled',
'showdiff' => 'Prikaži izmene',
'anoneditwarning' => "'''Upozorenje:''' niste prijavljeni.
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pretražiti srodne izveštaje] ili [{{fullurl:{{FULLPAGENAME}}|action=edit}} urediti stranicu]</span>.',
'noarticletext-nopermission' => 'Na ovoj stranici trenutno nema sadržaja.
Možete [[Special:Search/{{PAGENAME}}|potražiti ovaj naslov]] na drugim stranicama ili <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pretražiti srodne izveštaje]</span>.',
+'missing-revision' => 'Ne mogu da pronađem izmenu br. $1 na stranici pod nazivom „{{PAGENAME}}“.
+
+Ovo se obično dešava kada pratite zastarelu vezu do stranice koja je obrisana.
+Više informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dnevniku brisanja].',
'userpage-userdoesnotexist' => 'Korisnički nalog „<nowiki>$1</nowiki>“ nije otvoren.
Razmislite da li zaista želite da napravite ili uredite ovu stranicu.',
'userpage-userdoesnotexist-view' => 'Korisnički nalog „$1“ nije otvoren.',
'moveddeleted-notice' => 'Ova stranica je obrisana.
Istorija njenog brisanja i premeštanja nalazi se ispod:',
'log-fulllog' => 'Pogledaj celu istoriju',
-'edit-hook-aborted' => 'Izmena je prekinuta kukom.
-Obrazloženje nije ponuđeno.',
+'edit-hook-aborted' => 'Izmenu je prekinula kuka.
+Nije dato nikakvo obrazloženje.',
'edit-gone-missing' => 'Ne mogu da ažuriram stranicu.
Izgleda da je obrisana.',
'edit-conflict' => 'Sukob izmena.',
'expansion-depth-exceeded-warning' => 'Stranica u kojoj je prekoračena dubina proširenja',
'parser-unstrip-loop-warning' => 'Utvrđena je petlja',
'parser-unstrip-recursion-limit' => 'Prekoračeno je ograničenje rekurzije ($1)',
+'converter-manual-rule-error' => 'Pronađena je greška u pravilu za ručno pretvaranje jezika',
# "Undo" feature
'undo-success' => 'Izmena se može vratiti.
# Revision feed
'history-feed-title' => 'Istorija izmena',
-'history-feed-description' => 'Istorija izmena ove stranice',
+'history-feed-description' => 'Istorija izmena ove stranice na vikiju',
'history-feed-item-nocomment' => '$1 u $2',
'history-feed-empty' => 'Tražena stranica ne postoji.
Moguće da je obrisana s vikija ili je preimenovana.
'mergelogpagetext' => 'Ispod se nalazi spisak skorašnjih spajanja istorija stranica.',
# Diffs
-'history-title' => 'Istorija izmena za „$1“',
-'difference-title' => 'Razlika između izmena stranice „$1“',
+'history-title' => 'Istorija izmena stranice „$1“',
+'difference-title' => 'Razlika između izmena na stranici „$1“',
'difference-title-multipage' => 'Razlika između stranica „$1“ i „$2“',
'difference-multipage' => '(razlike između stranica)',
'lineno' => 'Red $1:',
'editundo' => 'poništi',
'diff-multi' => '({{PLURAL:$1|nije prikazana međuizmena|nisu prikazane $1 međuizmene|nije prikazano $1 međuizmena}} {{PLURAL:$2|jednog|$2|$2}} korisnika)',
'diff-multi-manyusers' => '({{PLURAL:$1|Nije prikazana međuizmena|Nisu prikazane $1 međuizmene|Nije prikazano $1 međuizmena}} od više od $2 korisnika)',
+'difference-missing-revision' => 'Ne mogu da pronađem {{PLURAL:$2|jednu izmenu|$2 izmene|$2 izmena}} od ove razlike ($1).
+
+Ovo se obično dešava kada pratite zastarelu vezu do stranice koja je obrisana.
+Više informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dnevniku brisanja].',
# Search results
'searchresults' => 'Rezultati pretrage',
'prefs-beta' => 'Beta mogućnosti',
'prefs-datetime' => 'Datum i vreme',
'prefs-labs' => 'Probne mogućnosti',
+'prefs-user-pages' => 'Korisničke stranice',
'prefs-personal' => 'Profil',
'prefs-rc' => 'Skorašnje izmene',
'prefs-watchlist' => 'Spisak nadgledanja',
'right-edit' => 'uređivanje stranica',
'right-createpage' => 'pravljenje stranica (izuzev stranica za razgovor)',
'right-createtalk' => 'pravljenje stranica za razgovor',
-'right-createaccount' => 'pravljenje novih korisničkih naloga',
+'right-createaccount' => 'otvaranje novih korisničkih naloga',
'right-minoredit' => 'označavanje izmena kao manje',
'right-move' => 'premeštanje stranica',
'right-move-subpages' => 'premeštanje stranica s njihovim podstranicama',
'right-writeapi' => 'pisanje API-ja',
'right-delete' => 'brisanje stranica',
'right-bigdelete' => 'brisanje stranica s velikom istorijom',
+'right-deletelogentry' => 'Brisanje i vraćanje određenih stavki u dnevniku',
'right-deleterevision' => 'brisanje i vraćanje određenih izmena stranica',
'right-deletedhistory' => 'pregledanje obrisanih stavki istorije bez povezanog teksta',
'right-deletedtext' => 'pregledanje obrisanog teksta i izmena između obrisanih izmena',
'right-suppressrevision' => 'pregledanje i vraćanje izmena koje su sakrivene od strane administratora',
'right-suppressionlog' => 'gledanje privatnih dnevnika',
'right-block' => 'blokiranje daljih izmena drugih korisnika',
-'right-blockemail' => 'blokiranje korisnika da šalju e-poruke',
+'right-blockemail' => 'onemogućavanje korisnicima da šalju e-poruke',
'right-hideuser' => 'blokiranje korisničkog imena i njegovo sakrivanje od javnosti',
'right-ipblock-exempt' => 'zaobilaženje blokiranja IP adrese, samoblokiranja i blokiranja opsega',
'right-proxyunbannable' => 'zaobilaženje samoblokiranja posrednika',
'action-edit' => 'uređivanje ove stranice',
'action-createpage' => 'pravljenje stranica',
'action-createtalk' => 'pravljenje stranica za razgovor',
-'action-createaccount' => 'pravljenje ovog korisničkog naloga',
+'action-createaccount' => 'otvaranje ovog korisničkog naloga',
'action-minoredit' => 'označavanje ove izmene kao manje',
'action-move' => 'premeštanje ove stranice',
'action-move-subpages' => 'premeštanje ove stranice i njenih podstranica',
'nchanges' => '$1 {{PLURAL:$1|izmena|izmene|izmena}}',
'recentchanges' => 'Skorašnje izmene',
'recentchanges-legend' => 'Postavke skorašnjih izmena',
-'recentchanges-summary' => 'Ovde pratite najskorije izmene na vikiju.',
+'recentchanges-summary' => 'Pratite skorašnje izmene na ovoj stranici.',
'recentchanges-feed-description' => 'Pratite skorašnje izmene uz pomoć ovog dovoda.',
'recentchanges-label-newpage' => 'Nova stranica',
'recentchanges-label-minor' => 'Manja izmena',
'backend-fail-writetemp' => 'Ne mogu da pišem u privremenoj datoteci.',
'backend-fail-closetemp' => 'Ne mogu da zatvorim privremenu datoteku.',
'backend-fail-read' => 'Ne mogu da pročitam datoteku $1.',
-'backend-fail-create' => 'Ne mogu da napravim datoteku $1.',
-'backend-fail-maxsize' => 'Ne mogu da napravim datoteku $1 jer je veća od {{PLURAL:$2|$2 bajta|$2 bajta|$2 bajtova}}.',
+'backend-fail-create' => 'Ne mogu da zapišem datoteku $1.',
+'backend-fail-maxsize' => 'Ne mogu da zapišem datoteku $1 jer je veća od {{PLURAL:$2|$2 bajta|$2 bajta|$2 bajtova}}.',
'backend-fail-readonly' => 'Skladišna osnova „$1“ trenutno ne može da se zapisuje. Navedeni razlog glasi: „$2“',
'backend-fail-synced' => 'Datoteka „$1“ je nedosledna između unutrašnjih skladišnih osnova',
'backend-fail-connect' => 'Ne mogu da se povežem sa skladišnom osnovom „$1“.',
'backend-fail-internal' => 'Došlo je do nepoznate greške u skladišnoj osnovi „$1“.',
'backend-fail-contenttype' => 'Ne mogu da utvrdim kakav sadržaj ima datoteka koju treba da smestim u „$1“.',
'backend-fail-batchsize' => 'Skladišna osnova je dobila blokadu od $1 {{PLURAL:$1|operacije|operacije|operacija}}; ograničenje je $2 {{PLURAL:$2|operacija|operacije|operacija}}.',
+'backend-fail-usable' => 'Ne mogu da zapišem datoteku $1 jer nemate dovoljno dozvola ili vam nedostaju fascikle/sadržaoci.',
# File journal errors
'filejournal-fail-dbconnect' => 'Ne mogu da se povežem s novinarskom bazom za skladišnu osnovu „$1“.',
'lockmanager-fail-releaselock' => 'Ne mogu da oslobodim katanac za „$1“.',
'lockmanager-fail-db-bucket' => 'Ne mogu da kontaktiram s dovoljno katanaca u kanti $1.',
'lockmanager-fail-db-release' => 'Ne mogu da oslobodim katance u bazi $1.',
+'lockmanager-fail-svr-acquire' => 'Ne mogu da dobijem katance na serveru $1.',
'lockmanager-fail-svr-release' => 'Ne mogu da oslobodim katance na serveru $1.',
# ZipDirectoryReader
'listfiles_search_for' => 'Naziv datoteke:',
'imgfile' => 'datoteka',
'listfiles' => 'Spisak datoteka',
-'listfiles_thumb' => 'Umanjeni prikaz',
+'listfiles_thumb' => 'Minijatura',
'listfiles_date' => 'Datum',
'listfiles_name' => 'Naziv',
'listfiles_user' => 'Korisnik',
'sharedupload' => 'Ova datoteka se nalazi na $1 i može se koristiti i na drugim projektima.',
'sharedupload-desc-there' => 'Ova datoteka se nalazi na $1 i može se koristiti i na drugim projektima.
Pogledajte [$2 stranicu za opis datoteke] za više detalja o njoj.',
-'sharedupload-desc-here' => 'Ova datoteka se nalazi na $1 i može se koristiti i na drugim projektima.
-Opis na [$2 stranici datoteke] je prikazan ispod.',
+'sharedupload-desc-here' => 'Ova datoteka se nalazi na $1 i može da se koristi na drugim projektima.
+Opis njene [$2 stranice za opis] je prikazan ispod.',
'sharedupload-desc-edit' => 'Ova datoteka se nalazi na $1 i može da se koristi na drugim projektima.
Njen opis možete da izmenite na [$2 odgovarajućoj stranici].',
'sharedupload-desc-create' => 'Ova datoteka se nalazi na $1 i može da se koristi na drugim projektima.
'filedelete' => 'Obriši $1',
'filedelete-legend' => 'Obriši datoteku',
'filedelete-intro' => "Brišete datoteku '''[[Media:$1|$1]]''' zajedno s njenom istorijom.",
-'filedelete-intro-old' => "Brišete izdanje datoteke '''[[Media:$1|$1]]''' od [$4 $2; $3].",
+'filedelete-intro-old' => "Brišete verziju datoteke '''[[Media:$1|$1]]''' od [$4 $2; $3].",
'filedelete-comment' => 'Razlog:',
'filedelete-submit' => 'Obriši',
'filedelete-success' => "Datoteka '''$1''' je obrisana.",
'disambiguations' => 'Stranice do višeznačnih odrednica',
'disambiguationspage' => 'Template:Višeznačna odrednica',
-'disambiguations-text' => "Sledeće stranice su povezane s '''višeznačnom odrednicom'''.
-One bi trebalo biti upućene ka odgovarajućem članku.
-Stranica se smatra višeznačnom odrednicom ako koristi šablon koji je povezan sa spiskom [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Sledeće stranice sadrže bar jednu vezu do '''višeznačne odrednice'''.
+Umesto toga, valjalo bi da vode do odgovarajuće teme.
+Stranica se smatra višeznačnom odrednicom ako koristi šablon koji vodi od [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Dvostruka preusmerenja',
'doubleredirectstext' => 'Ova stranica prikazuje stranice koje preusmeravaju na druga preusmerenja.
Možete suziti prikaz odabirući vrstu istorije, korisničkog imena ili tražene stranice.',
'logempty' => 'Nema pronađenih stavki u istoriji.',
'log-title-wildcard' => 'traži naslove koji počinju s ovim tekstom',
+'showhideselectedlogentries' => 'Prikaži/sakrij izabrane zapise',
# Special:AllPages
'allpages' => 'Sve stranice',
Pogledajte ''$2'' za više detalja.",
'dellogpage' => 'Dnevnik brisanja',
'dellogpagetext' => 'Ispod je spisak poslednjih brisanja.',
-'deletionlog' => 'Dnevnik brisanja',
+'deletionlog' => 'dnevnik brisanja',
'reverted' => 'Vraćeno na raniju izmenu',
'deletecomment' => 'Razlog:',
'deleteotherreason' => 'Drugi/dodatni razlog:',
'rollback' => 'Vrati izmene',
'rollback_short' => 'Vrati',
'rollbacklink' => 'vrati',
+'rollbacklinkcount' => 'vrati $1 {{PLURAL:$1|izmenu|izmene|izmena}}',
+'rollbacklinkcount-morethan' => 'vrati više od $1 {{PLURAL:$1|izmene|izmene|izmena}}',
'rollbackfailed' => 'Neuspešno vraćanje',
'cantrollback' => 'Ne mogu da vratim izmenu.
Poslednji autor je ujedno i jedini.',
'sp-contributions-submit' => 'Pretraži',
# What links here
-'whatlinkshere' => 'Šta je povezano ovde',
+'whatlinkshere' => 'Šta vodi ovde',
'whatlinkshere-title' => 'Stranice koje su povezane sa „$1“',
'whatlinkshere-page' => 'Stranica:',
'linkshere' => "Sledeće stranice imaju vezu do '''[[:$1]]''':",
# Block/unblock
'autoblockid' => 'Samoblokiranje #$1',
'block' => 'Blokiraj korisnika',
-'unblock' => 'Odblokiraj korisnika',
+'unblock' => 'Deblokiraj korisnika',
'blockip' => 'Blokiraj korisnika',
'blockip-title' => 'Blokiranje korisnika',
'blockip-legend' => 'Blokiraj korisnika',
** Neprihvatljivo korisničko ime',
'ipb-hardblock' => 'Zabrani prijavljenim korisnicima da uređuju s ove IP adrese',
'ipbcreateaccount' => 'Onemogući otvaranje naloga',
-'ipbemailban' => 'Zabrani korisniku slanje e-poruka',
+'ipbemailban' => 'Onemogući korisniku da šalje e-poruke',
'ipbenableautoblock' => 'Automatski blokiraj poslednju IP adresu ovog korisnika i sve daljnje adrese s kojih pokuša da uređuje',
'ipbsubmit' => 'Blokiraj ovog korisnika',
'ipbother' => 'Drugo vreme:',
'ipb-blockingself' => 'Ovom radnjom ćete blokirati sebe! Jeste li sigurni da to želite?',
'ipb-confirmhideuser' => 'Upravo ćete blokirati korisnika s uključenom mogućnošću „sakrij korisnika“. Ovim će korisničko ime biti sakriveno u svim spiskovima i izveštajima. Želite li to da uradite?',
'ipb-edit-dropdown' => 'Uredi razloge blokiranja',
-'ipb-unblock-addr' => 'Odblokiraj $1',
-'ipb-unblock' => 'Odblokiraj korisničko ime ili IP adresu',
+'ipb-unblock-addr' => 'Deblokiraj $1',
+'ipb-unblock' => 'Deblokiraj korisničko ime ili IP adresu',
'ipb-blocklist' => 'Pogledaj postojeća blokiranja',
'ipb-blocklist-contribs' => 'Doprinosi za $1',
-'unblockip' => 'Odblokiraj korisnika',
+'unblockip' => 'Deblokiraj korisnika',
'unblockiptext' => 'Koristite obrazac ispod da biste vratili pravo pisanja blokiranoj IP adresi ili korisničkom imenu.',
'ipusubmit' => 'Ukloni ovu blokadu',
'unblocked' => '[[User:$1|$1]] je deblokiran',
'ipblocklist-empty' => 'Spisak blokiranja je prazan.',
'ipblocklist-no-results' => 'Tražena IP adresa ili korisničko ime nije blokirano.',
'blocklink' => 'blokiraj',
-'unblocklink' => 'odblokiraj',
+'unblocklink' => 'deblokiraj',
'change-blocklink' => 'promeni blokiranje',
'contribslink' => 'doprinosi',
'emaillink' => 'pošalji e-poruku',
'blocklogtext' => 'Ovo je dnevnik blokiranja i deblokiranja korisnika.
Automatski blokirane IP adrese nisu navedene.
Tekuće zabrane i blokiranja možete naći [[Special:BlockList|ovde]].',
-'unblocklogentry' => '{{GENDER:|je odblokirao|je odblokirala|je odblokirao}} „$1“',
+'unblocklogentry' => '{{GENDER:|je deblokirao|je deblokirala|je deblokirao}} „$1“',
'block-log-flags-anononly' => 'samo anonimni korisnici',
'block-log-flags-nocreate' => 'onemogućeno otvaranje naloga',
'block-log-flags-noautoblock' => 'automatsko blokiranje je onemogućeno',
'tooltip-ca-edit' => 'Možete da uređujete ovu stranicu. Koristite pretpregled pre snimanja',
'tooltip-ca-addsection' => 'Započnite novi odeljak',
'tooltip-ca-viewsource' => 'Ova stranica je zaključana. Možete da vidite izvorni kod.',
-'tooltip-ca-history' => 'Prethodna izdanja ove stranice',
+'tooltip-ca-history' => 'Prethodne verzije ove stranice',
'tooltip-ca-protect' => 'Zaštitite ovu stranicu',
'tooltip-ca-unprotect' => 'Promeni zaštitu ove stranice',
'tooltip-ca-delete' => 'Obrišite ovu stranicu',
'tooltip-watchlistedit-raw-submit' => 'Ažuriraj spisak',
'tooltip-recreate' => 'Ponovo napravite stranicu iako je obrisana',
'tooltip-upload' => 'Započnite otpremanje',
-'tooltip-rollback' => '„Vrati“ poništava poslednju izmenu ove stranice s jednim klikom',
+'tooltip-rollback' => 'Opcija „Vrati“ vraća izmene poslednjeg korisnika',
'tooltip-undo' => 'Vraća ovu izmenu i otvara obrazac za uređivanje.',
'tooltip-preferences-save' => 'Sačuvaj postavke',
'tooltip-summary' => 'Unesite kratak opis',
'spamprotectionmatch' => 'Sledeći tekst je izazvao naš filter za nepoželjne poruke: $1',
'spambot_username' => 'Čišćenje nepoželjnih poruka u Medijavikiji',
'spam_reverting' => 'Vraćam na poslednju izmenu koja ne sadrži veze do $1',
-'spam_blanking' => 'Sve izmene koje sadrže veze do $1, brišem',
+'spam_blanking' => 'Sve izmene sadrže veze do $1. Čistim',
+'spam_deleting' => 'Sve izmene sadrže veze do $1. Brišem',
# Info page
'pageinfo-title' => 'Podaci o „$1“',
# Patrol log
'patrol-log-page' => 'Dnevnik patroliranja',
-'patrol-log-header' => 'Ovo je istorija pregledanih izmena.',
+'patrol-log-header' => 'Ovo je dnevnik patroliranih izmena.',
'log-show-hide-patrol' => '$1 dnevnik patroliranja',
# Image deletion
'exif-datetimeexpires' => 'Ne koristi nakon',
'exif-datetimereleased' => 'Objavljeno',
'exif-originaltransmissionref' => 'Izvorni prenos kôda lokacije',
-'exif-identifier' => 'Oznaka',
+'exif-identifier' => 'Naznaka',
'exif-lens' => 'Korišćeni objektiv',
'exif-serialnumber' => 'Serijski broj kamere',
'exif-cameraownername' => 'Vlasnik kamere',
# Delete conflict
'deletedwhileediting' => "'''Upozorenje''': ova stranica je obrisana nakon što ste počeli s uređivanjem!",
-'confirmrecreate' => "[[User:$1|$1]] ([[User talk:$1|razgovor]]) {{GENDER:$1|je obrisao|je obrisala|obrisa}} ovu stranicu nakon što ste počeli da je uređujete, sa sledećim razlogom:
+'confirmrecreate' => "[[User:$1|$1]] ([[User talk:$1|razgovor]]) {{GENDER:$1|je obrisao|je obrisala|je obrisao}} ovu stranicu nakon što ste počeli da je uređujete iz sledećeg razloga:
: ''$2''
Potvrdite da stvarno želite da napravite stranicu.",
'confirmrecreate-noreason' => 'Korisnik [[User:$1|$1]] ([[User talk:$1|razgovor]]) je obrisao ovu stranicu nakon što ste počeli da ga uređujete. Potvrdite da stvarno želite da ponovo napravite ovu stranicu.',
* <span class="mw-specialpagecached">privremeno memorisane posebne stranice</span>',
'specialpages-group-maintenance' => 'Izveštaji održavanja',
'specialpages-group-other' => 'Ostale posebne stranice',
-'specialpages-group-login' => 'Otvaranje naloga i prijavljivanje',
+'specialpages-group-login' => 'Prijava/registracija',
'specialpages-group-changes' => 'Skorašnje izmene i dnevnici',
'specialpages-group-media' => 'Izveštaji o multimedijalnom sadržaju i otpremanja',
'specialpages-group-users' => 'Korisnici i korisnička prava',
'sqlite-no-fts' => '$1 bez podrške pretrage celog teksta',
# New logging system
-'logentry-delete-delete' => '$1 je obrisao stranicu $3',
-'logentry-delete-restore' => '$1 je vratio stranicu $3',
-'logentry-delete-event' => '$1 je promenio vidljivost {{PLURAL:$5|događaja u istoriji|$5 događaja u istoriji|$5 događaja u istoriji}} na $3: $4',
-'logentry-delete-revision' => '$1 je promenio vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
-'logentry-delete-event-legacy' => '$1 je promenio vidljivost događajâ u istoriji na $3',
-'logentry-delete-revision-legacy' => '$1 je promenio vidljivost izmenâ na stranici $3',
-'logentry-suppress-delete' => '$1 je potisnuo stranicu $3',
-'logentry-suppress-event' => '$1 je potajno promenio vidljivost {{PLURAL:$5|događaja u istoriji|$5 događaja u istoriji|$5 događaja u istoriji}} na $3: $4',
-'logentry-suppress-revision' => '$1 je potajno promenio vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
-'logentry-suppress-event-legacy' => '$1 je potajno promenio vidljivost događajâ u istoriji na $3',
-'logentry-suppress-revision-legacy' => '$1 je potajno promenio vidljivost izmenâ na stranici $3',
+'logentry-delete-delete' => '$1 {{GENDER:|je obrisao|je obrisala|je obrisao}} $3',
+'logentry-delete-restore' => '$1 {{GENDER:|je vratio|je vratila|je vratio}} stranicu $3',
+'logentry-delete-event' => '$1 {{GENDER:$2|je promenio|je promenila|je promenio}} vidljivost {{PLURAL:$5|događaja|$5 događaja|$5 događaja}} u dnevniku na $3: $4',
+'logentry-delete-revision' => '$1 {{GENDER:|je promenio|je promenila|je promenio}} vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
+'logentry-delete-event-legacy' => '$1 {{GENDER:|je promenio|je promenila|je promenio}} vidljivost događajâ u dnevniku na $3',
+'logentry-delete-revision-legacy' => '$1 {{GENDER:|je promenio|je promenila|je promenio}} vidljivost izmenâ na stranici $3',
+'logentry-suppress-delete' => '$1 {{GENDER:|je potisnuo|je potisnula|je potisnuo}} stranicu $3',
+'logentry-suppress-event' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost {{PLURAL:$5|događaja|$5 događaja|$5 događaja}} u dnevniku na $3: $4',
+'logentry-suppress-revision' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
+'logentry-suppress-event-legacy' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost događajâ u dnevniku na $3',
+'logentry-suppress-revision-legacy' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost izmenâ na stranici $3',
'revdelete-content-hid' => 'sadržaj je sakriven',
'revdelete-summary-hid' => 'opis izmene je sakriven',
'revdelete-uname-hid' => 'korisničko ime je sakriveno',
'revdelete-uname-unhid' => 'korisničko ime je otkriveno',
'revdelete-restricted' => 'primenjena ograničenja za administratore',
'revdelete-unrestricted' => 'uklonjena ograničenja za administratore',
-'logentry-move-move' => '$1 je premestio stranicu $3 na $4',
-'logentry-move-move-noredirect' => '$1 je premestio stranicu $3 na $4 bez ostavljanja preusmerenja',
-'logentry-move-move_redir' => '$1 je premestio stranicu $3 na $4 preko preusmerenja',
-'logentry-move-move_redir-noredirect' => '$1 je premestio stranicu $3 na $4 preko preusmerenja bez ostavljanja preusmerenja',
-'logentry-patrol-patrol' => '$1 je označio izmenu $4 stranice $3 kao pregledanu',
-'logentry-patrol-patrol-auto' => '$1 je samostalno označio izmenu $4 stranice $3 kao pregledanu',
-'logentry-newusers-newusers' => '$1 je otvorio korisnički nalog',
-'logentry-newusers-create' => '$1 je otvorio korisnički nalog',
-'logentry-newusers-create2' => '$1 je otvorio korisnički nalog $3',
+'logentry-move-move' => '$1 {{GENDER:|je premestio|je premestila|je premestio}} stranicu $3 na $4',
+'logentry-move-move-noredirect' => '$1 {{GENDER:|je premestio|je premestila|je premestio}} stranicu $3 na $4 bez ostavljanja preusmerenja',
+'logentry-move-move_redir' => '$1 {{GENDER:|je premestio|je premestila|je premestio}} stranicu $3 na $4 preko preusmerenja',
+'logentry-move-move_redir-noredirect' => '$1 {{GENDER:|je premestio|je premestila|je premestio}} stranicu $3 na $4 preko preusmerenja bez ostavljanja preusmerenja',
+'logentry-patrol-patrol' => '$1 {{GENDER:|je označio|je označila|je označio}} izmenu $4 stranice $3 kao patroliranu',
+'logentry-patrol-patrol-auto' => '$1 je samostalno {{GENDER:|označio|označila|označio}} izmenu $4 stranice $3 kao pregledanu',
+'logentry-newusers-newusers' => '$1 {{GENDER:|je otvorio|je otvorila|je otvorio}} korisnički nalog',
+'logentry-newusers-create' => '$1 {{GENDER:|je otvorio|je otvorila|je otvorio}} korisnički nalog',
+'logentry-newusers-create2' => '$1 {{GENDER:|je otvorio|je otvorila|je otvorio}} korisnički nalog $3',
'logentry-newusers-autocreate' => 'Nalog $1 je samostalno otvoren',
'newuserlog-byemail' => 'lozinka je poslata e-poštom',
'api-error-empty-file' => 'Poslata datoteka je prazna.',
'api-error-emptypage' => 'Stvaranje novih praznih stranica nije dozvoljeno.',
'api-error-fetchfileerror' => 'Unutrašnja greška: došlo je do greške pri dobavljanju datoteke.',
+'api-error-fileexists-forbidden' => 'Već postoji datoteka s imenom „$1“ i ne može da se zameni.',
+'api-error-fileexists-shared-forbidden' => 'Već postoji datoteka s imenom „$1“ u zajedničkoj riznici i ne može da se zameni.',
'api-error-file-too-large' => 'Poslata datoteka je prevelika.',
'api-error-filename-tooshort' => 'Naziv datoteke je prekratak.',
'api-error-filetype-banned' => 'Ova vrsta datoteke je zabranjena.',
'duration-centuries' => '$1 {{PLURAL:$1|vek|veka|vekova}}',
'duration-millennia' => '$1 {{PLURAL:$1|milenijum|milenijuma|milenijuma}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|je zabranjena vrsta datoteke|su zabranjene vrste datoteka}}. {{PLURAL:$3|Dozvoljena je|Dozvoljene su}} $2.',
);
'tog-hidepatrolled' => 'Sumputkeun anu geus diroris ti béréndélan nu anyar robah',
'tog-newpageshidepatrolled' => 'Sumputkeun nu geus diroris tina béréndélan kaca anyar',
'tog-extendwatchlist' => 'Legaan béréndélan ngarah sakabéh parobahanana kaawaskeun',
-'tog-usenewrc' => 'Nu anyar robah dina wanda séjén (maké JavaScript)',
+'tog-usenewrc' => 'Parobahan grup dumasar kaca dina béréndélan anyar robah jeung awaskeuneun (maké JavaScript)',
'tog-numberheadings' => 'Nomeran lulugu sacara otomatis',
'tog-showtoolbar' => "Témbongkeun ''toolbar'' édit (JavaScript)",
'tog-editondblclick' => 'Édit kaca ku klik ganda (JavaScript)',
'tog-editsectiononrightclick' => 'Fungsikeun ngédit sub-bagean kalawan klik-katuhu dina judul bagean (JavaScript)',
'tog-showtoc' => 'Témbongkeun daptar eusi<br />(pikeun kaca nu leuwih ti tilu subjudul)',
'tog-rememberpassword' => 'Apalkeun login kuring dina ieu panyungsi (pikeun paling lila $1 {{PLURAL:$1|poé|poé}})',
-'tog-watchcreations' => 'Awaskeun kaca jieunan kuring',
-'tog-watchdefault' => 'Tambahkeun kaca nu diédit ku anjeun kana awaskeuneun anjeun',
-'tog-watchmoves' => 'Awaskeun kaca nu dipindahkeun ku kuring',
-'tog-watchdeletion' => 'Awaskeun kaca nu dihapus ku kuring',
+'tog-watchcreations' => 'Tambahkeun kaca-kaca jieunan kuring jeung berkas muatan kuring kana awaskeuneun',
+'tog-watchdefault' => 'Tambahkeun kaca jeung berkas anu diédit ku kuring kana awaskeuneun',
+'tog-watchmoves' => 'Tambahkeun kaca jeung berkas anu dipindahkeun ka awaskeuneun',
+'tog-watchdeletion' => 'Tambahkeun kaca jeung berkas anu dihapus kana awaskeuneun',
'tog-minordefault' => 'Tandaan sadaya éditan salaku minor luyu jeung ti dituna',
'tog-previewontop' => 'Témbongkeun sawangan méméh kotak édit (lain sanggeusna)',
'tog-previewonfirst' => 'Témbongkeun sawangan dina éditan munggaran',
'tog-nocache' => "Tumpurkeun ''cache'' kaca dina pangaprak",
-'tog-enotifwatchlistpages' => 'Surélékan mun robah',
+'tog-enotifwatchlistpages' => 'Lamun aya kaca atawa berkas anu diawaskeun robah, béjaan ngaliwatan surélék',
'tog-enotifusertalkpages' => 'Mun kaca obrolan kuring robah, béjaan ngaliwatan surélék',
-'tog-enotifminoredits' => 'Béjaan ogé (ngaliwatan surélék) mun aya parobahan leutik dina kacana',
+'tog-enotifminoredits' => 'Béjaan ogé (ngaliwatan surélék) lamun aya parobahan leutik dina kaca jeung berkasna',
'tog-enotifrevealaddr' => 'Témbongkeun alamat surélék kuring dina surat émbaran',
'tog-shownumberswatching' => 'Témbongkeun jumlah nu ngawaskeun',
'tog-oldsig' => 'Paraf nu geus aya:',
'exif-gpslongitude-e' => 'Gurat Wétan',
'exif-gpslongitude-w' => 'Gurat Kulon',
+# Pseudotags used for GPSAltitudeRef
+'exif-gpsaltitude-above-sealevel' => '$1 {{PLURAL:$1|méter|méter}} luhureun beungeut laut',
+'exif-gpsaltitude-below-sealevel' => '$1 {{PLURAL:$1|méter|méter}} handapeun beungeut laut',
+
+'exif-gpsstatus-a' => 'Keur ngukur',
+
'exif-gpsmeasuremode-2' => 'Ukuran 2-diménsi',
'exif-gpsmeasuremode-3' => 'Ukuran 3-diménsi',
'exif-gpsspeed-m' => 'Mil per jam',
'exif-gpsspeed-n' => 'Knot',
+# Pseudotags used for GPSDestDistanceRef
+'exif-gpsdestdistance-k' => 'Kilométer',
+'exif-gpsdestdistance-m' => 'Mil',
+'exif-gpsdestdistance-n' => 'Mil laut',
+
+'exif-gpsdop-excellent' => 'Sampurna ($1)',
+'exif-gpsdop-good' => 'Alus ($1)',
+'exif-gpsdop-moderate' => 'Moderat ($1)',
+'exif-gpsdop-fair' => 'Cukup ($1)',
+'exif-gpsdop-poor' => 'Awon ($1)',
+
+'exif-objectcycle-a' => 'Isuk-isuk wungkul',
+'exif-objectcycle-p' => 'Soré wungkul',
+'exif-objectcycle-b' => 'Isuk jeung beurang',
+
# Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
+'exif-gpsdirection-t' => 'Arah sajati',
'exif-gpsdirection-m' => 'Arah magnétik',
+'exif-ycbcrpositioning-1' => 'Nengah',
+
+'exif-dc-contributor' => 'Kontributor',
+'exif-dc-date' => 'Titimangsa',
+'exif-dc-publisher' => 'Pamedal',
+'exif-dc-relation' => 'Média anu tumali',
+'exif-dc-rights' => 'Hak',
+'exif-dc-source' => 'Média sumber',
+'exif-dc-type' => 'Jenis média',
+
+'exif-rating-rejected' => 'Ditolak',
+
+'exif-isospeedratings-overflow' => 'Leuwih ti 65535',
+
+'exif-iimcategory-ace' => 'Seni, budaya, jeung hiburan',
+'exif-iimcategory-clj' => 'Hukum jeung kajahatan',
+'exif-iimcategory-dis' => 'Bencana jeung kacilakaan',
+'exif-iimcategory-edu' => 'Atikan',
+'exif-iimcategory-evn' => 'Lingkungan',
+'exif-iimcategory-hth' => 'Kawaluyaan',
+'exif-iimcategory-pol' => 'Politik',
+'exif-iimcategory-rel' => 'Ageman jeung kayakinan',
+'exif-iimcategory-soi' => 'Isu sosial',
+'exif-iimcategory-spo' => 'Olahraga',
+'exif-iimcategory-wea' => 'Cuaca',
+
+'exif-urgency-normal' => 'Normal ($1)',
+
# External editor support
'edit-externally' => 'Édit koropak ieu migunakeun aplikasi éksternal',
'edit-externally-help' => 'Baca [//www.mediawiki.org/wiki/Manual:External_editors pituduh ngatur] pikeun émbaran leuwih jéntré.',
'revdelete-unrestricted' => 'Watesan akses kuncén dihapuskeun',
'newuserlog-byemail' => 'Sandi geus dikirim maké surélék.',
+# Feedback
+'feedback-subject' => 'Ngeunaan:',
+'feedback-message' => 'Surat:',
+'feedback-cancel' => 'Bolay',
+
# API errors
'api-error-file-too-large' => 'Berkas nu dikirim gedé teuing.',
'api-error-filename-tooshort' => 'Ngaran berkas pondok teuing.',
'api-error-illegal-filename' => 'Ngaran berkas kitu dipahing.',
'api-error-mustbeloggedin' => 'Anjeun kudu asup log pikeun ngunggahkeun berkas.',
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|detik|detik}}',
+'duration-minutes' => '$1 {{PLURAL:$1|menit|menit}}',
+'duration-hours' => '$1 {{PLURAL:$1|jam|jam}}',
+'duration-days' => '$1 {{PLURAL:$1|poé|poé}}',
+'duration-weeks' => '$1 {{PLURAL:$1|minggu|minggu}}',
+'duration-years' => '$1 {{PLURAL:$1|taun|taun}}',
+'duration-decades' => '$1 {{PLURAL:$1|dékadeu|dékadeu}}',
+'duration-centuries' => '$1 {{PLURAL:$1|abad|abad}}',
+'duration-millennia' => '$1 {{PLURAL:$1|milénium|milénium}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 kaasup tipeu koropak nu teu dicaram. {{PLURAL:$3|Nu diwidian nyaéta|Nu diwidian nyaéta}} $2.',
);
* @author Dafer45
* @author Diupwijk
* @author EPO
+ * @author Fader
* @author Fluff
* @author GameOn
* @author Greggegorius
'youhavenewmessages' => 'Du har $1 ($2).',
'newmessageslink' => 'nya meddelanden',
'newmessagesdifflink' => 'senaste ändringen',
+'youhavenewmessagesfromusers' => 'Du har $1 från {{PLURAL:$3|en annan användare|$3 användare}} ($2).',
+'youhavenewmessagesmanyusers' => 'Du har $1 från många användare ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|ett nytt meddelande|nya meddelanden}}',
+'newmessagesdifflinkplural' => 'senaste {{PLURAL:$1|ändring|ändringar}}',
'youhavenewmessagesmulti' => 'Du har nya meddelanden på $1',
'editsection' => 'redigera',
'editold' => 'redigera',
'remembermypassword' => 'Spara min inloggning på den här datorn (i max $1 {{PLURAL:$1|dygn|dygn}})',
'securelogin-stick-https' => 'Fortsätt vara ansluten till HTTPS efter inloggning',
'yourdomainname' => 'Din domän',
+'password-change-forbidden' => 'Du kan inte ändra lösenord på denna wiki.',
'externaldberror' => 'Antingen inträffade autentiseringsproblem med en extern databas, eller så får du inte uppdatera ditt externa konto.',
'login' => 'Logga in',
'nav-login-createaccount' => 'Logga in / skapa konto',
'noarticletext-nopermission' => 'Det finns för tillfället ingen text på denna sida.
Du kan [[Special:Search/{{PAGENAME}}|söka efter denna sidas titel]] i andra sidor,
eller <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} söka i relevanta loggar]</span>.',
+'missing-revision' => 'Revisionen #$1 av sidan med namnet "{{PAGENAME}}" finns inte.
+
+Detta orsakas vanligen av efter en gammal historiklänk till en sida som har raderats.
+Detaljer kan hittas i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} raderingsloggen].',
'userpage-userdoesnotexist' => '"<nowiki>$1</nowiki>" är inte ett registrerat användarkonto. Tänk efter om du vill skapa/redigera den här sidan.',
'userpage-userdoesnotexist-view' => 'Kontot "$1" är inte registrerat.',
'blocked-notice-logextract' => 'Användaren är blockerad.
'expansion-depth-exceeded-warning' => 'Sidan överskrider expansionsdjupet',
'parser-unstrip-loop-warning' => 'Tagavskalningsloop upptäcktes',
'parser-unstrip-recursion-limit' => 'Tagavskalningsloop överskred rekursionsgränsen ($1)',
+'converter-manual-rule-error' => 'Fel upptäcktes i manuell språkkonverteringsregel',
# "Undo" feature
'undo-success' => 'Redigeringen kan göras ogjord.
'right-writeapi' => 'Använda skriv-API:t',
'right-delete' => 'Radera sidor',
'right-bigdelete' => 'Radera sidor med stor historik',
+'right-deletelogentry' => 'Radera och återställ specifika loggposter',
'right-deleterevision' => 'Radera och återställa enskilda sidversioner',
'right-deletedhistory' => 'Se raderad historik utan tillhörande sidtext',
'right-deletedtext' => 'Visa raderad text och ändringar mellan raderade versioner',
'lockmanager-fail-releaselock' => 'Kunde inte att frigöra låset för "$1".',
'lockmanager-fail-db-bucket' => 'Kunde inte kontakta tillräckligt många låsdatabaser i hinken $1.',
'lockmanager-fail-db-release' => 'Kunde inte frigöra låsen på databasen $1 .',
+'lockmanager-fail-svr-acquire' => 'Kunde inte erhålla lås på servern $1 .',
'lockmanager-fail-svr-release' => 'Kunde inte frigöra låsen på servern $1.',
# ZipDirectoryReader
'rollback' => 'Rulla tillbaka ändringar',
'rollback_short' => 'Återställning',
'rollbacklink' => 'rulla tillbaka',
+'rollbacklinkcount' => 'rulla tillbaka $1 {{PLURAL:$1|redigering|redigeringar}}',
+'rollbacklinkcount-morethan' => 'rulla tillbaka mer än $1 {{PLURAL:$1|redigering|redigeringar}}',
'rollbackfailed' => 'Tillbakarullning misslyckades',
'cantrollback' => 'Det gick inte att rulla tillbaka, då sidan endast redigerats av en användare.',
'alreadyrolled' => 'Det gick inte att rulla tillbaka den senaste redigeringen av [[User:$2|$2]] ([[User talk:$2|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) på sidan [[:$1|$1]]. Någon annan har redan rullat tillbaka eller redigerat sidan.
* <span class="mw-specialpagecached">Cachade specialsidor (kan vara föråldrade).</span>',
'specialpages-group-maintenance' => 'Underhållsrapporter',
'specialpages-group-other' => 'Övriga specialsidor',
-'specialpages-group-login' => 'Inloggning/registrering',
+'specialpages-group-login' => 'Logga in / skapa konto',
'specialpages-group-changes' => 'Senaste ändringar och loggar',
'specialpages-group-media' => 'Filer och uppladdning',
'specialpages-group-users' => 'Användare och behörigheter',
'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennier}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Kunde inte erhålla lås på servern $1 .',
+'api-error-filetype-banned-type' => '$1 är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}. {{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.',
);
'tog-editsectiononrightclick' => 'Wezesha sehemu ya kuandikia kwa kubonyeza kitufe cha kulia cha puku yako juu ya sehemu ya majina husika (JavaScript)',
'tog-showtoc' => 'Onyesha mistari ya yaliyomo (kwa kila kurasa iliyo na zaidi ya vichwa vya habari 3)',
'tog-rememberpassword' => 'Kumbuka kuingia kwangu pamoja na neno la siri katika kivinjari hiki (kwa muda usiozidi {{PLURAL:$1|siku}} $1)',
-'tog-watchcreations' => 'Weka kurasa nilizoumba katika maangalizi yangu',
-'tog-watchdefault' => 'Weka kurasa zote nilizohariri katika maangalizi yangu',
-'tog-watchmoves' => 'Weka kurasa zote nilizohamisha katika maangalizi yangu',
-'tog-watchdeletion' => 'Weka kurasa zote nilizofuta katika maangalizi yangu',
+'tog-watchcreations' => 'Ongeza kurasa nilizoumba katika maangalizi yangu',
+'tog-watchdefault' => 'Ongeza kurasa zote nilizohariri katika maangalizi yangu',
+'tog-watchmoves' => 'Ongeza kurasa zote nilizohamisha katika maangalizi yangu',
+'tog-watchdeletion' => 'Ongeza kurasa zote nilizofuta katika maangalizi yangu',
'tog-minordefault' => 'Weka alama zote za mabadiliko madogo kama matumizi mbadala',
'tog-previewontop' => 'Onyesha mandhari kabla ya sanduku la kuhariria',
'tog-previewonfirst' => 'Onyesha mandhari unapoanza kuhariri',
'tog-nocache' => 'Kurasa zisiwekwe katika kache (akiba ya muda) ya kivinjari',
-'tog-enotifwatchlistpages' => 'Nitumie barua pepe pale kurasa zilizopo katika maangalizi yangu zikibadilishwa',
+'tog-enotifwatchlistpages' => 'Nitumie barua pepe pale kurasa zilizopo katika maangalizi yangu zinabadilishwa',
'tog-enotifusertalkpages' => 'Nitumie barua pepe pale ukurasa wangu wa majadiliano ukiwa na mabadiliko',
-'tog-enotifminoredits' => 'Pia nitumie barua pale mabadiliko ya ukurasa yanapokuwa madogo tu',
+'tog-enotifminoredits' => 'Pia nitumie barua pale mabadiliko ya ukurasa yanapokuwa madogo tu.',
'tog-enotifrevealaddr' => 'Onyesha anwani ya barua pepe yangu katika barua pepe za taarifa',
'tog-shownumberswatching' => 'Onyesha idadi ya watumiaji waangalizi',
'tog-oldsig' => 'Sahihi iliyopo:',
'youhavenewmessages' => 'Una $1 ($2).',
'newmessageslink' => 'ujumbe mpya',
'newmessagesdifflink' => 'badiliko la mwisho',
+'youhavenewmessagesfromusers' => 'Una $1 kutoka {{PLURAL:$3|another user|$3 users}} ($2)',
+'youhavenewmessagesmanyusers' => 'Una $1 kutoka kwa watumiaji wengi $2',
+'newmessageslinkplural' => '{{PLURAL:$1|a new message|ujumbe mpya}}',
+'newmessagesdifflinkplural' => 'last {{PLURAL:$1|change|mabadiliko}}',
'youhavenewmessagesmulti' => 'Umepokea jumbe mpya kule $1',
'editsection' => 'hariri',
'editold' => 'hariri',
'ns-specialprotected' => 'Kurasa maalumu haziwezi kuhaririwa.',
'titleprotected' => 'Jina hili limekingwa lisiumbwe na [[User:$1|$1]].
Sababu zilizotolewa ni "\'\'$2\'\'".',
+'exception-nologin' => 'Hujaingia',
+'exception-nologin-text' => 'Ukurasa huu unahitaji kuwa mtumiaji awe ameingia katika wiki hii.',
# Virus scanner
'virus-badscanner' => "Usanidi mbaya: kiskani virusi hakijulikani: ''$1''",
'remembermypassword' => 'Kumbuka kuingia kwangu katika kivinjari hiki (kwa muda usiozidi {{PLURAL:$1|siku}} $1)',
'securelogin-stick-https' => 'Endelea kuunganishwa na HTTPS baada ya kuingia',
'yourdomainname' => 'Tovuti yako:',
+'password-change-forbidden' => 'Hauwezi kubadili nywila katika Wiki hii.',
'externaldberror' => 'Huenda kulikuwa na hitilafu ya database au labda hauruhusiwi kubadilisha akaunti yako ya nje.',
'login' => 'Ingia',
'nav-login-createaccount' => 'Ingia/ sajili akaunti',
'note' => "'''Taarifa:'''",
'previewnote' => "'''Hii ni hakikisho tu.'''
Mabadiliko hayajahifadhiwa bado!",
+'continue-editing' => 'Endelea kuhariri',
'previewconflict' => 'Hakikisho hii inaonyesha maandiko yaliyopo sanduku la juu yataonekayo ukiyahifadhi.',
'session_fail_preview' => "'''Pole! Hatukuweza kuhifadhi sahihisho lako kwa sababu data za kipindi zilipotelewa.'''
Tafadhali jaribu tena.
'edit-no-change' => 'Uhariri wako haukufanikiwa, kwa sababu hapakuwa na mabadiliko yoyote kwenye maandishi.',
'edit-already-exists' => 'Haikufanikiwa kuanzisha ukurasa mpya.
Ukurasa wa jina hilo unapatikana tayari.',
+'defaultmessagetext' => 'Ujumbe uliopo',
# Parser/template warnings
'post-expand-template-inclusion-warning' => "'''Ilani:''' Kigezo kinajumlisha ukubwa uliozidi mno.
Unaweza kulitazama; maelezo mengine yapo kwenye [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kumbukumbu ya ufutaji].",
'rev-suppressed-text-view' => "Pitio la ukurasa huu '''limefichwa'''.
Unaweza kulitazama; maelezo mengine yapo kwenye [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} kumbukumbu ya ufichaji].",
+'rev-deleted-unhide-diff' => "Pitio la ukurasa huu '''limefutwa'''.
+Maelezo yanaweza kupatikana kwenye [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kumbukumbu ya ufutaji].
+Bado unaweza [$1 kutazama sahihisho hili] iwapo utapenda kuendelea.",
+'rev-suppressed-unhide-diff' => "Pitio la ukurasa huu '''limefutwa'''.
+Maelezo yanaweza kupatikana kwenye [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kumbukumbu ya ufutaji].
+Bado unaweza [$1 kutazama sahihisho hili] iwapo utapenda kuendelea.",
+'rev-deleted-diff-view' => "Pitio hilo la ukurasa huu '''limefutwa'''.
+Unaweza masasisho; maelezo mengine yapo kwenye [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} kumbukumbu ya ufutaji]",
'rev-delundel' => 'onyesha/ficha',
'rev-showdeleted' => 'onyesha',
'revisiondelete' => 'Kufuta/kurudisha mapitio',
# Diffs
'history-title' => 'Historia ya mapitio ya "$1"',
+'difference-title' => 'Tofauti kati ya marekesbisho "$1"',
+'difference-title-multipage' => 'Tofauti kati ya kurasa "$1" na "$2"',
'difference-multipage' => '(Tofauti kati ya kurasa)',
'lineno' => 'Mstari $1:',
'compareselectedversions' => 'Linganisha mapitio mawili uliyochagua',
'datedefault' => 'Chaguo-msingi',
'prefs-beta' => 'Zana za Beta',
'prefs-datetime' => 'Tarehe na saa',
+'prefs-labs' => 'Maumbile ya maabara',
'prefs-user-pages' => 'Kurasa za watumiaji',
'prefs-personal' => 'Kuhusu mtumiaji',
'prefs-rc' => 'Mabadiliko ya karibuni',
'action-userrights' => 'kubadilisha wezo zote za watumiaji',
'action-userrights-interwiki' => 'kuhariri wezo za watumiaji kwenye wiki zingine',
'action-siteadmin' => 'kufunga na kufungua hifadhidata',
+'action-sendemail' => 'tuma barua pepe',
# Recent changes
'nchanges' => '{{PLURAL:$1|badiliko|mabadiliko}} $1',
'newsectionsummary' => '/* $1 */ mjadala mpya',
'rc-enhanced-expand' => 'Onyesha maelezo mengine (inahitaji JavaScript)',
'rc-enhanced-hide' => 'Ficha maelezo mengine',
+'rc-old-title' => 'ilitengenezwa hapo awali na "$1"',
# Recent changes linked
'recentchangeslinked' => 'Mabadiliko husika',
'upload-http-error' => 'Imetokea hitilafu ya HTTP: $1',
# File backend
+'backend-fail-stream' => 'Haikuweza kutafuta faili "$1".',
+'backend-fail-notexists' => 'faili $1 haipo',
+'backend-fail-delete' => 'Haikuweza kufuta faili "$1".',
'backend-fail-alreadyexists' => 'Faili $1 linapatikana tayari.',
+'backend-fail-store' => 'Haikuweza kunakili faili "$1" kwa "$2".',
+'backend-fail-copy' => 'Haikuweza kunakili faili "$1" kwa "$2".',
+'backend-fail-move' => 'Haikuweza kuhamisha faili "$1" kwa "$2".',
+'backend-fail-opentemp' => 'Haikuweza kuanzisha faili ya muda.',
+'backend-fail-writetemp' => 'Haikuweza kuandika kwa faili ya muda.',
+'backend-fail-closetemp' => 'Haikuweza kufunga faili ya muda.',
+'backend-fail-read' => 'Haikuweza kusoma faili "$1".',
+'backend-fail-create' => 'Haikuweza kuandika faili "$1".',
# ZipDirectoryReader
'zip-file-open-error' => 'Ilitokea hitilafu wakati wa kufungua faili kwa ajili ya ukaguzi wa ZIP.',
'disambiguations' => 'Kurasa zinazoungana na kurasa za uanishaji',
'disambiguationspage' => 'Template:Maana',
-'disambiguations-text' => "Kurasa zinazofuata zina viungo vinavyoelekea '''kurasa ya kutofautishana maana'''.
-Ni afadhali kiungo kiende makala inayostahili moja kwa moja.<br />
-Kurasa za kutofautishana maana ni zile zinazotumia kigezo kinachoorodheshwa katika ukurasa wa [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Kurasa zinazofuata zina angalau kiungo kimoja kinachoelekea kwa '''kurasa ya kutofautishana maana'''.
+Ni afadhali kiungo kiende makala yanayostahili moja kwa moja.<br />
+Ukurasa unatibiwa kama ukurasa wa kutofautishana maana inazotumia kigezo kinachoorodheshwa katika ukurasa wa [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Maelekezo mawilimawili',
'doubleredirectstext' => 'Ukurasa huu unaorodhesha kurasa zinazoelekeza kurasa zingine za kuelekeza.
'mostimages' => 'Mafaili yanayoungwa kuliko yote',
'mostrevisions' => 'Kurasa zenye mapitio mengi kuliko zote',
'prefixindex' => 'Kurasa zote zenye viambishi awali',
+'prefixindex-namespace' => 'Kurasa zote zenye kiambishi awali ($1)',
'shortpages' => 'Kurasa fupi',
'longpages' => 'Kurasa ndefu',
'deadendpages' => 'Kurasa ambazo haziungi na ukurasa mwingine wowote',
'allpages-bad-ns' => 'Eneo la "$1" halipatikani kwenye {{SITENAME}}.',
'allpages-hide-redirects' => 'Ficha kurasa za kuelekeza',
+# SpecialCachedPage
+'cachedspecial-refresh-now' => 'Tazama ya hivi karibuni.',
+
# Special:Categories
'categories' => 'Jamii',
'categoriespagetext' => 'Jamii {{PLURAL:$1|inayofuata ina|zinazofuata zina}} kurasa au mafaili ya picha au sauti.
'emailsenttext' => 'Barua pepe yako imetumwa.',
'emailuserfooter' => 'Barua pepe hii imetumwa na $1 kwa $2 kwa kutumia zana ya "Kumtumia mtumiaji barua pepe" iliyopo {{SITENAME}}.',
+# User Messenger
+'usermessage-summary' => 'Inawacha ujumbe wa mfumo.',
+'usermessage-editor' => 'Jumbe za mfumo',
+
# Watchlist
'watchlist' => 'Maangalizi yangu',
'mywatchlist' => 'Maangalizi yangu',
'wlheader-enotif' => '* Huduma ya kuarifu kwa barua pepe imewezeshwa.',
'wlheader-showupdated' => "* Kurasa zilizobadilika tangu ulivyotembelea mara ya mwisho zinaonyeshwa katika hali ya '''kukooza'''",
'watchmethod-recent' => 'kupitia madabiliko ya karibuni ili kupata kurasa za maangalizi',
-'watchmethod-list' => 'kupitia kurasa za maangalizi ili kupata madabiliko ya karibuni',
-'watchlistcontains' => 'Orodha ya maangalizi yako ina {{PLURAL:$1|kitu|vitu}} $1.',
-'iteminvalidname' => "Kitu '$1' kina tatizo la jina batili...",
-'wlnote' => "{{PLURAL:$1|Badiliko la|Mabadiliko '''$1''' ya}} mwisho katika {{PLURAL:$2|saa iliyopita linaonyeshwa|masaa '''$2''' yaliyopita yanaonyeshwa}} chini, ilivyokuwa saa $4, tarehe $3.",
+'watchmethod-list' => 'Kupitia kurasa za maangalizi ili kupata madabiliko ya karibuni',
+'watchlistcontains' => 'Orodha ya maangalizi yako ina {{PLURAL:$1|pages|kurasa}}.',
+'iteminvalidname' => "Shida na kitu '$1' , jina batili...",
+'wlnote' => "{{PLURAL:$1|is the last change|Mabadiliko '''$1''' ya}} mwisho katika {{PLURAL:$2|hour|masaa '''$2''' yaliyopita yanaonyeshwa}} chini, ilivyokuwa saa $4, tarehe $3.",
'wlshowlast' => 'Onyesha kutoka masaa $1 siku $2 $3',
'watchlist-options' => 'Hitiari za maangalizi',
# Displayed when you click the "watch" button and it is in the process of watching
'watching' => 'Unafuatilia...',
'unwatching' => 'Umeacha kufuatilia...',
-'watcherrortext' => 'Hitilafu ilitokea ulipojaribu kubadilisha vipimo vya maangalizi yako kwa ajili ya "$1".',
+'watcherrortext' => 'Hitilafu ilitokea ulipojaribu kubadilisha mpangilio wa maangalizi yako ya "$1".',
'enotif_mailer' => 'Huduma ya taarifa ya barua pepe kutoka kwa {{SITENAME}}',
-'enotif_reset' => 'Weka alama ya kutembelewa kwenye kurasa zote',
+'enotif_reset' => 'Weka alama kwa kurasa zote zilizotembelewa',
'enotif_newpagetext' => 'Ukurasa huu ni mpya.',
'enotif_impersonal_salutation' => 'Kwa mtumiaji wa {{SITENAME}}',
'changed' => 'alibadilisha',
'created' => 'alianzisha',
'enotif_subject' => '$PAGEEDITOR $CHANGEDORCREATED ukurasa wa $PAGETITLE kwenye {{SITENAME}}',
-'enotif_lastvisited' => 'Tazama mabadiliko yote tangu ziara yako iliyopita kwenye ukurasa wa $1.',
+'enotif_lastvisited' => 'Tazama $1 kwa mabadiliko yote tangu ziara yako ya mwisho.',
'enotif_lastdiff' => 'Tazama badiliko hili hapo $1.',
'enotif_anon_editor' => 'mtumiaji bila jina $1',
'enotif_body' => 'Mpendwa $WATCHINGUSERNAME,
'deletepage' => 'Futa ukurasa',
'confirm' => 'Yakinisha',
'excontent' => "iliyokuwemo: '$1'",
-'excontentauthor' => 'yaliyomo yalikuwa: "$1" (yaliyechangiwa na mchangiaji mmoja tu anayeitwa "[[Special:Contributions/$2|$2]]")',
+'excontentauthor' => 'Yaliyomo yalikuwa: "$1" (na mchangiaji mmoja tu anayeitwa "[[Special:Contributions/$2|$2]]")',
'exbeforeblank' => 'maandishi kabla hayajafutwa yote yalikuwa: "$1"',
'exblank' => 'ukurasa ulikuwa tupu',
'delete-confirm' => 'Futa "$1"',
'actionfailed' => 'Tendo halikufaulu',
'deletedtext' => '"$1" imefutwa. Ona $2 kwa historia ya kurasa zilizofutwa hivi karibuni.',
'dellogpage' => 'Kumbukumbu ya ufutaji',
-'dellogpagetext' => 'Kurasa na mafaili zilizofutwa hivi karibuni zinaorodheshwa chini.',
+'dellogpagetext' => 'Hapa chini ni orodha ya mafaili yaliyofutwa hivi karibuni.',
'deletionlog' => 'kumbukumbu za kufuta',
'reverted' => 'Ilirejeshwa hadi pitio la zamani',
'deletecomment' => 'Sababu:',
'rollback-success' => 'Masahihisho aliyeyafanya $1 yalirejeshwa hadi kufika sahihisho la mwisho aliyefanya $2.',
# Edit tokens
-'sessionfailure-title' => 'Kushindikana cha kipindi',
+'sessionfailure-title' => 'Kushindikana kwa kipindi',
# Protect
'protectlogpage' => 'Kumbukumbu ya ulindaji',
'protectedarticle' => 'aliulinda "[[$1]]"',
'modifiedarticleprotection' => 'alibadilisha kiwango cha ulindaji kwa ajili ya "[[$1]]"',
-'unprotectedarticle' => 'alitoa ulindaji wa "[[$1]]"',
-'movedarticleprotection' => 'alihamisha ulindaji wa "[[$2]]" hadi "[[$1]]"',
+'unprotectedarticle' => 'ulindaji ulitolewa kutoka kwa "[[$1]]"',
+'movedarticleprotection' => 'mpangilio wa ulindaji wa kuhamishwa kutoka "[[$2]]" hadi "[[$1]]"',
'protect-title' => 'Kubadilisha kiwango cha ulindaji wa "$1"',
-'protect-title-notallowed' => 'Tazama kiwango cha ulindaji wa "$1"',
+'protect-title-notallowed' => 'Tazama kiwango cha ulindaji cha "$1"',
'prot_1movedto2' => 'alihamisha [[$1]] hadi [[$2]]',
'protect-badnamespace-title' => 'Eneo la wiki lisiloweza kulindwa',
'protect-badnamespace-text' => 'Kurasa zilizopo katika eneo hili la wiki haziwezi kulindwa',
'protectexpiry' => 'Itakwisha:',
'protect_expiry_invalid' => 'Muda wa kwisha ni batilifu.',
'protect_expiry_old' => 'Muda wa kuishi umepita tayari.',
-'protect-unchain-permissions' => 'Fungua chaguzi zingine za ulinzi',
+'protect-unchain-permissions' => 'Fungua chaguzi zingine za ulindaji',
'protect-text' => "Unaweza kutazama na kubadilisha kiwango cha ulindaji hapa kwa ukurasa '''$1'''.",
'protect-locked-dblock' => "Viwango vya ulindaji haviwezi kubadilishwa kwa sababu hifadhidata imefungwa.
Hapo panaandikwa viwango vya ulindaji wa ukurasa '''$1''':",
'ipb-unblock-addr' => 'Acha kumzuia $1',
'ipb-unblock' => 'Acha kumzuia mtumiaji au anwani wa IP',
'ipb-blocklist-contribs' => 'Michango ya $1',
+'unblockip' => 'Acha kuzuia mtumiaji',
'blocklist' => 'Watumiaji waliozuiliwa',
'ipblocklist' => 'Watumiaji waliozuiliwa',
+'ipblocklist-legend' => 'Tafuta mtumiaji aliyezuiwa',
'blocklist-timestamp' => 'Tarehe na saa',
+'blocklist-target' => 'Lengo',
'blocklist-expiry' => 'Itakwisha',
'blocklist-reason' => 'Sababu',
'ipblocklist-submit' => 'Tafuta',
'infiniteblock' => 'milele',
'expiringblock' => 'inakwisha tarehe $1 saa $2',
'emailblock' => 'barua pepe imezuiliwa',
+'ipblocklist-empty' => 'Orodha ya kuzuiwa ni tupu.',
'blocklink' => 'zuia',
'unblocklink' => 'acha kuzuia',
'change-blocklink' => 'badilisha zuia',
'contribslink' => 'michango',
+'emaillink' => 'tuma barua pepe',
'blocklogpage' => 'Kumbukumbu ya uzuio',
'blocklogentry' => 'amemzuia [[$1]] mpaka $2 $3',
'unblocklogentry' => 'aliachisha kuzuia $1',
'proxyblocksuccess' => 'Tayari.',
# Developer tools
-'lockdb' => 'Kufunga hifadhidata',
-'unlockdb' => 'Kufungua hifadhidata',
-'lockconfirm' => 'Ndiyo, kwa kweli nataka kufunga hifadhidata.',
-'unlockconfirm' => 'Ndiyo, kwa kweli nataka kufungua hifadhidata.',
+'lockdb' => 'Funga hifadhidata',
+'unlockdb' => 'Fungua hifadhidata',
+'lockconfirm' => 'Ndiyo, ni kweli nataka kufunga hifadhidata.',
+'unlockconfirm' => 'Ndiyo,nataka kufungua hifadhidata.',
'lockbtn' => 'Funga hifadhidata',
'unlockbtn' => 'Fungua hifadhidata',
'locknoconfirm' => 'Hujaweka alama katika sanduku la kuitika kitendo.',
'exif-lightsource' => 'Mwanga',
'exif-flash' => 'Taa ya picha',
'exif-flashenergy' => 'Nguvu ya taa ya picha',
+'exif-filesource' => 'Chanzo cha faili',
'exif-gpslatituderef' => 'Latitudo kwenda kaskazini au kusini',
'exif-gpslatitude' => 'Latitudo',
'exif-gpslongituderef' => 'Longitudo kwenda mashariki au magharibi',
'exif-gpslongitude' => 'Longitudo',
'exif-gpsaltituderef' => 'Rejeo ya mwinuko',
'exif-gpsaltitude' => 'Mwinuko',
+'exif-gpsmeasuremode' => 'Jinsi ya kupima',
'exif-gpsspeedref' => 'Kizio cha kupima mwendo',
'exif-gpsspeed' => 'Mwendo wa sanduku la GPS',
+'exif-gpstrack' => 'Mwelekeo wa harakati',
+'exif-gpsimgdirection' => 'Mwelekeo wa picha',
'exif-gpsdestlatitude' => 'Latitudo ya kikomo',
'exif-gpsdestlongitude' => 'Longitudo ya kikomo',
'exif-gpsdestdistance' => 'Mbali wa kikomo',
'exif-countrycreated' => 'Nchi palipopigwa picha',
'exif-countrycodecreated' => 'Kodi ya nchi picha palipopigwa',
'exif-countrydest' => 'Nchi inayoonyeshwa',
+'exif-citydest' => 'Mji umeonyeshwa',
'exif-objectname' => 'Jina fupi',
'exif-specialinstructions' => 'Maelekezo maalum',
'exif-headline' => 'Kichwa cha habari',
'exif-languagecode' => 'Lugha',
'exif-iimcategory' => 'Jamii',
'exif-datetimeexpires' => 'Usitumie baada ya',
+'exif-datetimereleased' => 'Ilitolewa mnamo',
'exif-cameraownername' => 'Mwenye kamera',
+'exif-label' => 'Lebo',
'exif-copyrighted' => 'Hali ya hakimiliki',
'exif-copyrightowner' => 'Mwenye hatimiliki',
'exif-pngfilecomment' => 'Maoni juu ya faili la PNG',
+'exif-disclaimer' => 'Kanusho',
+'exif-contentwarning' => 'Ilani ya maduhui',
'exif-giffilecomment' => 'Maoni juu ya faili la GIF',
+'exif-intellectualgenre' => 'Aina ya kifaa',
'exif-personinimage' => 'Mtu aliyepigwa picha',
+'exif-copyrighted-true' => 'Yenye hatimiliki',
+
'exif-unknowndate' => 'Tarehe haijulikani',
'exif-orientation-1' => 'Kawaida',
'exif-componentsconfiguration-0' => 'haipo',
+'exif-exposureprogram-0' => 'Haijafafanuliwa',
'exif-exposureprogram-1' => 'Kwa mikono',
'exif-exposureprogram-2' => 'Programu ya kawaida',
'exif-focalplaneresolutionunit-2' => 'inchi',
+'exif-customrendered-0' => 'Mchakato wa kawaida',
+
'exif-scenecapturetype-0' => 'Kawaida',
'exif-scenecapturetype-1' => 'Mandhari',
'exif-scenecapturetype-2' => 'Watu',
# Pseudotags used for GPSDestDistanceRef
'exif-gpsdestdistance-k' => 'Kilomita',
'exif-gpsdestdistance-m' => 'Maili',
-'exif-gpsdestdistance-n' => 'Maili ya baharia',
+'exif-gpsdestdistance-n' => 'Maili ya bahari',
'exif-gpsdop-excellent' => 'Nzuri sana ($1)',
'exif-gpsdop-good' => 'Nzuri ($1)',
'exif-dc-date' => 'Tarehe',
'exif-dc-publisher' => 'Mchapishaji',
'exif-dc-rights' => 'Haki',
+'exif-dc-source' => 'Chanzo cha media',
+'exif-dc-type' => 'Aina ya media',
-'exif-rating-rejected' => 'Lilikataliwa',
+'exif-rating-rejected' => 'Ilikataliwa',
-'exif-isospeedratings-overflow' => 'Zaidi na 65535',
+'exif-isospeedratings-overflow' => 'Zaidi ya 65535',
'exif-iimcategory-ace' => 'Sanaa, utamaduni na burudani',
'exif-iimcategory-clj' => 'Uhalifu na sheria',
'exif-iimcategory-pol' => 'Siasa',
'exif-iimcategory-rel' => 'Dini na imani',
'exif-iimcategory-sci' => 'Sayansi na teknolojia',
+'exif-iimcategory-soi' => 'Masuala ya kijamii',
'exif-iimcategory-spo' => 'Michezo',
'exif-iimcategory-war' => 'Vita, migogoro na vurugu',
'exif-iimcategory-wea' => 'Hali ya hewa',
# Special:Version
'version' => 'Toleo',
'version-specialpages' => 'Kurasa maalum',
+'version-variables' => 'Vibadili',
'version-skins' => 'Maumbo',
'version-other' => 'Zingine',
'version-version' => '(Toleo $1)',
'version-software' => 'Bidhaa pepe iliyosakinishwa',
'version-software-product' => 'Bidhaa',
'version-software-version' => 'Toleo',
+'version-entrypoints-header-url' => 'KISARA Kioneshi Sanifu Raslimali',
# Special:FilePath
'filepath' => 'Njia ya faili',
'logentry-suppress-revision' => '$1 alibadilisha kwa siri hali ya kuonekana wazi {{PLURAL:$5|kumbukumbu $5 ya|kumbukumbu $5 za}} ukurasa wa $3: $4',
'logentry-suppress-event-legacy' => '$1 alibadilisha kwa siri hali ya kuonekana wazi ya kumbukumbu za $3',
'logentry-suppress-revision-legacy' => '$1 alibadilisha kwa siri hali ya kuonekana wazi mapitio ya ukurasa $3',
+'revdelete-content-hid' => 'maudhui yamefichwa',
+'revdelete-summary-hid' => 'historia ya kuhariri imefichwa',
+'revdelete-uname-hid' => 'jina la mtumiaji limefichwa',
+'revdelete-content-unhid' => 'maudhui hayajafichwa',
+'revdelete-summary-unhid' => 'muhtasari wa kuhariri haujafichwa',
+'revdelete-uname-unhid' => 'jina la mtumiaji halijafichwa',
'revdelete-restricted' => 'aliwazuia pia wakabidhi wasiyaone maelezo',
'revdelete-unrestricted' => 'aliwarudishia wakabidhi uwezo wa kuona maelezo',
'logentry-move-move' => '$1 alihamisha ukurasa wa $3 hadi $4',
'feedback-close' => 'Tayari',
# API errors
+'api-error-empty-file' => 'Faili ulilowasilisha ni tupu.',
+'api-error-emptypage' => 'Kutengeneza mpya, kurasa tupu hazikubaliwi',
+'api-error-filename-tooshort' => 'Jina la faili ni fupi mno.',
+'api-error-filetype-banned' => 'Aina hili la faili hairuhusiwi.',
+'api-error-illegal-filename' => 'Jina hilo la faili haliruhusiwi.',
'api-error-unclassified' => 'Ilitokea hitilafu isiyojulikana.',
'api-error-unknown-code' => 'Hitilafu isiyojulikana: "$1".',
'api-error-unknown-warning' => 'Ilani isiyojulikana: "$1".',
'api-error-unknownerror' => 'Hitilafu isiyojulikana: "$1".',
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|second|sekunde}}',
+'duration-minutes' => '$1 {{PLURAL:$1|minute|dakikas}}',
+'duration-hours' => '$1 {{PLURAL:$1|hour|masaa}}',
+'duration-days' => '$1 {{PLURAL:$1|day|masiku}}',
+'duration-weeks' => '$1 {{PLURAL:$1|week|wiki}}',
+'duration-years' => '$1 {{PLURAL:$1|year|miaka}}',
+'duration-centuries' => '$1 {{PLURAL:$1|century|karne}}',
+
);
'viewtalkpage' => 'Zajta godki',
'otherlanguages' => 'We inkszych godkach',
'redirectedfrom' => '(Punkńyńto s $1)',
-'redirectpagesub' => 'Zajta překerowujůnco',
+'redirectpagesub' => 'Zajta przekerowujůnco',
'lastmodifiedat' => 'Ta zajta bůła uostatńo sprowjano $2, $1.',
'viewcount' => 'W ta zajta filowano {{PLURAL:$1|tylko roz|$1 rozůw}}.',
'protectedpage' => 'Zajta zawarto',
'undo-success' => 'Sprowjyńy zostouo wycůfane. Proša pomjarkować ukozane půnižyj dyferencyje mjyndzy wersyjami, coby zweryfikować jejich poprawność, potym zaś naškryflać pomjyńańo coby zakońčyć uoperacyjo.',
'undo-failure' => 'Sprowjyńo ńy idźe wycofać skuli kůnflikta ze wersyjůma postřednimi.',
'undo-norev' => 'Sprowjyńo ńy idźe cofnůńć skuli tego, co ńy istńije abo zostouo wyćepane.',
-'undo-summary' => 'Wycůfańy wersyji $1 naškryflanej bez [[Special:Contributions/$2|$2]] ([[User talk:$2|godka]])',
+'undo-summary' => 'Wycůfańy wersyji $1 naszkryflanej bez [[Special:Contributions/$2|$2]] ([[User talk:$2|godka]])',
# Account creation failure
'cantcreateaccounttitle' => 'Ńy idźe utwořić kůnta',
'last' => 'poprz.',
'page_first' => 'počůnek',
'page_last' => 'kůńec',
-'histlegend' => 'Wybůr růžńic do porůwnańo: postow kropki we boksach a naćiś enter abo knefel na dole.<br />
-Legynda: (bjež.) - růžńice s wersyjům bježůncům, (popř.) - růžńice s wersyjům popředzajůncům, d - drobne zmjany',
+'histlegend' => 'Wybůr růżńic do porůwnańo: postow kropki we boksach a naćiś enter abo knefel na dole.<br />
+Legynda: (akt.) - růżńice s wersyjům bjeżůncům, (poprz.) - růżńice s wersyjům poprzedzajůncům, d - drobne zmjany',
'history-fieldset-title' => 'Přeglůndej historyjo',
'history-show-deleted' => 'Jyno wyćepane',
'histfirst' => 'uod počůnku',
'right-createaccount' => 'Utwořůne nowe kůnta užytkowńikůw',
'right-minoredit' => 'Uoznoč půmjyńańo kej drobne',
'right-move' => 'Přećepane zajty',
-'right-move-subpages' => 'Přećep zajty wroz s jejich podzajtůma',
+'right-move-subpages' => 'Przećep zajty wroz s jejich podzajtůma',
'right-move-rootuserpages' => 'Překludzańy zajtůw uod užytkowńikůw',
'right-movefile' => 'Przećepańe plikůw',
'right-suppressredirect' => 'Ńy twůrz przekerowańo ze starygo mjana jak przećepujesz zajta',
# Move page
'move-page' => 'Przećep $1',
'move-page-legend' => 'Přećiś artikel',
-'movepagetext' => "Při půmocy formulařa půńižej možeš půmjyńyć nazwa zajty i přećepnůńć jei historja. Pod downym titlym uostańe zajta překerowujůnca. Zajty adresowane na stary titel uostanům jak bůuy.
+'movepagetext' => "Przi půmocy formularza půńiżej możesz půmjyńyć mjano zajty i przećepnůńć jej gyszichta. Pod downym mjanym uostańe śa zajta przekerowujůnca. Zajty adresowane na stare mjano uostanům jak bůły.
-Jak śe na to decyduješ, sprowdź, eli ńy je to [[Special:DoubleRedirects|podwůjne]] abo [[Special:BrokenRedirects|zuomane překerowańy]].
-Uodpowjadoš za to, coby linki wjoduy ku prawiduowym artiklům!
+Jak śe na to decydujesz, sprowdź, eli ńy je to [[Special:DoubleRedirects|podwůjne]] abo [[Special:BrokenRedirects|złomane przekerowańy]].
+Uodpowjadosz za to, coby linki wjodły ku prawym artiklům!
-Zajta '''ńy''' bydźe přećepano, jak:
-*je pusto i ńy bůua sprowjano
-*je zajtům překerowujůncą
-*zajta uo takym titlu juž sam jest
+Zajta '''ńy''' bydźe przećepano, jak:
+*je pusto i ńy bůła sprowjano
+*je zajtům przekerowujůncą
+*zajta uo takym mjane już sam je
'''DEJ POZŮR!'''
-To može byÄ\87 drastyÄ\8dno abo i Å\84ypÅ\99ewidywalno zmjano, jak pÅ\99eÄ\87epÅ\84yÅ¡ jako popularno zajta. Bydź pewny, aže wjeÅ¡ co robiyÅ¡, Å\84im klikÅ\84yÅ¡ knefel \"pÅ\99ećep\"!",
+To może byÄ\87 drastyczno abo Å\84yprzewidywalno zmjano, jak przeÄ\87epÅ\84ysz jako popularno zajta. Bydź pewny, aże wjesz co robiysz, Å\84im klikÅ\84ysz knefel \"przećep\"!",
'movepagetalktext' => 'Uodpowiednio zajta godki, jeśli jest, bydzie přećepano automatyčńe, pod warůnkiem, že:
*ńy přećepuješ zajty do inkšy přestřeńy mjan
*ńy ma sam zajty godki o takiym mjańe
* @file
*
* @author Aswn
+ * @author Balajijagadesh
+ * @author Caliberoviv
* @author Kaganer
* @author Kanags
* @author Karthi.dr
'tog-editsectiononrightclick' => 'பிரிவுத் தலைப்பின் மீது வலச் சொடுக்குவதன் மூலம் பகுதித் தொகுப்பை செயலாக்கவும் (ஜாவாஸ்கிரிப்ட் தேவை )',
'tog-showtoc' => 'பொருளடக்க பட்டியலைக் காண்பி (மூன்றுக்கு மேற்பட்ட தலைப்புகளையுடைய கட்டுரைகளுக்கு)',
'tog-rememberpassword' => 'எனது புகுபதிகை பற்றிய விவரங்களை இவ்வுலாவியில் (மிக அதிகமாக $1 {{PLURAL:$1|நாள்|நாட்கள்}}) வரை நினைவில் வைத்திருக்கவும்.',
-'tog-watchcreations' => 'நான் உருவாக்கும் பக்கங்களை எனது கவனிப்புப் பட்டியலில் சேர்க்கவும்.',
-'tog-watchdefault' => 'நான் தொகுக்கும் பக்கங்களை என் கவனிப்புப் பட்டியலில் சேர்',
-'tog-watchmoves' => 'நான் நகர்த்தும் பக்கங்களை என் கவனிப்புப் பட்டியலில் சேர்.',
-'tog-watchdeletion' => 'நான் நீக்கும் பக்கங்களை என் கவனிப்புப் பட்டியலில் சேர்',
+'tog-watchcreations' => 'நானà¯\8d à®\89à®°à¯\81வாà®\95à¯\8dà®\95à¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d மறà¯\8dà®±à¯\81à®®à¯\8d பதிவà¯\87à®±à¯\8dà®±à¯\81à®®à¯\8d à®\95à¯\8bபà¯\8dபà¯\81à®\95ளà¯\88 à®\8eனதà¯\81 à®\95வனிபà¯\8dபà¯\81பà¯\8d பà®\9fà¯\8dà®\9fியலிலà¯\8d à®\9aà¯\87à®°à¯\8dà®\95à¯\8dà®\95வà¯\81à®®à¯\8d.',
+'tog-watchdefault' => 'நானà¯\8d தà¯\8aà®\95à¯\81à®\95à¯\8dà®\95à¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d மறà¯\8dà®±à¯\81à®®à¯\8d à®\95à¯\8bபà¯\8dபà¯\81à®\95ளà¯\88 à®\8eனà¯\8d à®\95வனிபà¯\8dபà¯\81பà¯\8d பà®\9fà¯\8dà®\9fியலிலà¯\8d à®\9aà¯\87à®°à¯\8d',
+'tog-watchmoves' => 'நானà¯\8d நà®\95à®°à¯\8dதà¯\8dதà¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d மறà¯\8dà®±à¯\81à®®à¯\8d à®\95à¯\8bபà¯\8dபà¯\81à®\95ளà¯\88 à®\8eனà¯\8d à®\95வனிபà¯\8dபà¯\81பà¯\8d பà®\9fà¯\8dà®\9fியலிலà¯\8d à®\9aà¯\87à®°à¯\8d.',
+'tog-watchdeletion' => 'நானà¯\8d நà¯\80à®\95à¯\8dà®\95à¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d மறà¯\8dà®±à¯\81à®®à¯\8d à®\95à¯\8bபà¯\8dபà¯\81à®\95ளà¯\88 à®\8eனà¯\8d à®\95வனிபà¯\8dபà¯\81பà¯\8d பà®\9fà¯\8dà®\9fியலிலà¯\8d à®\9aà¯\87à®°à¯\8d',
'tog-minordefault' => 'இயல்பிருப்பாக அனைத்து தொகுப்புகளையும் சிறியது எனக் குறித்துக்கொள்.',
'tog-previewontop' => 'தொகுப்புப் பெட்டிக்கு முன்பு முன்தோற்றத்தைக் காட்டு',
'tog-previewonfirst' => 'முதல் தொகுப்பில் முன்தோற்றத்தைக் காட்டு',
'tog-nocache' => 'உலாவி பக்க இடைமாற்றை முடக்கு',
-'tog-enotifwatchlistpages' => 'நானà¯\8d à®\95வனிà®\95à¯\8dà®\95à¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d மாற்றப்பட்டால் எனக்கு மின்னஞ்சல் செய்க',
+'tog-enotifwatchlistpages' => 'நானà¯\8d à®\95வனிà®\95à¯\8dà®\95à¯\81à®®à¯\8d பà®\95à¯\8dà®\95à®®à¯\8d à®\85லà¯\8dலதà¯\81 à®\95à¯\8bபà¯\8dபà¯\81 மாற்றப்பட்டால் எனக்கு மின்னஞ்சல் செய்க',
'tog-enotifusertalkpages' => 'என் பயனர் பேச்சுப் பக்கம் மாற்றப்பட்டால் எனக்கு மின்னஞ்சல் செய்',
-'tog-enotifminoredits' => 'பà®\95à¯\8dà®\95à®\99à¯\8dகளுக்கான சிறு தொகுப்புக்கள் குறித்தும் எனக்கு மின்னஞ்சல் செய்யவும்',
+'tog-enotifminoredits' => 'பà®\95à¯\8dà®\95à®®à¯\8d மறà¯\8dà®±à¯\81à®®à¯\8d à®\95à¯\8bபà¯\8dபà¯\81களுக்கான சிறு தொகுப்புக்கள் குறித்தும் எனக்கு மின்னஞ்சல் செய்யவும்',
'tog-enotifrevealaddr' => 'அறிவித்தல் மின்னஞ்சல்களில் எனது மின்னஞ்சல் முகவரியை வெளிப்படுத்து',
'tog-shownumberswatching' => 'கவனிக்கும் பயனர்களின் எண்ணிக்கையைக் காட்டவும்',
'tog-oldsig' => 'நடப்பு கையொப்பம்:',
'category-empty' => "''இப்பகுப்பில் தற்போது பக்கங்களோ ஊடகங்களோ இல்லை.''",
'hidden-categories' => '{{PLURAL:$1|மறைக்கப்பட்ட பகுப்பு|மறைக்கப்பட்ட பகுப்புகள்}}',
'hidden-category-category' => 'மறைக்கப்பட்ட பகுப்புகள்',
-'category-subcat-count' => '{{PLURAL:$2|இந்தப் பகுப்பின் கீழ் பின்வரும் ஒரு துணைப் பகுப்பு மட்டுமே உள்ளது.|இந்தப்பகுப்பின் கீழ் உள்ள $2 துணைப் பகுப்புகளில் பின்வரும் {{PLURAL:$1|துணைப்பகுப்பும் உள்ளது.|$1 துணைப்பகுப்புகளும் உள்ளன.}}}}',
+'category-subcat-count' => '{{PLURAL:$2|இந்தப் பகுப்பின் கீழ் பின்வரும் ஒரு துணைப்பகுப்பு மட்டுமே உள்ளது.|இந்தப் பகுப்பில் மொத்தம் உள்ள $2 துணைப்பகுப்புகளில் பின்வரும் {{PLURAL:$1|துணைப்பகுப்பு உள்ளது.|$1 துணைப்பகுப்புகள் இங்கு காட்டப்பட்டுள்ளன.}}}}',
'category-subcat-count-limited' => 'இந்தப் பகுப்பின் கீழ் பின்வரும் {{PLURAL:$1|ஒரு துணைப் பகுப்பு மட்டுமே உள்ளது.|$1 துணைப் பகுப்புகள் உள்ளன.}}',
'category-article-count' => '{{PLURAL:$2|இந்தப் பகுப்பின் கீழ் பின்வரும் பக்கம் மட்டுமே உள்ளது.|இந்தப்பகுப்பின் கீழ் உள்ள $2 பக்கங்களில் பின்வரும் {{PLURAL:$1|பக்கமும் உள்ளது.|$1 பக்கங்களும் உள்ளன.}}}}',
'category-article-count-limited' => 'இப்பகுப்பில் பின்வரும் {{PLURAL:$1|பக்கம்|$1 பக்கங்கள்}} உள்ள{{PLURAL:$1|து|ன}}.',
'youhavenewmessages' => 'உங்களுக்குப் $1 உள்ளன ($2).',
'newmessageslink' => 'புதிய செய்திகள்',
'newmessagesdifflink' => 'கடைசி மாற்றம்',
+'newmessageslinkplural' => '{{PLURAL:$1|ஒரு புதிய செய்தி|புதிய செய்திகள்}}',
+'newmessagesdifflinkplural' => 'கடைசி {{PLURAL:$1|மாற்றம்|மாற்றங்கள்}}',
'youhavenewmessagesmulti' => '$1 இல் உங்களுக்கு புதிய செய்திகள் காத்திருக்கின்றன',
'editsection' => 'தொகு',
'editold' => 'தொகு',
'badarticleerror' => 'இச்செயற்பாட்டை இப்பக்கத்தில் செயற்படுத்த முடியாது.',
'cannotdelete' => '"$1" பக்கத்தையோ கோப்பையோ நீக்க முடியாது. (வேறு யாராலோ ஏற்கெனவே நீக்கப்பட்டிருக்கலாம்.)',
'cannotdelete-title' => '"$1" பக்கத்தை நீக்க முடியாது',
+'delete-hook-aborted' => 'நீக்கல் hook ஆல் தடைசெய்யப்பட்டது.
+அது எந்த விளக்கமும் அளிக்கவில்லை.',
'badtitle' => 'பழுதுள்ள தலைப்பு',
'badtitletext' => 'கோரப்பட்ட பக்கத்தின் தலைப்பு செல்லாது, வெறுமை, அல்லது பிழையாக இணைக்கப்பட்ட மொழிகளிடை அல்லது விக்கியிடைத் தலைப்பாகும்.',
'perfcached' => 'பின்வரும் தரவுகள் இடைக்கிடங்கில் உள்ளன, தரவுகள் புதுப்பித்தநிலையில் இல்லாமல் இருக்கலாம். அதிக அளவாக {{PLURAL:$1|ஒரு முடிவு|$1 முடிவுகள்}} இடைக்கிடங்கில் இருக்கலாம்.',
'ns-specialprotected' => 'சிறப்புப் பக்கங்களைத் தொகுக்க முடியாது.',
'titleprotected' => "பயனர் [[User:$1|$1]] இத்தலைப்பு உருவாக்கப்படுவதை தவிர்க்கும் வகையில் தடுத்துள்ளார்.
கொடுக்கப்பட்டக் காரணம் ''$2''.",
+'filereadonlyerror' => '"$1" கோப்பைத் திருத்த முடியவில்லை ஏனெனில் கோப்புப் பெட்டகம் "$2" படிக்க-மட்டும் வகையில் உள்ளது. அதனை பூட்டிய நிர்வாகி பின்வரும் விளக்கத்தை அளித்துள்ளார்: "$3"',
+'invalidtitle-knownnamespace' => "பெயரிடைவெளி ' $2 '' மற்றும் உரை '' $3 '' கொன்ட தலைப்பு செல்லாது",
+'invalidtitle-unknownnamespace' => 'அறியப்படாத பெயரிடைவெளி $1 மற்றும் உரை $2 கொண்ட தலைப்பு செல்லாது',
+'exception-nologin' => 'புகுபதிகை செய்யப்படவில்லை.',
+'exception-nologin-text' => 'இந்த பக்கம் / செய்கை இந்த விக்கியில் உங்களது புகுபதிகையை எதிர்பார்க்கிறது.',
# Virus scanner
'virus-badscanner' => "சரியற்ற உள்ளமைவு: அறியப்படாத வைரஸ் வருடி: '' $1 ''",
'remembermypassword' => 'எனது கடவுச்சொல்லை (கூடியது $1 {{PLURAL:$1|நாள்|நாட்கள்}}) அமர்வுகளிடையே நினைவில் வைத்திருக்கவும்.',
'securelogin-stick-https' => 'புகுபதிகைக்குப் பிறகும் HTTPS-இலேயே இருக்கவும்',
'yourdomainname' => 'உங்கள் உரிமைப்பரப்பு:',
+'password-change-forbidden' => 'நீங்கள் விக்கிகளில் கடவுச் சொற்களை மாற்ற முடியாது',
'externaldberror' => 'வெளி உறுதிப்படுத்தலில் ஏற்பட்ட தவறு காரணமாக உங்கள் வெளி கணக்கை இற்றைப்படுத்த முடியாது.',
'login' => 'புகுபதிகை',
'nav-login-createaccount' => 'புகுபதிகை/பயனர் கணக்கு தொடக்கம்',
'emailconfirmlink' => 'உங்கள் மின்னஞ்சல் முகவரியை உறுதிப்படுத்தவும்',
'invalidemailaddress' => 'மின்னஞ்சல் முகவரி பிழையான கட்டமைப்பைக் கொண்டுள்ளப்படியால் மின்னஞ்சல் முகவரியை ஏற்றுக்கொள்ள முடியாது. தயவுசெய்து சரியான கட்டமைப்புடன் கூடிய மின்னஞ்சல் முகவரியை இடவும் அல்லது அவ்விடத்தை வெற்றாக விடவும்.',
'cannotchangeemail' => 'கணக்கின் மின்னஞ்சல் முகவரிகளை இந்த விக்கிஇல் மாற்ற முடியாது.',
+'emaildisabled' => 'இந்த வலைதளம் மின்அஞ்சல்களை அனுப்ப முடியாது',
'accountcreated' => 'கணக்கு உருவாக்கப்பட்டது.',
'accountcreatedtext' => '$1 என்ற பெயரில் பயனர் கணக்கு உருவாக்கப்பட்டது.',
'createaccount-title' => '{{SITENAME}} தளத்துக்கான கணக்கு தொடக்கம்',
'noarticletext-nopermission' => 'தற்பொழுது இப்பக்கத்தில் உரை எதுவும் இல்லை.
நீங்கள் [[Special:Search/{{PAGENAME}}|பக்கத் தலைப்பை வைத்து]] அல்லது மற்ற பக்கங்களில்,
or <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} அல்லது தேடுதல் தொடர்பான பதிவுகளில் தேடவும்.]</span>.',
+'missing-revision' => "இந்த பரிசீலனை # $1 '' {{PAGENAME}}' பெயருள்ள பக்கத்தின் இல்லை.!N வேடிக்கையானN!இது தான் வழக்கமாக ஏற்பட்டிருக்கலாம் நீக்கப்பட்டுள்ளது பக்கத்திற்கு outdated வரலாறு இணைப்பை தொடர்ந்து.
+விவரங்கள் முடியும் கண்டறிய, [{{fullurl: {{# சிறப்பு: குறிப்பேடு}} / delete|page = {{FULLPAGENAMEE}}}} நீக்குதல் குறிப்பேடு].",
'userpage-userdoesnotexist' => '"<nowiki>$1</nowiki>" என்றக் கணக்கு இன்னமும் பதிவுச் செய்யப்படவில்லை. இதை உருவாக்க/தொகுக்க வேண்டுமா என்பதை உறுதிப்படுத்தவும்.',
'userpage-userdoesnotexist-view' => 'பயனர் கணக்கு "$1" பதியப்படவில்லை',
'blocked-notice-logextract' => 'இந்தப் பயனர் தற்சமயம் தடை செய்யப்பட்டுள்ளார். இவரது தடை பதிகையின் அண்மைய மாற்றம் கீழே தரப்பட்டுள்ளது:',
'edit-no-change' => 'வாசகங்களுக்கு எந்த மாற்றமும் செய்யப்படவில்லை என்பதனால் உங்கள் தொகுப்பு புறக்கணிக்கப்பட்டது.',
'edit-already-exists' => 'புதிய பக்கமொன்றை உருவாக்க முடியாது.
இப்பக்கம் ஏற்கனவே உள்ளது.',
+'defaultmessagetext' => 'இயல்பிருப்பு தகவல் உரை',
# Parser/template warnings
'expensive-parserfunction-warning' => 'எச்சரிக்கை: இப்பக்கம் அதிகளவு இலக்கணப் பாகுபடுத்திச் சார்புகளைக் கொண்டுள்ளது.
'parser-template-loop-warning' => 'வார்ப்புருவின் முழுச்சுற்று உணரப்பட்டுள்ளது: [[$1]]',
'parser-template-recursion-depth-warning' => 'வார்ப்புரு மறுநிகழ்வு (recursion) ஆழம் வரம்பை மீறிவிட்டது ( $1 )',
'language-converter-depth-warning' => 'மொழி மாற்றியின் ஆழம் வரம்பை மீறிவிட்டது ( $1 )',
+'node-count-exceeded-category' => 'பக்கங்கள் எங்கு முடிச்சு எண்ணிக்கை மிகவும் அதிகரித்துள்ளது',
+'node-count-exceeded-warning' => 'பக்க வரம்பை மீறிவிட்டது முடிச்சு எண்ணிக்கை',
+'expansion-depth-exceeded-category' => 'பக்கங்கள் எங்கு விரிவு ஆழம் மீறிவிட்டது',
+'expansion-depth-exceeded-warning' => 'விரிவு ஆழம் பக்க வரம்பை மீறிவிட்டது',
+'converter-manual-rule-error' => 'கைமுறை மொழி மாற்றம் விதியில் பிழை கண்டுபிடிக்கப்பட்டது',
# "Undo" feature
'undo-success' => "இத்தொகுப்பை மீளமைக்க முடியும். தயவு செய்து, கீழே காட்டப்பட்டுள்ள ஒப்பீட்டைப் பார்த்து, நீங்கள் செய்ய முயற்சிப்பது இதுதானா? என்பதை உறுதிப்படுத்திக் கொண்டு '''பக்கத்தைச் சேமிக்கவும்''' என்பதன் மேல் சொடுக்கவும்.",
# Suppression log
'suppressionlog' => 'அடக்கல் பதிகை',
'suppressionlogtext' => 'பின்வருவது நிர்வாகிகளிடமிருந்து மறைக்கப்பட்ட நீக்கல்களதும் தடுப்புகளதும் பட்டியலாகும்.
-நà®\9fà¯\88à®®à¯\81à®±à¯\88யிலà¯\81ளà¯\8dள தà®\9fà¯\81பà¯\8dபà¯\81à®\95ளà¯\88à®\95à¯\8d à®\95ாண [[Special:BlockList|à®\90.பி. தà®\9fà¯\88பà¯\8d பà®\9fà¯\8dà®\9fியலà¯\88பà¯\8d]] பாரà¯\8dà®\95à¯\8dà®\95வà¯\81à®®à¯\8d',
+நடைமுறையிலுள்ள தடுப்புகளைக் காண [[Special:BlockList|தடைப் பட்டியலைப்]] பார்க்கவும்',
# History merging
'mergehistory' => 'பக்க வரலாறுகளை இணை',
# Diffs
'history-title' => 'திருத்த வரலாறு - "$1"',
+'difference-title' => '"$1" பக்கத்தின் திருத்தங்களுக்கிடையேயான வேறுபாடு',
+'difference-title-multipage' => '"$1" மற்றும் "$2" பக்கங்களுக்கிடையேயான வேறுபாடு',
'difference-multipage' => 'பக்கங்களுக்கு இடையேயான வேறுபாடு',
'lineno' => 'வரிசை $1:',
'compareselectedversions' => 'தெரிவு செய்யப்பட்ட பதிப்புக்களை ஒப்பிடவும்',
'prefs-beta' => 'சோதனைநிலை அம்சங்கள்',
'prefs-datetime' => 'நாள் நேரம்',
'prefs-labs' => 'ஆய்வகச் சிறப்புக்கூறுகள்',
+'prefs-user-pages' => 'பயனர் பக்கங்கள்',
'prefs-personal' => 'பயனர் தரவு',
'prefs-rc' => 'அண்மைய மாற்றங்கள்',
'prefs-watchlist' => 'கவனிப்புப் பட்டியல்',
'savedprefs' => 'உங்கள் விருப்பத்தேர்வுகள் சேமிக்கப்பட்டுள்ளன.',
'timezonelegend' => 'நேர வலயம்:',
'localtime' => 'உள்ளூர் நேரம்:',
-'timezoneuseserverdefault' => 'வழà®\99à¯\8dà®\95னினà¯\8d à®\8eலà¯\8dலà¯\8bà®°à¯\81à®\95à¯\8dà®\95à¯\81à®®à¯\8d பà¯\8aதà¯\81வானவறà¯\8dà®±à¯\88 பயனà¯\8dபà®\9fà¯\81தà¯\8dதவà¯\81à®®à¯\8d',
+'timezoneuseserverdefault' => 'விà®\95à¯\8dà®\95ி à®\87யலà¯\8dபà¯\81நிலà¯\88யà¯\88 à®\89பயà¯\8bà®\95ிà®\95à¯\8dà®\95வà¯\81à®®à¯\8d ($1)',
'timezoneuseoffset' => 'மற்றவை (வித்தியாசத்தைக் குறிப்பிடவும்)',
'timezoneoffset' => 'நேர இடைவெளி¹:',
'servertime' => 'வழங்கி நேரம்:',
'right-writeapi' => 'எழுது API பயன்படுத்தவும்',
'right-delete' => 'பக்கங்களை நீக்குக',
'right-bigdelete' => 'பெரிய வரலாற்றைக் கொண்ட பக்கங்களை நீக்கல்',
+'right-deletelogentry' => 'குறிப்பிட்ட குறிப்பேடு உள்ளீடுகள் நீக்கியதை மீட்டல் மற்றும் நீக்குதல்',
'right-deleterevision' => 'பக்கமொன்றின் குறித்த திருத்தங்களை நிக்கல் மீட்டல்',
'right-deletedhistory' => 'நீக்கிய வரலாறு உள்ளீடுகளை காண்,அதனுடன் தொடர்புடைய உரை இல்லாமல்',
'right-deletedtext' => ' நீக்கப்பட்ட உரை மற்றும் நீக்கப்பட்ட பரிசீலனைகளுக்கு இடையேயான மாற்றங்களை காண்.',
'statistics-edits' => '{{SITENAME}} அமைக்கப்பட்டதிலிருந்து பக்க திருத்தங்கள்',
'statistics-edits-average' => 'ஒரு பக்கத்திற்கான சராசரி தொகுப்புக்கள்',
'statistics-views-total' => 'பார்வையிடப்பட்டதின் மொத்த எண்ணிக்கை',
+'statistics-views-total-desc' => 'தற்பொழுது இல்லாத மற்றும் சிறப்பு பக்கங்களின் காட்சிகள் இதில் சேர்க்கபடவில்லை',
'statistics-views-peredit' => 'ஒரு தொகுத்தலுக்காக பார்க்கப்பட்ட எண்ணிக்கை',
'statistics-users' => 'பதிவு செய்யப்பட்ட [[Special:ListUsers|பயனர்கள்]]',
'statistics-users-active' => 'தொடர் பங்களிப்பாளர்கள் (பயனர்கள்)',
'nmembers' => '$1 {{PLURAL:$1|உறுப்பினர்|உறுப்பினர்கள்}}',
'nrevisions' => '{{PLURAL:$1|ஒரு திருத்தம்|$1 திருத்தங்கள்}}',
'nviews' => '{{PLURAL:$1|ஒரு பார்வை|$1 பார்வைகள்}}',
+'nimagelinks' => '$1 {{PLURAL:$!|பக்கத்தில்|பக்கங்களில்}} பயன்படுத்தப்பட்டது',
+'ntransclusions' => '$1 {{PLURAL:$1|பக்கத்தில்|பக்கங்களில்}} பயன்படுத்தப்பட்டது',
'specialpage-empty' => 'இந்தப் புகாருக்குகந்த முடிவுகள் எதுவுமில்லை.',
'lonelypages' => 'உறவிலிப் பக்கங்கள்',
'lonelypagestext' => 'கீழ்கண்ட பக்கங்கள், {{SITENAME}}-இத்தளத்தின் மற்ற பக்கங்களில் எந்தவிதத்திலும் இணைக்கப்படவில்லை.',
'alllogstext' => '{{SITENAME}} தளத்தின் பதிவுகள் அனைத்திற்குமான ஒருங்கிணைந்த காட்சி. பதிவு வகை, பயனர் பெயர், அல்லது தொடர்புடைய பக்கத்தைத் தெரிவு செய்வதன்மூலம் காட்சி நோக்கை சுருக்கிக் கொள்ள முடியும்.',
'logempty' => 'பொருத்தமான பதிவுகள் யாதுமில்லை.',
'log-title-wildcard' => 'இவ்வுரையுடன் தொடங்கும் தலைப்புகளைத் தேடு',
+'showhideselectedlogentries' => 'தேர்ந்தெடுத்த குறிப்பேடு உள்ளீடுகள் காண்பி/மறை',
# Special:AllPages
'allpages' => 'அனைத்துப் பக்கங்கள்',
'allpagesprefix' => 'பின்வரும் முன்னொட்டு உடைய பக்கங்களை காட்டு:',
'allpagesbadtitle' => 'கொடுக்கப்பட்ட தலைப்பு செல்லுபடியற்றது அல்லது பிழையான விக்கியிடை அல்லது மொழி முன்னொட்டைக் கொண்டுள்ளது. இது தலைப்புக்களில் பயன்படுத்த முடியாத எழுத்துக்களையும் கொண்டிருக்கலாம்.',
'allpages-bad-ns' => '{{SITENAME}} தளத்தில் "$1" பெயர்வெளி கிடையாது.',
+'allpages-hide-redirects' => 'வழிமாற்றுகளைப் மறை',
# SpecialCachedPage
'cachedspecial-refresh-now' => 'அண்மையான பதிப்பை காண்க',
# Special:ListGroupRights
'listgrouprights' => 'பயனர் குழு உரிமைகள்',
+'listgrouprights-key' => '<span class="listgrouprights-granted">உரிமை வழங்கப்பட்டது</span>
+ * <span class="listgrouprights-revoked">உரிமை பறிக்கபட்டது</span>',
'listgrouprights-group' => 'குழு',
'listgrouprights-rights' => 'உரிமைகள்',
'listgrouprights-helppage' => 'Help:குழு உரிமைகள்',
'rollback' => 'முன்நிலையாக்கத் தொகுப்புகள்',
'rollback_short' => 'முன்நிலையாக்கு',
'rollbacklink' => 'முன்நிலையாக்கு',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|தொகுப்பை|தொகுப்புகளை}} முன்நிலையாக்குக',
'rollbackfailed' => 'முன்நிலையாக்கம் தோல்வி',
'cantrollback' => 'தொகுப்பை முன்நிலையாக்க முடியாது; கடைசிப் பங்களிப்பாளரே இக்கட்டுரையின் ஒரே ஆசிரியராகும்.',
'alreadyrolled' => '[[User:$2|$2]] ([[User talk:$2|Talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) பயனரால் செய்யப்பட்ட [[:$1]] இன் கடைசித் தொகுப்பை முன்நிலையாக்க முடியாது; வேறு யாரோ இப்பக்கத்தை ஏற்கெனவே தொகுத்தோ அல்லது முன்நிலையாக்கியோ உள்ளார்.
'cannotundelete' => 'நீக்கம் தோல்வி; வேறு யாராவது முன்னதாக இப்பக்கத்தை நீக்கியிருக்கலாம்.',
'undeletedpage' => "'''$1 மீட்கப்பட்டது'''
-அண்மைய நீக்கல்களுக்கும் மீட்புக்ளுக்கும் [[Special:Log/delete|நீக்கல் பதிவைப்]] பார்க்கவும்.",
+à®\85ணà¯\8dà®®à¯\88ய நà¯\80à®\95à¯\8dà®\95லà¯\8dà®\95ளà¯\81à®\95à¯\8dà®\95à¯\81à®®à¯\8d à®®à¯\80à®\9fà¯\8dபà¯\81à®\95à¯\8dà®\95ளà¯\81à®\95à¯\8dà®\95à¯\81à®®à¯\8d [[Special:Log/delete|நà¯\80à®\95à¯\8dà®\95லà¯\8d பதிவà¯\88பà¯\8d]] பாரà¯\8dà®\95à¯\8dà®\95வà¯\81à®®à¯\8d.",
'undelete-header' => 'அண்மையில் நீக்கப்பட்ட பக்கங்களைக் காண [[Special:Log/delete|நீக்கல் பதிவைப்]] பார்க்க.',
'undelete-search-title' => 'நீக்கப்பட்ட பக்கங்களைத் தேடு',
'undelete-search-box' => 'நீக்கப்பட்ட பக்கங்களைத் தேடு',
'javascripttest' => 'சாவாநிரல் சோதனை நடக்கின்றது',
'javascripttest-disabled' => 'இந்தச் செயல்பாடு முடக்கப்பட்டுள்ளது.',
'javascripttest-title' => '$1 சோதனைகள் நடக்கின்றன',
+'javascripttest-pagetext-noframework' => 'இந்த பக்கம் JavaScript பரிசோதனை ஓட்டத்திற்காக ஒதுக்கப்பட்டுள்ளது',
'javascripttest-pagetext-skins' => 'சோதனைகளை நடத்த முகப்புறை ஒன்றைத் தேர்வுசெய்:',
# Tooltip help for the actions
'version-software' => 'நிறுவப்பட்ட மென்பொருள்',
'version-software-product' => 'உற்பத்திப்பொருள்',
'version-software-version' => 'பதிப்பு',
+'version-entrypoints' => 'நுழைவு புள்ளி உரலிகள்',
+'version-entrypoints-header-entrypoint' => 'நுழைவு புள்ளி',
'version-entrypoints-header-url' => 'உரலி (URL)',
# Special:FilePath
'api-error-empty-file' => 'நீங்கள் அளித்த கோப்பு காலியாக உள்ளது.',
'api-error-emptypage' => 'புதிய, காலி பக்கங்கள் உருவாக்கல் அனுமதிக்கப்படவில்லை.',
'api-error-fetchfileerror' => 'உள்ளகப் பிழை: கோப்பைப் பெறுகையில் ஏதோ தவறு நேர்ந்துவிட்டது.',
+'api-error-fileexists-forbidden' => '"$1" என்ற பெயருள்ள கோப்பு ஏற்கனவே உள்ளது. மேலெழுத முடியாது.',
+'api-error-fileexists-shared-forbidden' => '"$1" என்ற பெயருள்ள கோப்பு ஏற்கனவே கோப்பு பகிர்மானப் பெட்டகத்தில் உள்ளது. மேலெழுத முடியாது.',
'api-error-file-too-large' => 'நீங்கள் அளித்த கோப்பு மிகவும் பெரியதாக உள்ளது.',
'api-error-filename-tooshort' => 'கோப்புப் பெயர் மிகவும் சிறியதாக உள்ளது.',
'api-error-filetype-banned' => 'இக்கோப்பு வகை தடைசெய்யப்பட்டுள்ளது.',
'api-error-uploaddisabled' => 'இந்த விக்கியில் பதிவேற்றல் செயலிழக்கச் செய்யப்பட்டுள்ளது.',
'api-error-verification-error' => 'இக்கோப்பு பிழையுடனோ தவறான விரிவுடனோ இருக்கலாம்.',
+# Durations
+'duration-seconds' => '$1 {{PLURAL:$1|நொடி|நொடிகள்}}',
+'duration-minutes' => '{{PLURAL: $1|நிமிடம்|நிமிடங்கள்}}',
+'duration-hours' => '$1 {{PLURAL:$1|மணி|மணிகள்}} முன்பு',
+'duration-days' => '$1 {{PLURAL:$1|நாள்|நாட்கள்}}',
+'duration-weeks' => '{{PLURAL: $1|வாரம்|வாரங்கள்}}',
+'duration-years' => '{{PLURAL: $1|வருடம்|வருடங்கள்}}',
+'duration-decades' => '$1 {{PLURAL:$1|பத்தாண்டு|பத்தாண்டுகள்}}',
+'duration-centuries' => '$1 {{PLURAL:$1|நூற்றாண்டு|நூற்றாண்டுகள்}}',
+'duration-millennia' => '$1 {{PLURAL:$1|ஆயிரம் ஆண்டு|ஆயிரம் ஆண்டுகள்}}',
+
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|அனுமதிக்கப்படாத கோப்பு வகையாகும் | அனுமதிக்கப்படாத கோப்பு வகைகளாகும்}}.. அனுமதிக்கப்பட்ட {{PLURAL:$3|கோப்புவகை|கோப்புவகைகள்}} $2 என்பது(வை) ஆகும்.',
);
'tog-hidepatrolled' => 'ఇటీవలి మార్పులలో నిఘా ఉన్న మార్పులను దాచిపెట్టు',
'tog-newpageshidepatrolled' => 'కొత్త పేజీల జాబితా నుంచి నిఘా ఉన్న పేజీలను దాచిపెట్టు',
'tog-extendwatchlist' => 'కేవలం ఇటీవలి మార్పులే కాక, మార్పులన్నీ చూపించటానికి నా వీక్షణా జాబితాను పెద్దది చేయి',
-'tog-usenewrc' => 'à°®à±\86à°°à±\81à°\97à±\88à°¨ à°\87à°\9fà±\80వలి మారà±\8dà°ªà±\81à°² à°ªà±\87à°\9cà±\80 (జావాస్క్రిప్టు అవసరం)',
+'tog-usenewrc' => 'à°\87à°\9fà±\80వలి మారà±\8dà°ªà±\81à°²à±\81 మరియà±\81 విà°\95à±\8dà°·à°£ à°\9cాబితాలలà±\8b మారà±\8dà°ªà±\81లనà±\81 à°ªà±\87à°\9cà±\80 వారిà°\97à°¾ à°\9aà±\82పిà°\82à°\9aà±\81 (జావాస్క్రిప్టు అవసరం)',
'tog-numberheadings' => 'శీర్షికలకు ఆటోమాటిక్గా వరుస సంఖ్యలు పెట్టు',
'tog-showtoolbar' => 'దిద్దుబాట్లు చేసేటప్పుడు, అందుకు సహాయపడే పరికరాలపెట్టెను చూపించు (జావాస్క్రిప్టు)',
'tog-editondblclick' => 'డబుల్ క్లిక్కు చేసినప్పుడు పేజీని మార్చు (జావాస్క్రిప్టు)',
'tog-editsectiononrightclick' => 'విభాగం పేరు మీద కుడి క్లిక్కుతో విభాగం మార్పు కావాలి (జావాస్క్రిప్టు)',
'tog-showtoc' => 'విషయసూచిక చూపించు (3 కంటే ఎక్కువ శీర్షికలున్న పేజీలకు)',
'tog-rememberpassword' => 'ఈ విహారిణిలో నా ప్రవేశాన్ని గుర్తుంచుకో (గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజుల}}కి)',
-'tog-watchcreations' => 'à°¨à±\87à°¨à±\81 à°¸à±\83à°·à±\8dà°\9fà°¿à°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\95à°²à±\81à°ªు',
-'tog-watchdefault' => 'à°¨à±\87à°¨à±\81 దిదà±\8dà°¦à±\81బాà°\9fà±\8dà°²à±\81 à°\9aà±\87సిన à°ªà±\87à°\9cà±\80లనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\95à°²à±\81à°ªు',
-'tog-watchmoves' => 'à°¨à±\87à°¨à±\81 తరలిà°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\95à°²à±\81à°ªు',
-'tog-watchdeletion' => 'à°¨à±\87à°¨à±\81 à°¤à±\8aà°²à°\97à°¿à°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\95à°²à±\81à°ªు',
+'tog-watchcreations' => 'à°¨à±\87à°¨à±\81 à°¸à±\83à°·à±\8dà°\9fà°¿à°\82à°\9aà±\87 à°ªà±\87à°\9cà±\80లనà±\81 మరియà±\81 దసà±\8dà°¤à±\8dరాలనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aు',
+'tog-watchdefault' => 'à°¨à±\87à°¨à±\81 మారà±\8dà°\9aà±\87 à°ªà±\87à°\9cà±\80లనà±\81 మరియà±\81 దసà±\8dà°¤à±\8dరాలనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aు',
+'tog-watchmoves' => 'à°¨à±\87à°¨à±\81 తరలిà°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 దసà±\8dà°¤à±\8dరాలనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aు',
+'tog-watchdeletion' => 'à°¨à±\87à°¨à±\81 à°¤à±\8aà°²à°\97à°¿à°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 దసà±\8dà°¤à±\8dరాలనà±\81 నా à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aు',
'tog-minordefault' => 'ప్రత్యేకంగా తెలుపనంతవరకూ నా మార్పులను చిన్న మార్పులుగా గుర్తించు',
'tog-previewontop' => 'వ్యాసం మార్పుల తరువాత ఎలావుంటుందో మార్పుల బాక్సుకు పైన చూపు',
'tog-previewonfirst' => 'దిద్దిబాట్లు చేసిన వ్యాసాన్ని భద్రపరిచే ముందు ఎలా వుంటుందో ఒకసారి చూపించు',
'tog-nocache' => 'విహారిణిలో పుటల కాషింగుని అచేతనంచేయి',
-'tog-enotifwatchlistpages' => 'నా వీక్షణాజాబితా లోని పేజీలు మారినపుడు నాకు ఈ-మెయిలు పంపించు',
+'tog-enotifwatchlistpages' => 'నా వీక్షణాజాబితా లోని పేజీ లేదా దస్త్రం మారినపుడు నాకు ఈ-మెయిలు పంపించు',
'tog-enotifusertalkpages' => 'నా చర్చా పేజీలో మార్పులు జరిగినపుడు నాకు ఈ-మెయిలు పంపించు',
-'tog-enotifminoredits' => 'à°\9aà°¿à°¨à±\8dà°¨ మారà±\8dà°ªà±\81à°²à±\81 à°\9aà±\87సినపà±\8dà°ªà±\81à°¡à±\81 à°\95à±\82à°¡à°¾ నాà°\95à±\81 à°\88-à°®à±\86యిలు పంపించు',
+'tog-enotifminoredits' => 'à°ªà±\87à°\9cà±\80à°²à±\81 మరియà±\81 దసà±\8dà°¤à±\8dరాలà°\95à±\81 à°\9cà°°à°¿à°\97à±\87 à°\9aà°¿à°¨à±\8dà°¨ మారà±\8dà°ªà±\81à°²à°\95à±\81 à°\95à±\82à°¡à°¾ నాà°\95à±\81 à°\88-à°®à±\86యిలà±\81à°¨ు పంపించు',
'tog-enotifrevealaddr' => 'గమనింపు మెయిళ్ళలో నా ఈ-మెయిలు చిరునామాను చూపించు',
'tog-shownumberswatching' => 'వీక్షకుల సంఖ్యను చూపించు',
'tog-oldsig' => 'ప్రస్తుత సంతకం:',
'vector-simplesearch-preference' => 'మెరుగైన అన్వేషణ సలహాలని చేతనంచేయి (వెక్టర్ అలంకారానికి మాత్రమే)',
'vector-view-create' => 'సృష్టించు',
'vector-view-edit' => 'సవరించు',
-'vector-view-history' => 'à°\9aà°°à°¿à°¤à±\8dరని చూడండి',
+'vector-view-history' => 'à°\9aà°°à°¿à°¤à±\8dà°°à°¨à±\81 చూడండి',
'vector-view-view' => 'చదువు',
'vector-view-viewsource' => 'మూలాన్ని చూడండి',
'actions' => 'పనులు',
'youhavenewmessages' => 'మీకు $1 ఉన్నాయి ($2).',
'newmessageslink' => 'కొత్త సందేశాలు',
'newmessagesdifflink' => 'క్రితం సంచికతో గల తేడాలు',
+'youhavenewmessagesfromusers' => 'మీకు {{PLURAL:$3|మరో వాడుకరి|$3 వాడుకరుల}} నుండి $1 ($2).',
+'youhavenewmessagesmanyusers' => 'మీకు చాలా వాడుకరుల నుండి $1 ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|ఒక కొత్త సందేశం వచ్చింది|కొత్త సందేశాలు ఉన్నాయి}}',
+'newmessagesdifflinkplural' => 'చివరి {{PLURAL:$1|మార్పు|మార్పులు}}',
'youhavenewmessagesmulti' => '$1లో మీకో సందేశం ఉంది',
'editsection' => 'మార్చు',
'editold' => 'సవరించు',
'ns-specialprotected' => 'ప్రత్యేక పేజీలపై దిద్దుబాట్లు చేయలేరు.',
'titleprotected' => "సభ్యులు [[User:$1|$1]] ఈ పేజీని సృష్టించనివ్వకుండా నిరోదిస్తున్నారు.
అందుకు ఇచ్చిన కారణం: ''$2''.",
+'exception-nologin' => 'లోనికి ప్రవేశించిలేరు',
+'exception-nologin-text' => 'ఈ వికీలో ఈ పేజీ లేదా పనికి మీరు తప్పనిసరిగా ప్రవేశించివుండాలి.',
# Virus scanner
'virus-badscanner' => "తప్పుడు స్వరూపణం: తెలియని వైరస్ స్కానర్: ''$1''",
'remembermypassword' => 'ఈ కంప్యూటరులో నా ప్రవేశాన్ని గుర్తుంచుకో (గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజుల}}కి)',
'securelogin-stick-https' => 'ప్రవేశం తర్వాత కూడా HTTPSకి అనుసంధానమై ఉండు',
'yourdomainname' => 'మీ డోమైను',
+'password-change-forbidden' => 'ఈ వికీలో మీరు సంకేతపదాలను మార్చలేరు.',
'externaldberror' => 'డేటాబేసు అధీకరణలో పొరపాటు జరిగింది లేదా మీ బయటి ఖాతాని తాజాకరించడానికి మీకు అనుమతి లేదు.',
'login' => 'లోనికి రండి',
'nav-login-createaccount' => 'లోనికి ప్రవేశించండి / ఖాతాని సృష్టించుకోండి',
'invalidemailaddress' => 'మీరు ఇచ్చిన ఈ-మెయిలు చిరునామా సరైన రీతిలో లేనందున అంగీకరించటంలేదు.
దయచేసి ఈ-మెయిలు చిరునామాను సరైన రీతిలో ఇవ్వండి లేదా ఖాళీగా వదిలేయండి.',
'cannotchangeemail' => 'ఈ వికీలో ఖాతా ఈ-మెయిలు చిరునామాను మార్చుకోలేరు.',
+'emaildisabled' => 'ఈ సైటు ఈమెయిళ్ళను పంపించలేదు.',
'accountcreated' => 'ఖాతాని సృష్టించాం',
'accountcreatedtext' => '$1 కి వాడుకరి ఖాతాని సృష్టించాం.',
'createaccount-title' => '{{SITENAME}} కోసం ఖాతా సృష్టి',
'passwordreset-legend' => 'సంకేతపదాన్ని మార్చుకోండి',
'passwordreset-disabled' => 'ఈ వికీలో సంకేతపదాల మార్పును అచేతనం చేసాం.',
'passwordreset-pretext' => '{{PLURAL:$1||డేటా శకలాల్లోంచి ఒకదాన్ని ఇవ్వండి}}',
-'passwordreset-username' => 'వాడుకరిపేరు:',
+'passwordreset-username' => 'వాడుకరి పేరు:',
'passwordreset-domain' => 'డొమైన్:',
'passwordreset-email' => 'ఈ-మెయిలు చిరునామా:',
'passwordreset-emailtitle' => '{{SITENAME}}లో ఖాతా వివరాలు',
'note' => "'''గమనిక:'''",
'previewnote' => "'''ఇది మునుజూపు మాత్రమేనని గుర్తుంచుకోండి.'''
మీ మార్పులు ఇంకా భద్రమవ్వలేదు!",
+'continue-editing' => 'దిద్దుబాటుని కొనసాగించండి',
'previewconflict' => 'భద్రపరచిన తరువాత పై టెక్స్ట్ ఏరియాలోని టెక్స్టు ఇలాగ కనిపిస్తుంది.',
'session_fail_preview' => "'''క్షమించండి! సెషను డేటా పోవడం వలన మీ మార్పులను స్వీకరించలేకపోతున్నాం.'''
దయచేసి మళ్ళీ ప్రయత్నించండి.
'edit-no-change' => 'పాఠ్యంలో ఏమీ మార్పులు లేవు గనక, మీ మార్పుని పట్టించుకోవట్లేదు.',
'edit-already-exists' => 'కొత్త పేజీని సృష్టించలేము.
అది ఇప్పటికే ఉంది.',
+'defaultmessagetext' => 'అప్రమేయ సందేశపు పాఠ్యం',
# Parser/template warnings
'expensive-parserfunction-warning' => 'హెచ్చరిక: ఈ పేజీలో ఖరీదైన పార్సరు పిలుపులు చాలా ఉన్నాయి.
# Diffs
'history-title' => '"$1" యొక్క కూర్పుల చరిత్ర',
'difference-title' => '"$1" యొక్క తిరిగిచూపుల నడుమ తేడాలు',
+'difference-title-multipage' => '"$1" మరియు "$2" పేజీల మధ్య తేడా',
'difference-multipage' => '(పేజీల మధ్య తేడా)',
'lineno' => 'పంక్తి $1:',
'compareselectedversions' => 'ఎంచుకున్న సంచికలను పోల్చిచూడు',
'number_of_watching_users_pageview' => '[వీక్షిస్తున్న సభ్యులు: {{PLURAL:$1|ఒక్కరు|$1}}]',
'rc_categories' => 'ఈ వర్గాలకు పరిమితం చెయ్యి ("|" తో వేరు చెయ్యండి)',
'rc_categories_any' => 'ఏదయినా',
+'rc-change-size-new' => 'మార్పు తర్వాత $1 {{PLURAL:$1|బైటు|బైట్లు}}',
'newsectionsummary' => '/* $1 */ కొత్త విభాగం',
'rc-enhanced-expand' => 'వివరాలని చూపించు (జావాస్క్రిప్ట్ అవసరం)',
'rc-enhanced-hide' => 'వివరాలను దాచు',
+'rc-old-title' => 'మొదట "$1"గా సృష్టించారు',
# Recent changes linked
'recentchangeslinked' => 'సంబంధిత మార్పులు',
'minlength1' => 'పైలు పేర్లు కనీసం ఒక్క అక్షరమైనా ఉండాలి.',
'illegalfilename' => '"$1" అనే దస్త్రపుపేరు పేజీ శీర్షికలలో వాడకూడని అక్షరాలను కలిగివుంది.
దస్త్రపు పేరుని మార్చి మళ్ళీ ఎక్కించడానికి ప్రయత్నించండి.',
+'filename-toolong' => 'దస్త్రపు పేరు 240 బైట్ల కంటే పొడవు ఉండకూడదు.',
'badfilename' => 'ఫైలు పేరు "$1"కి మార్చబడినది.',
'filetype-mime-mismatch' => 'దస్త్రపు పొడగింపు ".$1" ఆ దస్త్రం యొక్క MIME రకం ($2) తో సరిపోలలేదు.',
'filetype-badmime' => '"$1" MIME రకం ఉన్న ఫైళ్ళను ఎగుమతికి అనుమతించం.',
'uploadvirus' => 'ఈ ఫైలులో వైరస్ ఉంది! వివరాలు: $1',
'uploadjava' => 'ఇదొక ZIP ఫైలు, ఇందులో ఒక Java .class ఫైలు ఉంది.
Java ఫైళ్ళ వలన భద్రతకు తూట్లు పడే అవకాశం ఉంది కాబట్టి, వాటిని ఎక్కించడానికి అనుమతి లేదు.',
-'upload-source' => 'à°®à±\82à°² à°«à±\88à°²à±\81',
+'upload-source' => 'à°®à±\82à°² దసà±\8dà°¤à±\8dà°°à°\82',
'sourcefilename' => 'మూలం ఫైలుపేరు:',
'sourceurl' => 'మూల URL:',
'destfilename' => 'ఉద్దేశించిన ఫైలుపేరు:',
'upload-maxfilesize' => 'గరిష్ట ఫైలు పరిమాణం: $1',
-'upload-description' => 'à°«à±\88à°²ు వివరణ',
+'upload-description' => 'దసà±\8dà°¤à±\8dà°°à°ªు వివరణ',
'upload-options' => 'ఎక్కింపు వికల్పాలు',
'watchthisupload' => 'ఈ ఫైలుని గమనించు',
'filewasdeleted' => 'ఇదే పేరుతో ఉన్న ఒక ఫైలును గతంలో అప్లోడు చేసారు, తరువాతి కాలంలో దాన్ని తొలగించారు. దాన్నీ మళ్ళీ అప్లోడు చేసే ముందు, మీరు $1 ను చూడాలి',
'backend-fail-delete' => '$1 ఫైలును తొలగించలేకున్నాం.',
'backend-fail-alreadyexists' => '$1 అనే దస్త్రం ఇప్పటికే ఉంది.',
'backend-fail-opentemp' => 'తాత్కాలిక దస్త్రాన్ని తెరవలేకపోతున్నాం.',
+'backend-fail-closetemp' => 'తాత్కాలిక దస్త్రాన్ని మూసివేయలేకపోయాం.',
'backend-fail-read' => '$1 దస్త్రము చదువలేకపోతిమి.',
# ZipDirectoryReader
'listfiles_count' => 'కూర్పులు',
# File description page
-'file-anchor-link' => 'à°«à±\88à°²à±\81',
+'file-anchor-link' => 'దసà±\8dà°¤à±\8dà°°à°\82',
'filehist' => 'దస్త్రపు చరిత్ర',
'filehist-help' => 'తేదీ/సమయం ను నొక్కి ఆ సమయాన ఫైలు ఎలా ఉండేదో చూడవచ్చు.',
'filehist-deleteall' => 'అన్నిటినీ తొలగించు',
ఒక చిట్టా రకాన్ని గానీ, ఒక వాడుకరి పేరు గానీ (case-sensitive), లేదా ప్రభావిత పుటని (ఇది కూడా case-sensitive) గానీ ఎంచుకుని సంబంధిత చిట్టాను మాత్రమే చూడవచ్చు.',
'logempty' => 'సరిపోలిన అంశాలేమీ చిట్టాలో లేవు.',
'log-title-wildcard' => 'ఈ పాఠ్యంతో మొదలయ్యే పుస్తకాల కొరకు వెతుకు',
+'showhideselectedlogentries' => 'ఎంచుకున్న చిట్టా పద్దులను చూపించు/దాచు',
# Special:AllPages
'allpages' => 'అన్ని పేజీలు',
'allpages-bad-ns' => '{{SITENAME}} లో "$1" అనే నేమ్‌స్పేస్ లేదు.',
'allpages-hide-redirects' => 'దారిమార్పులను దాచు',
+# SpecialCachedPage
+'cachedspecial-refresh-now' => 'సరికొత్త కూర్పును చూడండి.',
+
# Special:Categories
'categories' => 'వర్గాలు',
'categoriespagetext' => 'ఈ క్రింది {{PLURAL:$1|వర్గం పేజీలను లేదా మాధ్యమాలను కలిగివుంది|వర్గాలు పేజీలను లేదా మాధ్యమాలను కలిగివున్నాయి}}.
'rollback' => 'దిద్దుబాట్లను రద్దుచేయి',
'rollback_short' => 'రద్దుచేయి',
'rollbacklink' => 'రద్దుచేయి',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|మార్పును|మార్పులను}} రద్దుచేయి',
+'rollbacklinkcount-morethan' => '$1 కంటే ఎక్కువ {{PLURAL:$1|మార్పును|మార్పులను}} రద్దుచేయి',
'rollbackfailed' => 'రద్దుచేయటం విఫలమైంది',
'cantrollback' => 'రచనను వెనక్కి తీసుకువెళ్ళలేము; ఈ పేజీకి ఇదొక్కటే రచన.',
'alreadyrolled' => '[[:$1]]లో [[User:$2|$2]] ([[User talk:$2|చర్చ]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) చేసిన చివరి మార్పును రద్దు చెయ్యలేము;
'protect-level-sysop' => 'నిర్వాహకులు మాత్రమే',
'protect-summary-cascade' => 'కాస్కేడింగు',
'protect-expiring' => '$1 (UTC)న కాలంచెల్లుతుంది',
+'protect-expiring-local' => '$1న కాలంచెల్లుతుంది',
'protect-expiry-indefinite' => 'నిరవధికం',
'protect-cascade' => 'ఈ పేజీకి జతపరిచిన పేజీలను కూడా రక్షించు (కాస్కేడింగు రక్షణ)',
'protect-cantedit' => 'ఈ పేజీ యొక్క సంరక్షణా స్థాయిని మీరు మార్చలేరు, ఎందుకంటే దాన్ని మార్చే అనుమతి మీకు లేదు.',
# Namespace 8 related
'allmessages' => 'అన్ని సిస్టం సందేశాలు',
'allmessagesname' => 'పేరు',
-'allmessagesdefault' => 'à°¡à±\80ఫాలà±\8dà°\9fు పాఠ్యం',
+'allmessagesdefault' => 'à°\85à°ªà±\8dà°°à°®à±\87à°¯ à°¸à°\82à°¦à±\87శపు పాఠ్యం',
'allmessagescurrent' => 'ప్రస్తుత పాఠ్యం',
'allmessagestext' => 'మీడియావికీ పేరుబరిలో ఉన్న అంతరవర్తి సందేశాల జాబితా ఇది.
సాధారణ మీడియావికీ స్థానికీకరణకి మీరు తోడ్పడాలనుకుంటే, దయచేసి [//www.mediawiki.org/wiki/Localisation మీడియావికీ స్థానికీకరణ] మరియు [//translatewiki.net ట్రాన్స్‌లేట్‌వికీ.నెట్] సైట్లను చూడండి.',
# JavaScriptTest
'javascripttest' => 'జావాస్క్రిప్ట్ పరీక్ష',
+'javascripttest-title' => '$1 పరీక్షలు నడుస్తున్నాయి',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'మీ వాడుకరి పేజీ',
'exif-iimcategory' => 'వర్గం',
'exif-iimsupplementalcategory' => 'అనుషంగిక వర్గాలు',
'exif-datetimeexpires' => 'దీని తరువాత వాడవద్దు',
+'exif-datetimereleased' => 'విడుదల తేదీ',
'exif-identifier' => 'గుర్తింపకం',
'exif-lens' => 'వాడిన కటకం',
'exif-serialnumber' => 'కెమేరా యొక్క సీరియల్ నంబర్',
'version-software' => 'స్థాపిత మృదూపకరణాలు',
'version-software-product' => 'ప్రోడక్టు',
'version-software-version' => 'వెర్షను',
+'version-entrypoints' => 'ప్రవేశ బిందు చిరునామాలు',
+'version-entrypoints-header-entrypoint' => 'ప్రవేశ బిందువు',
+'version-entrypoints-header-url' => 'చిరునామా',
# Special:FilePath
'filepath' => 'పూర్తి చిరునామా',
# API errors
'api-error-badaccess-groups' => 'ఈ వికీ లోనికి దస్త్రాలను ఎక్కించే అనుమతి మీకు లేదు.',
+'api-error-duplicate-archive-popup-title' => 'నకిలీ {{PLURAL:$1|దస్త్రాన్ని|దస్త్రాలను}} ఇప్పటికే తొలగించారు.',
+'api-error-duplicate-popup-title' => 'నకిలీ {{PLURAL:$1|దస్త్రం|దస్త్రాలు}}.',
'api-error-empty-file' => 'మీరు దాఖలుచేసిన ఫైల్ ఖాళీది.',
'api-error-emptypage' => 'కొత్త మరియు ఖాళీ పేజీలను సృష్టించడానికి అనుమతి లేదు.',
+'api-error-file-too-large' => 'మీరు సమర్పించిన దస్త్రం చాలా పెద్దగా ఉంది.',
'api-error-filename-tooshort' => 'దస్త్రపు పేరు మరీ చిన్నగా ఉంది.',
'api-error-filetype-banned' => 'ఈ రకపు దస్త్రాలని నిషేధించారు.',
'api-error-http' => 'అంతర్గత దోషము: సేవకానికి అనుసంధానమవలేకపోతున్నది.',
'api-error-mustbeloggedin' => 'దస్త్రాలను ఎక్కించడానికి మీరు ప్రవేశించివుండాలి.',
'api-error-nomodule' => 'అంతర్గత దోషము: ఎక్కింపు పర్వికము అమర్చబడలేదు.',
'api-error-ok-but-empty' => 'అంతర్గత దోషము: సేవకము నుండి ఎటువంటి స్పందనా లేదు.',
+'api-error-stashfailed' => 'అంతర్గత పొరపాటు: తాత్కాలిక దస్త్రాన్ని భద్రపరచడంలో సేవకి విఫలమైంది.',
'api-error-unclassified' => 'ఒక తెలియని దోషము సంభవించినది',
-'api-error-unknown-code' => 'తెలియని దోషము: $1',
+'api-error-unknown-code' => 'తెలియని పొరపాటు: "$1".',
+'api-error-unknown-error' => 'అంతర్గత పొరపాటు: మీ దస్త్రాన్ని ఎక్కించేప్పుడు ఏదో పొరపాటు జరిగింది.',
'api-error-unknown-warning' => 'తెలియని హెచ్చరిక: $1',
+'api-error-unknownerror' => 'తెలియని పొరపాటు: "$1".',
'api-error-uploaddisabled' => 'ఈ వికీలో ఎక్కింపులని అచేతనం చేసారు.',
'api-error-verification-error' => 'ఈ ఫైల్ పాడైవుండవచ్చు, లేదా తప్పుడు పొడిగింతను కలిగివుండవచ్చు.',
'duration-centuries' => '$1 {{PLURAL:$1|శతాబ్దం|శతాబ్దాలు}}',
'duration-millennia' => '$1 {{PLURAL:$1|సహస్రాబ్దం|సహస్రాబ్దాలు}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|అనేది అనుమతించబడిన ఫైలు రకం కాదు|అనేవి అనుమతించబడిన ఫైలు రకాలు కాదు}}. అనుమతించబడిన {{PLURAL:$3|ఫైలు రకం|ఫైలు రకాలు}} $2.',
);
* @file
*
* @author MF-Warburg
+ * @author Nemo bis
* @author Reedy
*/
'tog-underline' => 'Subliña ligasaun sira:',
'tog-justify' => 'Justifika parágrafu sira',
'tog-hideminor' => "Lá'os hatudu muda ki-ki'ik iha mudansa foufoun sira",
-'tog-usenewrc' => 'Uza lista "Mudansa foufoun sira" di\'ak liu (JavaScript)',
+'tog-usenewrc' => 'Iha lista "mudansa foufoun sira" no "lista hateke": Hatudu mudansa iha grupu sira - grupu ida ba pájina ida (presiza JavaScript)',
'tog-showtoolbar' => 'Hatudu kaixa edita (presiza JavaScript)',
'tog-watchcreations' => "Hateke pájina sira-ne'ebé ha'u kria",
'tog-watchdefault' => "Hateke pájina sira-ne'ebé ha'u edita",
'faqpage' => 'Project:FAQ',
# Vector skin
+'vector-action-protect' => 'Proteje',
'vector-view-create' => 'Kria',
'vector-view-edit' => 'Edita',
'vector-view-history' => 'Haree istória',
+'vector-view-view' => 'Lee',
'actions' => 'Aksaun sira',
'errorpagetitle' => 'Sala',
'lineno' => 'Liña $1:',
# Search results
+'searchresults' => 'Rezultadu sira',
+'searchresults-title' => 'Rezultadu sira ba buka "$1"',
'searchsubtitleinvalid' => "Ita buka tiha ona '''$1'''",
'prevn' => 'molok {{PLURAL:$1|$1}}',
'nextn' => 'oinmai {{PLURAL:$1|$1}}',
+'shown-title' => 'Hatudu {{PLURAL:$1|rezultadu|rezultadu}} $1 kada pájina',
'viewprevnext' => 'Haree ($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-new' => "'''Kria pájina \"[[:\$1]]\" iha wiki ne'e!'''",
+'searchprofile-everything' => 'Hotu',
+'searchprofile-articles-tooltip' => 'Buka iha $1',
'searchprofile-project-tooltip' => 'Buka iha $1',
'search-result-size' => '$1 ({{PLURAL:$2|liafuan ida|liafuan $2}})',
'search-section' => '(seksaun $1)',
'timezoneregion-europe' => 'Europa',
'youremail' => 'Korreiu eletróniku:',
'username' => "Naran uza-na'in:",
-'uid' => "Uza-na'in ID:",
+'uid' => "Númeru uza-na'in:",
'yourlanguage' => 'Lian:',
'gender-male' => 'Mane',
'gender-female' => 'Feto',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|diferensa|diferensa}}',
'recentchanges' => 'Mudansa foufoun sira',
+'recentchanges-label-newpage' => 'Pájina foun',
+'recentchanges-label-minor' => "Ne'e mudansa ki'ik",
+'recentchanges-label-bot' => 'Edita husi prosesu automátiku ("bot")',
'rcshowhideminor' => "$1 muda ki-ki'ik",
'rcshowhidebots' => '$1 bot sira',
'rcshowhideliu' => '$1 ema rejista',
'filehist-user' => "Uza-na'in",
'filehist-comment' => 'Komentáriu',
'imagelinks' => "Pájina iha ne'ebá fixeiru ne'e",
+'uploadnewversion-linktext' => 'Tau versaun foun imajen nian',
# File reversion
'filerevert-comment' => 'Razaun:',
'tooltip-ca-unwatch' => 'Hasai pájina ne\'e husi Ita-nia "lista hateke"',
'tooltip-search' => 'Buka iha {{SITENAME}}',
'tooltip-search-go' => "Bá pájina ho naran ne'e (se iha)",
+'tooltip-search-fulltext' => "Buka pájina sira-ne'ebé iha testu ne'e iha laran",
'tooltip-p-logo' => 'Pájina Mahuluk',
'tooltip-n-mainpage' => 'Vizita Pájina Mahuluk',
'tooltip-n-mainpage-description' => 'Vizita Pájina Mahuluk',
'showhidebots' => '($1 bot sira)',
'ilsubmit' => 'Buka',
+# Bad image list
+'bad_image_list' => "Formatu:
+
+Liña hotu tenke komesa ho *
+Ligasaun uluk iha liña tenke ligasaun bá imajen aat.
+Ligasaun seluk iha liña - ne'e pájina sira iha ne'ebé bele inklui imajen aat.",
+
# EXIF tags
'exif-make' => 'Fabrikante kámara nian',
'exif-model' => 'Kámara',
'tog-watchlisthideown' => 'Gözegçilik sanawymdan öz özgerdişlerimi gizle',
'tog-watchlisthidebots' => 'Gözegçilik sanawymdan bot özgerdişlerini gizle',
'tog-watchlisthideminor' => 'Gözegçilik sanawymdan ujypsyzja özgerdişleri gizle',
-'tog-watchlisthideliu' => 'Gözegçilik sanawymda, sessiýa açan ulanyjylar tarapyndan edilen özgerdişleri görkezme',
+'tog-watchlisthideliu' => 'Gözegçilik sanawymda, hasaba girilgi ulanyjylaryň özgerdişlerini görkezme',
'tog-watchlisthideanons' => 'Gözegçilik sanawymda, anonim ulanyjylar tarapyndan edilen özgerdişleri görkezme',
'tog-watchlisthidepatrolled' => 'Gözegçilik sanawymdan patrullyk edilen özgerdişleri gizle',
'tog-ccmeonemails' => 'Beýleki ulanyjylara iberen e-poçtalarymyň nusgalaryny maňa-da iber',
'remembermypassword' => 'Sessiýamy şu kompýuterde ýatda sakla (iň köp $1 {{PLURAL:$1|günläp|günläp}})',
'yourdomainname' => 'Siziň domeniňiz:',
'externaldberror' => 'Ýa tassyklama maglumat bazasynyň säwligi bar ýa-da öz ulanyjy hasabyňyzy täzelemegiňize rugsat berilmeýär.',
-'login' => 'Sessiýa aç',
-'nav-login-createaccount' => 'Sessiýa aç / täze hasap edin',
+'login' => 'Hasaba gir',
+'nav-login-createaccount' => 'Hasaba gir / täze hasap aç',
'loginprompt' => '{{SITENAME}} saýtynda sessiýa açmak üçin kukileri işletmegiňiz zerurdyr.',
-'userlogin' => 'Sessiýa aç / täze hasap edin',
+'userlogin' => 'Hasaba gir / täze hasap aç',
'userloginnocreate' => 'Sessiýa aç',
-'logout' => 'Sessiýany ýap',
-'userlogout' => 'Sessiýany ýap',
-'notloggedin' => 'Sessiýa açmansyňyz',
+'logout' => 'Hasapdan çyk',
+'userlogout' => 'Hasapdan çyk',
+'notloggedin' => 'Hasaba girmänsiňiz',
'nologin' => "Siziň heniz hasabyňyz ýokmy? '''$1'''",
'nologinlink' => 'Onda özüňize bir hasap ediniň',
'createaccount' => 'Täze hasap aç',
'badretype' => 'Girizen parollaryňyz biri-birine gabat gelmeýär.',
'userexists' => 'Girizen ulanyjy adyňyz ulanylýar.
Başga bir at saýlamagyňyzy haýyş edýäris.',
-'loginerror' => 'Sessiýa açyş säwligi',
+'loginerror' => 'Hasaba girmekde säwlik',
'createaccounterror' => 'Hasaby döredip bolmaýar: $1',
'nocookiesnew' => 'Ulanyjy hasaby döredildi, ýöne sessiýa açmadyňyz.
{{SITENAME}} sessiýa açmak üçin kukilerden peýdalanýar.
Siziň kukileriňiz togtadylgy dur.
Olary işletmegiňizi we gaýtadan synanyşyp görmegiňizi haýyş edýäris.',
'noname' => 'Dogry bir ulanyjy adyny görkezmediňiz.',
-'loginsuccesstitle' => 'Sessiýa açyldy',
-'loginsuccess' => "'''{{SITENAME}} saýtynda \"\$1\" ulanyjy ady bilen sessiýa açdyňyz.'''",
+'loginsuccesstitle' => 'Hasaba girdiňiz',
+'loginsuccess' => "'''{{SITENAME}} saýtynda \"\$1\" ulanyjy ady bilen hasaba girdiňiz.'''",
'nosuchuser' => '"$1" diýen at bilen ulanyjy ýok.
Ulanyjy atlary baş hem-de setir harplara duýgurdyr.
Ýazylyşyny barlaň ýa-da [[Special:UserLogin/signup|täze hasap açyň]].',
'showpreview' => 'Deslapky syny görkez',
'showlivepreview' => 'Gönümel deslapky syn',
'showdiff' => 'Üýtgeşmeleri görkez',
-'anoneditwarning' => "'''Duýduryş:''' Sessiýa açmansyňyz. Şonuň üçin hem IP adresiňiz bu sahypanyň özgerdişler geçmişine ýazylyp alynjakdyr.",
+'anoneditwarning' => "'''Üns beriň:''' Hasaba girmänsiňiz. Şonuň üçin hem IP adresiňiz bu sahypanyň özgerdişler geçmişine ýazylyp alynar.",
'anonpreviewwarning' => "''Sessiýa açmadyňyz. Ýazdyrsaňyz, sahypanyň redaktirleme geçmişine IP adresiňiz ýazylar.''",
'missingsummary' => "'''Ýatlatma:''' Redaktirleme mazmunyny ýazmadyňyz.
Sahypany ýazdyr düwmesine ýene bir gezek bassaňyz, özgerdişiňiz mazmunsyz ýazdyrylar.",
'nosuchsectiontext' => 'Siz ýok bölümi redaktirlejek bolduňyz.
Sahypany görýän mahalayňyz onuň ady üýtgedilen ýa-da öçürilen bolmagy mümkin.',
'loginreqtitle' => 'Sessiýa açmagyňyz zerur',
-'loginreqlink' => 'sessiýa açyň',
+'loginreqlink' => 'hasaba gir',
'loginreqpagetext' => 'Başga sahypalary görmek üçin $1.',
'accmailtitle' => 'Parol iberildi.',
'accmailtext' => "[[User talk:$1|$1]] üçin ugralla döredilen parol $2 adresine iberildi.
'tooltip-pt-preferences' => 'Ileri tutmalaryňyz',
'tooltip-pt-watchlist' => 'Gözegçilikde saklaýan sahypalarym',
'tooltip-pt-mycontris' => 'Eden goşantlaryňyzyň sanawy',
-'tooltip-pt-login' => 'Sessiýa açmagyňyz maslahat berilýär, ýöne hökmany däl.',
+'tooltip-pt-login' => 'Hasaba girmegiňiz maslahat berilýär, ýöne hökmany däl.',
'tooltip-pt-anonlogin' => 'Sessiýa açmagyňyz maslahat berilýär, yöne hökmany däl',
-'tooltip-pt-logout' => 'Sessiýany ýap',
+'tooltip-pt-logout' => 'Hasapdan çyk',
'tooltip-ca-talk' => 'Sahypanyň mazmuny barada garaýşyňy beýan et',
'tooltip-ca-edit' => 'Bu sahypany redaktirläp bilersiňiz. Ýazdyrmankaňyz synlap görmekligi ýatdan çykarmaň.',
'tooltip-ca-addsection' => 'Täze bölüm başlat',
* <strong class="mw-specialpagerestricted">Çäklendirilen ýörite sahypalar.</strong>',
'specialpages-group-maintenance' => 'Tehniki abatlaýyş hasabatlary',
'specialpages-group-other' => 'Başga ýörite sahypalar',
-'specialpages-group-login' => 'Sessiýa aç / hasap edin',
+'specialpages-group-login' => 'Hasaba gir / täze hasap aç',
'specialpages-group-changes' => 'Soňky üýtgeşmeler we gündelikler',
'specialpages-group-media' => 'Media hasabatlary we ýüklemeler',
'specialpages-group-users' => 'Ulanyjylar we hukuklar',
'index-category' => 'Mga pahinang may talatuntunan',
'noindex-category' => 'Mga pahinang walang talatuntunan',
'broken-file-category' => 'Mga pahina na may sirang mga kawing ng talaksan',
+'categoryviewer-pagedlinks' => '($1) ($2)',
+
+'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
'about' => 'Patungkol',
'article' => 'Pahina ng nilalaman',
Tingnan ang [[Special:Version|pahina ng bersiyon]].',
'ok' => 'Sige',
+'pagetitle' => '$1 - {{SITENAME}}',
+'pagetitle-view-mainpage' => '{{SITENAME}}',
+'backlinksubtitle' => '← $1',
'retrievedfrom' => 'Ikinuha mula sa "$1"',
'youhavenewmessages' => 'Mayroon kang $1 ($2).',
'newmessageslink' => 'mga bagong mensahe',
'newmessagesdifflink' => 'huling pagbabago',
+'youhavenewmessagesfromusers' => 'Mayroon kang $1 magmula sa {{PLURAL:$3|ibang tagagamit|$3 mga tagagamit}} ($2).',
+'youhavenewmessagesmanyusers' => 'Mayroon kang $1 magmula sa maraming mga tagagamit ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|isang bagong mensahe|bagong mga mensahe}}',
+'newmessagesdifflinkplural' => 'huling {{PLURAL:$1|pagbabago|mga pagbabago}}',
'youhavenewmessagesmulti' => 'Mayroon kang mga bagong mensahe sa $1',
'editsection' => 'baguhin',
+'editsection-brackets' => '[$1]',
'editold' => 'baguhin',
'viewsourceold' => 'tingnan ang pinagmulan',
'editlink' => 'baguhin',
'site-atom-feed' => '$1 kargang Atom',
'page-rss-feed' => '"$1" kargang RSS',
'page-atom-feed' => '"$1" kargang Atom',
+'feed-atom' => 'Atom',
+'feed-rss' => 'RSS',
'red-link-title' => '$1 (hindi umiiral ang pahina)',
'sort-descending' => 'Pagsunud-sunurin na bumababa',
'sort-ascending' => 'Pagsunud-sunurin na tumataas',
'remembermypassword' => 'Tandaan ang paglagda ko sa kompyuter na ito (pinakamarami na ang $1 {{PLURAL:$1|araw|mga araw}})',
'securelogin-stick-https' => 'Manatiling konektado sa HTTPS matapos lumagda',
'yourdomainname' => 'Dominyo mo:',
+'password-change-forbidden' => 'Hindi mo maaaring palitan ang mga hudyat sa wiking ito.',
'externaldberror' => 'Maaaring may kamalian sa pagpapatotoo ng kalipunan ng mga dato o kaya hindi ka pinahintulutang isapanahon ng iyong panlabas na kuwenta o patnugutan.',
'login' => 'Lumagda',
'nav-login-createaccount' => 'Lumagda / lumikha ng kuwenta',
Paki-andar mo ang mga ito at sumubok uli.",
'nocookiesfornew' => 'Hindi nalikha ang akawnt ng tagagamit, dahil hindi namin matiyak ang pinagmulan nito.
Tiyaking mayroon kang pinagaganang mga otap, ikargang muli ang pahinang ito at subuking muli.',
+'nocookiesforlogin' => '{{int:nocookieslogin}}',
'noname' => 'Hindi mo tinukoy ang isang tanggap na pangalan ng tagagamit.',
'loginsuccesstitle' => 'Matagumpay ang paglagda',
'loginsuccess' => "'''Nakalagda ka na sa {{SITENAME}} bilang si \"\$1\".'''",
'noarticletext-nopermission' => 'Kasalukuyang walang teksto sa pahinang ito.
Maaari mong [[Special:Search/{{PAGENAME}}|hanapin ang pamagat ng pahinang ito]] sa ibang mga pahina,
o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} maghanap sa kaugnay na mga talaan]</span>.',
+'missing-revision' => 'Hindi umiiral ang rebisyong #$1 ng pahinang napangalanang "{{PAGENAME}}".
+
+Karaniwang itong dulot ng pagsunod sa isang wala na sa panahong kawing ng kasaysayan na papunta sa isang pahinang nabura na.
+Matatagpuan ang mga detalye sa loob ng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} talaan ng pagbura].',
'userpage-userdoesnotexist' => 'Hindi nakatala ang kuwenta ng tagagamit na "<nowiki>$1</nowiki>".
Pakisuri kung ibig mong likhain/baguhin ang pahinang ito.',
'userpage-userdoesnotexist-view' => 'Hindi nakatala ang kuwenta ng tagagamit na "$1".',
'template-semiprotected' => '(bahagyang nakasanggalang)',
'hiddencategories' => 'Ang pahinang ito ay kasapi sa {{PLURAL:$1|1 nakatagong kategorya|$1 nakatagong kategorya}}:',
'edittools' => '<!-- Ang teksto rito ay ipapakita sa ilalim ng mga pormularyo ng pagbabago at pagkarga. -->',
+'edittools-upload' => '-',
'nocreatetitle' => 'May hangganan ang paglikha ng pahina',
'nocreatetext' => 'Naglagay ng hangganan (restriksyon/limitasyon) ang {{SITENAME}} sa kakayahang makalikha ng bagong mga pahina.
Maaari kang bumalik at magbago ng isang umiiral na pahina, o kaya [[Special:UserLogin|lumagda o lumikha ng kuwenta/akawnt]].',
'permissionserrors' => 'Mga kamalian sa mga pahintulot',
'permissionserrorstext' => 'Wala kang pahintulot na gawin iyan, dahil sa sumusunod na {{PLURAL:$1|dahilan|mga dahilan}}:',
'permissionserrorstext-withaction' => 'Wala kang pahintulot na $2, dahil sa sumusunod na {{PLURAL:$1|dahilan|mga dahilan}}:',
-'recreate-moveddeleted-warn' => "'''Babala: Muli mong inililikha ang isang pahinang binura na dati.'''
+'recreate-moveddeleted-warn' => "'''Babala: Muli mong nililikha ang isang pahinang binura na dati.'''
Dapat mong isaalang-alang kung nararapat bang ipagpatuloy ang pagbago sa pahinang ito.
-Ang tala ng pagbubura at paglilipat para sa pahinang ito ay ibinigay dito para sa inyong kaginhawaan:",
+Ang tala ng pagbubura at paglilipat para sa pahinang ito ay ibinigay dito para sa kaginhawaan:",
'moveddeleted-notice' => 'Ibinura na ang pahinang ito.
Ang tala ng pagbubura at paglilipat para sa pahinang ito ibinigay sa baba para sa inyong pagsasangguni.',
'log-fulllog' => 'Tingnan ang buong tala',
'expansion-depth-exceeded-warning' => 'Lumampas ang pahina sa lalim ng paglawak',
'parser-unstrip-loop-warning' => 'Napansin ang silo ng hindi pagtalop',
'parser-unstrip-recursion-limit' => 'Nalampasan ang hangganan ng rekursiyon ng hindi pagtalop ($1)',
+'converter-manual-rule-error' => 'Napansin ang kamalian sa alituntunin ng kinakamay na pagpapalit ng wika',
# "Undo" feature
'undo-success' => 'Matatanggal ang pagbabago.
'mergehistory-comment' => 'Pinagsanib ang [[:$1]] sa [[:$2]]: $3',
'mergehistory-same-destination' => 'Pinagmulan at patutunguhan hindi dapat magkatulad',
'mergehistory-reason' => 'Dahilan:',
+'mergehistory-revisionrow' => '$1 ($2) $3 . . $4 $5 $6',
# Merge log
'mergelog' => 'Tala ng pagsasanib',
'editundo' => 'ibalik',
'diff-multi' => '({{PLURAL:$1|Isang panggitnang pagbabago|$1 panggitnang mga pagbabago}} ng {{PLURAL:$2|isang tagagamit|$2 mga tagagamit}} ang hindi ipinakikita.)',
'diff-multi-manyusers' => '({{PLURAL:$1|Isang panggitnang pagbabago|$1 panggitnang mga pagbabago}} ng {{PLURAL:$2|isang tagagamit|$2 mga tagagamit}} ang hindi ipinapakikita.)',
+'difference-missing-revision' => 'Hindi natagpuan ang {{PLURAL:$2|isang rebisyon|$2 mga rebisyon}} ng kaibahang ($1) ito.
+
+Karaniwang itong isinanhi ng pagsunod sa isang wala na sa panahong kawing sa pagkakaiba na papunta sa isang pahinang nabura na.
+Matatagpuan ang mga detalye sa loob ng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} talaan ng pagbura].',
# Search results
'searchresults' => 'Kinalabasan/Resulta ng paghahanap',
'username' => 'Bansag:',
'uid' => 'ID ng tagagamit:',
'prefs-memberingroups' => 'Kasapi ng {{PLURAL:$1|na pangkat|na mga pangkat}}:',
+'prefs-memberingroups-type' => '$1',
'prefs-registration' => 'Oras ng pagtatala:',
+'prefs-registration-date-time' => '$1',
'yourrealname' => 'Tunay na pangalan:',
'yourlanguage' => 'Wika:',
'yourvariant' => 'Iba pang anyo ng wika ng nilalaman:',
'userrights-notallowed' => 'Walang pahintulot ang akawnt mo na magdagdag o magtanggal ng mga karapatan ng tagagamit.',
'userrights-changeable-col' => 'Mga pangkat na maaari mong baguhin',
'userrights-unchangeable-col' => 'Mga pangkat na hindi mo mababago',
+'userrights-irreversible-marker' => '$1*',
# Groups
'group' => 'Pangkat:',
'right-writeapi' => 'Gamit ng sinulat na API',
'right-delete' => 'Burahin ang mga pahina',
'right-bigdelete' => 'Burahin ang mga pahinang may malaking mga kasaysayan',
+'right-deletelogentry' => 'Burahin at huwag burahin ang partikular na mga lahok sa talaan',
'right-deleterevision' => 'Burahin at tanggalin sa pagkabura ang isang partikular na mga pagbabago ng mga pahina',
'right-deletedhistory' => 'Tingnan ang mga binurang pinasok na kasaysayan, na wala ang kanilang nakakabit na teksto',
'right-deletedtext' => 'Tingnan ang naburang teksto at mga pagbabago sa pagitan ng dalawang mga rebisyon',
'minoreditletter' => 'm',
'newpageletter' => 'B',
'boteditletter' => 'b',
+'unpatrolledletter' => '!',
'number_of_watching_users_pageview' => '[$1 binabantayang {{PLURAL:$1|tagagamit|mga tagagamit}}]',
'rc_categories' => 'Itakda lang sa mga kaurian (ihiwalay sa pamamagitan ng "|")',
'rc_categories_any' => 'Kahit ano',
+'rc-change-size' => '$1',
'rc-change-size-new' => '$1 {{PLURAL:$1|byte|mga byte}} pagkaraan ng pagbabago',
'newsectionsummary' => '/* $1 */ bagong seksyon',
'rc-enhanced-expand' => 'Ipakita ang mga detalye (kailangan ng JavaScript)',
'watchthisupload' => 'Bantayan ang talaksang ito',
'filewasdeleted' => 'Isang talaksan na may ganitong pangalan ay naikarga dati at nabura. Kailangan mong tingnan ang $1 bago magpatuloy sa pagkarga nito muli.',
'filename-bad-prefix' => "Ang talaksan na ikakarga mo ay nagsisimula sa '''\"\$1\"''', na isang hindi naglalarawang pangalan na karaniwang tinatakda ng mga kamerang digital. Paki pili ang isang mas naglalarawang pangalan para sa iyong talaksan.",
+'filename-prefix-blacklist' => ' #<!-- leave this line exactly as it is --> <pre>
+# Ang palaugnayan ay ang sumusunod:
+# * Ang lahat ng mga bagay mula sa isang panitik na "#" hanggang sa katapusan ng isang guhit ay isang puna
+# * Bawat isang guhit na mayroong laman ay isang unlapi para sa tipikal na mga pangalan ng talaksan na kusang itinalaga ng mga kamerang dihital
+CIMG # Casio
+DSC_ # Nikon
+DSCF # Fuji
+DSCN # Nikon
+DUW # ilang mga teleponong mobilo
+IMG # heneriko
+JD # Jenoptik
+MGP # Pentax
+PICT # samu\'t sari
+ #</pre> <!-- leave this line exactly as it is -->',
'upload-success-subj' => 'Matagumpay na pagkakarga',
'upload-success-msg' => 'Matagumpay ang ikinarga mo mula sa [$2]. Makukuha ito mula rito: [[:{{ns:file}}:$1]]',
'upload-failure-subj' => 'Problema sa pagkarga',
'lockmanager-fail-releaselock' => 'Hindi mapakawalan ang kandado para sa "$1".',
'lockmanager-fail-db-bucket' => 'Hindi sapat na makaugnay sa mga kalipunang pandato ng kandado sa timba na $1.',
'lockmanager-fail-db-release' => 'Hindi mapakawalan ang mga kandado sa kalipunan ng dato na $1.',
+'lockmanager-fail-svr-acquire' => 'Hindi magawang kunin ang mga kandado sa tagapaghain na $1.',
'lockmanager-fail-svr-release' => 'Hindi mapakawalan ang mga kandado sa tagapaghain na $1.',
# ZipDirectoryReader
'uploadnewversion-linktext' => 'Magkarga ng isang bagong bersyon ng talaksang ito',
'shared-repo-from' => 'mula sa $1',
'shared-repo' => 'isang pinagsasaluhang repositoryo',
+'shared-repo-name-wikimediacommons' => 'Wikimedia Commons',
+'filepage.css' => '/* Ang Cascading Style Sheets na inilagay dito ay kabilang sa pahina ng paglalarawan ng talaksan, na kabilang din sa mga wiki ng dayuhang kliyente */',
# File reversion
'filerevert' => 'Ibalik sa dati ang $1',
'disambiguations' => 'Mga pahinang nakakawing sa mga pahina ng paglilinaw',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "Ang sumusunod ay mga pahinang may ugnay (link) sa isang '''pahinang naglilinaw'''.
-Dapat silang umugnay sa tamang paksa<br />
-Tinuturing ang isang pahina bilang pahinang naglilinaw kung ginagamit nito ang isang suleras (template) na nakaugnay mula sa [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Ang sumusunod na mga pahina ay naglalaman ng kahit na isang kawing na papunta sa isang '''pahina ng paglilinaw'''.
+Sa halip, maaaring kailanganing kumawing ang mga ito sa isang mas naaangkop na pahina.<br />
+Ang isang pahina ay itinuturing bilang pahina ng paglilinaw kung gumagamit ito ng isang suleras na nakakawing magmula sa [[MediaWiki:Disambiguationspage|MediaWiki:Pahina ng mga paglilinaw]].",
'doubleredirects' => 'Mga dobleng karga',
'doubleredirectstext' => 'Nagtatala ang pahinang ito ng mga pahinang pumupunta sa iba pang mga pahinang nililipatan. Naglalaman ang bawat hanay ng mga kawing sa una ang pangalawang kapupuntahan, maging ng puntiryang pangalawang kapupuntahan, na karaniwang "tunay" na puntiryang pahina, na dapat kinatuturuan ng unang pupuntahan.
'nlinks' => '$1 {{PLURAL:$1|ugnay|mga ugnay}}',
'nmembers' => '$1 {{PLURAL:$1|kasapi|mga kasapi}}',
'nrevisions' => '$1 {{PLURAL:$1|pagbabago|mga pagbabago}}',
-'nviews' => '$1 {{PLURAL:$1|nakita|mga nakikita}}',
+'nviews' => '$1 {{PLURAL:$1|pagtingin|mga pagtingin}}',
'nimagelinks' => 'Ginamit sa $1 {{PLURAL:$1|pahina|mga pahina}}',
'ntransclusions' => 'ginamit sa $1 {{plural:$1|pahina|mga pahina}}',
'specialpage-empty' => 'Walang resulta para sa ulat na ito.',
# Book sources
'booksources' => 'Mapagkukuhanang mga aklat',
'booksources-search-legend' => 'Maghanap ng mapagkukunang aklat',
+'booksources-isbn' => 'ISBN:',
'booksources-go' => 'Punta',
'booksources-text' => 'Matatagpuan sa ibaba ang mga tala ng mga ugnay sa ibang mga websayt na nagbebenta ng bago at nagamit na mga aklat, at maaring mayroon din
na iba pang impormasyon tungkol sa mga aklat na hinahanap mo:',
'listgrouprights-rights' => 'Mga karapatan',
'listgrouprights-helppage' => 'Help:Mga pangkat ng karapatan',
'listgrouprights-members' => '(tala ng mga kasapi)',
+'listgrouprights-right-display' => '<span class="listgrouprights-granted">$1 <tt>($2)</tt></span>',
+'listgrouprights-right-revoked' => '<span class="listgrouprights-revoked">$1 <tt>($2)</tt></span>',
'listgrouprights-addgroup' => 'Maaaring idagdag ang {{PLURAL:$2|pangkat|mga pangkat}} na: $1',
'listgrouprights-removegroup' => 'Maaaring tanggalin ang {{PLURAL:$2|pangkat|mga pangkat}} na: $1',
'listgrouprights-addgroup-all' => 'Maaaring idagdag ang lahat ng mga pangkat',
# User Messenger
'usermessage-summary' => 'Nag-iiwan ng mensaheng pangsistema.',
'usermessage-editor' => 'Mensahero ng sistema',
+'usermessage-template' => 'MediaWiki:UserMessage',
# Watchlist
'watchlist' => 'Mga binabantayan ko',
'rollback' => 'Mga pagbabagong may kaugnayan sa pagpapagulong na pabalik sa (mas) dati',
'rollback_short' => 'Pagulunging pabalik sa (mas) dati',
'rollbacklink' => 'pagulunging pabalik sa (mas) dati',
+'rollbacklinkcount' => 'pagulunging pabalik ang $1 {{PLURAL:$1|pagbabago|mga pagbabago}}',
+'rollbacklinkcount-morethan' => 'pagulunging pabalik ang mahigit sa $1 {{PLURAL:$1|pagbabago|mga pagbabago}}',
'rollbackfailed' => 'Nabigo ang pagpapagulong na pabalik sa (mas) dati',
'cantrollback' => 'Hindi maibalik ang pagbabago; tanging ang may-akda lamang ng pahinang ito ang huling tagapagambag/tagapaglathala.',
'alreadyrolled' => 'Hindi mapagulong na pabalik sa dati ang huling pagbabago ng [[$1]] ni ([[User talk:$2|Usapan]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
$1',
'undelete-show-file-confirm' => 'Nakatitiyak ka bang ibig mong tanawin ang isang nabura nang pagbabago ng talaksang "<nowiki>$1</nowiki>" mula $2 noong $3?',
'undelete-show-file-submit' => 'Oo',
+'undelete-revisionrow' => '$1 $2 ($3) $4 . . $5 $6 $7',
# Namespace form on various pages
'namespace' => 'Espasyo ng pangalan:',
'sp-contributions-username' => 'IP Address o bansag:',
'sp-contributions-toponly' => 'Ipakita lang ang mga pamamatnugot na mga huling rebisyon',
'sp-contributions-submit' => 'Hanapin',
+'sp-contributions-explain' => '',
# What links here
'whatlinkshere' => 'Mga nakaturo dito',
'proxyblocker-disabled' => 'Nakapatay ang pagharang sa proxy.',
'proxyblockreason' => 'Hinarang ang IP address mo dahil bukas na proxy ito. Makipag-ugnayan sa iyong tagabigay ng serbisyong Internet o suportang teknikal at ipaalam sa kanila itong seryesong suliranin sa seguridad.',
'proxyblocksuccess' => 'Tapos na.',
+'sorbs' => 'DNSBL',
'sorbsreason' => 'Nakalista ang IP address mo bilang isang bukas na proxy sa DNSBL na ginagamit ng sayt na ito.',
'sorbs_create_account_reason' => 'Nakalista ang IP address mo bilang isang bukas na proxy sa DNSBL na ginagamit ng sayt na ito. Hindi ka makakalikha ng akawnt',
'cant-block-while-blocked' => 'Hindi mo mahahadlangan/mahaharang ang ibang mga tagagamit habang hinahadlangan ka.',
'common.css' => '/* Ang inilagay na CSS dito ay gagamitin para sa lahat ng mga pabalat */',
'standard.css' => '/* Ang inilagay na CSS dito ay makakaapekto sa mga tagagamit ng Karaniwang pabalat */',
'nostalgia.css' => '/* Ang CSS na inilagay dito ay makakaapekto sa mga tagagamit ng pabalat na Nostalgia */',
-'cologneblue.css' => "/* Ang CSS na inilagay dito ay makakaapekto sa mga tagagamit ng pabalat na Bugkaw na Kolon (''Cologne Blue'') */",
+'cologneblue.css' => "/* Ang Cascading Style Sheets na inilagay dito ay makakaapekto sa mga tagagamit ng pabalat na Bughaw na Kolown (''Cologne Blue'') */",
'monobook.css' => '/* Ang CSS na inilagay dito ay makakaapekto sa mga tagagamit ng pabalat na Monobook */',
'myskin.css' => "/* Ang CSS na inilagay dito ay makakaapekto sa lahat ng mga tagagamit ng pabalat na Balatko (''MySkin'') */",
'chick.css' => "/* Ang CSS na inilagay dito ay makakaapekto sa mga tagagamit ng pabalat na ''Chick'' */",
'simple.css' => "/* Ang CSS na iniligay dito ay makakaapekto sa mga tagagamit ng Payak (''Simple'') na pabalat */",
'modern.css' => "/* Ang CSS na iniligay dito ay makakaapekto sa tagagamit ng Makabagong (''Modern'') pabalat */",
+'vector.css' => '/* Ang inilagay na CSS dito ay makakaapekto sa mga tagagamit ng pabalat na Vector */',
'print.css' => '/* Ang CSS na inilagay dito ay makakaapekto sa kalalabasan o resulta ng paglilimbag */',
'handheld.css' => "/* Ang CSS na inilagay dito ay makakaapekto sa mga aparatong nahahawakan (''handheld device'') batay sa itinakdang pabalat sa ''\$wgHandheldStyle'' */",
+'noscript.css' => '/* Ang inilagay na Cascading Style Sheets dito ay makakaapekto sa mga tagagamit na hindi nagpapagana ng JavaScript */',
+'group-autoconfirmed.css' => '/* Ang inilagay na Mga Pilas ng Estilong Lumalagaslas (Cascading Style Sheets o CSS) dito ay makakaapekto lamang sa mga tagagamit na kusang natiyak */',
+'group-bot.css' => '/* Ang inilagay na Mga Pilas ng Estilong Lumalagaslas (Cascading Style Sheets o CSS) dito ay makakaapekto lamang sa mga bot */',
+'group-sysop.css' => '/* Ang inilagay na Mga Pilas ng Estilong Lumalagaslas (Cascading Style Sheets) dito ay makakaapekto lamang sa mga tagapagpaandar ng sistema */',
+'group-bureaucrat.css' => '/* Ang inilagay na Mga Pilas ng Estilong Lumalagaslas (Cascading Style Sheets o CSS) dito ay makakaapekto lamang sa mga burokrata */',
# Scripts
-'common.js' => '/* Anumang JavaScript dito ay ikakarga para sa lahat ng mga tagagamit ng bawat pahinang ikinarga. */',
-'standard.js' => '/* Anumang JavaScript dito ay ikakarga para lahat ng mga tagagamit na gumagamit ng Karaniwang pabalat */',
-'nostalgia.js' => '/* Anumang JavaScript dito ay ikakarga para lahat ng mga tagagamit na gumagamit ng pabalat na Nostalgia */',
-'cologneblue.js' => '/* Anumang JavaScript dito ay ikakarga para sa tagagamit ng pabalat na Bughaw na Kolon */',
-'monobook.js' => '/* Anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na MonoBook */',
-'myskin.js' => '/* Anumang JavaScript dito ay ikakarga para sa tagagamit na gumagamit ng pabalat na Balatko */',
-'chick.js' => "/* Anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na ''Chick'' */",
-'simple.js' => '/* Anumang JavaScript dito ay ikakarga para sa tagagamit na gumagamit ng Payak na pabalat */',
-'modern.js' => '/* Anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng Makabagong pabalat */',
+'common.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa lahat ng mga tagagamit ng bawat pahinang ikinarga. */',
+'standard.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng Karaniwang pabalat */',
+'nostalgia.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na Nostalgia */',
+'cologneblue.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit ng pabalat na Cologne Blue o Bughaw na Kolown */',
+'monobook.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na MonoBook */',
+'myskin.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa tagagamit na gumagamit ng pabalat na MySkin o Balat Ko */',
+'chick.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na Chick */',
+'simple.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng Payak na pabalat */',
+'modern.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng Modernong pabalat */',
+'vector.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na gumagamit ng pabalat na Vector */',
+'group-autoconfirmed.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagagamit na kusang natiyak lamang */',
+'group-bot.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga bot lamang */',
+'group-sysop.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga tagapagpaandar ng sistema lamang */',
+'group-bureaucrat.js' => '/* Ang anumang JavaScript dito ay ikakarga para sa mga burokrata lamang */',
# Metadata
'notacceptable' => 'Hindi makapagbigay ng dato ang serbidor ng wiki sa anyong mababasa ng iyong kliyente.',
# Skin names
'skinname-standard' => 'Klasiko',
'skinname-nostalgia' => 'Nostalhiya',
-'skinname-cologneblue' => 'Bughaw na Kolon',
+'skinname-cologneblue' => 'Bughaw na Kolown',
'skinname-monobook' => 'MonoAklat ("isang aklat")',
'skinname-myskin' => 'PabalatKo',
'skinname-chick' => "\"Pambabae\" (''Chick'')",
'skinname-simple' => 'Payak',
'skinname-modern' => 'Makabago (Moderno)',
+'skinname-vector' => 'Vector',
# Patrolling
'markaspatrolleddiff' => 'Tatakan bilang napatrolya na',
Maaaring manganib ang iyong sistema kapag ipinagana mo ito.",
'imagemaxsize' => "Takdang hangganan sa laki ng larawan: <br />''(para sa mga pahina ng paglalarawan ng talaksan)''",
'thumbsize' => 'Maliit na sukat (parang "kuko sa hinlalaki" lamang):',
+'widthheight' => '$1 × $2',
'widthheightpage' => '$1 × $2, $3 {{PLURAL:$3|pahina|mga pahina}}',
'file-info' => 'sukat ng talaksan: $1, tipo ng MIME: $2',
'file-info-size' => '$1 × $2 piksel, sukat ng talaksan: $3, tipo ng MIME: $4',
'sp-newimages-showfrom' => 'Ipakita ang mga bagong talaksang nagsisimula mula $2, $1',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
-'hours-abbrev' => '$1o',
+'video-dims' => '$1, $2 × $3',
+'seconds-abbrev' => '$1 segundo',
+'minutes-abbrev' => '$1 minuto',
+'hours-abbrev' => '$1 oras',
+'days-abbrev' => '$1 araw',
'seconds' => '{{PLURAL:$1|$1 segundo|$1 mga segundo}}',
'minutes' => '{{PLURAL:$1|$1 minuto|$1 mga minuto}}',
'hours' => '{{PLURAL:$1|$1 horas|$1 mga oras}}',
Ang unang kawing sa isang linya ay dapat na nakakawing sa isang talaksang may masamang kalagayan.
Anumang susunod na mga kawing sa pinanggalingang linya ay tinuturing na mga eksepsyon o bukod-tangi, iyong mga pahina kung saan ang mga talaksan ay maaaring lumitaw sa loob ng linya.',
+/*
+Short names for language variants used for language conversion links.
+To disable showing a particular link, set it to 'disable', e.g.
+'variantname-zh-sg' => 'disable',
+Variants for Chinese language
+*/
+'variantname-zh-hans' => 'hans',
+'variantname-zh-hant' => 'hant',
+'variantname-zh-cn' => 'cn',
+'variantname-zh-tw' => 'tw',
+'variantname-zh-hk' => 'hk',
+'variantname-zh-mo' => 'mo',
+'variantname-zh-sg' => 'sg',
+'variantname-zh-my' => 'my',
+'variantname-zh' => 'zh',
+
+# Variants for Gan language
+'variantname-gan-hans' => 'hans',
+'variantname-gan-hant' => 'hant',
+'variantname-gan' => 'gan',
+
+# Variants for Serbian language
+'variantname-sr-ec' => 'sr-ec',
+'variantname-sr-el' => 'sr-el',
+'variantname-sr' => 'sr',
+
+# Variants for Kazakh language
+'variantname-kk-kz' => 'kk-kz',
+'variantname-kk-tr' => 'kk-tr',
+'variantname-kk-cn' => 'kk-cn',
+'variantname-kk-cyrl' => 'kk-cyrl',
+'variantname-kk-latn' => 'kk-latn',
+'variantname-kk-arab' => 'kk-arab',
+'variantname-kk' => 'kk',
+
+# Variants for Kurdish language
+'variantname-ku-arab' => 'ku-Arab',
+'variantname-ku-latn' => 'ku-Latn',
+'variantname-ku' => 'ku',
+
+# Variants for Tajiki language
+'variantname-tg-cyrl' => 'tg-Cyrl',
+'variantname-tg-latn' => 'tg-Latn',
+'variantname-tg' => 'tg',
+
+# Variants for Inuktitut language
+'variantname-ike-cans' => 'ike-Cans',
+'variantname-ike-latn' => 'ike-Latn',
+'variantname-iu' => 'iu',
+
+# Variants for Tachelhit language
+'variantname-shi-tfng' => 'shi-Tfng',
+'variantname-shi-latn' => 'shi-Latn',
+'variantname-shi' => 'shi',
+
# Metadata
'metadata' => 'Metadatos',
'metadata-help' => 'Naglalaman ang talaksang ito ng karagdagang kabatiran na maaaring idinagdag mula sa isang kamerang dihital o iskaner na ginamit para likhain o para maging dihital ito.
* gpslatitude
* gpslongitude
* gpsaltitude',
+'metadata-langitem' => "'''$2:''' $1",
+'metadata-langitem-default' => '$1',
# EXIF tags
'exif-imagewidth' => 'Lapad',
'exif-exposuretime' => 'Oras ng pagkakalantad',
'exif-exposuretime-format' => '$1 seg ($2)<!--seg = segundo (seconds)-->',
'exif-fnumber' => 'F Bilang',
+'exif-fnumber-format' => 'f/$1',
'exif-exposureprogram' => 'Programa ng paglalantad',
'exif-spectralsensitivity' => 'Sensitibidad sa ispektrum',
'exif-isospeedratings' => 'Grado ng bilis ng ISO',
'exif-lightsource' => 'Pinagmumulan ng liwanag',
'exif-flash' => "Pangkisap (''flash'')",
'exif-focallength' => 'Haba ng lenteng pampokus (pantuon)',
+'exif-focallength-format' => '$1 mm',
'exif-subjectarea' => 'Saklaw na paksa',
'exif-flashenergy' => "Lakas ng kisap (''flash'')",
'exif-focalplanexresolution' => 'Resolusyong X ng kalatagan o lapyang pampokus',
'exif-gpsareainformation' => 'Pangalan ng lugar ng GPS',
'exif-gpsdatestamp' => 'Petsa ng GPS',
'exif-gpsdifferential' => 'Pagtatama sa pakakaiba ng GPS',
+'exif-coordinate-format' => '$1° $2′ $3″ $4',
'exif-jpegfilecomment' => 'Puna sa talaksang JPEG',
'exif-keywords' => 'Mga susing-salita',
'exif-worldregioncreated' => 'Rehiyon ng mundo kung saan kinuhanan ang larawan',
'exif-originalimageheight' => 'Taas ng larawan bago ito inani',
'exif-originalimagewidth' => 'Lapad ng larawan bago ito inani',
+# Make & model, can be wikified in order to link to the camera and model name
+'exif-contact-value' => '$1
+
+$2
+<div class="adr">
+$3
+
+$4, $5, $6 $7
+</div>
+$8',
+'exif-subjectnewscode-value' => '$2 ($1)',
+
# EXIF attributes
'exif-compression-1' => 'Walang kompresyon',
'exif-compression-2' => 'CCITT Pangkat 3 1-kodigo sa haba ng pagtakbo ng Pangdimensiyong Huffman na May Bahagyang Pagbabago',
'exif-compression-3' => 'Kodigo ng Pangkat 3 ng CCITT',
'exif-compression-4' => 'Kodigo ng Pangkat 4 ng CCITT',
+'exif-compression-5' => 'LZW',
+'exif-compression-6' => 'JPEG (luma)',
+'exif-compression-7' => 'JPEG',
+'exif-compression-8' => 'Paimpisin (Adobe)',
+'exif-compression-32773' => 'PackBits (Macintosh RLE)',
+'exif-compression-32946' => 'Paimpisin (PKZIP)',
+'exif-compression-34712' => 'JPEG2000',
'exif-copyrighted-true' => 'Nakakarapatang-ari',
'exif-copyrighted-false' => 'Nasasakupan ng madla',
+'exif-photometricinterpretation-2' => 'RGB',
+'exif-photometricinterpretation-6' => 'YCbCr',
+
'exif-unknowndate' => 'Hindi alam na araw',
'exif-orientation-1' => 'Karaniwan',
'exif-planarconfiguration-1' => 'pagkaayos sa malalaking bahagi (chunky)',
'exif-planarconfiguration-2' => 'planar na pagkaayos',
+'exif-xyresolution-i' => '$1 dpi',
+'exif-xyresolution-c' => '$1 dpc',
+
+'exif-colorspace-1' => 'sRGB',
'exif-colorspace-65535' => 'Hindi nakaakma sa pamantayang sukat',
'exif-componentsconfiguration-0' => 'wala',
+'exif-componentsconfiguration-1' => 'Y',
+'exif-componentsconfiguration-2' => 'Cb',
+'exif-componentsconfiguration-3' => 'Cr',
+'exif-componentsconfiguration-4' => 'R',
+'exif-componentsconfiguration-5' => 'G',
+'exif-componentsconfiguration-6' => 'B',
'exif-exposureprogram-0' => 'Hindi nabigyan ng kahulugan',
'exif-exposureprogram-1' => 'Manwal',
'exif-isospeedratings-overflow' => 'Mas mahigit kaysa sa 65535',
+'exif-maxaperturevalue-value' => '$1 APEX (f/$2)',
+
'exif-iimcategory-ace' => 'Sining, kalinangan at kaaliwan',
'exif-iimcategory-clj' => 'Krimen at batas',
'exif-iimcategory-dis' => 'Mga kalamidad at mga sakuna',
'confirmemail_invalid' => 'Hindi tamang kodigo ng kumpirmasyon. Maaaring lumagpas na sa taning ang kodigo.',
'confirmemail_needlogin' => 'Kailangan mong $1 upang kumpirmahin/mapatotohanan ang iyong adres ng e-liham.',
'confirmemail_success' => 'Nakumpirma/napatotohanan na ang adres ng e-liham mo. Maaari ka ng [[Special:UserLogin|lumagda]] at maglibang sa wiki.',
-'confirmemail_loggedin' => 'Nakumpirma/napatotohanan na ngayon ang adres ng e-liham mo.',
+'confirmemail_loggedin' => 'Natiyak na ngayon ang tirahan ng e-liham mo.',
'confirmemail_error' => 'May nangyaring kamalian sa pagsasagip ng iyong kumpirmasyon.',
'confirmemail_subject' => 'Kumpirmasyon/pagpapatotoong pang-adres ng e-liham ng {{SITENAME}}',
'confirmemail_body' => 'May isa, maaaring ikaw, na mula sa adres ng IP na $1,
'confirm-unwatch-button' => 'Sige',
'confirm-unwatch-top' => 'Aalisin ba ang pahinang ito mula sa bantayan mo?',
+# Separators for various lists, etc.
+'semicolon-separator' => '; ',
+'comma-separator' => ', ',
+'colon-separator' => ': ',
+'autocomment-prefix' => '- ',
+'pipe-separator' => ' | ',
+'word-separator' => ' ',
+'ellipsis' => '...',
+'percent' => '$1%',
+'parentheses' => '($1)',
+'brackets' => '[$1]',
+
# Multipage image navigation
'imgmultipageprev' => '← nakaraang pahina',
'imgmultipagenext' => 'susunod na pahina →',
'autoredircomment' => 'Ikinakarga sa [[$1]]',
'autosumm-new' => "Nilikha ang pahina na may '$1'",
+# Size units
+'size-bytes' => '$1 B',
+'size-kilobytes' => '$1 KB',
+'size-megabytes' => '$1 MB',
+'size-gigabytes' => '$1 GB',
+'size-terabytes' => '$1 TB',
+'size-petabytes' => '$1 PB',
+'size-exabytes' => '$1 EB',
+'size-zetabytes' => '$1 ZB',
+'size-yottabytes' => '$1 YB',
+
+# Bitrate units
+'bitrate-bits' => '$1bps',
+'bitrate-kilobits' => '$1kbps',
+'bitrate-megabits' => '$1Mbps',
+'bitrate-gigabits' => '$1Gbps',
+'bitrate-terabits' => '$1Tbps',
+'bitrate-petabits' => '$1Pbps',
+'bitrate-exabits' => '$1Ebps',
+'bitrate-zetabits' => '$1Zbps',
+'bitrate-yottabits' => '$1Ybps',
+
# Live preview
'livepreview-loading' => 'Ikinakarga...',
'livepreview-ready' => 'Ikinakarga… Handa na!',
'watchlistedit-noitems' => 'Hindi naglalaman ng mga pamagat ang iyong talaan ng mga binabantayan.',
'watchlistedit-normal-title' => 'Baguhin ang talaan ng mga binabantayan',
'watchlistedit-normal-legend' => 'Tanggalin ang mga pamagat mula sa binabantayan',
-'watchlistedit-normal-explain' => 'Pinapakita sa ibaba ang mga pamagat na nasa talaan mo ng mga binabantayan.
-Para tanggalin ang isang pamagat, lagyan ng tsek ang kahon katabi nito, at pindutin ang "{{int:Watchlistedit-normal-submit}}".
+'watchlistedit-normal-explain' => 'Ipinapakita sa ibaba ang mga pamagat na nasa talaan mo ng mga binabantayan.
+Upang matanggal ang isang pamagat, lagyan ng tsek ang kahong katabi nito, at pindutin ang "{{int:Watchlistedit-normal-submit}}".
Maaari mo ring [[Special:EditWatchlist/raw|baguhin ang hilaw na talaan]].',
'watchlistedit-normal-submit' => 'Tanggalin ang mga Pamagat',
'watchlistedit-normal-done' => 'Tinatanggal mula sa iyong talaan ng mga binabantayan ang {{PLURAL:$1|1 pamagat|$1 mga pamagat}}:',
'hebrew-calendar-m10' => 'Tamuz',
'hebrew-calendar-m11' => 'Av',
'hebrew-calendar-m12' => 'Elul',
+'hebrew-calendar-m1-gen' => 'Tishrei',
+'hebrew-calendar-m2-gen' => 'Cheshvan',
+'hebrew-calendar-m3-gen' => 'Kislev',
+'hebrew-calendar-m4-gen' => 'Tevet',
+'hebrew-calendar-m5-gen' => 'Shevat',
+'hebrew-calendar-m6-gen' => 'Adar',
+'hebrew-calendar-m6a-gen' => 'Adar I',
+'hebrew-calendar-m6b-gen' => 'Adar II',
+'hebrew-calendar-m7-gen' => 'Nisan',
+'hebrew-calendar-m8-gen' => 'Iyar',
+'hebrew-calendar-m9-gen' => 'Sivan',
+'hebrew-calendar-m10-gen' => 'Tamuz',
+'hebrew-calendar-m11-gen' => 'Av',
+'hebrew-calendar-m12-gen' => 'Elul',
# Signatures
'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|makipag-usap]])',
+'timezone-utc' => 'UTC',
# Core parser functions
'unknown_extension_tag' => 'Hindi nalalamang tatak ng karugtong na "$1"',
'version-variables' => 'Mga bagay na nababago/nagbabago',
'version-antispam' => 'Pag-iwas sa masasamang mga e-liham',
'version-skins' => 'Mga pabalat',
+'version-api' => 'API',
'version-other' => 'Iba pa',
'version-mediahandlers' => 'Mga tagahawak/tagapamahala ng midya',
'version-hooks' => 'Mga pangkawit',
'version-hook-name' => 'Pangalan ng pangkawit',
'version-hook-subscribedby' => 'Sinuskribi ng/ni/nina',
'version-version' => '(Bersyon $1)',
+'version-svn-revision' => '(r$2)',
'version-license' => 'Lisensiya',
'version-poweredby-credits' => "Ang wiking ito ay pinapatakbo ng '''[//www.mediawiki.org/ MediaWiki]''', karapatang-ari © 2001-$1 $2.",
'version-poweredby-others' => 'iba pa',
'version-entrypoints' => 'Mga URL na butas-pasukan',
'version-entrypoints-header-entrypoint' => 'Butas na pasukan',
'version-entrypoints-header-url' => 'URL',
+'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Landas ng artikulo]',
+'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Landas ng panitik]',
# Special:FilePath
'filepath' => 'Lokasyon ng talaksan (file path)',
* <span class="mw-specialpagerestricted">Pinaghihigpitang natatanging mga pahina.</span>',
'specialpages-group-maintenance' => 'Mga pagpapanatiling ulat',
'specialpages-group-other' => 'Iba pang natatanging mga pahina',
-'specialpages-group-login' => 'Lumagda/tumala',
+'specialpages-group-login' => 'Lumagda / lumikha ng akawnt',
'specialpages-group-changes' => 'Mga huling binago at mga tala',
'specialpages-group-media' => 'Mga ulat ng midya at mga pagkarga',
'specialpages-group-users' => 'Mga tagagamit at mga karapatan',
'duration-millennia' => '$1 {{PLURAL:$1|milenyo|mga milenyo}}',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Hindi magawang kunin ang mga kandado sa tagapaghain na $1.',
+'api-error-filetype-banned-type' => 'Ang $1 {{PLURAL:$4|ay isang hindi pinapahintulutang uri ng talaksan|ay hindi pinapahintulutang mga uri ng talaksan}}. Ang pinapayagang {{PLURAL:$3|uri ng talaksan ay ang|mga uri ng talaksan ay ang mga}} $2.',
);
'tog-watchlisthidebots' => 'Нијо кардеј ботон дәгишон ноғо доә сијоһиәдә',
'tog-watchlisthideminor' => 'Нијо кардеј гәдә дәгишон ноғо доә сијоһиәдә',
+'underline-always' => 'Һежо',
+
# Dates
'sunday' => 'Ишамбә',
'monday' => 'Дышанбә',
'copyrightpage' => '{{ns:project}}:Мыәллифә һуғуғ',
'currentevents' => 'Есәтнә һодисон',
'currentevents-url' => 'Project: Есәтнә һодисон',
-'disclaimers' => 'Чы мәсулијјәтику имтина.',
+'disclaimers' => 'Че мәсулијјәтику имтино.',
'disclaimerpage' => 'Project:Дејни бә гиј ныгәтеј',
'edithelp' => 'Арајиш бо редактә кардеј',
'edithelppage' => 'Help:Арајиш бо сәрост кардеј',
Ым сәһифә чоәдәнә дуз карде зәруријјәти јохләмишкәнән.
Жинтоно нышу доә быә бычи ым сәһифә позулмуш быә.",
'moveddeleted-notice' => 'Ым сәһифә молә быә.
-Арајиши горнә жинтоно нишо доә быән чы сәһифә молә ијән ном дәгиш кардә нывыштәјон.',
+Арајиши горнә жинтоно нишо доә быән че сәһифә молә ијән ном дәгиш кардә нывыштәјон.',
# Parser/template warnings
'post-expand-template-inclusion-warning' => "'''Дығғәт:''' Дахыл кардә быә ғәлибон сәкыштә памјә ве јоле.
'searchmenu-exists' => "'''Бы вики-нәхшәдә һесте сәһифә «[[:$1]]»'''",
'searchmenu-new' => "'''Сәһифә офәјеј «[[:$1]]» бә ым вики-нахшәдә!'''",
'searchprofile-articles' => 'Әсосә сәһифон',
-'searchprofile-project' => 'Чы араијшон ијән нахшон сәһифон',
+'searchprofile-project' => 'Че араијшон ијән нахшон сәһифон',
'searchprofile-images' => 'Мултимедијә',
'searchprofile-everything' => 'Һар вырәдә',
'searchprofile-advanced' => 'һовуж',
'search-suggest' => 'Еһтимол шымә нәзәрәдә ым гәтејдәбијон: $1',
'searchrelated' => 'ангыл кардә быә',
'searchall' => 'Һәммәј',
-'showingresultsheader' => "{{PLURAL:$5|Нәтиҹә'''$1''' из '''$3'''|Нәтиҹон '''$1 — $2''' чы '''$3'''}} бо '''$4'''",
+'showingresultsheader' => "{{PLURAL:$5|Нәтиҹә'''$1''' из '''$3'''|Нәтиҹон '''$1 — $2''' че '''$3'''}} бо '''$4'''",
'search-nonefound' => 'Бә шымә хәбәсә ујғун омә сәкыштә пәјдо ныбе.',
'powersearch-field' => 'Нәве',
'powersearch-toggleall' => 'Һәммәј',
'filehist-current' => 'есәтнә',
'filehist-datetime' => 'Тарых/Вахт',
'filehist-thumb' => 'Гәдә шикил',
-'filehist-thumbtext' => 'Миниатјур бо рәвојәти чы вахтику $1',
+'filehist-thumbtext' => 'Миниатјур бо рәвојәти че вахтику $1',
'filehist-user' => 'Иштирокәкә',
'filehist-dimensions' => 'Објекти улгу',
'filehist-comment' => 'Ғејд',
'linkstoimage' => '{{PLURAL:$1|сәһифә|$1 сәһифә}} сәбон вардә бә ын фајл:',
'nolinkstoimage' => 'Бә ым фајли сәбон вардә сәһифон нин.',
'sharedupload-desc-here' => 'Ым фајл чыјо пегәтә быә $1 ијән бәзыне истифодә бе бә ҹо нәхшонәдә.
-Мәлумот чы әчәј [$2 тәсвири сәһифәку] бә жиј доә быә.',
+Мәлумот чн әчәј [$2 тәсвири сәһифәку] бә жиј доә быә.',
# Random page
'randompage' => 'Рајрастә мәғолә',
# Special:AllPages
'allpages' => 'Һәммәј сәһифон',
-'alphaindexline' => 'чы $1 тоса $2',
+'alphaindexline' => 'че $1 тоса $2',
'allarticles' => 'Һәммәј сәһифон',
'allpagessubmit' => 'Бә вырә роснијеј',
'allmessagesname' => 'Хәбә',
'allmessagesdefault' => 'Иминә огәтә быә мәтн',
'allmessages-filter-all' => 'Һаммај',
+'allmessages-filter-submit' => 'Давард',
# Thumbnails
'thumbnail-more' => 'Һејве кардеј',
'tooltip-t-whatlinkshere' => 'Бә ым сәһифә сәбон вардә һәммәј вики сәһифон сијоһи',
'tooltip-t-recentchangeslinked' => 'Охонә дәгишон сәһифонәдә, бә ком сәһифон сәбон вардә ым сәһифә',
'tooltip-feed-atom' => 'Транслјасијә кардеј бә Atom бо ым сәһифә',
-'tooltip-t-contributions' => 'Чы иштирок кардәкәси дагиш кардә быә сәһифон сијоһи',
+'tooltip-t-contributions' => 'Че иштирок кардәкәси дагиш кардә быә сәһифон сијоһи',
'tooltip-t-emailuser' => 'Бы иштироәкә номә вығәнде',
'tooltip-t-upload' => 'Шикилон јаанки мултимедијә фајлон бо жај',
'tooltip-t-specialpages' => 'Хыдмәтә сәһифон сијоһи',
# EXIF tags
'exif-imagewidth' => 'Һовужи',
'exif-imagelength' => 'Былынди',
+'exif-source' => 'Сәвон',
'exif-languagecode' => 'Зывон',
'exif-gaincontrol-0' => 'Ни',
'exif-saturation-0' => 'Ади',
+'exif-dc-publisher' => 'Нәшрәкә',
+
# External editor support
'edit-externally' => 'Редактә кардеј ым фајли де заһири програм',
'edit-externally-help' => '(Бо мыффәссәлә мәлумотон бә [//www.mediawiki.org/wiki/Manual:External_editors дәрсәвон бо сохтәј] дијә быкан)',
'namespacesall' => 'һәммәј',
'monthsall' => 'һәммәј',
+# Table pager
+'table_pager_limit_submit' => 'Давард',
+
# Watchlist editing tools
'watchlisttools-view' => 'Сәһифонәдә дәгишон сијоһику',
'watchlisttools-edit' => 'Дијә кардеј/сәрост кардеј сијоһи',
* @author Mirzali
* @author Mskyrider
* @author Myildirim2007
+ * @author Nazif İLBEK
* @author Reedy
* @author Runningfridgesrule
* @author Sadrettin
'tog-hideminor' => 'Son değişiklikler sayfasında küçük değişiklikleri gizle',
'tog-hidepatrolled' => 'Son değişikliklerde gözden geçirilen düzenlemeleri gizle',
'tog-newpageshidepatrolled' => 'Kontrol edilmiş sayfaları yeni sayfalar listesinde gizle',
-'tog-extendwatchlist' => 'İzleme listesini sadece son değil, tüm değişiklikleri görmek için genişlet',
-'tog-usenewrc' => 'Gelişmiş son değişiklikleri kullan (JavaScript gerekir)',
+'tog-extendwatchlist' => 'İzleme listesini sadece en son değil, tüm değişiklikleri göstermek için genişlet',
+'tog-usenewrc' => 'Son değişiklikler sayfasındaki ve izleme listesindeki değişiklikleri gruplandırma (JavaScript gerektirir)',
'tog-numberheadings' => 'Başlıkları otomatik numaralandır',
-'tog-showtoolbar' => 'Düzenleme yaparken araç çubuğunu göster (JavaScript gerekir)',
-'tog-editondblclick' => 'Çift tıklayarak sayfayı düzenle (JavaScript gerekir)',
-'tog-editsection' => 'Bölümleri [değiştir] bağlantıları ile düzenlemeyi etkinleştir',
-'tog-editsectiononrightclick' => 'Bölümleri bölüm başlığına sağ tıklayarak değiştirebilme olanağı ver (JavaScript)',
+'tog-showtoolbar' => 'Düzenleme yaparken araç çubuğunu göster (JavaScript gerektirir)',
+'tog-editondblclick' => 'Çift tıklayarak sayfaları düzenle (JavaScript gerektirir)',
+'tog-editsection' => 'Bölümleri [{{int:Editsection}}] bağlantıları ile düzenlemeyi etkinleştir',
+'tog-editsectiononrightclick' => 'Bölüm başlığına sağ tıklayarak bölümleri düzenleyebilme olanağı ver (JavaScript gerektirir)',
'tog-showtoc' => 'İçindekiler tablosunu göster (3 taneden fazla başlığı olan sayfalar için)',
'tog-rememberpassword' => 'Girişimi bu tarayıcıda hatırla (en fazla $1 {{PLURAL:$1|gün|gün}} için)',
-'tog-watchcreations' => 'Oluşturmuş olduğum sayfaları izleme listeme ekle',
-'tog-watchdefault' => 'Değişiklik yapılan sayfayı izleme listesine ekle',
-'tog-watchmoves' => 'Taşıdığım sayfaları izleme listeme ekle',
-'tog-watchdeletion' => 'Sildiğim sayfaları izleme listeme ekle',
-'tog-minordefault' => "Değişikliği 'küçük değişiklik' olarak seçili getir",
-'tog-previewontop' => 'Önizlemeyi yazma alanın üstünde göster',
-'tog-previewonfirst' => 'Değiştirmede önizlemeyi göster',
-'tog-nocache' => 'Tarayıcı sayfalarını bellekleme',
-'tog-enotifwatchlistpages' => 'Sayfa değişikliklerinde bana e-posta gönder',
-'tog-enotifusertalkpages' => 'Kullanıcı sayfamda değişiklik olduğunda bana e-posta gönder',
-'tog-enotifminoredits' => 'Sayfalardaki küçük değişikliklerde de bana e-posta gönder',
+'tog-watchcreations' => 'Açtığım sayfaları ve yüklediğim dosyaları izleme listeme ekle',
+'tog-watchdefault' => 'Düzenleme yaptığım sayfaları ve dosyaları izleme listeme ekle',
+'tog-watchmoves' => 'Taşıdığım sayfaları ve dosyaları izleme listeme ekle',
+'tog-watchdeletion' => 'Sildiğim sayfaları ve dosyaları izleme listeme ekle',
+'tog-minordefault' => 'Varsayılan olarak bütün düzenlemeleri küçük olarak işaretle',
+'tog-previewontop' => 'Ön izlemeyi düzenleme kutusunun üstünde göster',
+'tog-previewonfirst' => 'İlk düzenlemede ön izlemeyi göster',
+'tog-nocache' => 'Tarayıcı sayfalarını önbelleğe almayı devre dışı bırak',
+'tog-enotifwatchlistpages' => 'İzleme listemdeki bir sayfanın ya da dosyanın değiştirilmesi durumunda bana e-posta gönder',
+'tog-enotifusertalkpages' => 'Kullanıcı mesaj sayfamda değişiklik olduğunda bana e-posta gönder',
+'tog-enotifminoredits' => 'Sayfalardaki ve dosyalardaki küçük değişikliklerde bana e-posta gönder',
'tog-enotifrevealaddr' => 'E-posta adresimi bildiri postalarımda göster.',
'tog-shownumberswatching' => 'İzleyen kullanıcı sayısını göster',
'tog-oldsig' => 'Mevcut imza:',
'tog-fancysig' => 'İmzaya vikimetin muamelesi yap (otomatik bir bağlantı olmadan)',
-'tog-externaleditor' => 'Harici düzenleyici kullan (deneyimli kullanıcılar içindir; bilgisayarınızda özel ayarlar gerektirir. [detaylı bilgi için : //www.mediawiki.org/wiki/Manual:External_editors])',
-'tog-externaldiff' => 'Harici karşılaştırıcı kullan (deneyimli kullanıcılar içindir; bilgisayarınızda özel ayarlar gerektirir. [detaylı bilgi için : //www.mediawiki.org/wiki/Manual:External_editors])',
-'tog-showjumplinks' => '"Git" bağlantısı etkinleştir',
-'tog-uselivepreview' => 'Canlı önizleme özelliğini kullan (JavaScript) (daha deneme aşamasında)',
+'tog-externaleditor' => 'Varsayılan olarak harici düzenleyici kullan (deneyimli kullanıcılar içindir ve bilgisayarınızda özel ayarlar gerektirir. [//www.mediawiki.org/wiki/Manual:External_editors Ayrıntılı bilgi için tıklayın.])',
+'tog-externaldiff' => 'Varsayılan olarak harici karşılaştırıcı kullan (deneyimli kullanıcılar içindir ve bilgisayarınızda özel ayarlar gerektirir. [//www.mediawiki.org/wiki/Manual:External_editors Ayrıntılı bilgi için tıklayın.])',
+'tog-showjumplinks' => '"{{int:jumpto}}" erişilebilirlik bağlantısı etkinleştir',
+'tog-uselivepreview' => 'Canlı ön izlemeyi kullan (JavaScript gerektirir ve özellik deneme aşamasındadır)',
'tog-forceeditsummary' => 'Özeti boş bıraktığımda beni uyar',
-'tog-watchlisthideown' => 'İzleme listemden benim değişikliklerimi gizle',
+'tog-watchlisthideown' => 'İzleme listemden düzenlemelerimi gizle',
'tog-watchlisthidebots' => 'İzleme listemden bot değişikliklerini gizle',
'tog-watchlisthideminor' => 'İzleme listemden küçük değişiklikleri gizle',
'tog-watchlisthideliu' => 'İzleme listemde, kayıtlı kullanıcılar tarafından yapılan değişiklikleri gösterme',
-'tog-watchlisthideanons' => 'İzleme listemde, anonim kullanıcılar tarafından yapılan değişiklikleri gösterme',
-'tog-watchlisthidepatrolled' => 'İzleme listesinde gözlenmiş değişiklikleri gizle',
+'tog-watchlisthideanons' => 'İzleme listemde, anonim kullanıcılar tarafından yapılan değişiklikleri gizle',
+'tog-watchlisthidepatrolled' => 'İzleme listesinde kontrol edilmiş değişiklikleri gizle',
'tog-nolangconversion' => 'Varyant dönüştürmesini devre dışı bırak',
'tog-ccmeonemails' => 'Diğer kullanıcılara gönderdiğim e-postaların kopyalarını bana da gönder',
-'tog-diffonly' => 'Sayfa içeriğini sürüm farklarının aşağısında gösterme',
+'tog-diffonly' => 'Sayfa içeriğini sürüm farklarının altında gösterme',
'tog-showhiddencats' => 'Gizli kategorileri göster',
'tog-noconvertlink' => 'Bağlantı başlığı dönüştürmesini devre dışı bırakma',
-'tog-norollbackdiff' => 'Rollback uygulandıktan sonra değişikliği sil',
+'tog-norollbackdiff' => 'Geridönüş uygulandıktan sonra değişikliği atla',
'underline-always' => 'Daima',
'underline-never' => 'Asla',
-'underline-default' => 'Tarayıcı karar versin',
+'underline-default' => 'Tarayıcı varsayılanı',
# Font style option in Special:Preferences
-'editfont-style' => 'Değişiklik alanı yazı tipi biçemi:',
+'editfont-style' => 'Düzenleme alanının yazı tipi:',
'editfont-default' => 'Tarayıcı varsayılanı',
-'editfont-monospace' => 'Sabit yer kaplayan yazı tipi',
-'editfont-sansserif' => 'Sans-serif yazı tipi',
-'editfont-serif' => 'Serif yazı tipi',
+'editfont-monospace' => 'Sabit aralıklı yazı tipi',
+'editfont-sansserif' => 'Çıkıntısız (Sans-serif) yazı tipi',
+'editfont-serif' => 'Çıkıntılı (serif) yazı tipi',
# Dates
'sunday' => 'Pazar',
'edithelp' => 'Nasıl değiştirilir?',
'edithelppage' => 'Help:Sayfa nasıl değiştirilir',
'helppage' => 'Help:İçindekiler',
-'mainpage' => 'Ana sayfa',
+'mainpage' => 'Ana Sayfa',
'mainpage-description' => 'Ana sayfa',
'policy-url' => 'Project:Politika',
'portal' => 'Topluluk portali',
'viewsourceold' => 'kaynağı gör',
'editlink' => 'değiştir',
'viewsourcelink' => 'kaynağı gör',
-'editsectionhint' => '$1 bölümünü değiştir',
+'editsectionhint' => 'Değiştirilen bölüm: $1',
'toc' => 'Konu başlıkları',
'showtoc' => 'göster',
'hidetoc' => 'gizle',
'cannotdelete' => '"$1" sayfa ya da dosyası silinemedi.
Başka bir kullanıcı tarafından silinmiş olabilir.',
'cannotdelete-title' => '"$1" sayfasını silemezsiniz',
+'delete-hook-aborted' => 'Silme işlemi kanca tarafından durduruldu.
+Hiçbir açıklama yapılmadı.',
'badtitle' => 'Geçersiz başlık',
'badtitletext' => 'Girilen sayfa adı ya hatalı ya boş ya da diller arası bağlantı veya vikiler arası bağlantı içerdiğinden geçerli değil. Başlıklarda kullanılması yasak olan bir ya da daha çok karakter içeriyor olabilir.',
-'perfcached' => 'Veriler daha önceden hazırlanmış olabilir. Bu sebeple güncel olmayabilir! A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.',
-'perfcachedts' => 'Aşağıda saklanmış bilgiler bulunmaktadır, son güncelleme tarihi: $1. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.',
+'perfcached' => 'Aşağıdaki veriler önbellekten alınmıştır ve güncel olmayabilir. Önbellekte en fazla {{PLURAL:$1|bir sonuç|$1 sonuç}} mevcut.',
+'perfcachedts' => 'Aşağıdaki veri önbelleklenmiştir, son güncelleme tarihi: $1. Önbellekte en fazla {{PLURAL:$1|bir sonuç|$4 sonuç}} mevcut.',
'querypage-no-updates' => 'Şu an için güncellemeler devre dışı bırakıldı. Buradaki veri hemen yenilenmeyecektir.',
'wrong_wfQuery_params' => 'wfQuery() ye yanlış parametre<br />
Fonksiyon: $1<br />
'ns-specialprotected' => '{{ns:special}} alanadı içindeki sayfalar değiştirilemez.',
'titleprotected' => "[[User:$1|$1]] tarafından oluşturulması engellenmesi için bu sayfa koruma altına alınmıştır.
Verilen sebep: ''$2''.",
+'invalidtitle-knownnamespace' => '"$2" alan adı için "$3" metni geçersiz bir başlık',
+'exception-nologin' => 'Giriş yapılmamış',
+'exception-nologin-text' => 'Bu sayfa ya da eylem için bu vikide oturum açmış olmanız gerekir.',
# Virus scanner
'virus-badscanner' => "Yanlış ayarlama: bilinmeyen virüs tarayıcı: ''$1''",
# Login and logout pages
'logouttext' => "'''Oturumu kapattınız.'''
-Şimdi kimliğinizi belirtmeksizin {{SITENAME}} sitesini kullanmaya devam edebilirsiniz ya da aynı kullanıcı adıyla ya da ister başka bir kullanıcı adıyla [[Special:UserLogin|yeniden oturum açabilirsiniz]].
+Şimdi anonim olarak {{SITENAME}} sitesini kullanmaya devam edebilirsiniz ya da aynı kullanıcı adıyla ya da ister başka bir kullanıcı adıyla [[Special:UserLogin|yeniden oturum açabilirsiniz]].
Tarayıcınızın önbelleğini temizleyene kadar bazı sayfalar sanki hâlâ oturumunuz açıkmış gibi görünebilir.",
'welcomecreation' => '== Hoş geldin, $1! ==
'remembermypassword' => 'Girişimi bu tarayıcıda hatırla (en fazla $1 {{PLURAL:$1|gün|gün}} için)',
'securelogin-stick-https' => "Giriş yaptıktan sonra HTTPS'e bağlı kal",
'yourdomainname' => 'Alan adınız:',
+'password-change-forbidden' => 'Bu vikide parolanızı değiştiremezsiniz.',
'externaldberror' => 'Ya doğrulama veritabanı hatası var ya da kullanıcı hesabınızı güncellemeye yetkiniz yok.',
'login' => 'Oturum aç',
'nav-login-createaccount' => 'Oturum aç / hesap oluştur',
Çerezlerin açık olduğundan emin olun ve bu sayfayı yeniden yükleyip tekrar deneyin.',
'noname' => 'Geçerli bir kullanıcı adı girmediniz.',
'loginsuccesstitle' => 'Oturum açıldı',
-'loginsuccess' => '{{SITENAME}} sitesinde "$1" kullanıcı adıyla oturum açmış bulunmaktasınız.',
+'loginsuccess' => "'''{{SITENAME}} üzerinde \"\$1\" kullanıcı adıyla oturum açtınız.'''",
'nosuchuser' => '"$1" adında bir kullanıcı bulunmamaktadır.
Kullanıcı adları büyük-küçük harf duyarlıdır.
Yazılışı kontrol edin veya [[Special:UserLogin/signup|yeni bir hesap açın]].',
'mailerror' => 'E-posta gönderim hatası: $1',
'acct_creation_throttle_hit' => 'Sizin IP adresinizi kullanarak bu vikiyi ziyaret edenler son günde {{PLURAL:$1|1 hesap|$1 hesap}} oluşturdu, bu sayı bu zaman aralığında izin verilen azami sayıdır.
Sonuç olarak, bu IP adresini kullanan ziyaretçiler şu anda daha fazla hesap açamazlar.',
-'emailauthenticated' => 'E-posta adresiniz $2 $3 tarihinde doğrulanmıştı.',
+'emailauthenticated' => 'E-posta adresiniz $2 $3 tarihinde doğrulandı.',
'emailnotauthenticated' => 'E-posta adresiniz henüz onaylanmadı.
Aşağıdaki işlevlerin hiçbiri için e-posta gönderilmeyecektir.',
'noemailprefs' => 'Bu özelliklerin çalışması için bir e-posta adresi belirtiniz.',
'invalidemailaddress' => 'Geçersiz bir formatta yazıldığından dolayı bu e-posta adresi kabul edilemez.
Lütfen geçerli bir formatta e-posta adresi yazın veya bu bölümü boş bırakın.',
'cannotchangeemail' => 'Hesabın e-posta adresi bu wiki üzerinden değiştirilemez.',
+'emaildisabled' => 'Bu siteden e-posta gönderemezsiniz.',
'accountcreated' => 'Hesap açıldı',
'accountcreatedtext' => '$1 için bir kullanıcı hesabı açıldı.',
'createaccount-title' => '{{SITENAME}} için yeni kullanıcı hesabı oluşturulması',
Ayrıca buraya katkıda bulunarak, bu katkının kendiniz tarafından yazıldığına ya da kamuya açık bir kaynaktan ya da başka bir özgür/ücretsiz kaynaktan kopyalandığına güvence vermiş oluyorsunuz. '''Buraya, telif sahibinin izni olmadan telif hakkı ile korunan eserleri eklemeyiz! '''",
'copyrightwarning2' => 'Lütfen, {{SITENAME}} sitesine bulunacağınız tüm katkıların diğer üyeler tarafından düzenlenebileceğini, değiştirilebileceğini ya da silinebileceğini hatırlayın. Yazılarınızın merhametsizce değiştirilebilmesine rıza göstermiyorsanız buraya katkıda bulunmayın. <br />
Ayrıca bu ekleyeceğiniz yazıyı sizin yazdığınızı ya da serbest kopyalama izni veren bir kaynaktan kopyaladığınızı bize taahhüt etmektesiniz (ayrıntılar için referans: $1).',
-'longpageerror' => "'''HATA: Girdiğiniz metnin uzunluğu {{PLURAL:$1|bir kilobayt|$1 kilobayt}}, ve en fazla uzunluktan {{PLURAL:$2|bir kilobayt|$2 kilobayt}} daha fazladır.
-Kaydedilmesi mümkün değildir.'''",
+'longpageerror' => "'''Hata: Girdiğiniz metnin uzunluğu kabul edilebilir en fazla uzunluk olan {{PLURAL:$2|bir kilobayt|$2 kilobayt}}tan fazladır ve {{PLURAL:$1|bir kilobayt|$1 kilobayt}} büyüklüğündedir.'''
+Değişikliğiniz kaydedilemez.",
'readonlywarning' => "'''DİKKAT: Bakım nedeni ile veritabanı şu anda kilitlidir. Bu sebeple değişiklikleriniz şu anda kaydedilememektedir. Yazdıklarınızı başka bir editöre alıp saklayabilir ve daha sonra tekrar buraya getirip kaydedebilirsiniz'''
Kilitleyen hizmetli şu açıklamayı eklemiştir: $1",
'last' => 'son',
'page_first' => 'ilk',
'page_last' => 'son',
-'histlegend' => "Fark seçimi: karşılaştırmayı istediğiniz 2 sürümün önündeki daireleri işaretleyip, enter'a ya da sayfanın en altında bulunan düğmeye basın.<br />
-Tanımlar: '''({{int:cur}})''' = güncel sürümle aradaki fark, '''({{int:last}})''' = bir önceki sürümle aradaki fark, '''{{int:minoreditletter}}''' = küçük değişiklik.",
+'histlegend' => "Fark seçimi: Karşılaştırmayı istediğiniz 2 sürümün önündeki daireleri işaretleyip, \"{{int:Compareselectedversions}}\" düğmesine basın.<br />
+Tanımlar: '''({{int:cur}})''' = son revizyon ile arasındaki fark, '''({{int:last}})''' = bir önceki revizyon ile arasındaki fark, '''{{int:minoreditletter}}''' = küçük değişiklik.",
'history-fieldset-title' => 'Geçmişe gözat',
'history-show-deleted' => 'Sadece silinenler',
'histfirst' => 'En eski',
'prefs-beta' => 'Beta özellikleri',
'prefs-datetime' => 'Tarih ve saat',
'prefs-labs' => 'Lab özellikleri',
+'prefs-user-pages' => 'Kullanıcı sayfaları',
'prefs-personal' => 'Kullanıcı bilgileri',
'prefs-rc' => 'Son değişiklikler',
'prefs-watchlist' => 'İzleme listesi',
'prefs-help-watchlist-token' => 'Bu alanı gizli bir anahtarla doldurmak, izleme listeniz için bir RSS beslemesi oluşturur.
Bu alandaki anahtarı bilen herkes izleme listenizi okuyabilir, bu yüzden güvenli bir değer seçin.
Kullanabileceğiniz rastgele-üretilmiş bir değer: $1',
-'savedprefs' => 'Ayarlar kaydedildi.',
+'savedprefs' => 'Tercihleriniz kaydedildi.',
'timezonelegend' => 'Zaman dilimi:',
'localtime' => 'Yerel saat:',
'timezoneuseserverdefault' => 'Viki varsayılanını kullanın ($1)',
'backend-fail-opentemp' => 'Geçici dosya açılamadı.',
'backend-fail-closetemp' => 'Geçici dosya kapanamadı.',
'backend-fail-read' => '$1 dosyası okunamadı.',
-'backend-fail-create' => '$1 dosyası oluşturulamadı.',
+'backend-fail-create' => '$1 dosyası yazılamadı.',
# ZipDirectoryReader
'zip-file-open-error' => 'Dosya ZIP denetimleri için açılırken bir hata ile karşılaşıldı.',
'logentry-move-move' => '$1 $3 sayfasını $4 sayfasına taşıdı',
'logentry-move-move-noredirect' => '$1 $3 sayfasını $4 sayfasına yönlendirme olmaksızın taşıdı',
'logentry-move-move_redir' => '$1 $3 sayfasını $4 sayfasına yönlendirme üzerinden taşıdı',
+'logentry-patrol-patrol-auto' => '$1 $3 sayfasını $4 sürümü ile kontrol etti',
'logentry-newusers-newusers' => '$1 kullanıcı hesabı oluşturdu',
'logentry-newusers-create' => '$1 kullanıcı hesabı oluşturdu',
'logentry-newusers-create2' => '$1 kullanıcı hesabı oluşturdu $3',
'api-error-uploaddisabled' => 'Yükleme bu vikide devre dışı bırakılmıştır.',
'api-error-verification-error' => 'Dosya bozuk veya yanlış uzantıya sahip olabilir.',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|izin verilen bir dosya türü değil|izin verilen bir dosya türü değil}}. İzin verilen {{PLURAL:$3|dosya türü|dosya türleri}} $2.',
);
'tog-hidepatrolled' => 'يېقىنقى ئۆزگەرتىشتە كۆزەتكەن تەھرىرنى يوشۇر',
'tog-newpageshidepatrolled' => 'يېڭى بەت تىزىملىكىدە كۆزەتكەن تەھرىرنى يوشۇر',
'tog-extendwatchlist' => 'كۈچەيتىلگەن كۆزەت تىزىملىكىدە يېقىنقى ئۆزگەرتىشنىلا كۆرسەتمەي بەلكى ھەممە ئۆزگەرتىشنى كۆرسەت',
-'tog-usenewrc' => 'كۈچەيتىلگەن يېقىنقى ئۆزگەرتىشنى قوزغات (JavaScript زۆرۈر)',
+'tog-usenewrc' => 'بەت گۇرۇپپىلىنىشىغا ئاساسەن يېقىنقى ئۆزگەرتىش ۋە كۆزەت تىزىمى (JavaScript زۆرۈر)',
'tog-numberheadings' => 'ماۋزۇغا ئۆزلۈكىدىن تەرتىپ نومۇرى قوش',
'tog-showtoolbar' => 'تەھرىر قورال ستونىنى كۆرسەت (JavaScript زۆرۈر)',
'tog-editondblclick' => 'قوش چەككەندە بەت تەھرىرلە (JavaScript زۆرۈر)',
'tog-editsectiononrightclick' => 'ماۋزۇنى چاشقىنەكتە ئوڭ چېكىپ ئابزاس تەھرىرلەشكە يول قوي (JavaScript زۆرۈر)',
'tog-showtoc' => 'مەزمۇن جەدۋىلى كۆرسەت (بىر بەتتە 3 تىن ئارتۇق ماۋزۇ بار بەتكە قارىتىلغان)',
'tog-rememberpassword' => 'بۇ كومپيۇتېردا كىرگىنىمنى ئەستە ساقلا(ئەڭ ئۇزۇن بولغاندا $1 {{PLURAL:$1|كۈن|كۈن}})',
-'tog-watchcreations' => 'مەن قۇرغان بەتنى كۆزەت تىزىملىكىمگە قوش',
-'tog-watchdefault' => 'مەن تەھرىرلىگەن بەتنى كۆزەت تىزىملىكىمگە قوش',
-'tog-watchmoves' => 'مەن يۆتكىگەن بەتنى كۆزەت تىزىملىكىمگە قوش',
-'tog-watchdeletion' => 'مەن ئۆچۈرگەن بەتنى كۆزەت تىزىملىكىمگە قوش',
+'tog-watchcreations' => 'مەن قۇرغان بەت ۋە يۈكلىگەن ھۆججەتلەرنى كۆزەت تىزىملىكىمگە قوش',
+'tog-watchdefault' => 'مەن تەھرىرلىگەن بەت ۋە ھۆججەتنى كۆزەت تىزىملىكىمگە قوش',
+'tog-watchmoves' => 'مەن يۆتكىگەن بەت ۋە ھۆججەتنى كۆزەت تىزىملىكىمگە قوش',
+'tog-watchdeletion' => 'مەن ئۆچۈرگەن بەت ۋە ھۆججەتنى كۆزەت تىزىملىكىمگە قوش',
'tog-minordefault' => 'ھەممە تەھرىرلەشنى ئازراقلا تەھرىرگە تەڭشە',
'tog-previewontop' => 'تەھرىر رامكىسىنىڭ ئۈستىدە ئالدىن كۆزىتىشنى كۆرسەت',
'tog-previewonfirst' => 'تۇنجى قېتىم تەھرىرلىگەندە ئالدىن كۆزىتىشنى كۆرسەت',
'tog-nocache' => 'توركۆرگۈ بەت غەملەشنى چەكلە',
-'tog-enotifwatchlistpages' => 'كۆزەت تىزىملىكىمدىكى بەت ئۆزگەرگەندە ئېلخەت يوللا',
+'tog-enotifwatchlistpages' => 'كۆزەت تىزىملىكىمدىكى بەت ۋە ھۆججەت ئۆزگەرگەندە ئېلخەت يوللا',
'tog-enotifusertalkpages' => 'مۇنازىرە بېتىم ئۆزگەرگەندە ئېلخەت يوللا',
-'tog-enotifminoredits' => 'بەت ئازراقلا تەھرىرلەنگەندىمۇ ئېلخەت يوللا',
+'tog-enotifminoredits' => 'بەت ۋە ھۆججەت ئازراقلا تەھرىرلەنگەندىمۇ ئېلخەت يوللا',
'tog-enotifrevealaddr' => 'ئۇقتۇرۇش ئېلخەت تىزىملىكىدە ئېلخەت ئادرېسىمنى ئاشكارىلا',
'tog-shownumberswatching' => 'بۇ بەتنى كۆزىتىۋاتقان ئىشلەتكۈچى سانىنى كۆرسەت',
-'tog-oldsig' => 'Ù\86Û\86Û\8bÛ\95تتÙ\89Ù\83Ù\89 ئÙ\89Ù\85زاÙ\86Ù\89 ئاÙ\84دÙ\89Ù\86 Ù\83Û\86زÛ\95ت:',
+'tog-oldsig' => 'Ù\85Û\95Û\8bجÛ\87ت ئÙ\89Ù\85زا:',
'tog-fancysig' => 'ئىمزاغا wiki تېكستى سۈپىتىدە مۇئامىلە قىل (ئۆزلۈكىدىن ئۇلانما ھاسىل بولمايدۇ)',
'tog-externaleditor' => 'كۆڭۈلدىكى ئەھۋالدا سىرتقى تەھرىرلىگۈچ ئىشلىتىدۇ (ئالىي ئىشلەتكۈچىگە تەمىنلىنىدۇ، كومپيۇتېرىڭىزدا بىر قىسىم ئالاھىدە تەڭشەش ئېلىپ بېرىشىڭىز لازىم
[//www.mediawiki.org/wiki/Manual:External_editors تېخىمۇ كۆپ ئۇچۇر.])',
'userpage-userdoesnotexist-view' => '"$1" ئىشلەتكۈچى ھېساباتى خەتلەتمىگەن.',
'blocked-notice-logextract' => 'بۇ ئىشلەتكۈچى نۆۋەتتە چەكلەنگەن.
پايدىلىنىش ئۈچۈن يېقىنقى تۆۋەندە چەكلەش خاتىرە تۈرلىرى تەمىنلەندى:',
-'clearyourcache' => "'''دىققەت - ساقلىغاندىن كېيىن، تور كۆرگۈنىڭ غەملىكىنى تازىلىغاندىن كېيىنلا ئاندىن ئېلىپ بارغان ئۆزگەرتىشنى كۆرەلەيسىز.'''
+'clearyourcache' => "'''دىققەت''' - ساقلىغاندىن كېيىن، تور كۆرگۈنىڭ غەملىكىنى تازىلىغاندىن كېيىنلا ئاندىن ئېلىپ بارغان ئۆزگەرتىشنى كۆرەلەيسىز.
* '''Mozilla / Firefox / Safari:''' دا ''Shift'' كۇنۇپكىسىنى بېسىپ تۇرۇپ ''قايتا يۈكلە''نى ياكى ''Ctrl-F5'' ياكى ''Ctrl-R'' (''Mac تا Command-R'')؛
* '''Google Chrome:''' دا ''Ctrl-Shift-R'' (''Command-Shift-R'' Mac)
*'''Internet Explorer:''' دا ''Ctrl'' نى بېسىپ تۇرۇپ ''يېڭىلا,'' ياكى ''Ctrl-F5''؛
* '''Konqueror: دا ''' ''قايتا يۈكلە'' ياكى ''F5''؛
-* '''Opera:''' دا ''قورال → مايىللىق''؛ نى بېسىڭ.",
+* '''Opera:''' دا ''قورال → مايىللىق''؛ نى بېسىپ غەملەكنى تازىلاڭ.",
'usercssyoucanpreview' => "ئەسكەرتىش:''' ساقلاشتىن ئىلگىرى \"{{int:showpreview}}\" توپچىنى ئىشلىتىپ يېڭى CSS نى سىناڭ.",
'userjsyoucanpreview' => "ئەسكەرتىش:''' ساقلاشتىن ئىلگىرى \"{{int:showpreview}}\" توپچىنى ئىشلىتىپ يېڭى JS نى سىناڭ.",
'usercsspreview' => "'''دىققەت سىز پەقەت ئۆزىڭىزنىڭ شەخسىي CSS نى ئالدىن كۆزىتىۋاتىسىز.'''
'updated' => '(يېڭىلاندى)',
'note' => "'''ئىزاھات:'''",
'previewnote' => "'''ئېسىڭىزدە بولسۇنكى بۇ پەقەتلا ئالدىن كۆزىتىش.'''
-ئۆزگەرتكەن مەزمۇنىڭىز تېخى ساقلانمىدى!",
+ئۆزگەرتكەنلىرىڭىز تېخى ساقلانمىدى!",
'previewconflict' => 'بۇ ئالدىن كۆزىتىشتە ئۈستىدىكى تېكست تەھرىرلەش رايونىدىكى مەزمۇننى كۆرسەتتى. ئۇ ساقلانى تاللىغاندىن كېيىن كۆرۈنىدۇ.',
'session_fail_preview' => "'''كەچۈرۈڭ! سىزنىڭ جەريان سانلىق مەلۇماتىڭىز يوقاپ كەتكەندە ئېلىپ بارغان تەھرىرىڭىزنى بىر تەرەپ قىلالمايمىز.'''
قايتا سىناڭ.
[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ئۆچۈرۈش خاتىرىسى]دىن تەپسىلىي ئۇچۇرنى تاپقىلى بولىدۇ.",
'rev-deleted-text-unhide' => "بۇ بەتنىڭ تۈزىتىلگەن نەشرى '''ئۆچۈرۈلگەن'''.
[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ئۆچۈرۈش خاتىرىسى]دىن تەپسىلىي ئۇچۇرنى تاپقىلى بولىدۇ.
-باشÙ\82Û\87رغÛ\87Ú\86Ù\89 بÙ\88Ù\84Û\87Ø´ سÛ\88Ù¾Ù\89تÙ\89دÛ\95Ø\8c داÛ\8bاÙ\85Ù\84اشتÛ\87رÙ\85اÙ\82Ú\86Ù\89 بÙ\88Ù\84سÙ\89ÚÙ\89ز Ù\8aÛ\95Ù\86Ù\89Ù\84ا [$1 بÛ\87 تÛ\88زÙ\89تÙ\89Ù\84Ú¯Û\95Ù\86 نەشرىنى كۆرسەت]ەلەيسىز.",
+داÛ\8bاÙ\85Ù\84اشتÛ\87رÙ\85اÙ\82Ú\86Ù\89 بÙ\88Ù\84سÙ\89ÚÙ\89ز Ù\8aÛ\95Ù\86Ù\89Ù\84ا [$1 بÛ\87 نەشرىنى كۆرسەت]ەلەيسىز.",
'rev-suppressed-text-unhide' => "بۇ بەتنىڭ تۈزىتىلگەن نەشرى '''يوقىتىلغان'''.
[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} يوقىتىش خاتىرىسى]دىن تەپسىلىي ئۇچۇرنى تاپقىلى بولىدۇ.باشقۇرغۇچى بولۇش سۈپىتىدە، داۋاملاشتۇرماقچى بولسىڭىز يەنىلا [$1 بۇ تۈزىتىلگەن نەشرىنى كۆرسەت]ەلەيسىز.",
'rev-deleted-text-view' => "بۇ بەتنىڭ تۈزىتىلگەن نەشرى '''ئۆچۈرۈلگەن'''.
'mergelogpagetext' => 'تۆۋەندىكىسى يېقىندا بىر بەتنىڭ تۈزىتىش تارىخىنىڭ باشقا بىر بەتكە بىرلەشتۈرۈلگەنلىك تىزىملىكى',
# Diffs
-'history-title' => '"$1" نىڭ ئۆزگەرتىش خاتىرىسى',
+'history-title' => '"$1" نىڭ ئۆزگەرتىش نەشر تارىخى',
'difference-multipage' => '(بەتلەر ئارىسىدىكى پەرق)',
'lineno' => '$1 -قۇر:',
'compareselectedversions' => 'تاللانغان نەشرىنى سېلىشتۇر',
'prefs-registration' => 'خەتلەتكەن ۋاقىت:',
'yourrealname' => 'ﺗﻮﻟﯘﻕ ئىسىم:',
'yourlanguage' => 'تىل:',
+'yourvariant' => 'مەزمۇن تىل شالغۇتى:',
'yournick' => 'ئىمزا:',
'prefs-help-signature' => 'مۇنازىرە بەتتە "<nowiki>~~~~</nowiki>" ئىمزا ئىشلىتىلسە ئۇ ئۆزلۈكىدىن ئىمزايىڭىزغا ئۆزگىرىپ ۋاقىت تامغا قوشۇلىدۇ.',
'badsig' => 'ئەسلى ئىمزا خاتا.
'group-suppress' => 'نازارەتچىلەر',
'group-all' => '(ھەممىسى)',
-'group-user-member' => 'ئىشلەتكۈچى',
+'group-user-member' => '{{GENDER:$1|ئىشلەتكۈچى}}',
'group-autoconfirmed-member' => 'ئۆزلۈكىدىن جەزملەنگەن ئىشلەتكۈچى',
'group-bot-member' => 'ماشىنا ئادەم',
'group-sysop-member' => 'باشقۇرغۇچى',
'filehist-filesize' => 'ھۆججەت چوڭلۇقى',
'filehist-comment' => 'ئىزاھات',
'filehist-missing' => 'ھۆججەت يوقالغان',
-'imagelinks' => 'ھۆججەت ئۇلىنىشى',
+'imagelinks' => 'ھۆججەت ئىشلىتىلىشى',
'linkstoimage' => 'تۆۋەندىكى {{PLURAL:$1|بەت|$1 بەت}} بۇ ھۆججەتكە ئۇلانغان:',
'linkstoimage-more' => '{{PLURAL:$1|دىن كۆپ بەت ئۇلانما|دىن كۆپ بەت ئۇلانما}} بۇ ھۆججەتكە ئۇلانغان.
تۆۋەندىكى تىزىملىك پەقەت بۇ ھۆججەتنىڭ ئەڭ بېشىدىكى {{PLURAL:$1|بەت| $1 بەت}} ئۇلىنىشىنىلا كۆرسىتىدۇ.
'listusers-editsonly' => 'تەھرىرلەنگەن ئىشلەتكۈچىلەرنىلا كۆرسەت',
'listusers-creationsort' => 'قۇرۇلغان ۋاقتى بويىچە تەرتىپلە',
'usereditcount' => '$1 {{PLURAL:$1|قېتىم|قېتىم}} تەھرىرلەنگەن',
-'usercreated' => ' $1 $2 قۇرۇلغان',
+'usercreated' => '$1 دا $2 دە {{GENDER:$3|قۇرغان}}',
'newpages' => 'يېڭى بەتلەر',
'newpages-username' => 'ئىشلەتكۇچى ئىسمى:',
'ancientpages' => 'ئەڭ كونا بەتلەر',
'right-writeapi' => 'Використання API для запису',
'right-delete' => 'Вилучення сторінок',
'right-bigdelete' => 'Вилучення сторінок з великою історією',
+'right-deletelogentry' => 'Вилучення та відновлення окремих записів журналу',
'right-deleterevision' => 'Вилучення і відновлення окремих версій сторінок',
'right-deletedhistory' => 'Перегляд історії вилучених сторінок без перегляду вилученого тексту',
'right-deletedtext' => 'перегляд вилученого тексту та змін між вилученими версіями',
'rc_categories' => 'Тільки з категорій (разділювач «|»)',
'rc_categories_any' => 'Будь-який',
'rc-change-size' => '$1',
-'rc-change-size-new' => 'Розмір після зміни: {{PLURAL:$1|байт|байти|байтів}}',
+'rc-change-size-new' => 'Розмір після зміни: $1 {{PLURAL:$1|байт|байти|байтів}}',
'newsectionsummary' => '/* $1 */ нова тема',
'rc-enhanced-expand' => 'Показати деталі (потрібен JavaScript)',
'rc-enhanced-hide' => 'Сховати деталі',
'rollback' => 'Відкинути редагування',
'rollback_short' => 'Відкинути',
'rollbacklink' => 'відкинути',
+'rollbacklinkcount' => 'скасування $1 {{PLURAL:$1|редагування|редагувань|редагувань}}',
+'rollbacklinkcount-morethan' => 'скасування більш, ніж $1 {{PLURAL:$1|редагування|редагувань|редагувань}}',
'rollbackfailed' => 'Відкинути зміни не вдалося',
'cantrollback' => 'Неможливо відкинути редагування, останній, хто редагував, є єдиним автором цієї сторінки.',
'alreadyrolled' => 'Неможливо відкинути останні редагування [[:$1]], зроблені [[User:$2|$2]] ([[User talk:$2|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); хтось інший уже змінив чи відкинув редагування цієї статті.
'duration-centuries' => '$1 {{PLURAL:$1|століття|століття|століть}}',
'duration-millennia' => '$1 {{PLURAL:$1|тисячоліття|тисячоліття|тисячоліть}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 — {{PLURAL:$4|недозволений тип файлів|недозволені типи файлів}}. {{PLURAL:$3|Дозволений тип файлів|Дозволені типи файлів}}: $2.',
);
* @author Chris H
* @author Istabani
* @author Meno25
+ * @author Muhammad Shuaib
* @author O.bangash
* @author Rachitrali
* @author Reedy
'category-subcat-count' => '{{PLURAL:$2|اِس زمرہ میں صرف درج ذیل ذیلی زمرہ ہے.|اِس زمرہ میں درج ذیل {{PLURAL:$1|ذیلی زمرہ|$1 ذیلی زمرہ جات}}, کل $2 میں سے.}}',
'category-subcat-count-limited' => 'اِس زمرہ میں درج ذیل {{PLURAL:$1|ذیلی زمرہ ہے|$1 ذیلی زمرہ جات ہیں}}.',
'listingcontinuesabbrev' => '۔جاری',
+'noindex-category' => 'غیر مندرج صفحات',
'about' => 'تعارف',
'article' => 'صفحۂ مشمول',
'recentchanges-label-newpage' => 'اِس ترمیم نے نیا صفحہ تخلیق کردیا',
'recentchanges-label-minor' => 'یہ ایک معمولی ترمیم ہے',
'recentchanges-label-bot' => 'یہ ایک روبالہ سے سرانجام شدہ ترمیم ہے',
+'recentchanges-label-unpatrolled' => 'اس ترمیم کی اب تک مراجعت نہیں کی گئی',
'rcnote' => "درج ذیل گزشتہ {{PLURAL:$2|دِن|'''$2''' ایام}} میں ہونے والی {{PLURAL:$1|'''ایک''' تبدیلی ہے|آخری '''$1''' تبدیلیاں ہیں}}، $5، $4.",
'rcnotefrom' => "ذیل میں '''$2''' سے کی گئی تبدیلیاں ہیں ('''$1''' تبدیلیاں دکھائی جارہی ہیں)۔",
'rclistfrom' => '$1 سےنئی تبدیلیاں دکھانا شروع کریں',
'rcshowhidebots' => 'خودکار صارف $1',
'rcshowhideliu' => 'داخل شدہ صارف $1',
'rcshowhideanons' => 'گمنام صارف $1',
+'rcshowhidepatr' => '$1 مراجعت شدہ ترامیم',
'rcshowhidemine' => 'ذاتی ترامیم $1',
'rclinks' => 'آخری $2 روز میں ہونے والی $1 تبدیلیوں کا مشاہدہ کریں<br />$3',
'diff' => 'فرق',
'recentchangeslinked-feed' => 'متعلقہ تبدیلیاں',
'recentchangeslinked-toolbox' => 'متعلقہ تبدیلیاں',
'recentchangeslinked-title' => '"$1" سے متعلقہ تبدیلیاں',
+'recentchangeslinked-summary' => 'یہ ان تبدیلیوں کی فہرست ہے جو حال ہی میں کسی مخصوص صفحہ سے مربوط صفحات (یا مخصوص زمرہ کے اراکین) میں کی گئی ہیںـ
+
+[[SpecialWatchlist | آپ کی زیر نظر فہرست]] میں یہ صفحات متجل (bold) نظر آئیں گےـ',
'recentchangeslinked-page' => 'صفحۂ منصوبہ دیکھئے',
# Upload
'statistics-header-users' => 'ارکان کے اعداد و شمار',
'disambiguations' => 'ضد ابہام صفحات',
+'disambiguationspage' => 'سانچہ:ضدابہام',
'doubleredirects' => 'دوہرے متبادل ربط',
'nolinkshere' => "'''[[:$1]]''' سے کوئی روابط نہیں۔",
'isredirect' => 'لوٹایا گیا صفحہ',
'isimage' => 'ربطِ ملف',
+'whatlinkshere-links' => 'روابط',
'whatlinkshere-hideredirs' => 'رجوع مکررات $1',
'whatlinkshere-hidelinks' => 'روابط $1',
'whatlinkshere-hideimages' => 'روابطِ تصویر $1',
'tooltip-diff' => 'دیکھئے کہ اپنے متن میں کیا تبدیلیاں کیں',
'tooltip-compareselectedversions' => 'اِس صفحہ کی دو منتخب نظرثانیوں میں فرق دیکھئے',
'tooltip-watch' => 'اِس صفحہ کو اپنی زیرِنظرفہرست میں شامل کریں',
+'tooltip-undo' => "''استرجع'' اس ترمیم کو پچھلی ترمیم کے جانب واپس کردیگا اور نمائشی انداز میں خانہ ترمیم کھول دے گا۔ آپ مختصراً سبب بیان کرنے کے بھی مجاز ہونگے۔",
'tooltip-summary' => 'مختصر خلاصہ درج کریں',
# Attribution
'previousdiff' => '← پُرانی تدوین',
'nextdiff' => 'صفحہ کا نام:',
+# Media information
+'file-nohires' => 'اس سے بڑی تصمیم دستیاب نہیں۔',
+'show-big-image' => 'مکمل تصمیم',
+
# Special:NewFiles
'newimages' => 'نئی فائلوں کی گیلری',
'showhidebots' => '($1 بوٹ)',
* @author Behzod Saidov <behzodsaidov@gmail.com>
* @author Casual
* @author Lyncos
+ * @author Sociologist
* @author Urhixidur
+ * @author Xexdof
*/
$fallback8bitEncoding = 'windows-1252';
$messages = array(
# User preference toggles
+'tog-hideminor' => 'Yangi oʻzgarishlardagi kichik tahrirlani yashir',
+'tog-rememberpassword' => 'Hisob ma’lumotlarini ushbu kompyuterda eslab qolish (eng ko‘pi bilan $1 {{PLURAL:$1|kunga|kunga}})',
+'tog-watchcreations' => 'Men yaratgan sahifalarni va yuklagan fayllarni kuzatuv roʻyxatimga qoʻsh',
+'tog-watchdefault' => 'Men tahrirlagan sahifa va fayllarni kuzatuv roʻyxatimga qoʻsh',
+'tog-watchmoves' => 'Men koʻchirgan sahifa va fayllarni kuzatuv roʻyxatimga qoʻsh',
+'tog-watchdeletion' => 'Men yoʻqotgan sahifa va fayllarni kuzatuv roʻyxatimga qoʻsh',
+'tog-enotifwatchlistpages' => 'Kuzatuv roʻyxatimdagi sahifa yoki fayllar oʻzgartirilsa, e-pochtamga bu haqda xat yuborilsin',
+'tog-enotifusertalkpages' => 'Munozara sahifam oʻzgartirilsa, e-pochtamga bu haqda xat yuborilsin',
'tog-oldsig' => 'Mavjud imzo:',
'tog-fancysig' => 'Imzoni wikimatn sifatida qara (avtomatik ishoratsiz)',
+'tog-ccmeonemails' => 'Men boshqa foydalanuvchilarga yuborayotgan xatnig nusxasi oʻzimning e-pochtamga ham yuborilsin',
+'tog-showhiddencats' => 'Yashirin turkumlarni koʻrsat',
'underline-always' => 'Har doim',
'underline-never' => 'Hech qachon',
+'underline-default' => 'Brauzer moslamari boʻyicha',
+
+# Font style option in Special:Preferences
+'editfont-default' => 'Brauzer moslamari boʻyicha',
# Dates
'sunday' => 'Yakshanba',
'subcategories' => 'Ostturkumlar',
'category-empty' => "''Ushbu turkumda hozircha sahifa yoki fayllar yoʻq.''",
'hidden-categories' => '{{PLURAL:$1|Yashirin turkum|Yashirin turkumlar}}',
+'hidden-category-category' => 'Yashirin turkumlar',
'category-subcat-count' => '{{PLURAL:$2|Ushbu turkumda faqat bitta ostturkum mavjud.|Ushbu turkumda quyidagi {{PLURAL:$1|ostturkum|$1 ostturkumlar}}, hammasi boʻlib $2 ta ostturkum mavjud.}}',
'category-article-count' => '{{PLURAL:$2|Ushbu turkumda faqat bitta sahifa mavjud.|Ushbu turkumda quyidagi {{PLURAL:$1|sahifa|$1 sahifalar}}, hammasi boʻlib $2 ta sahifa mavjud.}}',
'listingcontinuesabbrev' => 'davomi',
# Cologne Blue skin
'qbedit' => 'Tahrirlash',
'qbspecialpages' => 'Maxsus sahifalar',
+'faq' => 'TSS',
# Vector skin
+'vector-action-addsection' => 'Mavzuni qoʻsh',
'vector-action-delete' => 'O‘chirish',
'vector-action-move' => 'Ko‘chirish',
'vector-view-create' => 'Yarat',
'editthispage' => 'Sahifani tahrirlash',
'create-this-page' => 'Bu sahifani yarat',
'delete' => 'O‘chirish',
+'deletethispage' => 'Bu sahifani oʻchir',
'protect' => 'Himoyalash',
'protect_change' => 'o‘zgartirish',
'protectthispage' => 'Ushbu sahifani himoyalash',
'aboutsite' => '{{SITENAME}} haqida',
'aboutpage' => 'Project:Haqida',
'copyright' => 'Kontent $1 ostidadir.',
+'copyrightpage' => '{{ns:project}}:Mualliflik huquqlari',
'currentevents' => 'Joriy hodisalar',
'currentevents-url' => 'Project:Joriy hodisalar',
'disclaimers' => 'Ogohlantirishlar',
'privacy' => 'Konfidensiallik siyosati',
'privacypage' => 'Project:Konfidensiallik siyosati',
+'ok' => 'OK',
'retrievedfrom' => ' "$1" dan olindi',
'youhavenewmessages' => 'Sizga $1 keldi ($2).',
'newmessageslink' => 'yangi xabarlar',
'newmessagesdifflink' => 'soʻnggi oʻzgarish',
+'youhavenewmessagesmulti' => 'Sizga yangi xat keldi: $1',
'editsection' => 'tahrirlash',
'editold' => 'tahrir',
'editlink' => 'tahrirla',
'viewsourcelink' => 'manbasini koʻr',
'editsectionhint' => 'Boʻlimni tahrirlash: $1',
'toc' => 'Mundarija',
-'showtoc' => "Ko'rsatish",
+'showtoc' => 'koʻrsatish',
'hidetoc' => 'yashirish',
'collapsible-collapse' => 'Yashir',
'collapsible-expand' => 'Koʻrsat',
'nstab-main' => 'Maqola',
'nstab-user' => 'Foydalanuvchi sahifasi',
'nstab-special' => 'Maxsus sahifa',
-'nstab-project' => 'Loyiha sahifasi',
+'nstab-project' => 'Vikipediya',
'nstab-image' => 'Fayl',
+'nstab-mediawiki' => 'Xabar',
'nstab-template' => 'Andoza',
'nstab-help' => 'Yordam sahifasi',
'nstab-category' => 'Turkum',
'viewsource' => 'Manbasini koʻrish',
'protectedpagetext' => 'Bu sahifa tahrirlashdan saqlanish maqsadida qulflangan.',
'viewsourcetext' => "Siz bu sahifaning manbasini ko'rishingiz va uni nusxasini olishingiz mumkin:",
+'namespaceprotected' => "Sizda '''$1''' nomfazosi sahifalarini tahrirlash huquqi yoʻq",
+'customcssprotected' => 'Sizda uchbu CSS sahifani tahrirlash huquqi yoʻq, chunki bu yerda boshqa foydalanuvchining shaxsiy moslamalari saqlanadi.',
+'customjsprotected' => 'Sizda uchbu JavaScript sahifani tahrirlash huquqi yoʻq, chunki bu yerda boshqa foydalanuvchining shaxsiy moslamalari saqlanadi.',
# Login and logout pages
'logouttext' => "'''Siz saytdan muvaffaqiyatli chiqdingiz.'''
{{SITENAME}} saytidan anonim holda foydalanishda davom etishindiz mumkin. Yoki siz yana hozirgi yoki boshqa foydalanuvchi nomi bilan qaytadan tizimga kirishingiz mumkin.
Shuni e'tiborga olingki, ayrim sahifalar siz brauzeringiz keshini tozalamaguningizga qadar xuddi tizimga kirganingizdagidek ko'rinishda davom etaverishi mumkin.",
+'welcomecreation' => '== Xush kelibsiz, $1! ==
+Siz yangi hisob yaratdingiz.
+[[Special:Preferences|{{SITENAME}}dagi shaxsiy moslamalaringizni]] oʻzgartirish yodingizdan chiqmasin.',
'yourname' => 'Foydalanuvchi nomi',
'yourpassword' => 'Maxfiy soʻz',
'yourpasswordagain' => 'Maxfiy so‘zni qayta kiriting:',
'nav-login-createaccount' => 'Kirish / Hisob yaratish',
'loginprompt' => "{{SITENAME}}ga kirish uchun kukilar yoqilgan bo'lishi kerak.",
'userlogin' => 'Kirish / Hisob yaratish',
+'userloginnocreate' => 'Kirish',
'logout' => 'Chiqish',
'userlogout' => 'Chiqish',
+'notloggedin' => 'Kirish amalga oshirilmadi',
'nologin' => "Hisobingiz yoʻqmi? '''$1'''.",
'nologinlink' => 'Hisob yaratish',
'createaccount' => 'Hisob yaratish',
'gotaccount' => "Hisobingiz bormi? '''$1'''.",
'gotaccountlink' => 'Kirish',
+'userlogin-resetlink' => 'Kirish maʻlumotlaringiz esdan chiqdimi?',
'loginsuccesstitle' => 'Kirish muvaffaqiyatli amalga oshdi',
'loginsuccess' => "'''{{SITENAME}}ga \"\$1\" foydalanuvchi nomi bilan kirdingiz.'''",
+'nosuchusershort' => '"$1" ismli ishtirokchi yoʻq.
+Xatosiz yozishga urinib koʻring.',
'wrongpassword' => 'Kiritgan mahfiy soʻzingiz notoʻgʻri. Iltimos, qaytadan kiritib koʻring.',
+'mailmypassword' => 'Yangi parolni e-mail qil',
+'emailauthenticated' => 'Sizning e-mail manzilingiz $2, $3 da tasdiqlangan.',
'loginlanguagelabel' => 'Til: $1',
# Change password dialog
+'resetpass' => 'Maxfiy soʻzni oʻzgartirish',
'retypenew' => 'Yangi mahfiy soʻzni qayta tering:',
+'resetpass-submit-loggedin' => 'Maxfiy soʻzni oʻzgartirish',
# Edit page toolbar
'bold_sample' => 'Qalin matn',
'cur' => 'joriy',
'next' => 'keyingi',
'last' => 'oxirgi',
+'page_first' => 'birinchi',
+'page_last' => 'oxirgi',
'histlegend' => 'Farqlar: solishtirish uchun kerakli radiobokslarni belgilang va pastdagi tugmani yoki Enterni bosing.<br />
Bu yerda: (joriy) = hozirgi koʻrinish bilan farq,
(oxirgi) = avvalgi koʻrinish bilan farq, k = kichkina tahrir.',
+'history-fieldset-title' => 'Tarixni koʻr',
'history-show-deleted' => 'Faqat o‘chirilganlari',
'histfirst' => 'Eng avvalgi',
'histlast' => 'Eng soʻnggi',
+'historysize' => '({{PLURAL:$1|1 bayt|$1 bayt}})',
+'historyempty' => '(boʻsh)',
# Revision feed
'history-feed-item-nocomment' => '$1 $2 da',
# Revision deletion
'rev-delundel' => 'koʻrsat/yashir',
+'rev-showdeleted' => 'koʻrsatish',
+'revdelete-log' => 'Sabab:',
+'revdelete-otherreason' => 'Boshqa/qoʻshimcha sabab:',
+'revdelete-reasonotherlist' => 'Boshqa sabab',
# Diffs
'history-title' => '"$1"ning tarixi',
'nextn-title' => 'Keyingi $1 {{PLURAL:$1|natija|natijalar}}',
'shown-title' => 'Har sahifada $1 natija koʻrsat',
'viewprevnext' => 'Koʻrish ($1 {{int:pipe-separator}} $2) ($3).',
+'searchmenu-legend' => 'Qidiruv shartlari',
+'searchmenu-exists' => "'''Ushbu vikida \"[[:\$1]]\" nomli sahifa mavjud.'''",
'searchmenu-new' => "'''Ushbu vikida \"[[:\$1]]\" sahifani yarat!'''",
'searchhelp-url' => 'Help:Mundarija',
'searchprofile-articles' => 'Asosiy sahifalar',
+'searchprofile-project' => 'Yordam va proekt sahifalari.',
'searchprofile-images' => 'Multimediya',
'searchprofile-everything' => 'Har yerda',
'searchprofile-advanced' => 'Kengaytirilgan',
'search-section' => '($1 boʻlimi)',
'search-suggest' => 'Balki buni nazarda tutgandirsiz: $1',
'search-interwiki-default' => '$1 natijalar:',
+'searcheverything-enable' => 'Barcha nomfazolarda qidir',
'searchall' => 'barchasi',
'showingresults' => "#<b>$2</b> boshlanayotgan <b>$1</b> natijalar ko'rsatilyapti.",
'showingresultsheader' => "$4 uchun {{PLURAL:$5|'''$3'''dan '''$1''' natija|'''$3'''dan '''$1 - $2''' natijalar}}",
+'search-nonefound' => 'Talabga javob beradigan natija topilmadi.',
'powersearch' => 'Qidiruv',
+'powersearch-legend' => 'Kengaytirilgan qidiruv',
'powersearch-ns' => 'Bu nom-fazolarda izla:',
'powersearch-redir' => 'Yoʻnaltirishlarni koʻrsat',
'powersearch-field' => 'Qidir',
# Preferences page
'preferences' => 'Moslamalar',
'mypreferences' => 'Moslamalarim',
-'prefs-skin' => 'Tashqi ko‘rinish',
+'prefs-edits' => 'Tahrirlar soni',
+'changepassword' => 'Maxfiy soʻzni oʻzgartirish',
+'prefs-skin' => 'Tashqi ko‘rinishi',
+'datedefault' => 'Farqi yoʻq',
'prefs-datetime' => 'Sana va vaqt',
'prefs-personal' => 'Shaxsiy ma’lumotlar',
'prefs-rc' => 'Yangi o‘zgartirishlar',
'prefs-watchlist' => "Kuzatuv ro'yxati",
+'prefs-watchlist-days-max' => 'Eng ko‘pi $1 {{PLURAL:$1|kun|kun}}',
'prefs-misc' => 'Boshqa moslamalar',
+'prefs-resetpass' => 'Maxfiy soʻzni oʻzgartirish',
+'prefs-changeemail' => 'E-mail manzilingizni o‘zgartirish',
+'prefs-rendering' => 'Tashqi ko‘rinishi',
'saveprefs' => 'Saqlash',
'resetprefs' => 'Bekor qilish',
+'restoreprefs' => 'Barcha moslamalarni dastlabki holiga qaytar',
'prefs-editing' => 'Tahrirlash',
+'prefs-edit-boxsize' => 'Tahrir oynasining oʻlchami',
+'rows' => 'Qatorlar soni:',
+'columns' => 'Ustunlar soni:',
'searchresultshead' => 'Qidiruv natijalari',
+'recentchangesdays-max' => 'Eng koʻpi $1 kun',
+'recentchangescount' => 'Sukut boʻyicha koʻrsatiladigan tahrirlar soni',
+'timezonelegend' => 'Vaqt mintaqangiz:',
+'localtime' => 'Mahalliy vaqt:',
+'servertime' => 'Server vaqti:',
+'timezoneregion-africa' => 'Afrika',
+'timezoneregion-america' => 'Amerika',
+'timezoneregion-antarctica' => 'Antarktika',
+'timezoneregion-arctic' => 'Arktika',
+'timezoneregion-asia' => 'Osiyo',
+'timezoneregion-atlantic' => 'Atlantika okeani',
+'timezoneregion-australia' => 'Avstraliya',
+'timezoneregion-europe' => 'Yevropa',
+'timezoneregion-indian' => 'Hind okeani',
+'timezoneregion-pacific' => 'Tinch okeani',
+'allowemail' => 'Boshqa foydalanuvchilardan elektron xat olishga ruxsat berish',
+'prefs-searchoptions' => 'Qidiruv shartlari',
+'prefs-namespaces' => 'Nomfazolar',
+'defaultns' => 'Aks holda quyidagi nomfazolarda qidir:',
'prefs-files' => 'Fayllar',
+'prefs-emailconfirm-label' => 'Elektron pochta manzilini tasdiqlash:',
+'prefs-textboxsize' => 'Tahrir oynasining oʻlchami',
'youremail' => 'E-mail:',
+'username' => 'Foydalanuvchi nomi',
+'uid' => 'Identifikator:',
+'prefs-memberingroups' => 'Qaysi {{PLURAL:$1|guruh|guruhlar}} aʼzosi:',
+'prefs-registration' => 'Hisob ochilgan vaqt',
'yourrealname' => 'Haqiqiy ism *:',
+'yourlanguage' => 'Til:',
+'yournick' => 'Yangi imzo',
+'prefs-help-signature' => 'Munozara sahifalarida imzo "<nowiki>~~~~</nowiki>" orqali qoʻyiladi (u sizning imzoingiz va joriy vaqtga aylantiriladi).',
+'yourgender' => 'Jinsi:',
+'gender-unknown' => 'Koʻrsatilmagan',
+'gender-male' => 'Erkak',
+'gender-female' => 'Ayol',
+'email' => 'E-mail:',
+'prefs-help-email-required' => 'E-mail manzilni koʻrsatish shart emas',
+'prefs-info' => 'Asosiy maʼlumot',
+'prefs-signature' => 'Imzo',
+'prefs-dateformat' => 'Sana formati',
+'prefs-timeoffset' => 'Vaqt farqi',
+'prefs-advancedediting' => 'Qoʻshimcha moslamalar',
+'prefs-advancedrc' => 'Qoʻshimcha moslamalar',
+'prefs-advancedrendering' => 'Qoʻshimcha moslamalar',
+'prefs-advancedsearchoptions' => 'Qoʻshimcha moslamalar',
+'prefs-advancedwatchlist' => 'Qoʻshimcha moslamalar',
+
+# User rights
+'userrights-groupsmember' => 'Aʼzolik:',
+'userrights-reason' => 'Sabab:',
# Groups
+'group' => 'Guruh',
+'group-user' => 'Foydalanuvchilar',
+'group-autoconfirmed' => 'Tasdiqlangan foydalanuvchilar',
+'group-bot' => 'Botlar',
'group-sysop' => 'Administratorlar',
+'group-bureaucrat' => 'Rasmiyatchilar',
+'group-suppress' => 'Tekshiruvchilar',
+'grouppage-user' => '{{ns:project}}:Foydalanuvchilar',
+'grouppage-autoconfirmed' => '{{ns:project}}:Tasdiqlangan foydalanuvchilar',
+'grouppage-bot' => '{{ns:project}}:Botlar',
'grouppage-sysop' => '{{ns:project}}:Administratorlar',
+'grouppage-bureaucrat' => '{{ns:project}}:Rasmiyatchilar',
+'grouppage-suppress' => '{{ns:project}}:Tekshiruvchilar',
# Associated actions - in the sentence "You do not have permission to X"
'action-edit' => 'ushbu sahifani tahrirlash',
+'action-move' => 'bu sahifani koʻchir',
+'action-move-subpages' => 'Bu sahifani va uning ostsahifalarini koʻchir',
# Recent changes
'recentchanges' => 'Yangi o‘zgartirishlar',
'recentchanges-summary' => "Bu sahifada siz oxirgi o'zgartirishlarni ko'rishingiz mumkin.",
-'recentchanges-label-newpage' => 'Bu tahrir yangi sahifani yaratdi',
+'recentchanges-label-newpage' => 'Bu tahrir orqali yangi sahifa yaratildi',
'recentchanges-label-minor' => 'Bu kichik tahrir',
'recentchanges-label-bot' => 'Bu tahrirni bot bajardi',
-'recentchanges-label-unpatrolled' => 'Bu tahrir hali tekshirilmadi',
+'recentchanges-label-unpatrolled' => 'Bu tahrir hali tekshirilmagan',
'rcnote' => "Quyida $5, $4ga koʻra oxirgi {{PLURAL:$2|kun|'''$2''' kun}} davomida sodir boʻlgan {{PLURAL:$1|'''1''' oʻzgartirish|'''$1''' oʻzgartirishlar}} koʻrsatilgan.",
'rclistfrom' => "$1dan boshlab yangi o'zgartirishlarni ko'rsat.",
'rcshowhideminor' => 'Kichik tahrirlarni $1',
'show' => 'koʻrsat',
'minoreditletter' => 'k',
'newpageletter' => 'Y',
+'rc-enhanced-expand' => 'Tasfilotlarni koʻrsat (JavaScript talab qilinadi)',
+'rc-enhanced-hide' => 'Tafsilotlolarni yashir',
# Recent changes linked
-'recentchangeslinked' => "Bog'langan o'zgarishlar",
+'recentchangeslinked' => 'Bogʻlangan oʻzgarishlar',
+'recentchangeslinked-feed' => 'Bogʻliq oʻzgarishlar',
'recentchangeslinked-toolbox' => 'Bogʻliq oʻzgarishlar',
'recentchangeslinked-title' => '"$1"ga aloqador oʻzgarishlar',
'recentchangeslinked-noresult' => 'Berilgan davrda bogʻlangan sahifalarda oʻzgarishlar boʻlmagan.',
# Upload
'upload' => 'Fayl yuklash',
'uploadbtn' => 'Fayl yukla',
+'uploaderror' => 'Yuklashda xatolik',
'uploadlogpage' => 'Yuklash qaydlari',
'filedesc' => 'Qisqa izoh',
+'filereuploadsummary' => 'Fayldagi oʻzgarishlar:',
+'filesource' => 'Manba:',
'uploadedimage' => '"[[$1]]" yuklandi',
# Special:ListFiles
+'imgfile' => 'fayl',
'listfiles' => 'Fayllar roʻyxati',
+'listfiles_date' => 'Sana',
+'listfiles_user' => 'Foydalanuvchi',
+'listfiles_size' => 'Oʻlchami',
+'listfiles_description' => 'Taʻrif',
# File description page
'file-anchor-link' => 'Fayl',
'filehist' => 'Fayl tarixi',
'filehist-help' => 'Faylning biror paytdagi holatini koʻrish uchun tegishli sana/vaqtga bosingiz.',
+'filehist-deleteone' => 'o‘chirish',
+'filehist-revert' => 'qaytarish',
'filehist-current' => 'joriy',
'filehist-datetime' => 'Sana/Vaqt',
'filehist-thumb' => 'Miniatyura',
Uning [$2 fayl tavsifi sahifasidan] olingan tavsifi quyida keltirilgan.',
'uploadnewversion-linktext' => 'Bu faylning yangi versiyasini yukla',
+# File reversion
+'filerevert-comment' => 'Sabab:',
+
+# File deletion
+'filedelete-comment' => 'Sabab:',
+
# Unused templates
'unusedtemplates' => 'Ishlatilinmagan andozalar',
'pager-older-n' => '{{PLURAL:$1|eskiroq 1|eskiroq $1}}',
# Book sources
+'booksources' => 'Kitob manbaʻlar',
'booksources-go' => 'O‘tish',
# Special:Log
[[Special:UnusedCategories|Unused categories]] are not shown here.
Also see [[Special:WantedCategories|wanted categories]].',
+# Special:DeletedContributions
+'sp-deletedcontributions-contribs' => 'hissa',
+
+# Special:LinkSearch
+'linksearch-ns' => 'Nomfazo:',
+'linksearch-ok' => 'Qidirish',
+
+# Special:ListUsers
+'listusers-submit' => 'Koʻrsat',
+
# Special:ListGroupRights
+'listgrouprights-group' => 'Guruh',
+'listgrouprights-rights' => 'Huquqlar',
'listgrouprights-members' => '(a’zolar ro‘yxati)',
# E-mail user
'emailuser' => 'Bu foydalanuvchiga e-maktub joʻnat',
+'noemailtext' => "Bu foydalanuvchi e-mail manzil ko'rsatgani yo'q.",
+'emailsend' => 'Joʻnatish',
# Watchlist
'watchlist' => 'Kuzatuv roʻyxatim',
'blocklink' => 'chetlashtir',
'contribslink' => 'hissasi',
'blocklogpage' => 'Chetlashtirish qaydlari',
+'block-log-flags-nocreate' => 'hisob ochish toʻxtatilgan',
# Move page
'movearticle' => "Sahifani ko'chirish",
'tooltip-recreate' => "Bu sahifani u o'chirilgan bo'lishiga qaramasdan qayta yaratish",
'tooltip-summary' => 'Qisqa mazmun kiriting',
+# Attribution
+'others' => 'boshqalar',
+
+# Info page
+'pageinfo-title' => '"$1" sahifasi haqida maʼlumot',
+'pageinfo-header-edits' => 'Tahrirlar',
+'pageinfo-header-watchlist' => 'Kuzatuv roʻyxati',
+'pageinfo-subjectpage' => 'Sahifa:',
+'pageinfo-talkpage' => 'Munozara sahifasi',
+'pageinfo-watchers' => 'Kuzatuvchilar soni',
+'pageinfo-edits' => 'Tahrirlar soni',
+
# Browsing diffs
'previousdiff' => '← Avvalgi tahrir',
'nextdiff' => 'Keyingi tahrir →',
# Media information
-'imagemaxsize' => "Tasvir ta'rifi sahifasidagi tasvirning kattaligi:",
-'thumbsize' => 'Tasvirning kichiklashtirilgan versiyasining kattaligi:',
+'imagemaxsize' => 'Tasvir taʼrifi sahifasidagi tasvirning oʻlchami:',
+'thumbsize' => 'Tasvirning kichiklashtirilgan versiyasining oʻlchami:',
'file-info-size' => '$1 × $2 piksel, fayl hajmi: $3, MIME tipi: $4',
'file-nohires' => 'Bundan kattaroq tasvir yoʻq.',
'svg-long-desc' => 'SVG fayl, asl oʻlchamlari $1 × $2 piksel, fayl hajmi: $3',
'metadata-expand' => 'Batafsil axborot koʻrsat',
'metadata-collapse' => 'Batafsil axborotni yashir',
+# EXIF tags
+'exif-imagewidth' => 'Eni',
+'exif-imagelength' => 'Boʻyi',
+'exif-artist' => 'Muallif',
+'exif-source' => 'Manba',
+'exif-iimcategory' => 'Turkum',
+
+# Pseudotags used for GPSLatitudeRef and GPSDestLatitudeRef
+'exif-gpslatitude-n' => 'Shimoliy kenglik',
+'exif-gpslatitude-s' => 'Janubiy kenglik',
+
+# Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef
+'exif-gpslongitude-e' => 'Sharqiy uzunlik',
+'exif-gpslongitude-w' => 'Gʻarbiy uzunlik',
+
+# Pseudotags used for GPSDestDistanceRef
+'exif-gpsdestdistance-k' => 'Kilometr',
+'exif-gpsdestdistance-m' => 'Mil',
+
+'exif-iimcategory-clj' => 'Jinoyat va qonun',
+'exif-iimcategory-dis' => 'Halokatlar',
+'exif-iimcategory-fin' => 'Iqtisodiyot va biznes',
+'exif-iimcategory-edu' => 'Maʼrifat',
+'exif-iimcategory-evn' => 'Atrofimizdagi olam',
+'exif-iimcategory-hum' => 'Inson huquqlari',
+'exif-iimcategory-lab' => 'Mehnat',
+'exif-iimcategory-lif' => 'Turmush tarzi va hordiq',
+'exif-iimcategory-pol' => 'Siyosat',
+'exif-iimcategory-rel' => 'Din va imon',
+'exif-iimcategory-sci' => 'Fan va texnologiyalar',
+'exif-iimcategory-spo' => 'Sport',
+'exif-iimcategory-wea' => 'Ob-havo',
+
# External editor support
'edit-externally' => 'Bu faylni tashqi dasturiy ilovalar yordamida tahrirla',
'edit-externally-help' => "(Batafsil ma'lumotlar uchun [//www.mediawiki.org/wiki/Manual:External_editors bu yerga] qarang)",
'unit-pixel' => 'piksel',
+# Multipage image navigation
+'imgmultipageprev' => '← oldingi sahifa',
+'imgmultipagenext' => 'keyingi sahifa →',
+'imgmultigoto' => '$1 sahifasiga oʻtish',
+
+# Table pager
+'table_pager_next' => 'Keyingi sahifa',
+'table_pager_prev' => 'Oldingi sahifa',
+'table_pager_first' => 'Birinchi sahifa',
+'table_pager_last' => 'Oxirgi sahifa',
+
+# Auto-summaries
+'autoredircomment' => '[[$1]]ga yoʻnaltirildi',
+'autosumm-new' => '"$1" yozuvi orqali yangi sahifa yaratildi',
+
+# Watchlist editing tools
+'watchlisttools-edit' => 'Kuzatuv roʻyxatimni koʻrish/oʻzgartirish',
+'watchlisttools-raw' => 'Kuzatuv roʻyxatimni tahrirlash',
+
+# Signatures
+'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|munozara]])',
+
+# Special:Version
+'version-specialpages' => 'Maxsus sahifalar',
+
# Special:SpecialPages
'specialpages' => 'Maxsus sahifalar',
+# HTML forms
+'htmlform-reset' => 'Oʻzgarishlarni bekor qilish',
+
+# New logging system
+'logentry-move-move' => '$1 $3 sahifasini $4ga koʻchirdi',
+
+# Feedback
+'feedback-close' => 'Bajarildi',
+
+# API errors
+'api-error-unknown-code' => 'Noaniq xato: "$1".',
+'api-error-unknownerror' => 'Noaniq xato: "$1".',
+
);
'api-error-unknown-warning' => 'Tadmatoi varutuz: $1',
'api-error-unknownerror' => 'Tundmatoi petuz: "$1"',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|om laskmatoi failantip|oma laskmatomad failantipad}}. Lasktud {{PLURAL:$3|failantip om|failantipad oma}} $2.',
);
'remembermypassword' => 'Nhớ thông tin đăng nhập của tôi trên máy tính này (cho đến $1 ngày)',
'securelogin-stick-https' => 'Giữ kết nối với HTTPS sau khi đăng nhập',
'yourdomainname' => 'Tên miền của bạn:',
+'password-change-forbidden' => 'Bạn không thể đổi mật khẩu trên wiki này.',
'externaldberror' => 'Có lỗi khi xác nhận cơ sở dữ liệu bên ngoài hoặc bạn không được phép cập nhật tài khoản bên ngoài.',
'login' => 'Đăng nhập',
'nav-login-createaccount' => 'Đăng nhập / Mở tài khoản',
'noarticletext-nopermission' => 'Trang này hiện đang trống.
Bạn có thể [[Special:Search/{{PAGENAME}}|tìm kiếm tựa trang này]] tại các trang khác,
hoặc <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} tìm kiếm các nhật trình liên quan]</span>.',
+'missing-revision' => 'Phiên bản #$1 của trang có tên “{{PAGENAME}}” không tồn tại.
+
+Lỗi này thường xuất hiện đối khi theo dõi liên kết lỗi thời đến phiên bản cũ của một trang đã bị xóa.
+Xem chi tiết trong [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} nhật trình xóa].',
'userpage-userdoesnotexist' => 'Tài khoản mang tên “<nowiki>$1</nowiki>” chưa được đăng ký. Xin hãy kiểm tra lại nếu bạn muốn tạo/sửa trang này.',
'userpage-userdoesnotexist-view' => 'Tài khoản “$1” chưa được đăng ký.',
'blocked-notice-logextract' => 'Người dùng này hiện đang bị cấm sửa đổi. Nhật trình cấm gần nhất được ghi ở dưới để tiện theo dõi:',
'expansion-depth-exceeded-warning' => 'Trang bung bản mẫu sâu quá',
'parser-unstrip-loop-warning' => 'Vòng lặp unstrip',
'parser-unstrip-recursion-limit' => 'Đã vượt quá giới hạn về độ sâu đệ quy unstrip ($1)',
+'converter-manual-rule-error' => 'Lỗi được phát hiện trong quy tắc chuyển đổi ngôn ngữ thủ công',
# "Undo" feature
'undo-success' => 'Các sửa đổi có thể được lùi lại. Xin hãy kiểm tra phần so sánh bên dưới để xác nhận lại những gì bạn muốn làm, sau đó lưu thay đổi ở dưới để hoàn tất việc lùi lại sửa đổi.',
'editundo' => 'lùi sửa',
'diff-multi' => '(Không hiển thị {{PLURAL:$1||$1}} phiên bản {{PLURAL:$2||của $2 thành viên}} ở giữa)',
'diff-multi-manyusers' => '(Không hiển thị {{PLURAL:$1||$1}} phiên bản của hơn $2 thành viên ở giữa)',
+'difference-missing-revision' => 'Không tìm thấy {{PLURAL:$2|một phiên bản|$2 phiên bản}} trong khác biệt này ($1).
+
+Lỗi này thường xuất hiện đối khi theo dõi liên kết lỗi thời đến khác biệt giữa các bản của trang đã bị xóa.
+Xem chi tiết trong [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} nhật trình xóa].',
# Search results
'searchresults' => 'Kết quả tìm kiếm',
'right-writeapi' => 'Sử dụng API để viết',
'right-delete' => 'Xóa trang',
'right-bigdelete' => 'Xóa trang có lịch sử lớn',
+'right-deletelogentry' => 'Xóa và phục hồi khoản mục nhật trình nào đó',
'right-deleterevision' => 'Xóa và phục hồi phiên bản nào đó của trang',
'right-deletedhistory' => 'Xem phần lịch sử đã xóa, mà không xem nội dung đi kèm',
'right-deletedtext' => 'Xem văn bản đã xóa và các thay đổi giữa phiên bản đã xóa',
'right-browsearchive' => 'Tìm kiếm trang đã bị xóa',
'right-undelete' => 'Phục hồi trang',
-'right-suppressrevision' => 'Xem lại và phục hồi phiên bản mà Sysop không thấy',
+'right-suppressrevision' => 'Xem và phục hồi phiên bản mà bảo quản viên không thấy',
'right-suppressionlog' => 'Xem nhật trình riêng tư',
'right-block' => 'Cấm thành viên khác sửa đổi',
'right-blockemail' => 'Cấm người dùng gửi thư điện tử',
'action-deletedhistory' => 'xem các phiên bản đã bị xóa của trang này',
'action-browsearchive' => 'tìm kiếm trang đã bị xóa',
'action-undelete' => 'phục hồi trang này',
-'action-suppressrevision' => 'duyệt và phục hồi phiên bản bị giấu này',
+'action-suppressrevision' => 'xem và phục hồi phiên bản ẩn này',
'action-suppressionlog' => 'xem nhật trình ẩn giấu này',
'action-block' => 'cấm không cho người dùng này sửa đổi',
'action-protect' => 'thay đổi mức khóa của trang này',
'lockmanager-fail-releaselock' => 'Không thể thả khóa cho “$1”.',
'lockmanager-fail-db-bucket' => 'Không thể liên lạc với đủ cơ sở dữ liệu khóa trong nhóm $1.',
'lockmanager-fail-db-release' => 'Không thể thả các chìa khóa trên cơ sở dữ liệu $1.',
+'lockmanager-fail-svr-acquire' => 'Không thể lấy các chìa khóa trên máy chủ $1.',
'lockmanager-fail-svr-release' => 'Không thể thả các chìa khóa trên máy chủ $1.',
# ZipDirectoryReader
'disambiguations' => 'Trang liên kết đến trang định hướng',
'disambiguationspage' => 'Template:disambig',
-'disambiguations-text' => "Các trang này có liên kết đến một '''trang định hướng'''. Nên sửa các liên kết này để chỉ đến một trang đúng nghĩa hơn.<br />Các trang định hướng là trang sử dụng những bản mẫu được liệt kê ở [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "Các trang này có liên kết đến ít nhất một '''trang định hướng''', những trang này có thể có liên kết đến các trang đúng nghĩa hơn.<br />Các trang định hướng là trang sử dụng những bản mẫu được liệt kê ở [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'Đổi hướng kép',
'doubleredirectstext' => 'Trang này liệt kê các trang đổi hướng đến một trang đổi hướng khác.
'rollback' => 'Lùi tất cả sửa đổi',
'rollback_short' => 'Lùi tất cả',
'rollbacklink' => 'lùi tất cả',
+'rollbacklinkcount' => 'lùi tất cả $1 sửa đổi',
+'rollbacklinkcount-morethan' => 'lùi tất cả hơn $1 sửa đổi',
'rollbackfailed' => 'Lùi sửa đổi không thành công',
'cantrollback' => 'Không lùi sửa đổi được;
người viết trang cuối cùng cũng là tác giả duy nhất của trang này.',
'tooltip-watchlistedit-raw-submit' => 'Cập nhật danh sách theo dõi',
'tooltip-recreate' => 'Tạo lại trang dù cho nó vừa bị xóa',
'tooltip-upload' => 'Bắt đầu tải lên',
-'tooltip-rollback' => '"Lùi tất cả" sẽ lùi mọi sửa đổi của người sửa đổi cuối cùng chỉ bằng một cú nhấp chuột.',
+'tooltip-rollback' => '“Lùi tất cả” sẽ lùi mọi sửa đổi của người sửa đổi cuối cùng chỉ bằng một cú nhấp chuột.',
'tooltip-undo' => '"Lùi lại" sẽ lùi sửa đổi này và mở trang sửa đổi ở chế độ xem thử. Cho phép thêm lý do vào tóm lược.',
'tooltip-preferences-save' => 'Lưu tùy chọn',
'tooltip-summary' => 'Hãy nhập câu tóm lược',
'duration-millennia' => '$1 thiên niên kỷ',
# Unknown messages
-'lockmanager-fail-svr-acquire' => 'Không thể lấy các chìa khóa trên máy chủ $1.',
+'api-error-filetype-banned-type' => '{{PLURAL:$4|Định dạng|Các định dạng}} $1 không được chấp nhận. Chỉ chấp nhận {{PLURAL:$3|loại tập tin|các loại tập tin}} sau: $2.',
);
'category-empty' => "''Kase gruppa on tühjä.''",
'hidden-categories' => '{{PLURAL:$1|Salautõttu gruppa|Salautõtud gruppad}}',
'category-subcat-count' => '{{PLURAL:$2|Senez gruppaz on ainult vahtiaava alagruppa.|{{PLURAL:$1|Vahtiaava alagruppa kuulub|Vahtiaava $1 alagruppaa kuuluvad}} sihee gruppaa. Alagruppaďďe cisla gruppaza on $2.}}',
-'category-article-count' => '{{PLURAL:$2|Senez gruppaz on ainult vahtiaava cülci.|{{PLURAL:$1|Vahtiaava alagruppa kuulub|Vahtiaava $1 tšültšiä kuuluvad}} sihee gruppaa. Cülcije cisla gruppaza on $2.}}',
+'category-article-count' => '{{PLURAL:$2|Senez gruppaz on ainult vahtiaava cülci.|{{PLURAL:$1|Vahtiaava alagruppa kuulub|Vahtiaava $1 cülciä kuuluvad}} sihee gruppaa. Cülcije cisla gruppaza on $2.}}',
+'category-file-count' => '{{PLURAL:$2|Senez gruppaz on ainult vahtiaava faili.|{{PLURAL:$1|Vahtiaava alagruppa kuulub|Vahtiaava $1 failid kuuluvad}} sihee gruppaa. Cülcije cisla gruppaza on $2.}}',
'listingcontinuesabbrev' => 'ladvaub',
'about' => 'Täätühsed',
'vector-view-viewsource' => 'Lähtekoodi',
'actions' => 'Tekod',
'namespaces' => 'Nimiruumid',
+'variants' => 'Variandid',
'errorpagetitle' => 'Vika',
'returnto' => 'Mee takaz cüľľelle $1.',
# Short words for each namespace, by default used in the namespace tab in monobook
'nstab-main' => 'Artikkeli',
'nstab-user' => 'Cäüttijäcülci',
+'nstab-media' => 'Media-cülci',
'nstab-special' => 'Osoobennoi cülci',
'nstab-project' => 'Projekticülci',
'nstab-image' => 'Faili',
'userlogout' => 'Cirjut uloz',
'nologin' => "Kui Teille veel ebõõ cäüttijänimi, '''$1'''.",
'nologinlink' => 'võitta loovva luguu',
+'createaccount' => 'Uusi cäüttijää',
'gotaccountlink' => 'Cirjut süäme',
'createaccountreason' => 'Süü:',
'mailmypassword' => 'Lähet uusi salasõna elektropoštiikaa',
'noarticletext' => '{{GRAMMAR:inessive|{{SITENAME}}}} ebõõ sene nimissä cülciä.
* Võid [[Special:Search/{{PAGENAME}}|ettsiä cüľľee nimellä]] muilta cüľľeltä.
* Võid cirjuttõma uuvvõõ cüľľee <span class="plainlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit}} {{PAGENAME}}]</span>.',
+'noarticletext-nopermission' => 'Paraika kazell lehocüllell eb õõ teksta.
+Tüü võittõ [[Special:Search/{{PAGENAME}}|kaze nime nimettamizõ löütä]] muiss artikkeliiss, ehci <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}}löütä azjakõhaizõd cirjauhsõd žurnaaliiss]</span>.',
'previewnote' => "'''Kase on ainult prestavleńńa.''' Cülciä ebõõ veel salvotõttu!",
'editing' => 'Muutun $1',
'editingsection' => 'Muuttõmizõll on õsa cüľľess $1',
'permissionserrorstext-withaction' => 'Teill ebõõ luppa $2 {{PLURAL:$1|vahtiaavass süüss|vahtiaaviss süiss}} peräss:',
'moveddeleted-notice' => "Kase cülci on pühittü. Alla on sene cüľľee pühi'istori.",
+# Parser/template warnings
+'post-expand-template-inclusion-warning' => "'''Etetäätämin:''' lizettävije šabloonije summaarin koko on liiga suuri.
+Mõnõd šabloonad eväd lee lizettü.",
+'post-expand-template-inclusion-category' => 'Lehocülled, jõgõit vart lizettävije šabloonije sallittu koko on ületettü',
+'post-expand-template-argument-category' => 'Lehocülled, jõgad sisälletä väl’l’ä-jätettü šabloonije argumentad.',
+
# History pages
'viewpagelogs' => 'Näüt sene cüľľee logid',
'currentrev' => 'Nücüin verzija',
'last' => 'entin',
'histlegend' => "Merkid: ({{int:cur}}) = vahõ nütšüizese verzijaa, ({{int:last}}) = vahõ entiizese verzijaa, '''{{int:minoreditletter}}''' = peeni muutuz",
'history-fieldset-title' => 'Ľistvoit muutuzistoria',
+'history-show-deleted' => "Tol'ko poisõttu",
'histfirst' => 'Kõikkõa varaizõpid',
'histlast' => 'Kõikkõa viimõizõpid',
'revdelete-radio-unset' => 'Eb',
'revdelete-log' => 'Süü:',
'revdel-restore' => 'muutu näcüvüss',
+'revdel-restore-deleted' => 'poizõttu verssijad',
+'revdel-restore-visible' => 'nähtäväd verssijad',
'pagehist' => 'Cüľľee istori',
'revdelete-reasonotherlist' => 'Muu süü',
'notextmatches' => 'Ettsisõnaa eb löütünnü cüľľee tekstiissä',
'prevn' => '{{PLURAL:$1|edellinen|$1 entiiss}}',
'nextn' => '{{PLURAL:$1|$1}} vahtiaava →',
+'prevn-title' => '{{PLURAL:$1|Eellin cirjauz|$1 eellissa cirjaussa}}',
+'nextn-title' => '{{PLURAL:$1|Sõurava cirjauz|$1 sõurava cirjaussa}}',
+'shown-title' => 'Näüttä $1 {{PLURAL:$1|cirjauz|cirjaussa}} lehocüllell',
'viewprevnext' => 'Näüt ($1 {{int:pipe-separator}} $2) ($3)',
+'searchmenu-exists' => 'Kazez viki-projeektaz on lehocülci «[[:$1]]» õõmaz',
+'searchmenu-new' => 'Tehä lehocülci «[[:$1]]» senez viki-projeektaz!',
+'searchprofile-articles' => 'Pääcülcid',
+'searchprofile-project' => 'Selvitühse ja projeekta lehocülled',
'searchprofile-images' => 'Multimedia',
'searchprofile-everything' => 'Kõik',
+'searchprofile-advanced' => 'Lizätez',
+'searchprofile-articles-tooltip' => 'Etsi "$1":az',
'searchprofile-project-tooltip' => 'Etsi "$1":az',
+'searchprofile-images-tooltip' => 'Failõje õttsigo',
+'searchprofile-everything-tooltip' => 'Õttsigo kõikill lehocüllill (ceskussõõmizõ lehocülled siällhulgaz)',
+'searchprofile-advanced-tooltip' => 'Annõttu nimije tiloiz õttsia',
'search-result-size' => '$1 ({{PLURAL:$2|1 sõna|$2 sõna}})',
'search-redirect' => '(mešaituz $1)',
'search-section' => '(alajako $1)',
'search-interwiki-more' => '(lisä)',
'search-mwsuggest-enabled' => 'tarittsõmisijõkaa',
'search-mwsuggest-disabled' => 'tarittsõmizõtta',
+'searchrelated' => 'sittu',
'searchall' => 'kõik',
+'showingresultsheader' => "{{PLURAL:$5|Tuloz '''$1''' '''$3'''-ss|Tulohsõd '''$1-$2''' '''$3'''-ss}} «$4» vart",
'nonefound' => "'''Zametšańńa''': Ettsü etsib anult osiss nimiruumii.
Proovvi lizät etsün alkuu ''all:'', nii ettsü etsib kõikkõõ sisältoo (taas juttucüľľeekaa, sablooniikaa, jne.), vai tarvittõga nimiruumi niku prefiksi.",
+'search-nonefound' => 'Cüsümühse mukaizõssi eb õõ mitäid löütettü.',
'powersearch' => 'Etenennü ettsü',
'powersearch-legend' => 'Etenennü ettsü',
'powersearch-ns' => 'Etsi nimiruumõssa:',
# Groups
'group-user' => 'Сäüttijäd',
'group-sysop' => 'Praviťeľad',
+'group-all' => '{kõik)',
'group-user-member' => 'cäüttijä',
'recentchanges' => 'Viimõizõd muutussõd',
'recentchanges-legend' => 'Viimass muutuhsõss valimizõd',
'recentchanges-feed-description' => 'Sell sivull võib vahtia uutizijõ muutuhsiit.',
+'recentchanges-label-newpage' => 'Kaze kirjauhsõka õli muu lehocülci lootu.',
'recentchanges-label-minor' => 'Kase on peeni muutuz',
+'recentchanges-label-bot' => 'Kase kõrjauz on robotaka lootu',
+'recentchanges-label-unpatrolled' => 'Kasta kõrjaussa eb tarkisõtti veel',
'rcnote' => 'Alla on {{PLURAL:$1|ühsi muutuz|viimeiziit $1 muutussiit}} viimeize {{PLURAL:$2|ühee päivää|$2 päivää}}, $4 $5.',
'rclistfrom' => 'Näüt uuvvõd muutuhsõd $1 alguss',
'rcshowhideminor' => '$1 peened muutussõd',
'recentchangeslinked-feed' => 'Sukulaizõd muutussõd',
'recentchangeslinked-toolbox' => 'Sukulaizõd muutussõd',
'recentchangeslinked-title' => 'Cüľľelt $1 linkitettüďďe cülcije muutuhsõd.',
+'recentchangeslinked-noresult' => 'Cüseizez aigakõhaz eväd õltu milliziitäid muuttumiziit.',
'recentchangeslinked-summary' => "Kase osoobennoi cülci näütteb muutusõd cülcillä, kummalõõ on seltä cüľľeltä näüteltü.
Cüľľed, kummad õmad teďďellä [[Special:Watchlist|kattsõspiizgalla]] on cirjuttõnnu '''pimmiässi'''.",
'recentchangeslinked-page' => 'Cüľľee nimi:',
'fileuploadsummary' => 'Turvotuz:',
'uploadedimage' => '"[[$1]]" on laajõngoitõttu ülez',
+'license' => 'Litseenttsija:',
+'license-header' => 'Litseenttsija',
+
# Special:ListFiles
'imgfile' => 'faili',
'listfiles_thumb' => 'Peenikuva',
'filehist-comment' => 'Zametšańńa',
'imagelinks' => 'Faililinkid',
'linkstoimage' => 'Selle kuvallõ {{PLURAL:$1|näütteeb kase сülсi|näütteväd kaned сüľľed}}:',
+'nolinkstoimage' => 'Eb ühelläid lehocüllell cäütetä kase faila.',
'sharedupload' => 'Kase faili on $1:lt ja muud projektõd saavad cäüttää sitä.',
+'sharedupload-desc-here' => 'Se fajla on $1-ss ja võib muiz projeektiz õlla cäütettävänä.
+Seness [$2 kuvauhsõ lehocülless] informaattsija on alapallõ annõttu.',
'uploadnewversion-linktext' => 'Laajõngoit uusi verzija seness failiss ülez',
# File reversion
'statistics' => 'Staťisťikka',
'statistics-pages' => 'Cülcid',
+'disambiguationspage' => 'Template:disambig',
+
'brokenredirects-edit' => 'muuttaa',
'brokenredirects-delete' => 'pühi',
'linksearch' => 'Ulkopoolizõd linkid',
'linksearch-ns' => 'Nimiruumi:',
'linksearch-ok' => 'Etsi',
+'linksearch-line' => 'Linki $1-sõ $2-ss',
# Special:ListUsers
'listusers-submit' => 'Näüt väľľää',
'delete-legend' => 'Pühi',
'confirmdeletetext' => 'Õlõtta pühcimässä cüľľee vai failii ja kõigõ sene istorii. Õlka nii üvä, kõvissõga jot tahotta sitä tehä, jot saatta arvoa sledstvijäd jot pühcimüz on [[{{MediaWiki:Policy-url}}|poolissaa]] mukka.',
'actioncomplete' => 'Töö tehtü lõppuu',
+'actionfailed' => 'Vika',
'deletedtext' => '"$1" on pühittü.
Cüľľellä $2 on spiiska viimeiziss pühcimühsiiss.',
'dellogpage' => 'Pühitüd cüľľed',
# Thumbnails
'thumbnail-more' => 'Suurõt',
+'thumbnail_error' => 'Vika: $1',
# Special:Import
'import-upload-filename' => 'Failinimi:',
'watchlisttools-edit' => 'Muuttaa spiiskaa',
'watchlisttools-raw' => 'Muut lähtefaili',
+# Core parser functions
+'duplicate-defaultsort' => '\'\'\'Warning:\'\'\' Default sort key "$2" overrides earlier default sort key "$1".',
+
# Special:FilePath
'filepath-page' => 'Faili:',
'filepath-submit' => 'Mee',
'specialpages' => 'Osoobenoid cüľľed',
# Special:Tags
+'tag-filter' => "[[Special:Tags|Deskriptorije]] fil'tra:",
'tags-edit' => 'muuttaa',
# HTML forms
'nstab-project' => 'Pakli han proyekto',
'nstab-image' => 'Fayl',
'nstab-mediawiki' => 'Mensahe',
-'nstab-template' => 'Plantilya',
+'nstab-template' => 'Batakan',
'nstab-help' => 'Pakli hin bulig',
'nstab-category' => 'Kaarangay',
'statistics-articles' => 'Unod nga mga pakli',
'statistics-pages' => 'Mga pakli',
'statistics-pages-desc' => 'Ngatanan nga mga pakli ha sulod hini nga wiki, lakip an hiruhimangraw nga mga pakli, mga redirect, ngan iba pa',
-'statistics-files' => 'Ginkarga nga mga paypay',
+'statistics-files' => 'Mga paypay nga iginkarga pasaka',
'statistics-edits' => 'Mga pagliwat hit pakli tikang gintukod hini nga {{SITENAME}}',
'statistics-edits-average' => 'Average nga mga pagliwat kada pakli',
'statistics-views-total' => 'Ngatanan nga mga panginano',
'statistics-views-peredit' => 'Mga panginano kada pagliwat',
-'statistics-users' => 'Mga nakarehistro nga [[Special:ListUsers|gumaramit]]',
-'statistics-users-active' => 'Mga nanggigios nga gumaramit',
+'statistics-users' => 'Mga [[Special:ListUsers|gumaramit]] nga nakarehistro',
+'statistics-users-active' => 'Mga gumaramit nga nanggigios',
'statistics-users-active-desc' => 'Mga gumaramit nga may-ada iginbuhat ha urhi nga {{PLURAL:$1|ka adlaw|$1 ka mga adlaw}}',
'statistics-mostpopular' => 'Gidamoi nga ginpanginanohan nga mga pakli',
'listusers-blocked' => '(ginpugngan)',
# Special:ActiveUsers
-'activeusers' => 'Lista han mga nanggigios nga gumaramit',
+'activeusers' => 'Taramdan hin mga gumaramit nga nanggigios',
'activeusers-hidebots' => 'Igtago an mga bot',
'activeusers-hidesysops' => 'Igtago an mga magdudumara',
'activeusers-noresult' => 'Waray gumaramit nga nahiagian.',
'helppage' => 'Help:დინორე',
'mainpage' => 'დუდხასჷლა',
'mainpage-description' => 'დუდხასჷლა',
-'portal' => 'á\83¡á\83\90á\83\96á\83\9dá\83\92á\83\90á\83\93á\83\9dá\83\94á\83\91აშ ხასჷლეფი',
-'portal-url' => 'Project:á\83¡á\83\90á\83\96á\83\9dá\83\92á\83\90á\83\93á\83\9dá\83\94á\83\91á\83\90á\83¨ á\83®á\83\90á\83¡á\83\98á\83\9aá\83\94á\83¤',
+'portal' => 'á\83¯á\83\90á\83 á\83\90á\83\9aá\83£აშ ხასჷლეფი',
+'portal-url' => 'Project:á\83¯á\83\90á\83 á\83\90á\83\9aá\83£á\83\90á\83¨ á\83®á\83\90á\83¡á\83·á\83\9aá\83\94á\83¤á\83\98',
'privacy' => 'ანონიმურობაშ პოლიტიკა',
'privacypage' => 'Project:ანონიმურობაშ პოლიტიკა',
'version' => 'ვერსია',
# Special:SpecialPages
-'specialpages' => 'á\83¡á\83\9eá\83\94á\83ªá\83\98á\83\90á\83\9aá\83£á\83 á\83®á\83\90á\83¡á\83\98á\83\9aá\83\94á\83¤',
+'specialpages' => 'á\83\92á\83·á\83¨á\83\90á\83\99á\83\94á\83 á\83«á\83\90á\83¤á\83\98á\83\9aá\83\98 á\83®á\83\90á\83¡á\83·á\83\9aá\83\94á\83¤á\83\98',
# External image whitelist
'external_image_whitelist' => '"#ქჷდიტე თე ღოზი კოკობო მუჭო რენ თეში<pre>
'tog-showtoc' => 'ווייז דאס אינהאלט קעסטל<br />(פאר בלעטער מיט מער ווי 3 קעפלעך)',
'tog-rememberpassword' => 'געדענק מיין אריינלאגירן אין דעם בלעטערער (ביז $1 {{PLURAL:$1|טאָג|טעג}})',
'tog-watchcreations' => 'צולייגן בלעטער וואס איך באשאף און טעקעס וואס איך לאד ארויף צו מיין אכטונג ליסטע',
-'tog-watchdefault' => '×\90×\95×\99פפ×\90ס×\9f ×\90×±×\98×\90Ö¸×\9e×\90Ö·×\98×\99ש ×\93×\99 ×\90ר×\98×\99ק×\9c×¢×\9f ×°×\90ָס ×\90×\99×\9a ×\91×\90Ö·×\90ַר×\91×¢×\98',
+'tog-watchdefault' => 'צ×\95×\9c×\99×\99×\92×\9f ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×\90×\99×\9a רע×\93×\90ק×\98×\99ר צ×\95 ×\9e×\99×\99×\9f ×\90×\9b×\98×\95× ×\92 ×\9c×\99ס×\98×¢',
'tog-watchmoves' => 'צולייגן בלעטער וואס איך באוועג און טעקעס וואס איך לאד ארויף צו מיין אכטונג ליסטע',
'tog-watchdeletion' => 'צולייגן בלעטער וואס איך מעק אויס צו מיין אויפפאסונג ליסטע',
'tog-minordefault' => 'באגרענעצן אלע רעדאַקטירונגען גרונטלעך אלס מינערדיק',
'youhavenewmessages' => 'איר האט $1 ($2).',
'newmessageslink' => 'נייע מעלדונגען',
'newmessagesdifflink' => 'לעצטע ענדערונג',
+'youhavenewmessagesfromusers' => 'איר האט $1 פון {{PLURAL:$3|אן אנדער באניצער|$3 באניצער}} ($2).',
+'youhavenewmessagesmanyusers' => 'איר האט $1 פון אסאך באניצער ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|א נייע מעלדונג|נייע מעלדונגען}}',
+'newmessagesdifflinkplural' => 'לעצטע {{PLURAL:$1|ענדערונג|ענדערונגען}}',
'youhavenewmessagesmulti' => 'איר האט נייע מעלדונגען אין $1',
'editsection' => 'באַאַרבעטן',
'editold' => 'רעדאַקטירן',
דער סיסאפ וואס האט זי פארשפארט האט געגעבן דעם הסבר: "$3"',
'invalidtitle-knownnamespace' => 'אומגילטירער טיטל מיט נאמענטייל "$2" און טעקסט "$3"',
'invalidtitle-unknownnamespace' => 'אומגילטיקער טיטל מיט אומבאוואוסטן נאמענטייל נומער $1 און טעקסט "$2"',
+'exception-nologin' => 'נישט אַרײַנלאגירט',
+'exception-nologin-text' => 'דער בלאט אדער אקציע פֿאדערט אז איר זענט אריינלאגירט ביי דער וויקי.',
# Virus scanner
'virus-badscanner' => "שלעכטע קאנפֿיגוראציע: אומבאוואוסטער ווירוס איבערקוקער: ''$1''",
'remembermypassword' => 'געדיינק מײַן אַרײַנלאגירן אויף דעם קאמפיוטער (ביז $1 {{PLURAL:$1|טאָג|טעג}})',
'securelogin-stick-https' => 'בלייַבן פארבונדן צו HTTPS נאָכן ארײַנלאָגירן',
'yourdomainname' => 'אײַער געביט:',
+'password-change-forbidden' => 'איר קען נישט ענדערן פאסווערטער אויף דער וויקי.',
'externaldberror' => 'עס איז אדער פארגעקומען אן אויטענטיקאציע דאטנבאזע פעלער אדער איר זענט נישט ערמעגליכט צו דערהיינטיגן אייער דרויסנדיגע קאנטע.',
'login' => 'אַרײַנלאָגירן',
'nav-login-createaccount' => 'ארײַנלאָגירן / זיך אײַנשרײַבן',
'tmp-write-error' => 'טעות בײַם שרייַבן צייַטווייַליקע טעקע.',
'large-file' => 'רעקאמענדירט אז טעקעס זאל נישט זײַן גרעסער פֿון$1;
די טעקע איז $2.',
+'largefileserver' => 'די טעקע איז גרעסער פונעם מאקסימום פאר דעם סערווער.',
'emptyfile' => 'די טעקע וואס איר האט ארויפֿלגעלאָדן איז ליידיג.
-עס קען זיין אז די סיבה איז פשוט א טייפא.
+עס קען זיין אז די סיבה איז פשוט א טייפא אינעם טעקע־נאמען.
ביטע קוקט איבער צי איר ווילט ארויפֿלאדן די דאזיקע טעקע.',
'windows-nonascii-filename' => 'די וויקי שטיצט נישט טעקע־נעמען מיט ספעציעלע צייכענען.',
'fileexists' => "א טעקע מיט דעם נאָמען עקזיסטירט שוין, ביטע זײַט בודק '''<tt>[[:$1]]</tt>''' ווען איר זענט נישט זיכער אַז איר ווילט זי ענדערן.
'disambiguations' => 'בלעטער וואס פֿארבינדן מיט באדייטן בלעטער',
'disambiguationspage' => 'Template:באדייטן',
-'disambiguations-text' => "×\93×\99 ק×\95×\9e×¢× ×\93×\99×\92×¢ ×\91×\9c×¢×\98ער פ×\90ר×\91×\99× ×\93×¢×\9f צ×\95 ×\90 '''×\91×\90×\93×\99×\99×\98×\9f ×\91×\9c×\90×\98'''. ×\96×\99×\99 ×\91ר×\95×\99×\9b×\9f ×¢× ×\93ערש×\98 פֿ×\90ר×\91×\99× ×\93×\9f צ×\95 ×\93ער רע×\9c×¢×\95×\95×\90× ×\98ער ×\98×¢×\9e×¢ ×\91×\9c×\90×\98.<br />×\90 ×\91×\9c×\90×\98 ×\95×\95ער×\98 פ×\90ררע×\9b×¢× ×\98 ×\90×\9cס ×\90 ×\91×\9c×\90×\98 ×\95×\95ער×\98 ×\92ערע×\9bנט פאר א באדײַטן בלאט אויב ער באניצט זיך מיט א מוסטער וואס איז פארבינדען פון [[MediaWiki:Disambiguationspage]].",
+'disambiguations-text' => "×\93×\99 ק×\95×\9e×¢× ×\93×\99×\92×¢ ×\91×\9c×¢×\98ער פ×\90ר×\91×\99× ×\93×\9f צ×\95 ×\90 '''×\91×\90×\93×\99×\99×\98×\9f ×\91×\9c×\90×\98'''. ×\96×\99×\99 ×\91ר×\95×\99×\9b×\9f ×¢× ×\93ערש×\98 פֿ×\90ר×\91×\99× ×\93×\9f צ×\95 ×\93×¢×\9d רע×\9c×¢×\95×\95×\90× ×\98×\9f ×\98×¢×\9e×¢ ×\91×\9c×\90×\98.<br />×\90 ×\91×\9c×\90×\98 ×\95×\95ער×\98 פ×\90ררע×\9b×¢נט פאר א באדײַטן בלאט אויב ער באניצט זיך מיט א מוסטער וואס איז פארבינדען פון [[MediaWiki:Disambiguationspage]].",
'doubleredirects' => 'געטאפלטע ווײַטערפֿירונגען',
'doubleredirectstext' => 'דער בלאט רעכנט אויס בלעטער וואס פירן ווייטער צו אנדערע ווייטערפירן בלעטער.
'wantedpages' => 'געזוכטע בלעטער',
'wantedpages-badtitle' => 'אומגילטיקער טיטל אין רעזולטאַט: $1',
'wantedfiles' => 'געזוכטע טעקעס',
+'wantedfiletext-cat' => 'די פֿאלגנדע טעקעס ווערן געניצט אבער זיי עקזיסטירן נישט. טעקעס פון פֿרעמדע רעפאזיטאריעס קענען ווערן אריינגערעכנט טראץ זיי עקזיסטירן יא. אזעלכע גרייזן וועלן ווערן <del>אויסגעשריכן </del>. דערצו, בלעטער וואס ניצן אומעקזיסטירנדע טעקעס ווערן אריינגערעכנט אין [[:$1]].',
'wantedtemplates' => 'געזוכטע מוסטערן',
'mostlinked' => 'מערסט פֿארבינדענע בלעטער',
'mostlinkedcategories' => 'מערסט פֿארבינדענע קאטעגאריעס',
מען קען פֿאַרשמעלרן די אויסוואל דורך אויסוויילן א סארט לאג, באַניצער נאמען אדער אנרירנדע בלעטער.',
'logempty' => 'נישטא קיין פאַסנדיקע זאכן אין לאג.',
'log-title-wildcard' => 'זוכן טיטלען וואס הייבן אָן מיט דעם טעקסט',
+'showhideselectedlogentries' => 'ווײַזן/באַהאַלטן געקליבענע לאגבוך אקציעס',
# Special:AllPages
'allpages' => 'אַלע בלעטער',
'rollback' => 'צוריקדרייען רעדאַקטירונגען',
'rollback_short' => 'צוריקדרייען',
'rollbacklink' => 'צוריקדרייען',
+'rollbacklinkcount' => 'צוריקדרייען $1 {{PLURAL:$1|רעדאקטירונג|רעדאקטירונגען}}',
+'rollbacklinkcount-morethan' => 'צוריקדרייען מער ווי $1 {{PLURAL:$1|רעדאקטירונג|רעדאקטירונגען}}',
'rollbackfailed' => 'צוריקדרייען דורכגעפֿאַלן',
'cantrollback' => 'מען קען נישט צוריקדרייען די ענדערונג – דער לעצטער בײַשטייערער איז דער איינציגסטער שרײַבער אין דעם בלאַט.',
'alreadyrolled' => 'מען קען נישט צוריקדרייען די לעצטע ענדערונג פון בלאט [[:$1]] פֿון
'proxyblockreason' => 'אייער איי.פי. אדרעס איז געווארן געבלאקט צוליב דעם ווייל דאס איז א אפענער פראקסי. ביטע פארבינדט זיך מיט אייער אינטערנעט סערוויס פראוויידער אדער טעקס סאפארט צו אינפארמירן זיי איבער דעם ערענסטן זיכערהייט פראבלעם.',
'proxyblocksuccess' => 'געטאן.',
'cant-block-while-blocked' => 'איר קען נישט בלאקירן קיין אנדערע באניצער ווען איר זענט אליין בלאקירט.',
+'ipbblocked' => 'איר קען נישט בלאקירן אדער אויפבלאקירן אנדערע באניצער, ווייל איר זענט אליין בלאקירט.',
'ipbnounblockself' => 'איר זענט נישט ערלויבט זיך אליין אויסבלאקירן',
# Developer tools
'movepage-page-exists' => "דער בלאַט $1 עקזיסטירט שוין און מ'קען אים נישט אויטאָמאַטיש איבערשרײַבן.",
'movepage-page-moved' => 'דער בלאַט $1 איז געוורן באַוועגט צו $2.',
'movepage-page-unmoved' => 'מען קען נישט באוועגן בלאט $1 צו $2.',
+'movepage-max-pages' => 'דער מאקסימום פון $1 {{PLURAL:$1|בלאט|בלעטער}} האט מען שוין באוועגט און נאך בלעטער וועט מען נישט באוועגן אויטאמאטיש.',
'movelogpage' => 'באוועגן לאג',
'movelogpagetext' => 'פֿאלגנד איז א ליסטע פֿון בלעטער באוועגט.',
'movesubpage' => '{{PLURAL:$1|אונטערבלאַט|אונטערבלעטער}}',
'exif-scenecapturetype' => 'סצענע אויפנעם טיפ',
'exif-gaincontrol' => 'סצענע קאנטראל',
'exif-contrast' => 'קאנטראסט',
-'exif-devicesettingdescription' => 'זאך סעטינגס אראפמאלונג',
+'exif-saturation' => 'זעטיקונג',
+'exif-sharpness' => 'שארף',
+'exif-devicesettingdescription' => 'אפאראט שטעלונגען אראפמאלונג',
'exif-gpslatituderef' => 'צפון אדער דרום גארטל־ליניע',
'exif-gpslatitude' => 'גארטל־ליניע',
'exif-gpslongituderef' => 'מזרח אדער מערב לענג',
'exif-provinceorstatedest' => 'פראווינץ אדער שטאַט געוויזן',
'exif-citydest' => 'געוויזענע שטָאט',
'exif-objectname' => 'קורצער טיטל',
+'exif-specialinstructions' => 'באזונדערע אנווייזונגען',
+'exif-headline' => 'קעפל',
+'exif-credit' => 'קרעדיט/פארזארגער',
'exif-source' => 'מקור',
'exif-editstatus' => 'רעדאקציאנעלער סטאטוס פון בילד',
'exif-urgency' => 'דרינגלעכקייט',
'exif-focalplaneresolutionunit-2' => 'אינטשעס',
+'exif-sensingmethod-1' => 'אומדעפינירט',
+
'exif-customrendered-0' => 'נארמאלער פראצעס',
'exif-customrendered-1' => 'קאסטעם פראצעס',
'exif-sharpness-2' => 'הארט',
'exif-subjectdistancerange-0' => 'אומבאַוויסט',
+'exif-subjectdistancerange-1' => 'מאקרא',
+'exif-subjectdistancerange-2' => 'נאנטע ווייזונג',
+'exif-subjectdistancerange-3' => 'ווײַטע ווײַזונג',
# Pseudotags used for GPSLatitudeRef and GPSDestLatitudeRef
-'exif-gpslatitude-n' => 'צפ×\95×\9f ×\9c×¢×\98×\99×\98×\95×\93',
-'exif-gpslatitude-s' => '×\93ר×\95×\9d ×\9c×\90×\98×\99×\98×\95×\93',
+'exif-gpslatitude-n' => 'צפ×\95×\9f ×\91ר×\99×\99×\98',
+'exif-gpslatitude-s' => '×\93ר×\95×\9d ×\91ר×\99×\99×\98',
# Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef
'exif-gpslongitude-e' => 'מזרח לענג',
'exif-gpslongitude-w' => 'מערב לענג',
# Pseudotags used for GPSAltitudeRef
-'exif-gpsaltitude-above-sealevel' => '$1 {{PLURAL:$1|ngמעטער|מעטער}} איבערן ים־שפיגלl',
+'exif-gpsaltitude-above-sealevel' => '$1 {{PLURAL:$1|מעטער|מעטער}} איבערן ים־שפיגלl',
'exif-gpsaltitude-below-sealevel' => '$1 {{PLURAL:$1|מעטער|מעטער}} אונטערן ים־שפיגל',
# Pseudotags used for GPSSpeedRef
# Special:Version
'version' => 'ווערסיע',
+'version-extensions' => 'אינסטאלירטע פארברייטערונגען',
'version-specialpages' => 'ספעציעלע בלעטער',
'version-variables' => 'וואַריאַבלען',
'version-skins' => 'באניצער־אייבערפלאכן',
'duration-centuries' => '$1 {{PLURAL:$1|יארהונדערט|יארהונדערטער}}',
'duration-millennia' => '$1 {{PLURAL:$1|יארטויזנט|יארטויזנטער}}',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1 {{PLURAL:$4|איז נישט קיין דערלויבטער טעקע־טיפ |זענען נישט קיין דערלויבטע טעקע־טיפן}}. {{PLURAL:$3|דערלויבטער טעקע־טיפ איז|דערלויבטע טעקע־טיפן זענען}} $2.',
);
'tog-editsectiononrightclick' => '启用右击段落标题编辑段落(需要JavaScript)',
'tog-showtoc' => '显示目录(对于有多于3个标题的页面)',
'tog-rememberpassword' => '在浏览器上记住我的登录状态(最长$1天)',
-'tog-watchcreations' => '添加我创建的页面至我的监视列表',
-'tog-watchdefault' => '添加我编辑的页面至我的监视列表',
+'tog-watchcreations' => '添加我创建的页面和上传的文件至我的监视列表',
+'tog-watchdefault' => '添加我编辑的页面和文件至我的监视列表',
'tog-watchmoves' => '将我移动的页面和文件添加到我的监视列表',
-'tog-watchdeletion' => '添加我删除的页面至我的监视列表',
+'tog-watchdeletion' => '添加我删除的页面和文件至我的监视列表',
'tog-minordefault' => '默认标记编辑为小编辑',
'tog-previewontop' => '在编辑框上方显示预览',
'tog-previewonfirst' => '首次编辑时显示预览',
'tog-nocache' => '停用浏览器页面缓存',
-'tog-enotifwatchlistpages' => '当我的监视列表中的页面更改时发送电子邮件通知我',
+'tog-enotifwatchlistpages' => 'å½\93æ\88\91ç\9a\84ç\9b\91è§\86å\88\97表ä¸ç\9a\84页é\9d¢æ\88\96æ\96\87件æ\9b´æ\94¹æ\97¶å\8f\91é\80\81ç\94µå\90é\82®ä»¶é\80\9aç\9f¥æ\88\91',
'tog-enotifusertalkpages' => '当我的讨论页更改时发送电子邮件通知我',
-'tog-enotifminoredits' => '当页面有小编辑时发送电子邮件通知我',
+'tog-enotifminoredits' => '当页面和文件有小编辑时发送电子邮件通知我',
'tog-enotifrevealaddr' => '在通知电子邮件中显示我的电子邮件地址',
'tog-shownumberswatching' => '显示监视用户数',
'tog-oldsig' => '当前签名:',
'badaccess-groups' => '您刚才请求的操作只有{{PLURAL:$2|这个用户组|以下用户组}}中的用户才能使用: $1',
'versionrequired' => '需要版本为$1的MediaWiki',
-'versionrequiredtext' => '需要版本为$1的MediaWiki才能使用本页。请见[[Special:Version|版本页面]]。',
+'versionrequiredtext' => '需要版本为$1的MediaWiki才能使用本页。
+请见[[Special:Version|版本页面]]。',
'ok' => '确定',
'retrievedfrom' => '来自“$1”',
'youhavenewmessages' => '你有$1($2)。',
'newmessageslink' => '新信息',
'newmessagesdifflink' => '最后更改',
+'youhavenewmessagesfromusers' => '你有来自{{PLURAL:$3| 另一位用户| $3位用户}}的$1($2)。',
+'youhavenewmessagesmanyusers' => '你有来自多位用户的$1($2)。',
+'newmessageslinkplural' => '{{PLURAL:$1|一条新信息|$1条信息}}',
+'newmessagesdifflinkplural' => '最新$1次更改',
'youhavenewmessagesmulti' => '你在$1有新信息',
'editsection' => '编辑',
'editold' => '编辑',
'remembermypassword' => '在此浏览器上保留我的登录信息(最长$1{{PLURAL:$1|日|日}})',
'securelogin-stick-https' => '登陆后继续使用 HTTPS 连接',
'yourdomainname' => '您的域名:',
+'password-change-forbidden' => '您不能更改此wiki上的密码。',
'externaldberror' => '这可能是由于验证数据库错误或您被禁止更新您的外部账号。',
'login' => '登录',
'nav-login-createaccount' => '登录/创建账户',
'noarticletext' => '本页面目前没有内容。你可以在其他页面中[[Special:Search/{{PAGENAME}}|搜索该页标题]]、<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索相关日志]或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编辑本页面]。</span>',
'noarticletext-nopermission' => '此页目前没有内容,您可以在其它页[[Special:Search/{{PAGENAME}}|搜索此页标题]],
或<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索有关日志]</span>。',
+'missing-revision' => '“{{PAGENAME}}”的修订#$1不存在。
+
+这通常是因为进入了一个已被删除的页面的历史链接。
+详细信息可以在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中找到。',
'userpage-userdoesnotexist' => '用户账户"$1"未注册。
请在创建/编辑该页之前进行核对。',
'userpage-userdoesnotexist-view' => '用户账户“$1”未曾创建。',
'expansion-depth-exceeded-warning' => '页面超过了扩展深度',
'parser-unstrip-loop-warning' => '检测到回圈',
'parser-unstrip-recursion-limit' => '递归超过限制 ($1)',
+'converter-manual-rule-error' => '手动语言转换规则中检测到错误',
# "Undo" feature
'undo-success' => '此编辑可以被撤销。请检查以下比较以核实这正是您想做的,然后保存以下更改完成撤销编辑。',
'editundo' => '撤销',
'diff-multi' => '(未显示$2个用户的$1个中间版本)',
'diff-multi-manyusers' => '(未显示超过$2个用户的$1个中间版本)',
+'difference-missing-revision' => '此差异对比的{{PLURAL:$2|一个修订|$2个修订}}($1){{PLURAL:$2|没有}}找到。
+
+这通常是因为进入了一个已被删除的页面的修订差异对比链接。
+详细信息可以在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中找到。',
# Search results
'searchresults' => '搜索结果',
'right-autoconfirmed' => '编辑半保护页面',
'right-bot' => '被视为自动过程',
'right-nominornewtalk' => '不使小编辑在讨论页面引发新信息提示',
-'right-apihighlimits' => '在API问题中使用更高的限制',
+'right-apihighlimits' => '在API查询中使用更高的限制',
'right-writeapi' => '使用书写API',
'right-delete' => '删除页面',
'right-bigdelete' => '删除有大型历史的页面',
+'right-deletelogentry' => '删除和恢复特定的日志项目',
'right-deleterevision' => '删除和恢复页面的特定版本',
'right-deletedhistory' => '查看被删除的历史条目,无其相关文字',
'right-deletedtext' => '查看被删除的版本间的被删除的文字和更改',
'disambiguations' => '链接至消歧义页的页面',
'disambiguationspage' => 'Template:消歧义',
-'disambiguations-text' => "以ä¸\8bç\9a\84页é\9d¢é\83½æ\9c\89å\88°'''æ¶\88æ§ä¹\89页'''ç\9a\84é\93¾æ\8e¥ï¼\8cä½\86å®\83们åº\94该é\93¾æ\8e¥å\88°适当的页面。<br />一个页面如果使用了[[MediaWiki:Disambiguationspage]]内的模板,则会被视为消歧义页。",
+'disambiguations-text' => "以ä¸\8bç\9a\84页é\9d¢é\83½æ\9c\89å\88°'''æ¶\88æ§ä¹\89页'''ç\9a\84é\93¾æ\8e¥ï¼\8cä½\86å®\83们å\8f¯è\83½å\8f¯ä»¥é\93¾æ\8e¥å\88°æ\9b´适当的页面。<br />一个页面如果使用了[[MediaWiki:Disambiguationspage]]内的模板,则会被视为消歧义页。",
'doubleredirects' => '双重重定向页',
'doubleredirectstext' => '此页列出了所有重定向到另一重定向页面的页面。每一行都包含有到第一和第二个重定向页面的链接,以及第二个重定向页面的目标——通常就是“真正的”目标页面,亦即是第一个重定向页面应该指向的页面。<del>已划去</del>的为已经解决的项目。',
# Special:ActiveUsers
'activeusers' => '活跃用户列表',
'activeusers-intro' => '这个列表列出了最近$1天进行过操作的用户。',
-'activeusers-count' => '最近$3天编辑了$1次',
+'activeusers-count' => '最近$3天内有$1次编辑',
'activeusers-from' => '显示用户开始于:',
'activeusers-hidebots' => '隐藏机器人',
'activeusers-hidesysops' => '隐藏管理员',
'rollback' => '回退编辑',
'rollback_short' => '回退',
'rollbacklink' => '回退',
+'rollbacklinkcount' => '回退$1次编辑',
+'rollbacklinkcount-morethan' => '回退超过$1次的编辑',
'rollbackfailed' => '回退失败',
'cantrollback' => '无法恢复编辑。最后的贡献者是本文的唯一作者。',
'alreadyrolled' => '无法回退[[User:$2|$2]]([[User talk:$2|讨论]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])对[[:$1]]的编辑,其他人已经编辑或者回退了该页。
# Export
'export' => '导出页面',
-'exporttext' => '您可以将特定页面或一组页面的文本以及编辑历史以 XML 格式导出;这样可以将有关页面通过“[[Special:Import|导入页面]]”页面导入到另一个运行 MediaWiki 的网站。
+'exporttext' => '您可以将特定页面或一组页面的文本以及编辑历史以XML格式导出;这样可以将有关页面通过“[[Special:Import|导入页面]]”页面导入到另一个运行MediaWiki的网站。
-要导出页面,请在下面的文本框中输入页面标题,每行一个标题,
-并选择你是否需要导出带有页面历史的以前的修订本,
-或是只选择导出带有最后一次编辑信息的当前修订版本。
+要导出页面,请在下面的文本框中输入页面标题,每行一个标题,并选择你是否需要导出带有页面历史的以前的修订本,或是只选择导出带有最后一次编辑信息的当前修订版本。
此外你还可以利用链接导出文件,例如你可以使用[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]导出“[[{{MediaWiki:Mainpage}}]]”页面。',
'exportall' => '导出所有页面',
*<span class="mw-specialpagerestricted">非公开特殊页面。</span>',
'specialpages-group-maintenance' => '维护报告',
'specialpages-group-other' => '其它特殊页面',
-'specialpages-group-login' => '登录/注册',
+'specialpages-group-login' => '登录/创建账户',
'specialpages-group-changes' => '最近更改与日志',
'specialpages-group-media' => '媒体文件报告与上传',
'specialpages-group-users' => '用户与权限',
'duration-centuries' => '$1个世纪',
'duration-millennia' => '$1千年',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1{{PLURAL:$4|不是允许的文件类型}}。允许的{{PLURAL:$3|文件类型是|文件类型有}}$2。',
);
'tog-underline' => '連結加底線:',
'tog-justify' => '段落對齊',
'tog-hideminor' => '最近更改中隱藏小修改',
-'tog-hidepatrolled' => 'æ\96¼æ\9c\80è¿\91æ\9b´æ\94¹ä¸é\9a±è\97\8få·¡æ\9f¥é\81\8eç\9a\84編輯',
-'tog-newpageshidepatrolled' => 'æ\96¼æ\96°é \81é\9d¢æ¸\85å\96®ä¸é\9a±è\97\8få·¡æ\9f¥é\81\8eç\9a\84é \81é\9d¢',
+'tog-hidepatrolled' => '最近更改中隱藏巡查過的編輯',
+'tog-newpageshidepatrolled' => '新頁面清單中隱藏巡查過的頁面',
'tog-extendwatchlist' => '展開監視清單以顯示所有更改,不只是最近的',
'tog-usenewrc' => '在最近更改和監視列表中整合同一頁的修改 (需要JavaScript)',
'tog-numberheadings' => '標題自動編號',
'tog-watchdeletion' => '將我刪除的頁面和檔案添加到我的監視列表',
'tog-minordefault' => '預設將編輯設定為小編輯',
'tog-previewontop' => '在編輯框上方顯示預覽',
-'tog-previewonfirst' => '第一次編輯時顯示原文內容的預覽',
+'tog-previewonfirst' => '第一次編輯時顯示預覽',
'tog-nocache' => '禁止瀏覽器頁面快取',
'tog-enotifwatchlistpages' => '當在我的監視列表中的頁面或檔案改變時發電子郵件給我',
-'tog-enotifusertalkpages' => '當我的對話頁發生改變時發電子郵件給我',
+'tog-enotifusertalkpages' => '當我的對話頁更改時發電子郵件給我',
'tog-enotifminoredits' => '即使是頁面和檔案的小修改也向我發電子郵件',
'tog-enotifrevealaddr' => '在通知電子郵件中顯示我的電子郵件位址',
'tog-shownumberswatching' => '顯示監視用戶的數目',
'tog-watchlisthidepatrolled' => '監視清單中隱藏已巡查的編輯',
'tog-nolangconversion' => '不進行用字轉換',
'tog-ccmeonemails' => '當我寄電子郵件給其他用戶時,也寄一份複本到我的信箱。',
-'tog-diffonly' => '在比較兩個修訂版本差異時不顯示頁面內容',
+'tog-diffonly' => '比較版本差異時不顯示頁面內容',
'tog-showhiddencats' => '顯示隱藏分類',
'tog-noconvertlink' => '不轉換連結標題',
'tog-norollbackdiff' => '進行回退後略過差異比較',
'viewtalkpage' => '檢視討論頁面',
'otherlanguages' => '其他語言',
'redirectedfrom' => '(重定向自$1)',
-'redirectpagesub' => '重定向頁面',
+'redirectpagesub' => '重定向頁',
'lastmodifiedat' => '此頁面最後修訂於 $1 $2。',
'viewcount' => '本頁面已經被瀏覽$1次。',
'protectedpage' => '受保護頁面',
'jumpto' => '跳轉到:',
'jumptonavigation' => '導覽',
'jumptosearch' => '搜尋',
-'view-pool-error' => '抱歉,伺服器在這段時間中已經超出負荷。
-太多用戶嘗試檢視這個頁面。
-在嘗試訪問這個頁面之前請再稍等一會。
+'view-pool-error' => '抱歉,現時伺服器已超出負荷。
+太多用戶正嘗試檢視此頁。
+請稍等一會後再次訪問此頁。
$1',
'pool-timeout' => '等待鎖死時超時',
'mainpage' => '首頁',
'mainpage-description' => '首頁',
'policy-url' => 'Project:方針',
-'portal' => '社群入口',
+'portal' => '社群主頁',
'portal-url' => 'Project:社區主頁',
'privacy' => '隱私權政策',
'privacypage' => 'Project:隱私權政策',
'badaccess-groups' => '您剛才的請求只有{{PLURAL:$2|這個|這些}}用戶組的用戶才能使用:$1',
'versionrequired' => '需要MediaWiki $1 版',
-'versionrequiredtext' => '需要版本$1的 MediaWiki 才能使用此頁。參見[[Special:Version|版本頁]]。',
+'versionrequiredtext' => '需要版本$1的 MediaWiki 才能使用此頁。
+參見[[Special:Version|版本頁]]。',
'ok' => '確定',
'retrievedfrom' => '取自「$1」',
'youhavenewmessages' => '您有$1($2)。',
'newmessageslink' => '新訊息',
-'newmessagesdifflink' => '上次更改',
+'newmessagesdifflink' => '最後更改',
+'youhavenewmessagesfromusers' => '你有來自{{PLURAL:$3| 另一位用戶| $3位用戶}}的$1 ( $2 )。',
+'youhavenewmessagesmanyusers' => '你有來自多位用戶的$1( $2 )。',
+'newmessageslinkplural' => '$1項新訊息',
+'newmessagesdifflinkplural' => '最新$1次更改',
'youhavenewmessagesmulti' => '您在 $1 有一條新訊息',
'editsection' => '編輯',
'editold' => '編輯',
'nstab-mediawiki' => '訊息',
'nstab-template' => '模板',
'nstab-help' => '幫助頁面',
-'nstab-category' => '類別',
+'nstab-category' => '分類',
# Main script and global functions
'nosuchaction' => '這個命令不存在',
<blockquote><tt>$1</tt></blockquote>
來自於函數 "<tt>$2</tt>"。
數據庫返回錯誤 "<tt>$3: $4</tt>"。',
-'dberrortextcl' => '發生了一個資料庫查詢語法錯誤。
+'dberrortextcl' => '發生資料庫查詢語法錯誤。
最後一次的資料庫查詢是:
「$1」
來自於函數「$2」。
'remembermypassword' => '在這個瀏覽器上記住我的登入資訊(可維持 $1 {{PLURAL:$1|天|天}})',
'securelogin-stick-https' => '登入後繼續以HTTPS連接',
'yourdomainname' => '您的網域:',
+'password-change-forbidden' => '您不可更改此wiki上的密碼。',
'externaldberror' => '這可能是由於驗證資料庫錯誤或您被禁止更新您的外部賬號。',
'login' => '登入',
'nav-login-createaccount' => '登入/建立新帳號',
或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 編輯此頁]</span>。',
'noarticletext-nopermission' => '此頁目前沒有內容,您可以在其它頁[[Special:Search/{{PAGENAME}}|搜索此頁標題]],
或<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索有關日誌]</span>。',
+'missing-revision' => '「{{PAGENAME}}」的#$1修訂版本不存在。
+
+這通常是因為過時的頁面歷史鏈接被刪除。
+詳情請閱[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌]。',
'userpage-userdoesnotexist' => '未曾創建用戶名「<nowiki>$1</nowiki>」。請在創建/編輯這個頁面前先檢查一下。',
'userpage-userdoesnotexist-view' => '未曾建立用戶名「$1」。',
'blocked-notice-logextract' => '這位用戶現正被封鎖。
'expansion-depth-exceeded-warning' => '頁面超出擴展深度',
'parser-unstrip-loop-warning' => '檢測到迴圈',
'parser-unstrip-recursion-limit' => '遞歸超過限制 ($1)',
+'converter-manual-rule-error' => '手動語言轉換規則中檢測到錯誤',
# "Undo" feature
'undo-success' => '該編輯可以被撤銷。請檢查以下對比以核實這正是您想做的,然後儲存以下更改以完成撤銷編輯。',
'editundo' => '撤銷',
'diff-multi' => '(由{{PLURAL:$2|1名用戶|$2名用戶}}作出的{{PLURAL:$1|一個中途修訂版本|$1個中途修訂版本}}未被顯示)',
'diff-multi-manyusers' => '(由多於$2名用戶作出的{{PLURAL:$1|一個中途修訂版本|$1個中途修訂版本}} 未被顯示)',
+'difference-missing-revision' => '{{PLURAL:$2|1次修訂|$2 次修訂}}差異($1)不存在。
+
+這通常是因為過時的頁面修訂差異鏈接被刪除。
+詳情請閱[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌]。',
# Search results
'searchresults' => '搜尋結果',
'prefs-memberingroups' => '{{PLURAL:$1|群組}}:',
'prefs-registration' => '註冊時間:',
'yourrealname' => '真實姓名:',
-'yourlanguage' => '介面語言:',
+'yourlanguage' => '語言:',
'yourvariant' => '內容語言變體:',
'prefs-help-variant' => '您希望用於顯示本站內容的語種或拼寫語系。',
'yournick' => '新簽名:',
'right-writeapi' => '使用API編寫',
'right-delete' => '刪除頁面',
'right-bigdelete' => '刪除大量歷史之頁面',
-'right-deleterevision' => '刪除及同反刪除頁面中的指定修訂',
+'right-deletelogentry' => '刪除及恢復特定的日誌項目',
+'right-deleterevision' => '刪除及同恢復頁面中的指定修訂',
'right-deletedhistory' => '檢視已刪除之歷史項目,不含關聯的文本',
'right-deletedtext' => '檢視已刪除修訂中之已刪除的字以及更改',
'right-browsearchive' => '搜尋已刪除之頁面',
'right-undelete' => '反刪除頁面',
'right-suppressrevision' => '檢視及恢復由操作員隱藏之修訂',
-'right-suppressionlog' => '檢視私人的日誌',
+'right-suppressionlog' => '檢視非公開的日誌',
'right-block' => '封鎖其他用戶防止編輯',
'right-blockemail' => '封鎖用戶不可發電郵',
'right-hideuser' => '封鎖用戶名,對公眾隱藏',
Template:消除歧义
Template:消歧義
Template:消除歧義',
-'disambiguations-text' => "以下的頁面都有到'''消歧義頁'''的鏈接,但它們應該鏈接到適當的頁面。<br />一個頁面如果使用了[[MediaWiki:Disambiguationspage]]內的模板,則會被視為消歧義頁。",
+'disambiguations-text' => "以下的頁面都有至少一個連到'''消歧義頁'''的鏈接,但它們應鏈接到合適的頁面。<br />一個頁面如果使用了[[MediaWiki:Disambiguationspage]]內的模板,則會被視為消歧義頁。",
'doubleredirects' => '雙重重定向頁面',
'doubleredirectstext' => '這一頁列出所有重定向頁面重定向到另一個重定向頁的頁面。每一行都包含到第一和第二個重定向頁面的連結,以及第二個重定向頁面的目標,通常顯示的都會是"真正"的目標頁面,也就是第一個重定向頁面應該指向的頁面。
# Special:ActiveUsers
'activeusers' => '活躍用戶列表',
'activeusers-intro' => '這個是在最近$1天之內有一些動作的用戶列表。',
-'activeusers-count' => 'æ\96¼$3天å\85§ç\9a\84$1次編輯',
+'activeusers-count' => 'æ\9c\80è¿\91$3天å\85§æ\9c\89$1次編輯',
'activeusers-from' => '顯示用戶開始於:',
'activeusers-hidebots' => '隱藏機器人',
'activeusers-hidesysops' => '隱藏管理員',
'rollback' => '恢復編輯',
'rollback_short' => '恢復',
'rollbacklink' => '恢復',
+'rollbacklinkcount' => '恢復 $1 次編輯',
+'rollbacklinkcount-morethan' => '恢復多過 $1 次編輯',
'rollbackfailed' => '無法恢復',
'cantrollback' => '無法恢復編輯;最後的貢獻者是本文的唯一作者。',
'alreadyrolled' => '無法回退由[[User:$2|$2]]([[User talk:$2|討論]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]在[[:$1]]上的編輯;其他人已經編輯或者回退了該頁。
# Export
'export' => '匯出頁面',
-'exporttext' => '您可以將特定頁面或一組頁面的文字以及編輯歷史以 XML 格式匯出;這樣可以將有關頁面透過「[[Special:Import|匯入頁面]]」頁面匯入到另一個執行 MediaWiki 的網站。
+'exporttext' => '您可以將特定頁面或一組頁面的文字以及編輯歷史以XML格式匯出;這樣可以將有關頁面透過「[[Special:Import|匯入頁面]]」頁面匯入到另一個執行MediaWiki的網站。
-要匯出頁面,請在下面的文字框中輸入頁面標題,每行一個標題,
-並選擇{{GENDER:|你|妳|你}}是否需要匯出帶有頁面歷史的以前的修訂版本,
-或是只選擇匯出帶有最後一次編輯訊息的目前修訂版本。
+要匯出頁面,請在下面的文字框中輸入頁面標題,每行一個標題,並選擇{{GENDER:|你|妳|你}}是否需要匯出帶有頁面歷史的以前的修訂版本,或是只選擇匯出帶有最後一次編輯訊息的目前修訂版本。
-此外{{GENDER:|你|妳|你}}還可以利用連結匯出檔案,例如{{GENDER:|你|妳|你}}可以使用 [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] 匯出「[[{{MediaWiki:Mainpage}}]]」頁面。',
+此外{{GENDER:|你|妳|你}}還可以利用連結匯出檔案,例如{{GENDER:|你|妳|你}}可以使用[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]匯出「[[{{MediaWiki:Mainpage}}]]」頁面。',
'exportall' => '導出所有頁面',
'exportcuronly' => '僅包含目前的修訂,而不是全部的歷史。',
'exportnohistory' => "----
* <span class="mw-specialpagecached">用於重新整理的特殊頁面(可能過時)。</span>',
'specialpages-group-maintenance' => '維護報告',
'specialpages-group-other' => '其它特殊頁面',
-'specialpages-group-login' => '登入/創建',
+'specialpages-group-login' => '登入/建立新帳號',
'specialpages-group-changes' => '最近更改和日誌',
'specialpages-group-media' => '媒體報告和上傳',
'specialpages-group-users' => '用戶和權限',
'duration-centuries' => '$1世紀',
'duration-millennia' => '$1千年',
+# Unknown messages
+'api-error-filetype-banned-type' => '$1{{PLURAL:$4|不是允許的檔案類型|不是允許的檔案類型}}。 允許的{{PLURAL:$3|檔案類型是|檔案類型是}} $2。',
);
<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索有關日誌],
或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 編輯此頁]</span>。',
'userpage-userdoesnotexist' => '使用者帳號「<nowiki>$1</nowiki>」未曾建立。請在建立/編輯這個頁面前先檢查一下。',
-'clearyourcache' => "'''注意:''' 在儲存以後, 您必須清除瀏覽器的快取才能看到所作出的改變。 '''Mozilla / Firefox / Safari:''' 按著 ''Shift'' 再點擊''重新整理''(或按下''Ctrl-Shift-R'',在蘋果Mac上按下''Cmd-Shift-R'');'''IE:''' 按著 ''Ctrl'' 再點擊 ''重新整理'',或按下 ''Ctrl-F5'';'''Konqueror:''' 只需點擊 ''重新整理'';'''Opera:''' 使用者需要在 ''工具-設定'' 中完整地清除它們的快取。",
'usercsspreview' => "'''注意您只是在預覽您的個人 CSS, 還沒有儲存﹗'''",
'userjspreview' => "'''注意您只是在測試/預覽您的個人 JavaScript,還沒有儲存﹗'''",
'previewnote' => "'''請記住這只是預覽,內容尚未儲存!'''",
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
-INPUT_FILTER =
+INPUT_FILTER = "{{INPUT_FILTER}}"
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
fj_backend varchar(255) NOT NULL,
-- The storage path that was affected (may be internal paths)
fj_path blob NOT NULL,
- -- SHA-1 file path hash in base-36
- fj_path_sha1 varbinary(32) NOT NULL default '',
-- Primitive operation description (create/update/delete)
fj_op varchar(16) NOT NULL default '',
-- SHA-1 file content hash in base-36
$this->addOption( 'ratefile', 'File to check periodically for batch size', false, true );
$this->addOption( 'skiphash', 'Skip SHA-1 sync checks for files' );
$this->addOption( 'missingonly', 'Only copy files missing from destination listing' );
+ $this->addOption( 'utf8only', 'Skip source files that do not have valid UTF-8 names' );
$this->setBatchSize( 50 );
}
$rateFile = $this->getOption( 'ratefile' );
+ if ( $this->hasOption( 'utf8only' ) && !extension_loaded( 'mbstring' ) ) {
+ $this->error( "Cannot check for UTF-8, mbstring extension missing.", 1 ); // die
+ }
+
$count = 0;
foreach ( $containers as $container ) {
if ( $subDir != '' ) {
foreach ( $srcPathsRel as $srcPathRel ) {
$srcPath = $src->getRootStoragePath() . "/$backendRel/$srcPathRel";
$dstPath = $dst->getRootStoragePath() . "/$backendRel/$srcPathRel";
- if ( $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) ) {
+ if ( $this->hasOption( 'utf8only' ) && !mb_check_encoding( $srcPath, 'UTF-8' ) ) {
+ $this->error( "Detected illegal (non-UTF8) path for $srcPath." );
+ continue;
+ } elseif ( $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) ) {
$this->output( "Already have $srcPathRel.\n" );
continue; // assume already copied...
}
$fsFile = $src->getLocalReference( array( 'src' => $srcPath, 'latest' => 1 ) );
if ( !$fsFile ) {
$this->error( "Could not get local copy of $srcPath.", 1 ); // die
+ } elseif ( !$fsFile->exists() ) {
+ // FSFileBackends just return the path for getLocalReference() and paths with
+ // illegal slashes may get normalized to a different path. This can cause the
+ // local reference to not exist...skip these broken files.
+ $this->error( "Detected possible illegal path for $srcPath." );
+ continue;
}
$fsFiles[] = $fsFile; // keep TempFSFile objects alive as needed
// Note: prepare() is usually fast for key/value backends
__METHOD__,
array( 'FOR UPDATE' )
);
- if ( $path && file_exists( $path ) && !$inuse ) {
- if( unlink( $path ) ) { // delete
+ if ( $path && $repo->fileExists( $path ) && !$inuse ) {
+ if ( $repo->quickPurge( $path ) ) {
$count++;
$dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" );
} else {
# Include-able script to determine the location of our php if any
+# We search for a environment var called PHP, native php,
+# a local copy, home directory location used by installphp.sh
+# and previous home directory location
+# The binary path is returned in $PHP if any
-if [ -d "$DEV/php" -a -x "$DEV/php/bin/php" ]; then
- # Quick local copy
- PHP="$DEV/php/bin/php"
-elif [ -d "$HOME/.mediawiki/php" -a -x "$HOME/.mediawiki/php/bin/php" ]; then
- # Previous home directory location to install php in
- PHP="$HOME/.mediawiki/php/bin/php"
-elif [ -d "$HOME/.mwphp" -a -x "$HOME/.mwphp/bin/php" ]; then
- # Previous home directory location to install php in
- PHP="$HOME/.mwphp/bin/php"
-fi
+for binary in $PHP `which php || true` "$DEV/php/bin/php" "$HOME/.mediawiki/php/bin/php" "$HOME/.mwphp/bin/php" ]; do
+ if [ -x "$binary" ]; then
+ if "$binary" -r 'exit((int)!version_compare(PHP_VERSION, "5.4", ">="));'; then
+ PHP="$binary"
+ break
+ fi
+ fi
+done
'version-entrypoints-index-php',
'version-entrypoints-api-php',
'version-entrypoints-load-php',
+ 'ipb-default-expiry',
);
/** Optional messages, which may be translated only if changed in the target language. */
'youhavenewmessages',
'newmessageslink',
'newmessagesdifflink',
+ 'youhavenewmessagesfromusers',
+ 'youhavenewmessagesmanyusers',
+ 'newmessageslinkplural',
+ 'newmessagesdifflinkplural',
'youhavenewmessagesmulti',
'newtalkseparator',
'editsection',
'readonly',
'enterlockreason',
'readonlytext',
- 'missing-article',
- 'missingarticle-rev',
- 'missingarticle-diff',
+ 'missing-article', // not used anymore in core, but kept for extensions
+ 'missingarticle-rev', // not used anymore in core, but kept for extensions
+ 'missingarticle-diff', // not used anymore in core, but kept for extensions
'readonly_lag',
'internalerror',
'internalerror_info',
'remembermypassword',
'securelogin-stick-https',
'yourdomainname',
+ 'password-change-forbidden',
'externaldberror',
'login',
'nav-login-createaccount',
'noarticletext',
'noarticletext-nopermission',
'noarticletextanon',
+ 'missing-revision',
'userpage-userdoesnotexist',
'userpage-userdoesnotexist-view',
'blocked-notice-logextract',
'expansion-depth-exceeded-warning',
'parser-unstrip-loop-warning',
'parser-unstrip-recursion-limit',
+ 'converter-manual-rule-error',
),
'undo' => array(
'undo-success',
'editundo',
'diff-multi',
'diff-multi-manyusers',
+ 'difference-missing-revision',
),
'search' => array(
'search-summary',
'rollback',
'rollback_short',
'rollbacklink',
+ 'rollbacklinkcount',
+ 'rollbacklinkcount-morethan',
'rollbackfailed',
'cantrollback',
'alreadyrolled',
'cant-see-hidden-user',
'ipbblocked',
'ipbnounblockself',
+ 'ipb-default-expiry',
),
'developertools' => array(
'lockdb',
--- /dev/null
+<?php
+/**
+ * Doxygen filter to show correct member variable types in documentation.
+ *
+ * Should be filled in doxygen INPUT_FILTER as "php mwdoc-filter.php"
+ *
+ * Original source code by Goran Rakic
+ * http://blog.goranrakic.com/
+ * http://stackoverflow.com/questions/4325224
+ *
+ * @file
+ */
+
+$source = file_get_contents( $argv[1] );
+$regexp = '#\@var\s+([^\s]+)([^/]+)/\s+(var|public|protected|private)\s+(\$[^\s;=]+)#';
+$replac = '${2} */ ${3} ${1} ${4}';
+$source = preg_replace($regexp, $replac, $source);
+
+echo $source;
/** doxygen configuration template for mediawiki */
$doxygenTemplate = $mwPath . 'maintenance/Doxyfile';
+/** doxygen input filter to tweak source file before they are parsed */
+$doxygenInputFilter = "php {$mwPath}maintenance/mwdoc-filter.php";
+
/** svnstat command, used to get the version of each file */
$svnstat = $mwPath . 'bin/svnstat';
* @return string
*/
function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input, $exclude, $excludePatterns, $doxyGenerateMan ) {
+ global $doxygenInputFilter;
$template = file_get_contents( $doxygenTemplate );
-
// Replace template placeholders by correct values.
$replacements = array(
'{{OUTPUT_DIRECTORY}}' => $outputDirectory,
'{{EXCLUDE_PATTERNS}}' => $excludePatterns,
'{{HAVE_DOT}}' => `which dot` ? 'YES' : 'NO',
'{{GENERATE_MAN}}' => $doxyGenerateMan ? 'YES' : 'NO',
+ '{{INPUT_FILTER}}' => $doxygenInputFilter,
);
$tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
$tmpFileName = tempnam( wfTempDir(), 'mwdocgen-' );
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+CREATE INDEX &mw_prefix.ipblocks_i05 ON &mw_prefix.ipblocks (ipb_parent_block_id);
+
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.page_restrictions DROP CONSTRAINT &mw_prefix.page_restrictions_pk;
+
+ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_id);
+
+CREATE UNIQUE INDEX &mw_prefix.page_restrictions_u01 ON &mw_prefix.page_restrictions (pr_page,pr_type);
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+CREATE INDEX &mw_prefix.revision_i05 ON &mw_prefix.revision (rev_page,rev_user,rev_timestamp);
+
--- /dev/null
+define mw_prefix='{$wgDBprefix}';
+
+/*$mw$*/
+BEGIN
+ EXECUTE IMMEDIATE 'ALTER TABLE &mw_prefix.user_former_groups MODIFY ufg_group VARCHAR2(32) NOT NULL';
+EXCEPTION WHEN OTHERS THEN
+ IF (SQLCODE = -01442) THEN NULL; ELSE RAISE; END IF;
+END;
+/*$mw$*/
define mw_prefix='{$wgDBprefix}';
-ALTER TABLE &mw_prefix.user_groups MODIFY ug_group VARCHAR2(32) NOT NULL;
+/*$mw$*/
+BEGIN
+ EXECUTE IMMEDIATE 'ALTER TABLE &mw_prefix.user_groups MODIFY ug_group VARCHAR2(32) NOT NULL';
+EXCEPTION WHEN OTHERS THEN
+ IF (SQLCODE = -01442) THEN NULL; ELSE RAISE; END IF;
+END;
+/*$mw$*/
CREATE INDEX &mw_prefix.revision_i02 ON &mw_prefix.revision (rev_page,rev_timestamp);
CREATE INDEX &mw_prefix.revision_i03 ON &mw_prefix.revision (rev_user,rev_timestamp);
CREATE INDEX &mw_prefix.revision_i04 ON &mw_prefix.revision (rev_user_text,rev_timestamp);
+CREATE INDEX &mw_prefix.revision_i05 ON &mw_prefix.revision (rev_page,rev_user,rev_timestamp);
CREATE SEQUENCE text_old_id_seq;
CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
CREATE INDEX &mw_prefix.ipblocks_i02 ON &mw_prefix.ipblocks (ipb_range_start, ipb_range_end);
CREATE INDEX &mw_prefix.ipblocks_i03 ON &mw_prefix.ipblocks (ipb_timestamp);
CREATE INDEX &mw_prefix.ipblocks_i04 ON &mw_prefix.ipblocks (ipb_expiry);
+CREATE INDEX &mw_prefix.ipblocks_i05 ON &mw_prefix.ipblocks (ipb_parent_block_id);
CREATE TABLE &mw_prefix.image (
img_name VARCHAR2(255) NOT NULL,
pr_user NUMBER NULL,
pr_expiry TIMESTAMP(6) WITH TIME ZONE NULL
);
-ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_id);
ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_fk1 FOREIGN KEY (pr_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
+CREATE UNIQUE INDEX &mw_prefix.page_restrictions_u01 ON &mw_prefix.page_restrictions (pr_page,pr_type);
CREATE INDEX &mw_prefix.page_restrictions_i01 ON &mw_prefix.page_restrictions (pr_type,pr_level);
CREATE INDEX &mw_prefix.page_restrictions_i02 ON &mw_prefix.page_restrictions (pr_level);
CREATE INDEX &mw_prefix.page_restrictions_i03 ON &mw_prefix.page_restrictions (pr_cascade);
<?php
/**
- * CLI script to easily parse some wikitext.
+ * Parse some wikitext.
+ *
* Wikitext can be given by stdin or using a file. The wikitext will be parsed
* using 'CLIParser' as a title. This can be overriden with --title option.
*
* </p>$
* @endcode
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
* @ingroup Maintenance
* @author Antoine Musso <hashar at free dot fr>
* @license GNU General Public License 2.0 or later
*/
+
require_once( dirname(__FILE__) . '/Maintenance.php' );
+/**
+ * Maintenance script to parse some wikitext.
+ *
+ * @ingroup Maintenance
+ */
class CLIParser extends Maintenance {
protected $parser;
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that manually runs an SQL patch outside of the general updaters.
+ *
+ * @ingroup Maintenance
+ */
class PatchSql extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Script to populate category table.
+ * Populate the category table.
*
* 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
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
-
+/**
+ * Mainteance script to populate the category table.
+ *
+ * @ingroup Maintenance
+ */
class PopulateCategory extends Maintenance {
const REPORTING_INTERVAL = 1000;
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to populate the img_sha1 field.
+ *
+ * @ingroup Maintenance
+ */
class PopulateImageSha1 extends LoggedUpdateMaintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that makes the required database updates for populating the
+ * log_search table retroactively
+ *
+ * @ingroup Maintenance
+ */
class PopulateLogSearch extends LoggedUpdateMaintenance {
static $tableMap = array( 'rev' => 'revision', 'fa' => 'filearchive', 'oi' => 'oldimage', 'ar' => 'archive' );
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that makes the required database updates for
+ * Special:ProtectedPages to show all protected pages.
+ *
+ * @ingroup Maintenance
+ */
class PopulateLogUsertext extends LoggedUpdateMaintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that makes the required database updates for rev_parent_id
+ * to be of any use.
+ *
+ * @ingroup Maintenance
+ */
class PopulateParentId extends LoggedUpdateMaintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that populates the rev_len field for old revisions
+ * created before MW 1.10.
+ *
+ * @ingroup Maintenance
+ */
class PopulateRevisionLength extends LoggedUpdateMaintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that fills the rev_sha1 and ar_sha1 columns of revision
+ * and archive tables for revisions created before MW 1.19.
+ *
+ * @ingroup Maintenance
+ */
class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
public function __construct() {
parent::__construct();
<?php
/**
* Take page text out of an XML dump file and preprocess it to obj.
- * It may be useful for getting preprocessor statistics or filling the
+ * It may be useful for getting preprocessor statistics or filling the
* preprocessor cache.
*
- * Copyright (C) 2011 Platonides - http://www.mediawiki.org/
+ * Copyright © 2011 Platonides - http://www.mediawiki.org/
*
* 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
require_once( dirname( __FILE__ ) . '/dumpIterator.php' );
+/**
+ * Maintenance script that takes page text out of an XML dump file and
+ * preprocesses it to obj.
+ *
+ * @ingroup Maintenance
+ */
class PreprocessDump extends DumpIterator {
/* Variables for dressing up as a parser */
<?php
/**
- * Protect or unprotect an article.
+ * Protect or unprotect a page.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that protects or unprotects a page.
+ *
+ * @ingroup Maintenance
+ */
class Protect extends Maintenance {
public function __construct() {
parent::__construct();
- $this->mDescription = "Protect or unprotect an article from the command line.";
+ $this->mDescription = "Protect or unprotect a page from the command line.";
$this->addOption( 'unprotect', 'Removes protection' );
$this->addOption( 'semiprotect', 'Adds semi-protection' );
$this->addOption( 'cascade', 'Add cascading protection' );
<?php
/**
- * Prune file cache for pages, objects, resources, ect...
+ * Prune file cache for pages, objects, resources, etc.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that prunes file cache for pages, objects, resources, etc.
+ *
+ * @ingroup Maintenance
+ */
class PruneFileCache extends Maintenance {
protected $minSurviveTimestamp;
<?php
/**
- * Scans the deletion log and purges affected files within a timeframe.
+ * Scan the deletion log and purges affected files within a timeframe.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that scans the deletion log and purges affected files
+ * within a timeframe.
+ *
+ * @ingroup Maintenance
+ */
class PurgeDeletedFiles extends Maintenance {
public function __construct() {
parent::__construct();
protected function purgeFromArchiveTable( LocalFile $file ) {
$db = $file->getRepo()->getSlaveDB();
- $res = $db->select( 'filearchive',
+ $res = $db->select( 'filearchive',
array( 'fa_archive_name' ),
array( 'fa_name' => $file->getName() ),
__METHOD__
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that sends purge requests for listed pages to squid.
+ *
+ * @ingroup Maintenance
+ */
class PurgeList extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that purges old text records from the database.
+ *
+ * @ingroup Maintenance
+ */
class PurgeOldText extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Maintenance script to remove old objects from the parser cache.
+ * Remove old objects from the parser cache.
* This only works when the parser cache is in an SQL database.
*
* This program is free software; you can redistribute it and/or modify
require( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to remove old objects from the parser cache.
+ *
+ * @ingroup Maintenance
+ */
class PurgeParserCache extends Maintenance {
var $lastProgress;
function __construct() {
parent::__construct();
- $this->addDescription( "Remove old objects from the parser cache. " .
+ $this->addDescription( "Remove old objects from the parser cache. " .
"This only works when the parser cache is in an SQL database." );
$this->addOption( 'expiredate', 'Delete objects expiring before this date.', false, true );
- $this->addOption( 'age',
- 'Delete objects created more than this many seconds ago, assuming $wgParserCacheExpireTime '.
- 'has been consistent.',
+ $this->addOption( 'age',
+ 'Delete objects created more than this many seconds ago, assuming $wgParserCacheExpireTime ' .
+ 'has been consistent.',
false, true );
}
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
* @licence GNU General Public Licence 2.0 or later
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that reassigns edits from a user or IP address
+ * to another user.
+ *
+ * @ingroup Maintenance
+ */
class ReassignEdits extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that builds file cache for content pages.
+ *
+ * @ingroup Maintenance
+ */
class RebuildFileCache extends Maintenance {
public function __construct() {
parent::__construct();
foreach ( $res as $row ) {
$rebuilt = false;
$wgRequestTime = microtime( true ); # bug 22852
-
+
$wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
if ( null == $wgTitle ) {
$this->output( "Page {$row->page_id} has bad title\n" );
<?php
/**
- * Script to update image metadata records
+ * Update image metadata records.
*
* Usage: php rebuildImages.php [--missing] [--dry-run]
* Options:
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to update image metadata records.
+ *
+ * @ingroup Maintenance
+ */
class ImageBuilder extends Maintenance {
/**
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to rebuild the localisation cache.
+ *
+ * @ingroup Maintenance
+ */
class RebuildLocalisationCache extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Rebuild the localisation cache";
$this->addOption( 'force', 'Rebuild all files, even ones not out of date' );
$this->addOption( 'threads', 'Fork more than one thread', false, true );
- $this->addOption( 'outdir', 'Override the output directory (normally $wgCacheDirectory)',
+ $this->addOption( 'outdir', 'Override the output directory (normally $wgCacheDirectory)',
false, true );
}
public function memoryLimit() {
+ if ( $this->hasOption( 'memory-limit' ) ) {
+ return parent::memoryLimit();
+ }
return '1000M';
}
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that rebuilds link tracking tables from scratch.
+ *
+ * @ingroup Maintenance
+ */
class RebuildAll extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * This script purges all language messages from the cache
+ * Purge all languages from the message cache.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that purges all languages from the message cache.
+ *
+ * @ingroup Maintenance
+ */
class RebuildMessages extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Rebuild link tracking tables from scratch. This takes several
- * hours, depending on the database size and server configuration.
+ * Rebuild recent changes from scratch. This takes several hours,
+ * depending on the database size and server configuration.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @todo Document
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that rebuilds recent changes from scratch.
+ *
+ * @ingroup Maintenance
+ */
class RebuildRecentchanges extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @todo document
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that rebuilds search index table from scratch.
+ *
+ * @ingroup Maintenance
+ */
class RebuildTextIndex extends Maintenance {
const RTI_CHUNK_SIZE = 500;
<?php
/**
- * Script to refresh image metadata fields. See also rebuildImages.php
+ * Refresh image metadata fields. See also rebuildImages.php
*
* Usage: php refreshImageMetadata.php
*
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to refresh image metadata fields.
+ *
+ * @ingroup Maintenance
+ */
class RefreshImageMetadata extends Maintenance {
/**
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to refresh link tables.
+ *
+ * @ingroup Maintenance
+ */
class RefreshLinks extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that removes unused user accounts from the database.
+ *
+ * @ingroup Maintenance
+ */
class RemoveUnusedAccounts extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
+ * Change the prefix of database tables.
* Run this script to after changing $wgDBprefix on a wiki.
* The wiki will have to get downtime to do this correctly.
*
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that changes the prefix of database tables.
+ *
+ * @ingroup Maintenance
+ */
class RenameDbPrefix extends Maintenance {
public function __construct() {
parent::__construct();
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that takes page text out of an XML dump file
+ * and render basic HTML out to files.
+ *
+ * @ingroup Maintenance
+ */
class DumpRenderer extends Maintenance {
private $count = 0;
<?php
/**
- * Script to reset the user_token for all users on the wiki. Useful if you
- * believe that your user table was acidentally leaked to an external source.
+ * Reset the user_token for all users on the wiki. Useful if you believe
+ * that your user table was acidentally leaked to an external source.
*
* 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
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to reset the user_token for all users on the wiki.
+ *
+ * @ingroup Maintenance
+ */
class ResetUserTokens extends Maintenance {
public function __construct() {
parent::__construct();
}
public function execute() {
-
+
if ( !$this->getOption( 'nowarn' ) ) {
$this->output( "The script is about to reset the user_token for ALL USERS in the database.\n" );
$this->output( "This may log some of them out and is not necessary unless you believe your\n" );
$this->output( "Abort with control-c in the next five seconds (skip this countdown with --nowarn) ... " );
wfCountDown( 5 );
}
-
+
// We list user by user_id from one of the slave database
$dbr = wfGetDB( DB_SLAVE );
$result = $dbr->select( 'user',
foreach ( $result as $id ) {
$user = User::newFromId( $id->user_id );
-
+
$username = $user->getName();
-
+
$this->output( "Resetting user_token for $username: " );
-
+
// Change value
$user->setToken();
$user->saveSettings();
-
+
$this->output( " OK\n" );
-
+
}
-
+
}
}
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to rollback all edits by a given user or IP provided
+ * they're the most recent edit.
+ *
+ * @ingroup Maintenance
+ */
class RollbackEdits extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to run a database query in batches and wait for slaves.
+ *
+ * @ingroup Maintenance
+ */
class BatchedQueryRunner extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * This script starts pending jobs.
+ * Run pending jobs.
*
- * Usage:
+ * Options:
* --maxjobs <num> (default 10000)
* --type <job_cmd>
*
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that runs pending jobs.
+ *
+ * @ingroup Maintenance
+ */
class RunJobs extends Maintenance {
public function __construct() {
parent::__construct();
}
public function memoryLimit() {
+ if ( $this->hasOption( 'memory-limit' ) ) {
+ return parent::memoryLimit();
+ }
// Don't eat all memory on the machine if we get a bad job.
return "150M";
}
$wgTitle = Title::newFromText( 'RunJobs.php' );
$dbw = wfGetDB( DB_MASTER );
$n = 0;
- $conds = '';
+
if ( $type === false ) {
$conds = Job::defaultQueueConditions( );
} else {
- $conds = "job_cmd = " . $dbw->addQuotes( $type );
+ $conds = array( 'job_cmd' => $type );
}
while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) {
'scripts' => 'resources/jquery/jquery.autoEllipsis.js',
'dependencies' => 'jquery.highlightText',
),
+ 'jquery.badge' => array(
+ 'scripts' => 'resources/jquery/jquery.badge.js',
+ 'styles' => 'resources/jquery/jquery.badge.css',
+ ),
'jquery.byteLength' => array(
'scripts' => 'resources/jquery/jquery.byteLength.js',
),
'scripts' => 'resources/mediawiki/mediawiki.user.js',
'dependencies' => array(
'jquery.cookie',
+ 'mediawiki.api',
),
),
'mediawiki.util' => array(
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
-.ui-button-icons-only { width: 3.4em; }
-button.ui-button-icons-only { width: 3.7em; }
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
/*button text element */
-.ui-button .ui-button-text { display: block; line-height: 1.4em; }
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: 0.3em 1em 0.25em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: 0.3em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 0.3em 1em 0.25em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: 0.3em 2.1em 0.25em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
-/* for older versions of jQuery UI */
-.ui-button-text-icon .ui-button-text { padding: 0.3em 1em 0.3em 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: 0.3em 1em; }
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
body .ui-button {
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- margin: 0.5em 0 0.5em 0.4em !important;
+ margin: 0.5em 0 0.5em 0.4em;
border: 1px solid #a6a6a6 !important;
/* @embed */
background: #f2f2f2 url(images/button-off.png) repeat-x scroll 50% 100% !important;
width: auto;
overflow: visible;
}
+
+/* Corner radius */
+/* This is normally handled in jquery.ui.theme.css, but in our case, the corner
+ styling of our buttons doesn't match our default widget corner styling */
+.ui-button.ui-corner-all, .ui-button.ui-corner-top, .ui-button.ui-corner-left, .ui-button.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-button.ui-corner-all, .ui-button.ui-corner-top, .ui-button.ui-corner-right, .ui-button.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-button.ui-corner-all, .ui-button.ui-corner-bottom, .ui-button.ui-corner-left, .ui-button.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-button.ui-corner-all, .ui-button.ui-corner-bottom, .ui-button.ui-corner-right, .ui-button.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+
body .ui-button:hover {
border-color: #6e7273;
/* @embed */
--- /dev/null
+.mw-badge {
+ min-width: 8px;
+ height: 14px;
+ border: 1px solid white;
+ border-radius: 8px;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ box-shadow: 0px 1px 4px #ccc;
+ -moz-box-shadow: 0px 1px 4px #ccc;
+ -webkit-box-shadow: 0px 1px 4px #ccc;
+ background-color: #b60a00;
+ background-image: linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+ background-image: -o-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+ background-image: -moz-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+ background-image: -webkit-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+ background-image: -ms-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #a70802), color-stop(1, #cf0e00));
+ padding: 0 3px;
+ text-align: center;
+}
+
+.mw-badge-content {
+ font-size: 12px;
+ line-height: 14px;
+ color: white;
+ vertical-align: top;
+}
+
+.mw-badge-inline {
+ display: inline-block;
+ margin-left: 3px;
+}
+
+.mw-badge-overlay {
+ position: absolute;
+ bottom: -1px;
+ right: -3px;
+ z-index: 50;
+}
--- /dev/null
+// Badger v1.0 by Daniel Raftery
+// http://thrivingkings.com/badger
+// http://twitter.com/ThrivingKings
+// Modified by Ryan Kaldari <rkaldari@wikimedia.org>
+
+/**
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY.
+ */
+
+(function( $ ) {
+ $.fn.badge = function( badge, options ) {
+ var existingBadge = this.find( '.mw-badge' );
+ options = $.extend( {}, options );
+
+ badge = String(badge);
+ if ( badge.charAt(0) === '+' ) {
+ if ( existingBadge.length > 0 ) {
+ oldBadge = existingBadge.text();
+ badge = Math.round( Number( oldBadge ) + Number( badge.substr(1) ) );
+ } else {
+ badge = badge.substr(1);
+ }
+ } else if ( badge.charAt(0) === '-' ) {
+ if ( existingBadge.length > 0 ) {
+ oldBadge = existingBadge.text();
+ badge = Math.round( Number( oldBadge ) - Number( badge.substr(1) ) );
+ } else {
+ badge = 0;
+ }
+ }
+
+ if ( Number(badge) <= 0 ) {
+ // Clear any existing badge
+ existingBadge.remove();
+ } else {
+ // Don't add duplicates
+ var $badge = existingBadge;
+ if ( existingBadge.length > 0 ) {
+ this.find( '.mw-badge-content' ).text( badge );
+ } else {
+ $badge = $('<div/>')
+ .addClass('mw-badge')
+ .addClass('mw-badge-overlay')
+ .append(
+ $('<span/>')
+ .addClass('mw-badge-content')
+ .text(badge)
+ );
+ this.append($badge);
+ }
+
+ if ( options.type ) {
+ if ( options.type == 'inline' ) {
+ $badge.removeClass('mw-badge-overlay')
+ .addClass('mw-badge-inline');
+ } else if ( options.type == 'overlay' ) {
+ $badge.removeClass('mw-badge-inline')
+ .addClass('mw-badge-overlay');
+ }
+ }
+
+ // If a callback was specified, call it with the badge number
+ if ( options.callback ) {
+ options.callback( badge );
+ }
+ }
+ };
+} ) ( jQuery );
/*!
- * jQuery JavaScript Library v1.7.2
+ * jQuery JavaScript Library v1.8.0
* http://jquery.com/
*
- * Copyright 2011, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
* Includes Sizzle.js
* http://sizzlejs.com/
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
*
- * Date: Wed Mar 21 12:46:34 2012 -0700
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: Thu Aug 09 2012 16:24:48 GMT-0400 (Eastern Daylight Time)
*/
(function( window, undefined ) {
+var
+ // A central reference to the root jQuery(document)
+ rootjQuery,
-// Use the correct document accordingly with window argument (sandbox)
-var document = window.document,
- navigator = window.navigator,
- location = window.location;
-var jQuery = (function() {
+ // The deferred used on DOM ready
+ readyList,
-// Define a local copy of jQuery
-var jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+ location = window.location,
+ navigator = window.navigator,
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$,
- // A central reference to the root jQuery(document)
- rootjQuery,
+ // Save a reference to some core methods
+ core_push = Array.prototype.push,
+ core_slice = Array.prototype.slice,
+ core_indexOf = Array.prototype.indexOf,
+ core_toString = Object.prototype.toString,
+ core_hasOwn = Object.prototype.hasOwnProperty,
+ core_trim = String.prototype.trim,
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
- // A simple way to check for HTML strings or ID strings
- // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
- quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+ // Used for matching numbers
+ core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
- // Check if a string has a non-whitespace character in it
- rnotwhite = /\S/,
+ // Used for detecting and trimming whitespace
+ core_rnotwhite = /\S/,
+ core_rspace = /\s+/,
- // Used for trimming whitespace
- trimLeft = /^\s+/,
- trimRight = /\s+$/,
+ // IE doesn't match non-breaking spaces with \s
+ rtrim = core_rnotwhite.test("\xA0") ? (/^[\s\xA0]+|[\s\xA0]+$/g) : /^\s+|\s+$/g,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
// Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
// JSON RegExp
rvalidchars = /^[\],:{}\s]*$/,
- rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
- rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
-
- // Useragent RegExp
- rwebkit = /(webkit)[ \/]([\w.]+)/,
- ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
- rmsie = /(msie) ([\w.]+)/,
- rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
// Matches dashed string for camelizing
- rdashAlpha = /-([a-z]|[0-9])/ig,
rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
// Used by jQuery.camelCase as callback to replace()
fcamelCase = function( all, letter ) {
return ( letter + "" ).toUpperCase();
},
- // Keep a UserAgent string for use with jQuery.browser
- userAgent = navigator.userAgent,
-
- // For matching the engine and version of the browser
- browserMatch,
-
- // The deferred used on DOM ready
- readyList,
-
- // The ready event handler
- DOMContentLoaded,
-
- // Save a reference to some core methods
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty,
- push = Array.prototype.push,
- slice = Array.prototype.slice,
- trim = String.prototype.trim,
- indexOf = Array.prototype.indexOf,
+ // The ready event handler and self cleanup method
+ DOMContentLoaded = function() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ } else if ( document.readyState === "complete" ) {
+ // we're here because readyState === "complete" in oldIE
+ // which is good enough for us to call the dom ready!
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ },
// [[Class]] -> type pairs
class2type = {};
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
- // Handle $(""), $(null), or $(undefined)
+ // Handle $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
return this;
}
- // The body element only exists once, optimize finding it
- if ( selector === "body" && !context && document.body ) {
- this.context = document;
- this[0] = document.body;
- this.selector = selector;
- this.length = 1;
- return this;
- }
-
// Handle HTML strings
if ( typeof selector === "string" ) {
- // Are we dealing with HTML string or an ID?
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
- match = quickExpr.exec( selector );
+ match = rquickExpr.exec( selector );
}
- // Verify a match, and that no context was specified for #id
+ // Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
- doc = ( context ? context.ownerDocument || context : document );
-
- // If a single string is passed in and it's a single tag
- // just do a createElement and skip the rest
- ret = rsingleTag.exec( selector );
-
- if ( ret ) {
- if ( jQuery.isPlainObject( context ) ) {
- selector = [ document.createElement( ret[1] ) ];
- jQuery.fn.attr.call( selector, context, true );
-
- } else {
- selector = [ doc.createElement( ret[1] ) ];
- }
+ doc = ( context && context.nodeType ? context.ownerDocument || context : document );
- } else {
- ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
- selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
+ // scripts is true for back-compat
+ selector = jQuery.parseHTML( match[1], doc, true );
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ this.attr.call( selector, context, true );
}
return jQuery.merge( this, selector );
- // HANDLE: $("#id")
+ // HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
selector: "",
// The current version of jQuery being used
- jquery: "1.7.2",
+ jquery: "1.8.0",
// The default length of a jQuery object is 0
length: 0,
},
toArray: function() {
- return slice.call( this, 0 );
+ return core_slice.call( this );
},
// Get the Nth element in the matched element set OR
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
- // Build a new jQuery matched element set
- var ret = this.constructor();
- if ( jQuery.isArray( elems ) ) {
- push.apply( ret, elems );
-
- } else {
- jQuery.merge( ret, elems );
- }
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
},
ready: function( fn ) {
- // Attach the listeners
- jQuery.bindReady();
-
// Add the callback
- readyList.add( fn );
+ jQuery.ready.promise().done( fn );
return this;
},
},
slice: function() {
- return this.pushStack( slice.apply( this, arguments ),
- "slice", slice.call(arguments).join(",") );
+ return this.pushStack( core_slice.apply( this, arguments ),
+ "slice", core_slice.call(arguments).join(",") );
},
map: function( callback ) {
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
- push: push,
+ push: core_push,
sort: [].sort,
splice: [].splice
};
// Handle when the DOM is ready
ready: function( wait ) {
- // Either a released hold or an DOMready/load event and not yet ready
- if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.fireWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger( "ready" ).off( "ready" );
- }
- }
- },
- bindReady: function() {
- if ( readyList ) {
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
- readyList = jQuery.Callbacks( "once memory" );
-
- // Catch cases where $(document).ready() is called after the
- // browser event has already occurred.
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}
- // Mozilla, Opera and webkit nightlies currently support this event
- if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", jQuery.ready, false );
-
- // If IE event model is used
- } else if ( document.attachEvent ) {
- // ensure firing before onload,
- // maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", DOMContentLoaded );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", jQuery.ready );
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
- // If IE and not a frame
- // continually check to see if the document is ready
- var toplevel = false;
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
- try {
- toplevel = window.frameElement == null;
- } catch(e) {}
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
- if ( document.documentElement.doScroll && toplevel ) {
- doScrollCheck();
- }
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
}
},
type: function( obj ) {
return obj == null ?
String( obj ) :
- class2type[ toString.call(obj) ] || "object";
+ class2type[ core_toString.call(obj) ] || "object";
},
isPlainObject: function( obj ) {
try {
// Not own constructor property must be Object
if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ !core_hasOwn.call(obj, "constructor") &&
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
var key;
for ( key in obj ) {}
- return key === undefined || hasOwn.call( obj, key );
+ return key === undefined || core_hasOwn.call( obj, key );
},
isEmptyObject: function( obj ) {
- for ( var name in obj ) {
+ var name;
+ for ( name in obj ) {
return false;
}
return true;
throw new Error( msg );
},
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // scripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, scripts ) {
+ var parsed;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ scripts = context;
+ context = 0;
+ }
+ context = context || document;
+
+ // Single tag
+ if ( (parsed = rsingleTag.exec( data )) ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
+ return jQuery.merge( [],
+ (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
+ },
+
parseJSON: function( data ) {
- if ( typeof data !== "string" || !data ) {
+ if ( !data || typeof data !== "string") {
return null;
}
// Cross-browser xml parsing
parseXML: function( data ) {
- if ( typeof data !== "string" || !data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
return null;
}
- var xml, tmp;
try {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
- if ( data && rnotwhite.test( data ) ) {
+ if ( data && core_rnotwhite.test( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
},
// args is for internal usage only
- each: function( object, callback, args ) {
- var name, i = 0,
- length = object.length,
- isObj = length === undefined || jQuery.isFunction( object );
+ each: function( obj, callback, args ) {
+ var name,
+ i = 0,
+ length = obj.length,
+ isObj = length === undefined || jQuery.isFunction( obj );
if ( args ) {
if ( isObj ) {
- for ( name in object ) {
- if ( callback.apply( object[ name ], args ) === false ) {
+ for ( name in obj ) {
+ if ( callback.apply( obj[ name ], args ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
- if ( callback.apply( object[ i++ ], args ) === false ) {
+ if ( callback.apply( obj[ i++ ], args ) === false ) {
break;
}
}
// A special, fast, case for the most common use of each
} else {
if ( isObj ) {
- for ( name in object ) {
- if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ for ( name in obj ) {
+ if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
- if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
break;
}
}
}
}
- return object;
+ return obj;
},
// Use native String.trim function wherever possible
- trim: trim ?
+ trim: core_trim ?
function( text ) {
return text == null ?
"" :
- trim.call( text );
+ core_trim.call( text );
} :
// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
- text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ text.toString().replace( rtrim, "" );
},
// results is for internal usage only
- makeArray: function( array, results ) {
- var ret = results || [];
+ makeArray: function( arr, results ) {
+ var type,
+ ret = results || [];
- if ( array != null ) {
+ if ( arr != null ) {
// The window, strings (and functions) also have 'length'
// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
- var type = jQuery.type( array );
+ type = jQuery.type( arr );
- if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
- push.call( ret, array );
+ if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
+ core_push.call( ret, arr );
} else {
- jQuery.merge( ret, array );
+ jQuery.merge( ret, arr );
}
}
return ret;
},
- inArray: function( elem, array, i ) {
+ inArray: function( elem, arr, i ) {
var len;
- if ( array ) {
- if ( indexOf ) {
- return indexOf.call( array, elem, i );
+ if ( arr ) {
+ if ( core_indexOf ) {
+ return core_indexOf.call( arr, elem, i );
}
- len = array.length;
+ len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
- if ( i in array && array[ i ] === elem ) {
+ if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
},
merge: function( first, second ) {
- var i = first.length,
+ var l = second.length,
+ i = first.length,
j = 0;
- if ( typeof second.length === "number" ) {
- for ( var l = second.length; j < l; j++ ) {
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
},
grep: function( elems, callback, inv ) {
- var ret = [], retVal;
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
- for ( var i = 0, length = elems.length; i < length; i++ ) {
+ for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
// arg is for internal usage only
map: function( elems, callback, arg ) {
- var value, key, ret = [],
+ var value, key,
+ ret = [],
i = 0,
length = elems.length,
// jquery objects are treated as arrays
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
+ var tmp, args, proxy;
+
if ( typeof context === "string" ) {
- var tmp = fn[ context ];
+ tmp = fn[ context ];
context = fn;
fn = tmp;
}
}
// Simulated bind
- var args = slice.call( arguments, 2 ),
- proxy = function() {
- return fn.apply( context, args.concat( slice.call( arguments ) ) );
- };
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
+ };
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
return proxy;
},
- // Mutifunctional method to get and set values to a collection
+ // Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
var exec,
now: function() {
return ( new Date() ).getTime();
- },
-
- // Use of jQuery.browser is frowned upon.
- // More details: http://docs.jquery.com/Utilities/jQuery.browser
- uaMatch: function( ua ) {
- ua = ua.toLowerCase();
+ }
+});
- var match = rwebkit.exec( ua ) ||
- ropera.exec( ua ) ||
- rmsie.exec( ua ) ||
- ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
- [];
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
- return { browser: match[1] || "", version: match[2] || "0" };
- },
+ readyList = jQuery.Deferred();
- sub: function() {
- function jQuerySub( selector, context ) {
- return new jQuerySub.fn.init( selector, context );
- }
- jQuery.extend( true, jQuerySub, this );
- jQuerySub.superclass = this;
- jQuerySub.fn = jQuerySub.prototype = this();
- jQuerySub.fn.constructor = jQuerySub;
- jQuerySub.sub = this.sub;
- jQuerySub.fn.init = function init( selector, context ) {
- if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
- context = jQuerySub( context );
- }
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready, 1 );
- return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
- };
- jQuerySub.fn.init.prototype = jQuerySub.fn;
- var rootjQuerySub = jQuerySub(document);
- return jQuerySub;
- },
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
- browser: {}
-});
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
-browserMatch = jQuery.uaMatch( userAgent );
-if ( browserMatch.browser ) {
- jQuery.browser[ browserMatch.browser ] = true;
- jQuery.browser.version = browserMatch.version;
-}
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
-// Deprecated, use jQuery.browser.webkit instead
-if ( jQuery.browser.webkit ) {
- jQuery.browser.safari = true;
-}
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
-// IE doesn't match non-breaking spaces with \s
-if ( rnotwhite.test( "\xA0" ) ) {
- trimLeft = /^[\s\xA0]+/;
- trimRight = /[\s\xA0]+$/;
-}
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
-// All jQuery objects should point back to these
-rootjQuery = jQuery(document);
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
-// Cleanup functions for the document ready method
-if ( document.addEventListener ) {
- DOMContentLoaded = function() {
- document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
- jQuery.ready();
- };
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
-} else if ( document.attachEvent ) {
- DOMContentLoaded = function() {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( document.readyState === "complete" ) {
- document.detachEvent( "onreadystatechange", DOMContentLoaded );
- jQuery.ready();
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
}
- };
-}
-
-// The DOM ready check for Internet Explorer
-function doScrollCheck() {
- if ( jQuery.isReady ) {
- return;
- }
-
- try {
- // If IE is used, use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- document.documentElement.doScroll("left");
- } catch(e) {
- setTimeout( doScrollCheck, 1 );
- return;
}
+ return readyList.promise( obj );
+};
- // and execute any waiting functions
- jQuery.ready();
-}
-
-return jQuery;
-
-})();
-
-
-// String to Object flags format cache
-var flagsCache = {};
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
-// Convert String-formatted flags into Object-formatted ones and store in cache
-function createFlags( flags ) {
- var object = flagsCache[ flags ] = {},
- i, length;
- flags = flags.split( /\s+/ );
- for ( i = 0, length = flags.length; i < length; i++ ) {
- object[ flags[i] ] = true;
- }
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.split( core_rspace ), function( _, flag ) {
+ object[ flag ] = true;
+ });
return object;
}
/*
* Create a callback list using the following parameters:
*
- * flags: an optional list of space-separated flags that will change how
- * the callback list behaves
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
- * Possible flags:
+ * Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
-jQuery.Callbacks = function( flags ) {
+jQuery.Callbacks = function( options ) {
- // Convert flags from String-formatted to Object-formatted
+ // Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
- flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
- var // Actual callback list
- list = [],
- // Stack of fire calls for repeatable lists
- stack = [],
- // Last fire value (for non-forgettable lists)
+ var // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
- // Add one or several callbacks to the list
- add = function( args ) {
- var i,
- length,
- elem,
- type,
- actual;
- for ( i = 0, length = args.length; i < length; i++ ) {
- elem = args[ i ];
- type = jQuery.type( elem );
- if ( type === "array" ) {
- // Inspect recursively
- add( elem );
- } else if ( type === "function" ) {
- // Add if not in unique mode and callback is not in
- if ( !flags.unique || !self.has( elem ) ) {
- list.push( elem );
- }
- }
- }
- },
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
// Fire callbacks
- fire = function( context, args ) {
- args = args || [];
- memory = !flags.memory || [ context, args ];
+ fire = function( data ) {
+ memory = options.memory && data;
fired = true;
- firing = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
+ firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
- memory = true; // Mark as halted
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
- if ( !flags.once ) {
- if ( stack && stack.length ) {
- memory = stack.shift();
- self.fireWith( memory[ 0 ], memory[ 1 ] );
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
}
- } else if ( memory === true ) {
- self.disable();
- } else {
+ } else if ( memory ) {
list = [];
+ } else {
+ self.disable();
}
}
},
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
- var length = list.length;
- add( arguments );
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) {
+ list.push( arg );
+ } else if ( arg && arg.length ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
- // we should call right away, unless previous
- // firing was halted (stopOnFalse)
- } else if ( memory && memory !== true ) {
- firingStart = length;
- fire( memory[ 0 ], memory[ 1 ] );
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
}
}
return this;
// Remove a callback from the list
remove: function() {
if ( list ) {
- var args = arguments,
- argIndex = 0,
- argLength = args.length;
- for ( ; argIndex < argLength ; argIndex++ ) {
- for ( var i = 0; i < list.length; i++ ) {
- if ( args[ argIndex ] === list[ i ] ) {
- // Handle firingIndex and firingLength
- if ( firing ) {
- if ( i <= firingLength ) {
- firingLength--;
- if ( i <= firingIndex ) {
- firingIndex--;
- }
- }
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
}
- // Remove the element
- list.splice( i--, 1 );
- // If we have some unicity property then
- // we only need to do this once
- if ( flags.unique ) {
- break;
+ if ( index <= firingIndex ) {
+ firingIndex--;
}
}
}
- }
+ });
}
return this;
},
// Control if a given callback is in the list
has: function( fn ) {
- if ( list ) {
- var i = 0,
- length = list.length;
- for ( ; i < length; i++ ) {
- if ( fn === list[ i ] ) {
- return true;
- }
- }
- }
- return false;
+ return jQuery.inArray( fn, list ) > -1;
},
// Remove all callbacks from the list
empty: function() {
// Lock the list in its current state
lock: function() {
stack = undefined;
- if ( !memory || memory === true ) {
+ if ( !memory ) {
self.disable();
}
return this;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
- if ( stack ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( list && ( !fired || stack ) ) {
if ( firing ) {
- if ( !flags.once ) {
- stack.push( [ context, args ] );
- }
- } else if ( !( flags.once && memory ) ) {
- fire( context, args );
+ stack.push( args );
+ } else {
+ fire( args );
}
}
return this;
return self;
};
-
-
-
-
-var // Static reference to slice
- sliceDeferred = [].slice;
-
jQuery.extend({
Deferred: function( func ) {
- var doneList = jQuery.Callbacks( "once memory" ),
- failList = jQuery.Callbacks( "once memory" ),
- progressList = jQuery.Callbacks( "memory" ),
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
state = "pending",
- lists = {
- resolve: doneList,
- reject: failList,
- notify: progressList
- },
promise = {
- done: doneList.add,
- fail: failList.add,
- progress: progressList.add,
-
state: function() {
return state;
},
-
- // Deprecated
- isResolved: doneList.fired,
- isRejected: failList.fired,
-
- then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
- deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
- return this;
- },
always: function() {
- deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ deferred.done( arguments ).fail( arguments );
return this;
},
- pipe: function( fnDone, fnFail, fnProgress ) {
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
- jQuery.each( {
- done: [ fnDone, "resolve" ],
- fail: [ fnFail, "reject" ],
- progress: [ fnProgress, "notify" ]
- }, function( handler, data ) {
- var fn = data[ 0 ],
- action = data[ 1 ],
- returned;
- if ( jQuery.isFunction( fn ) ) {
- deferred[ handler ](function() {
- returned = fn.apply( this, arguments );
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
+ function() {
+ var returned = fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
} else {
newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
}
- });
- } else {
- deferred[ handler ]( newDefer[ action ] );
- }
+ } :
+ newDefer[ action ]
+ );
});
+ fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
- if ( obj == null ) {
- obj = promise;
- } else {
- for ( var key in promise ) {
- obj[ key ] = promise[ key ];
- }
- }
- return obj;
+ return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise;
}
},
- deferred = promise.promise({}),
- key;
+ deferred = {};
- for ( key in lists ) {
- deferred[ key ] = lists[ key ].fire;
- deferred[ key + "With" ] = lists[ key ].fireWith;
- }
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
- // Handle state
- deferred.done( function() {
- state = "resolved";
- }, failList.disable, progressList.lock ).fail( function() {
- state = "rejected";
- }, doneList.disable, progressList.lock );
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ] = list.fire
+ deferred[ tuple[0] ] = list.fire;
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
// Call given func if any
if ( func ) {
},
// Deferred helper
- when: function( firstParam ) {
- var args = sliceDeferred.call( arguments, 0 ),
- i = 0,
- length = args.length,
- pValues = new Array( length ),
- count = length,
- pCount = length,
- deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
- firstParam :
- jQuery.Deferred(),
- promise = deferred.promise();
- function resolveFunc( i ) {
- return function( value ) {
- args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
- if ( !( --count ) ) {
- deferred.resolveWith( deferred, args );
- }
- };
- }
- function progressFunc( i ) {
- return function( value ) {
- pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
- deferred.notifyWith( promise, pValues );
- };
- }
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
- if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
- args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
} else {
- --count;
+ --remaining;
}
}
- if ( !count ) {
- deferred.resolveWith( deferred, args );
- }
- } else if ( deferred !== firstParam ) {
- deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
}
- return promise;
- }
-});
-
-
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+ return deferred.promise();
+ }
+});
jQuery.support = (function() {
var support,
opt,
input,
fragment,
- tds,
- events,
eventName,
i,
isSupported,
- div = document.createElement( "div" ),
- documentElement = document.documentElement;
+ clickFn,
+ div = document.createElement("div");
// Preliminary tests
- div.setAttribute("className", "t");
- div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+ div.setAttribute( "className", "t" );
+ div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
- all = div.getElementsByTagName( "*" );
- a = div.getElementsByTagName( "a" )[ 0 ];
+ all = div.getElementsByTagName("*");
+ a = div.getElementsByTagName("a")[ 0 ];
+ a.style.cssText = "top:1px;float:left;opacity:.5";
// Can't get basic test support
if ( !all || !all.length || !a ) {
}
// First batch of supports tests
- select = document.createElement( "select" );
+ select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
- input = div.getElementsByTagName( "input" )[ 0 ];
+ input = div.getElementsByTagName("input")[ 0 ];
support = {
// IE strips leading whitespace when .innerHTML is used
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.55/.test( a.style.opacity ),
+ opacity: /^0.5/.test( a.style.opacity ),
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
// Where outerHTML is undefined, this still works
html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+ // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+ boxModel: ( document.compatMode === "CSS1Compat" ),
+
// Will be defined later
submitBubbles: true,
changeBubbles: true,
inlineBlockNeedsLayout: false,
shrinkWrapBlocks: false,
reliableMarginRight: true,
- pixelMargin: true
+ boxSizingReliable: true,
+ pixelPosition: false
};
- // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
- jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
-
// Make sure checked status is properly cloned
input.checked = true;
support.noCloneChecked = input.cloneNode( true ).checked;
}
if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
- div.attachEvent( "onclick", function() {
+ div.attachEvent( "onclick", clickFn = function() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
support.noCloneEvent = false;
});
- div.cloneNode( true ).fireEvent( "onclick" );
+ div.cloneNode( true ).fireEvent("onclick");
+ div.detachEvent( "onclick", clickFn );
}
// Check if a radio maintains its value
// after being appended to the DOM
input = document.createElement("input");
input.value = "t";
- input.setAttribute("type", "radio");
+ input.setAttribute( "type", "radio" );
support.radioValue = input.value === "t";
- input.setAttribute("checked", "checked");
+ input.setAttribute( "checked", "checked" );
// #11217 - WebKit loses check when the name is after the checked attribute
input.setAttribute( "name", "t" );
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
if ( div.attachEvent ) {
for ( i in {
- submit: 1,
- change: 1,
- focusin: 1
+ submit: true,
+ change: true,
+ focusin: true
}) {
eventName = "on" + i;
isSupported = ( eventName in div );
}
}
- fragment.removeChild( div );
-
- // Null elements to avoid leaks in IE
- fragment = select = opt = div = input = null;
-
// Run tests that need a body at doc ready
jQuery(function() {
- var container, outer, inner, table, td, offsetSupport,
- marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
- paddingMarginBorderVisibility, paddingMarginBorder,
+ var container, div, tds, marginDiv,
+ divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
body = document.getElementsByTagName("body")[0];
if ( !body ) {
return;
}
- conMarginTop = 1;
- paddingMarginBorder = "padding:0;margin:0;border:";
- positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
- paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
- style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
- html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
- "<table " + style + "' cellpadding='0' cellspacing='0'>" +
- "<tr><td></td></tr></table>";
-
container = document.createElement("div");
- container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+ container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
body.insertBefore( container, body.firstChild );
// Construct the test element
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
// (only IE 8 fails this test)
- div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
- tds = div.getElementsByTagName( "td" );
+ div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName("td");
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
isSupported = ( tds[ 0 ].offsetHeight === 0 );
tds[ 0 ].style.display = "";
// (IE <= 8 fail this test)
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. For more
- // info see bug #3333
- // Fails in WebKit before Feb 2011 nightlies
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Check box-sizing and margin behavior
+ div.innerHTML = "";
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+ support.boxSizing = ( div.offsetWidth === 4 );
+ support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+ // NOTE: To any future maintainer, window.getComputedStyle was used here
+ // instead of getComputedStyle because it gave a better gzip size.
+ // The difference between window.getComputedStyle and getComputedStyle is
+ // 7 bytes
if ( window.getComputedStyle ) {
- div.innerHTML = "";
- marginDiv = document.createElement( "div" );
- marginDiv.style.width = "0";
- marginDiv.style.marginRight = "0";
- div.style.width = "2px";
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = document.createElement("div");
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
div.appendChild( marginDiv );
support.reliableMarginRight =
- ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
}
if ( typeof div.style.zoom !== "undefined" ) {
// them layout
// (IE < 8 does this)
div.innerHTML = "";
- div.style.width = div.style.padding = "1px";
- div.style.border = 0;
- div.style.overflow = "hidden";
- div.style.display = "inline";
- div.style.zoom = 1;
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "block";
div.style.overflow = "visible";
- div.innerHTML = "<div style='width:5px;'></div>";
+ div.innerHTML = "<div></div>";
+ div.firstChild.style.width = "5px";
support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
- }
-
- div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
- div.innerHTML = html;
-
- outer = div.firstChild;
- inner = outer.firstChild;
- td = outer.nextSibling.firstChild.firstChild;
-
- offsetSupport = {
- doesNotAddBorder: ( inner.offsetTop !== 5 ),
- doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
- };
- inner.style.position = "fixed";
- inner.style.top = "20px";
-
- // safari subtracts parent border width here which is 5px
- offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
- inner.style.position = inner.style.top = "";
-
- outer.style.overflow = "hidden";
- outer.style.position = "relative";
-
- offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
- offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
-
- if ( window.getComputedStyle ) {
- div.style.marginTop = "1%";
- support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
- }
-
- if ( typeof container.style.zoom !== "undefined" ) {
container.style.zoom = 1;
}
+ // Null elements to avoid leaks in IE
body.removeChild( container );
- marginDiv = div = container = null;
-
- jQuery.extend( support, offsetSupport );
+ container = div = tds = marginDiv = null;
});
+ // Null elements to avoid leaks in IE
+ fragment.removeChild( div );
+ all = a = select = opt = input = fragment = div = null;
+
return support;
})();
-
-
-
-
var rbrace = /^(?:\{.*\}|\[.*\])$/,
rmultiDash = /([A-Z])/g;
jQuery.extend({
cache: {},
+ deletedIds: [],
+
// Please use with caution
uuid: 0,
return;
}
- var privateCache, thisCache, ret,
+ var thisCache, ret,
internalKey = jQuery.expando,
getByName = typeof name === "string",
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
- isEvents = name === "events";
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
- if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
- elem[ internalKey ] = id = ++jQuery.uuid;
+ elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid;
} else {
id = internalKey;
}
}
}
- privateCache = thisCache = cache[ id ];
+ thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
thisCache[ jQuery.camelCase( name ) ] = data;
}
- // Users should not attempt to inspect the internal events object using jQuery.data,
- // it is undocumented and subject to change. But does anyone listen? No.
- if ( isEvents && !thisCache[ name ] ) {
- return privateCache.events;
- }
-
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( getByName ) {
var thisCache, i, l,
- // Reference to internal data cache key
- internalKey = jQuery.expando,
-
isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
-
- // See jQuery.data for more information
- id = isNode ? elem[ internalKey ] : internalKey;
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( name in thisCache ) {
name = [ name ];
} else {
- name = name.split( " " );
+ name = name.split(" ");
}
}
}
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
- if ( !isEmptyDataObject(cache[ id ]) ) {
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
- // Browsers that fail expando deletion also refuse to delete expandos on
- // the window, but it will allow it on all other JS objects; other browsers
- // don't care
- // Ensure that `cache` is not a window object #10080
- if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
delete cache[ id ];
+
+ // When all else fails, null
} else {
cache[ id ] = null;
}
-
- // We destroyed the cache and need to eliminate the expando on the node to avoid
- // false lookups in the cache for entries that no longer exist
- if ( isNode ) {
- // IE does not allow us to delete expando properties from nodes,
- // nor does it have a removeAttribute function on Document nodes;
- // we must handle all of these cases
- if ( jQuery.support.deleteExpando ) {
- delete elem[ internalKey ];
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( internalKey );
- } else {
- elem[ internalKey ] = null;
- }
- }
},
// For internal use only.
// A method for determining if a DOM node can handle the data expando
acceptData: function( elem ) {
- if ( elem.nodeName ) {
- var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
-
- if ( match ) {
- return !(match === true || elem.getAttribute("classid") !== match);
- }
- }
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
- return true;
+ // nodes accept data unless otherwise specified; rejection can be conditional
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
}
});
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
- jQuery.isNumeric( data ) ? +data :
- rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
- for ( var name in obj ) {
+ var name;
+ for ( name in obj ) {
// if the public data object is empty, the private is still empty
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
return true;
}
-
-
-
-
-function handleQueueMarkDefer( elem, type, src ) {
- var deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- defer = jQuery._data( elem, deferDataKey );
- if ( defer &&
- ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
- ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
- // Give room for hard-coded callbacks to fire first
- // and eventually mark/queue something else on the element
- setTimeout( function() {
- if ( !jQuery._data( elem, queueDataKey ) &&
- !jQuery._data( elem, markDataKey ) ) {
- jQuery.removeData( elem, deferDataKey, true );
- defer.fire();
- }
- }, 0 );
- }
-}
-
jQuery.extend({
-
- _mark: function( elem, type ) {
- if ( elem ) {
- type = ( type || "fx" ) + "mark";
- jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
- }
- },
-
- _unmark: function( force, elem, type ) {
- if ( force !== true ) {
- type = elem;
- elem = force;
- force = false;
- }
- if ( elem ) {
- type = type || "fx";
- var key = type + "mark",
- count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
- if ( count ) {
- jQuery._data( elem, key, count );
- } else {
- jQuery.removeData( elem, key, true );
- handleQueueMarkDefer( elem, type, "mark" );
- }
- }
- },
-
queue: function( elem, type, data ) {
- var q;
+ var queue;
+
if ( elem ) {
type = ( type || "fx" ) + "queue";
- q = jQuery._data( elem, type );
+ queue = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
- if ( !q || jQuery.isArray(data) ) {
- q = jQuery._data( elem, type, jQuery.makeArray(data) );
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
- q.push( data );
+ queue.push( data );
}
}
- return q || [];
+ return queue || [];
}
},
var queue = jQuery.queue( elem, type ),
fn = queue.shift(),
- hooks = {};
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
}
if ( fn ) {
+
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
- jQuery._data( elem, type + ".run", hooks );
- fn.call( elem, function() {
- jQuery.dequeue( elem, type );
- }, hooks );
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
}
-
- if ( !queue.length ) {
- jQuery.removeData( elem, type + "queue " + type + ".run", true );
- handleQueueMarkDefer( elem, type, "queue" );
+ if ( !queue.length && hooks ) {
+ hooks.empty.fire();
}
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery.removeData( elem, type + "queue", true );
+ jQuery.removeData( elem, key, true );
+ })
+ });
}
});
this.each(function() {
var queue = jQuery.queue( this, type, data );
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
},
// Get a promise resolved when queues of a certain type
// are emptied (fx is the type by default)
- promise: function( type, object ) {
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
if ( typeof type !== "string" ) {
- object = type;
+ obj = type;
type = undefined;
}
type = type || "fx";
- var defer = jQuery.Deferred(),
- elements = this,
- i = elements.length,
- count = 1,
- deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- tmp;
- function resolve() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- }
+
while( i-- ) {
- if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
- ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
- jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
- jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
+ if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) {
count++;
- tmp.add( resolve );
+ tmp.empty.add( resolve );
}
}
resolve();
- return defer.promise( object );
+ return defer.promise( obj );
}
});
-
-
-
-
-var rclass = /[\n\t\r]/g,
- rspace = /\s+/,
+var nodeHook, boolHook, fixSpecified,
+ rclass = /[\t\r\n]/g,
rreturn = /\r/g,
rtype = /^(?:button|input)$/i,
rfocusable = /^(?:button|input|object|select|textarea)$/i,
- rclickable = /^a(?:rea)?$/i,
+ rclickable = /^a(?:rea|)$/i,
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- getSetAttribute = jQuery.support.getSetAttribute,
- nodeHook, boolHook, fixSpecified;
+ getSetAttribute = jQuery.support.getSetAttribute;
jQuery.fn.extend({
attr: function( name, value ) {
}
if ( value && typeof value === "string" ) {
- classNames = value.split( rspace );
+ classNames = value.split( core_rspace );
for ( i = 0, l = this.length; i < l; i++ ) {
elem = this[ i ];
},
removeClass: function( value ) {
- var classNames, i, l, elem, className, c, cl;
+ var removes, className, elem, c, cl, i, l;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).removeClass( value.call(this, j, this.className) );
});
}
-
if ( (value && typeof value === "string") || value === undefined ) {
- classNames = ( value || "" ).split( rspace );
+ removes = ( value || "" ).split( core_rspace );
for ( i = 0, l = this.length; i < l; i++ ) {
elem = this[ i ];
-
if ( elem.nodeType === 1 && elem.className ) {
- if ( value ) {
- className = (" " + elem.className + " ").replace( rclass, " " );
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- className = className.replace(" " + classNames[ c ] + " ", " ");
- }
- elem.className = jQuery.trim( className );
- } else {
- elem.className = "";
+ className = (" " + elem.className + " ").replace( rclass, " " );
+
+ // loop over each item in the removal list
+ for ( c = 0, cl = removes.length; c < cl; c++ ) {
+ // Remove until there is nothing to remove,
+ while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) {
+ className = className.replace( " " + removes[ c ] + " " , " " );
+ }
}
+ elem.className = value ? jQuery.trim( className ) : "";
}
}
}
i = 0,
self = jQuery( this ),
state = stateVal,
- classNames = value.split( rspace );
+ classNames = value.split( core_rspace );
while ( (className = classNames[ i++ ]) ) {
- // check each className given, space seperated list
+ // check each className given, space separated list
state = isBool ? state : !self.hasClass( className );
self[ state ? "addClass" : "removeClass" ]( className );
}
isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
- var self = jQuery(this), val;
+ var val,
+ self = jQuery(this);
if ( this.nodeType !== 1 ) {
return;
}
},
- attrFn: {
- val: true,
- css: true,
- html: true,
- text: true,
- data: true,
- width: true,
- height: true,
- offset: true
- },
+ // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
+ attrFn: {},
attr: function( elem, name, value, pass ) {
var ret, hooks, notxml,
return;
}
- if ( pass && name in jQuery.attrFn ) {
+ if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
return jQuery( elem )[ name ]( value );
}
},
removeAttr: function( elem, value ) {
- var propName, attrNames, name, l, isBool,
+ var propName, attrNames, name, isBool,
i = 0;
if ( value && elem.nodeType === 1 ) {
- attrNames = value.toLowerCase().split( rspace );
- l = attrNames.length;
- for ( ; i < l; i++ ) {
+ attrNames = value.split( core_rspace );
+
+ for ( ; i < attrNames.length; i++ ) {
name = attrNames[ i ];
if ( name ) {
}
});
-// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
-jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
-
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
get: function( elem, name ) {
var ret;
ret = elem.getAttributeNode( name );
- return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
- ret.nodeValue :
+ return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
+ ret.value :
undefined;
},
set: function( elem, value, name ) {
ret = document.createAttribute( name );
elem.setAttributeNode( ret );
}
- return ( ret.nodeValue = value + "" );
+ return ( ret.value = value + "" );
}
};
- // Apply the nodeHook to tabindex
- jQuery.attrHooks.tabindex.set = nodeHook.set;
-
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
}
});
});
-
-
-
-
var rformElems = /^(?:textarea|input|select)$/i,
- rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
- rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
+ rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
- rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
- quickParse = function( selector ) {
- var quick = rquickIs.exec( selector );
- if ( quick ) {
- // 0 1 2 3
- // [ _, tag, id, class ]
- quick[1] = ( quick[1] || "" ).toLowerCase();
- quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
- }
- return quick;
- },
- quickIs = function( elem, m ) {
- var attrs = elem.attributes || {};
- return (
- (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
- (!m[2] || (attrs.id || {}).value === m[2]) &&
- (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
- );
- },
hoverHack = function( events ) {
return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
};
var elemData, eventHandle, events,
t, tns, type, namespaces, handleObj,
- handleObjIn, quick, handlers, special;
+ handleObjIn, handlers, special;
// Don't attach events to noData or text/comment nodes (allow plain objects tho)
if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
handler: handler,
guid: handler.guid,
selector: selector,
- quick: selector && quickParse( selector ),
namespace: namespaces.join(".")
}, handleObjIn );
// Detach an event or set of events from an element
remove: function( elem, types, handler, selector, mappedTypes ) {
- var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
- t, tns, type, origType, namespaces, origCount,
- j, events, special, handle, eventType, handleObj;
+ var t, tns, type, origType, namespaces, origCount,
+ j, events, special, eventType, handleObj,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
if ( !elemData || !(events = elemData.events) ) {
return;
type = ( selector? special.delegateType : special.bindType ) || type;
eventType = events[ type ] || [];
origCount = eventType.length;
- namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
// Remove matching events
for ( j = 0; j < eventType.length; j++ ) {
// Remove generic event handler if we removed something and no more handlers exist
// (avoids potential for endless recursion during removal of special event handlers)
if ( eventType.length === 0 && origCount !== eventType.length ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
- handle = elemData.handle;
- if ( handle ) {
- handle.elem = null;
- }
+ delete elemData.handle;
// removeData also checks for emptiness and clears the expando if empty
// so use it instead of delete
- jQuery.removeData( elem, [ "events", "handle" ], true );
+ jQuery.removeData( elem, "events", true );
}
},
}
// Event object or event type
- var type = event.type || event,
- namespaces = [],
- cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+ var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
+ type = event.type || event,
+ namespaces = [];
// focus/blur morphs to focusin/out; ensure we're not firing them right now
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
event.isTrigger = true;
event.exclusive = exclusive;
event.namespace = namespaces.join( "." );
- event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
// Handle a global trigger
bubbleType = special.delegateType || type;
cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
- old = null;
- for ( ; cur; cur = cur.parentNode ) {
+ for ( old = elem; cur; cur = cur.parentNode ) {
eventPath.push([ cur, bubbleType ]);
old = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
- if ( old && old === elem.ownerDocument ) {
+ if ( old === (elem.ownerDocument || document) ) {
eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
}
}
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event || window.event );
- var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related,
+ handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
delegateCount = handlers.delegateCount,
- args = [].slice.call( arguments, 0 ),
+ args = [].slice.call( arguments ),
run_all = !event.exclusive && !event.namespace,
special = jQuery.event.special[ event.type ] || {},
- handlerQueue = [],
- i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+ handlerQueue = [];
// Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
// Pregenerate a single jQuery object for reuse with .is()
jqcur = jQuery(this);
- jqcur.context = this.ownerDocument || this;
+ jqcur.context = this;
for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
- // Don't process events on disabled elements (#6911, #8165)
- if ( cur.disabled !== true ) {
+ // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx)
+ if ( cur.disabled !== true || event.type !== "click" ) {
selMatch = {};
matches = [];
jqcur[0] = cur;
sel = handleObj.selector;
if ( selMatch[ sel ] === undefined ) {
- selMatch[ sel ] = (
- handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
- );
+ selMatch[ sel ] = jqcur.is( sel );
}
if ( selMatch[ sel ] ) {
matches.push( handleObj );
event.target = event.target.parentNode;
}
- // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
- if ( event.metaKey === undefined ) {
- event.metaKey = event.ctrlKey;
- }
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
+ event.metaKey = !!event.metaKey;
return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
},
}
} :
function( elem, type, handle ) {
+ var name = "on" + type;
+
if ( elem.detachEvent ) {
- elem.detachEvent( "on" + type, handle );
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8 –
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
+ if ( typeof elem[ name ] === "undefined" ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
}
};
bindType: fix,
handle: function( event ) {
- var target = this,
+ var ret,
+ target = this,
related = event.relatedTarget,
handleObj = event.handleObj,
- selector = handleObj.selector,
- ret;
+ selector = handleObj.selector;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
// Node name check avoids a VML-related crash in IE (#9807)
var elem = e.target,
form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
- if ( form && !form._submit_attached ) {
+ if ( form && !jQuery._data( form, "_submit_attached" ) ) {
jQuery.event.add( form, "submit._submit", function( event ) {
event._submit_bubble = true;
});
- form._submit_attached = true;
+ jQuery._data( form, "_submit_attached", true );
}
});
// return undefined since we don't need an event listener
},
-
+
postDispatch: function( event ) {
// If form was submitted by the user, bubble the event up the tree
if ( event._submit_bubble ) {
jQuery.event.add( this, "click._change", function( event ) {
if ( this._just_changed && !event.isTrigger ) {
this._just_changed = false;
- jQuery.event.simulate( "change", this, event, true );
}
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event, true );
});
}
return false;
jQuery.event.add( this, "beforeactivate._change", function( e ) {
var elem = e.target;
- if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
jQuery.event.add( elem, "change._change", function( event ) {
if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
jQuery.event.simulate( "change", this.parentNode, event, true );
}
});
- elem._change_attached = true;
+ jQuery._data( elem, "_change_attached", true );
}
});
},
return this.on( types, selector, data, fn, 1 );
},
off: function( types, selector, fn ) {
+ var handleObj, type;
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
- var handleObj = types.handleObj;
+ handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
handleObj.selector,
}
if ( typeof types === "object" ) {
// ( types-object [, selector] )
- for ( var type in types ) {
+ for ( type in types ) {
this.off( type, selector, types[ type ] );
}
return this;
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
- return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
},
trigger: function( type, data ) {
this.trigger( name );
};
- if ( jQuery.attrFn ) {
- jQuery.attrFn[ name ] = true;
- }
-
if ( rkeyEvent.test( name ) ) {
jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
}
jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
}
});
+/*!\r
+ * Sizzle CSS Selector Engine\r
+ * Copyright 2012 jQuery Foundation and other contributors\r
+ * Released under the MIT license\r
+ * http://sizzlejs.com/\r
+ */\r
+(function( window, undefined ) {\r
+\r
+var cachedruns,\r
+ dirruns,\r
+ sortOrder,\r
+ siblingCheck,\r
+ assertGetIdNotName,\r
+\r
+ document = window.document,\r
+ docElem = document.documentElement,\r
+\r
+ strundefined = "undefined",\r
+ hasDuplicate = false,\r
+ baseHasDuplicate = true,\r
+ done = 0,\r
+ slice = [].slice,\r
+ push = [].push,\r
+\r
+ expando = ( "sizcache" + Math.random() ).replace( ".", "" ),\r
+\r
+ // Regex\r
+\r
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace\r
+ whitespace = "[\\x20\\t\\r\\n\\f]",\r
+ // http://www.w3.org/TR/css3-syntax/#characters\r
+ characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",\r
+\r
+ // Loosely modeled on CSS identifier characters\r
+ // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)\r
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\r
+ identifier = characterEncoding.replace( "w", "w#" ),\r
+\r
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors\r
+ operators = "([*^$|!~]?=)",\r
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +\r
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",\r
+ pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",\r
+ pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",\r
+ combinators = whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*",\r
+ groups = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + attributes + "|" + pseudos.replace( 2, 7 ) + "|[^\\\\(),])+",\r
+\r
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\r
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),\r
+\r
+ rcombinators = new RegExp( "^" + combinators ),\r
+\r
+ // All simple (non-comma) selectors, excluding insignifant trailing whitespace\r
+ rgroups = new RegExp( groups + "?(?=" + whitespace + "*,|$)", "g" ),\r
+\r
+ // A selector, or everything after leading whitespace\r
+ // Optionally followed in either case by a ")" for terminating sub-selectors\r
+ rselector = new RegExp( "^(?:(?!,)(?:(?:^|,)" + whitespace + "*" + groups + ")*?|" + whitespace + "*(.*?))(\\)|$)" ),\r
+\r
+ // All combinators and selector components (attribute test, tag, pseudo, etc.), the latter appearing together when consecutive\r
+ rtokens = new RegExp( groups.slice( 19, -6 ) + "\\x20\\t\\r\\n\\f>+~])+|" + combinators, "g" ),\r
+\r
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors\r
+ rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,\r
+\r
+ rsibling = /[\x20\t\r\n\f]*[+~]/,\r
+ rendsWithNot = /:not\($/,\r
+\r
+ rheader = /h\d/i,\r
+ rinputs = /input|select|textarea|button/i,\r
+\r
+ rbackslash = /\\(?!\\)/g,\r
+\r
+ matchExpr = {\r
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),\r
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),\r
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),\r
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "[-", "[-\\*" ) + ")" ),\r
+ "ATTR": new RegExp( "^" + attributes ),\r
+ "PSEUDO": new RegExp( "^" + pseudos ),\r
+ "CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace +\r
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +\r
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),\r
+ "POS": new RegExp( pos, "ig" ),\r
+ // For use in libraries implementing .is()\r
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )\r
+ },\r
+\r
+ classCache = {},\r
+ cachedClasses = [],\r
+ compilerCache = {},\r
+ cachedSelectors = [],\r
+\r
+ // Mark a function for use in filtering\r
+ markFunction = function( fn ) {\r
+ fn.sizzleFilter = true;\r
+ return fn;\r
+ },\r
+\r
+ // Returns a function to use in pseudos for input types\r
+ createInputFunction = function( type ) {\r
+ return function( elem ) {\r
+ // Check the input's nodeName and type\r
+ return elem.nodeName.toLowerCase() === "input" && elem.type === type;\r
+ };\r
+ },\r
+\r
+ // Returns a function to use in pseudos for buttons\r
+ createButtonFunction = function( type ) {\r
+ return function( elem ) {\r
+ var name = elem.nodeName.toLowerCase();\r
+ return (name === "input" || name === "button") && elem.type === type;\r
+ };\r
+ },\r
+\r
+ // Used for testing something on an element\r
+ assert = function( fn ) {\r
+ var pass = false,\r
+ div = document.createElement("div");\r
+ try {\r
+ pass = fn( div );\r
+ } catch (e) {}\r
+ // release memory in IE\r
+ div = null;\r
+ return pass;\r
+ },\r
+\r
+ // Check if attributes should be retrieved by attribute nodes\r
+ assertAttributes = assert(function( div ) {\r
+ div.innerHTML = "<select></select>";\r
+ var type = typeof div.lastChild.getAttribute("multiple");\r
+ // IE8 returns a string for some attributes even when not present\r
+ return type !== "boolean" && type !== "string";\r
+ }),\r
+\r
+ // Check if getElementById returns elements by name\r
+ // Check if getElementsByName privileges form controls or returns elements by ID\r
+ assertUsableName = assert(function( div ) {\r
+ // Inject content\r
+ div.id = expando + 0;\r
+ div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";\r
+ docElem.insertBefore( div, docElem.firstChild );\r
+\r
+ // Test\r
+ var pass = document.getElementsByName &&\r
+ // buggy browsers will return fewer than the correct 2\r
+ document.getElementsByName( expando ).length ===\r
+ // buggy browsers will return more than the correct 0\r
+ 2 + document.getElementsByName( expando + 0 ).length;\r
+ assertGetIdNotName = !document.getElementById( expando );\r
+\r
+ // Cleanup\r
+ docElem.removeChild( div );\r
+\r
+ return pass;\r
+ }),\r
+\r
+ // Check if the browser returns only elements\r
+ // when doing getElementsByTagName("*")\r
+ assertTagNameNoComments = assert(function( div ) {\r
+ div.appendChild( document.createComment("") );\r
+ return div.getElementsByTagName("*").length === 0;\r
+ }),\r
+\r
+ // Check if getAttribute returns normalized href attributes\r
+ assertHrefNotNormalized = assert(function( div ) {\r
+ div.innerHTML = "<a href='#'></a>";\r
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&\r
+ div.firstChild.getAttribute("href") === "#";\r
+ }),\r
+\r
+ // Check if getElementsByClassName can be trusted\r
+ assertUsableClassName = assert(function( div ) {\r
+ // Opera can't find a second classname (in 9.6)\r
+ div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";\r
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
+ return false;\r
+ }\r
+\r
+ // Safari caches class attributes, doesn't catch changes (in 3.2)\r
+ div.lastChild.className = "e";\r
+ return div.getElementsByClassName("e").length !== 1;\r
+ });\r
+\r
+var Sizzle = function( selector, context, results, seed ) {\r
+ results = results || [];\r
+ context = context || document;\r
+ var match, elem, xml, m,\r
+ nodeType = context.nodeType;\r
+\r
+ if ( nodeType !== 1 && nodeType !== 9 ) {\r
+ return [];\r
+ }\r
+\r
+ if ( !selector || typeof selector !== "string" ) {\r
+ return results;\r
+ }\r
+\r
+ xml = isXML( context );\r
+\r
+ if ( !xml && !seed ) {\r
+ if ( (match = rquickExpr.exec( selector )) ) {\r
+ // Speed-up: Sizzle("#ID")\r
+ if ( (m = match[1]) ) {\r
+ if ( nodeType === 9 ) {\r
+ elem = context.getElementById( m );\r
+ // Check parentNode to catch when Blackberry 4.6 returns\r
+ // nodes that are no longer in the document #6963\r
+ if ( elem && elem.parentNode ) {\r
+ // Handle the case where IE, Opera, and Webkit return items\r
+ // by name instead of ID\r
+ if ( elem.id === m ) {\r
+ results.push( elem );\r
+ return results;\r
+ }\r
+ } else {\r
+ return results;\r
+ }\r
+ } else {\r
+ // Context is not a document\r
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&\r
+ contains( context, elem ) && elem.id === m ) {\r
+ results.push( elem );\r
+ return results;\r
+ }\r
+ }\r
+\r
+ // Speed-up: Sizzle("TAG")\r
+ } else if ( match[2] ) {\r
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );\r
+ return results;\r
+\r
+ // Speed-up: Sizzle(".CLASS")\r
+ } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {\r
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );\r
+ return results;\r
+ }\r
+ }\r
+ }\r
+\r
+ // All others\r
+ return select( selector, context, results, seed, xml );\r
+};\r
+\r
+var Expr = Sizzle.selectors = {\r
+\r
+ // Can be adjusted by the user\r
+ cacheLength: 50,\r
+\r
+ match: matchExpr,\r
+\r
+ order: [ "ID", "TAG" ],\r
+\r
+ attrHandle: {},\r
+\r
+ createPseudo: markFunction,\r
+\r
+ find: {\r
+ "ID": assertGetIdNotName ?\r
+ function( id, context, xml ) {\r
+ if ( typeof context.getElementById !== strundefined && !xml ) {\r
+ var m = context.getElementById( id );\r
+ // Check parentNode to catch when Blackberry 4.6 returns\r
+ // nodes that are no longer in the document #6963\r
+ return m && m.parentNode ? [m] : [];\r
+ }\r
+ } :\r
+ function( id, context, xml ) {\r
+ if ( typeof context.getElementById !== strundefined && !xml ) {\r
+ var m = context.getElementById( id );\r
+\r
+ return m ?\r
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?\r
+ [m] :\r
+ undefined :\r
+ [];\r
+ }\r
+ },\r
+\r
+ "TAG": assertTagNameNoComments ?\r
+ function( tag, context ) {\r
+ if ( typeof context.getElementsByTagName !== strundefined ) {\r
+ return context.getElementsByTagName( tag );\r
+ }\r
+ } :\r
+ function( tag, context ) {\r
+ var results = context.getElementsByTagName( tag );\r
+\r
+ // Filter out possible comments\r
+ if ( tag === "*" ) {\r
+ var elem,\r
+ tmp = [],\r
+ i = 0;\r
+\r
+ for ( ; (elem = results[i]); i++ ) {\r
+ if ( elem.nodeType === 1 ) {\r
+ tmp.push( elem );\r
+ }\r
+ }\r
+\r
+ return tmp;\r
+ }\r
+ return results;\r
+ }\r
+ },\r
+\r
+ relative: {\r
+ ">": { dir: "parentNode", first: true },\r
+ " ": { dir: "parentNode" },\r
+ "+": { dir: "previousSibling", first: true },\r
+ "~": { dir: "previousSibling" }\r
+ },\r
+\r
+ preFilter: {\r
+ "ATTR": function( match ) {\r
+ match[1] = match[1].replace( rbackslash, "" );\r
+\r
+ // Move the given value to match[3] whether quoted or unquoted\r
+ match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );\r
+\r
+ if ( match[2] === "~=" ) {\r
+ match[3] = " " + match[3] + " ";\r
+ }\r
+\r
+ return match.slice( 0, 4 );\r
+ },\r
+\r
+ "CHILD": function( match ) {\r
+ /* matches from matchExpr.CHILD\r
+ 1 type (only|nth|...)\r
+ 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)\r
+ 3 xn-component of xn+y argument ([+-]?\d*n|)\r
+ 4 sign of xn-component\r
+ 5 x of xn-component\r
+ 6 sign of y-component\r
+ 7 y of y-component\r
+ */\r
+ match[1] = match[1].toLowerCase();\r
+\r
+ if ( match[1] === "nth" ) {\r
+ // nth-child requires argument\r
+ if ( !match[2] ) {\r
+ Sizzle.error( match[0] );\r
+ }\r
+\r
+ // numeric x and y parameters for Expr.filter.CHILD\r
+ // remember that false/true cast respectively to 0/1\r
+ match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );\r
+ match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );\r
+\r
+ // other types prohibit arguments\r
+ } else if ( match[2] ) {\r
+ Sizzle.error( match[0] );\r
+ }\r
+\r
+ return match;\r
+ },\r
+\r
+ "PSEUDO": function( match ) {\r
+ var argument,\r
+ unquoted = match[4];\r
+\r
+ if ( matchExpr["CHILD"].test( match[0] ) ) {\r
+ return null;\r
+ }\r
+\r
+ // Relinquish our claim on characters in `unquoted` from a closing parenthesis on\r
+ if ( unquoted && (argument = rselector.exec( unquoted )) && argument.pop() ) {\r
+\r
+ match[0] = match[0].slice( 0, argument[0].length - unquoted.length - 1 );\r
+ unquoted = argument[0].slice( 0, -1 );\r
+ }\r
+\r
+ // Quoted or unquoted, we have the full argument\r
+ // Return only captures needed by the pseudo filter method (type and argument)\r
+ match.splice( 2, 3, unquoted || match[3] );\r
+ return match;\r
+ }\r
+ },\r
+\r
+ filter: {\r
+ "ID": assertGetIdNotName ?\r
+ function( id ) {\r
+ id = id.replace( rbackslash, "" );\r
+ return function( elem ) {\r
+ return elem.getAttribute("id") === id;\r
+ };\r
+ } :\r
+ function( id ) {\r
+ id = id.replace( rbackslash, "" );\r
+ return function( elem ) {\r
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");\r
+ return node && node.value === id;\r
+ };\r
+ },\r
+\r
+ "TAG": function( nodeName ) {\r
+ if ( nodeName === "*" ) {\r
+ return function() { return true; };\r
+ }\r
+ nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();\r
+\r
+ return function( elem ) {\r
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\r
+ };\r
+ },\r
+\r
+ "CLASS": function( className ) {\r
+ var pattern = classCache[ className ];\r
+ if ( !pattern ) {\r
+ pattern = classCache[ className ] = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" );\r
+ cachedClasses.push( className );\r
+ // Avoid too large of a cache\r
+ if ( cachedClasses.length > Expr.cacheLength ) {\r
+ delete classCache[ cachedClasses.shift() ];\r
+ }\r
+ }\r
+ return function( elem ) {\r
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );\r
+ };\r
+ },\r
+\r
+ "ATTR": function( name, operator, check ) {\r
+ if ( !operator ) {\r
+ return function( elem ) {\r
+ return Sizzle.attr( elem, name ) != null;\r
+ };\r
+ }\r
+\r
+ return function( elem ) {\r
+ var result = Sizzle.attr( elem, name ),\r
+ value = result + "";\r
+\r
+ if ( result == null ) {\r
+ return operator === "!=";\r
+ }\r
+\r
+ switch ( operator ) {\r
+ case "=":\r
+ return value === check;\r
+ case "!=":\r
+ return value !== check;\r
+ case "^=":\r
+ return check && value.indexOf( check ) === 0;\r
+ case "*=":\r
+ return check && value.indexOf( check ) > -1;\r
+ case "$=":\r
+ return check && value.substr( value.length - check.length ) === check;\r
+ case "~=":\r
+ return ( " " + value + " " ).indexOf( check ) > -1;\r
+ case "|=":\r
+ return value === check || value.substr( 0, check.length + 1 ) === check + "-";\r
+ }\r
+ };\r
+ },\r
+\r
+ "CHILD": function( type, argument, first, last ) {\r
+\r
+ if ( type === "nth" ) {\r
+ var doneName = done++;\r
+\r
+ return function( elem ) {\r
+ var parent, diff,\r
+ count = 0,\r
+ node = elem;\r
+\r
+ if ( first === 1 && last === 0 ) {\r
+ return true;\r
+ }\r
+\r
+ parent = elem.parentNode;\r
+\r
+ if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) {\r
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
+ if ( node.nodeType === 1 ) {\r
+ node.sizset = ++count;\r
+ if ( node === elem ) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ parent[ expando ] = doneName;\r
+ }\r
+\r
+ diff = elem.sizset - last;\r
+\r
+ if ( first === 0 ) {\r
+ return diff === 0;\r
+\r
+ } else {\r
+ return ( diff % first === 0 && diff / first >= 0 );\r
+ }\r
+ };\r
+ }\r
+\r
+ return function( elem ) {\r
+ var node = elem;\r
+\r
+ switch ( type ) {\r
+ case "only":\r
+ case "first":\r
+ while ( (node = node.previousSibling) ) {\r
+ if ( node.nodeType === 1 ) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ if ( type === "first" ) {\r
+ return true;\r
+ }\r
+\r
+ node = elem;\r
+\r
+ /* falls through */\r
+ case "last":\r
+ while ( (node = node.nextSibling) ) {\r
+ if ( node.nodeType === 1 ) {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+ };\r
+ },\r
+\r
+ "PSEUDO": function( pseudo, argument, context, xml ) {\r
+ // pseudo-class names are case-insensitive\r
+ // http://www.w3.org/TR/selectors/#pseudo-classes\r
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\r
+ var fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ];\r
+\r
+ if ( !fn ) {\r
+ Sizzle.error( "unsupported pseudo: " + pseudo );\r
+ }\r
+\r
+ // The user may set fn.sizzleFilter to indicate\r
+ // that arguments are needed to create the filter function\r
+ // just as Sizzle does\r
+ if ( !fn.sizzleFilter ) {\r
+ return fn;\r
+ }\r
+\r
+ return fn( argument, context, xml );\r
+ }\r
+ },\r
+\r
+ pseudos: {\r
+ "not": markFunction(function( selector, context, xml ) {\r
+ // Trim the selector passed to compile\r
+ // to avoid treating leading and trailing\r
+ // spaces as combinators\r
+ var matcher = compile( selector.replace( rtrim, "$1" ), context, xml );\r
+ return function( elem ) {\r
+ return !matcher( elem );\r
+ };\r
+ }),\r
+\r
+ "enabled": function( elem ) {\r
+ return elem.disabled === false;\r
+ },\r
+\r
+ "disabled": function( elem ) {\r
+ return elem.disabled === true;\r
+ },\r
+\r
+ "checked": function( elem ) {\r
+ // In CSS3, :checked should return both checked and selected elements\r
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\r
+ var nodeName = elem.nodeName.toLowerCase();\r
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);\r
+ },\r
+\r
+ "selected": function( elem ) {\r
+ // Accessing this property makes selected-by-default\r
+ // options in Safari work properly\r
+ if ( elem.parentNode ) {\r
+ elem.parentNode.selectedIndex;\r
+ }\r
+\r
+ return elem.selected === true;\r
+ },\r
+\r
+ "parent": function( elem ) {\r
+ return !Expr.pseudos["empty"]( elem );\r
+ },\r
+\r
+ "empty": function( elem ) {\r
+ // http://www.w3.org/TR/selectors/#empty-pseudo\r
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),\r
+ // not comment, processing instructions, or others\r
+ // Thanks to Diego Perini for the nodeName shortcut\r
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")\r
+ var nodeType;\r
+ elem = elem.firstChild;\r
+ while ( elem ) {\r
+ if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {\r
+ return false;\r
+ }\r
+ elem = elem.nextSibling;\r
+ }\r
+ return true;\r
+ },\r
+\r
+ "contains": markFunction(function( text ) {\r
+ return function( elem ) {\r
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;\r
+ };\r
+ }),\r
+\r
+ "has": markFunction(function( selector ) {\r
+ return function( elem ) {\r
+ return Sizzle( selector, elem ).length > 0;\r
+ };\r
+ }),\r
+\r
+ "header": function( elem ) {\r
+ return rheader.test( elem.nodeName );\r
+ },\r
+\r
+ "text": function( elem ) {\r
+ var type, attr;\r
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)\r
+ // use getAttribute instead to test this case\r
+ return elem.nodeName.toLowerCase() === "input" &&\r
+ (type = elem.type) === "text" &&\r
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );\r
+ },\r
+\r
+ // Input types\r
+ "radio": createInputFunction("radio"),\r
+ "checkbox": createInputFunction("checkbox"),\r
+ "file": createInputFunction("file"),\r
+ "password": createInputFunction("password"),\r
+ "image": createInputFunction("image"),\r
+\r
+ "submit": createButtonFunction("submit"),\r
+ "reset": createButtonFunction("reset"),\r
+\r
+ "button": function( elem ) {\r
+ var name = elem.nodeName.toLowerCase();\r
+ return name === "input" && elem.type === "button" || name === "button";\r
+ },\r
+\r
+ "input": function( elem ) {\r
+ return rinputs.test( elem.nodeName );\r
+ },\r
+\r
+ "focus": function( elem ) {\r
+ var doc = elem.ownerDocument;\r
+ return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href);\r
+ },\r
+\r
+ "active": function( elem ) {\r
+ return elem === elem.ownerDocument.activeElement;\r
+ }\r
+ },\r
+\r
+ setFilters: {\r
+ "first": function( elements, argument, not ) {\r
+ return not ? elements.slice( 1 ) : [ elements[0] ];\r
+ },\r
+\r
+ "last": function( elements, argument, not ) {\r
+ var elem = elements.pop();\r
+ return not ? elements : [ elem ];\r
+ },\r
+\r
+ "even": function( elements, argument, not ) {\r
+ var results = [],\r
+ i = not ? 1 : 0,\r
+ len = elements.length;\r
+ for ( ; i < len; i = i + 2 ) {\r
+ results.push( elements[i] );\r
+ }\r
+ return results;\r
+ },\r
+\r
+ "odd": function( elements, argument, not ) {\r
+ var results = [],\r
+ i = not ? 0 : 1,\r
+ len = elements.length;\r
+ for ( ; i < len; i = i + 2 ) {\r
+ results.push( elements[i] );\r
+ }\r
+ return results;\r
+ },\r
+\r
+ "lt": function( elements, argument, not ) {\r
+ return not ? elements.slice( +argument ) : elements.slice( 0, +argument );\r
+ },\r
+\r
+ "gt": function( elements, argument, not ) {\r
+ return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 );\r
+ },\r
+\r
+ "eq": function( elements, argument, not ) {\r
+ var elem = elements.splice( +argument, 1 );\r
+ return not ? elements : elem;\r
+ }\r
+ }\r
+};\r
+\r
+// Deprecated\r
+Expr.setFilters["nth"] = Expr.setFilters["eq"];\r
+\r
+// Back-compat\r
+Expr.filters = Expr.pseudos;\r
+\r
+// IE6/7 return a modified href\r
+if ( !assertHrefNotNormalized ) {\r
+ Expr.attrHandle = {\r
+ "href": function( elem ) {\r
+ return elem.getAttribute( "href", 2 );\r
+ },\r
+ "type": function( elem ) {\r
+ return elem.getAttribute("type");\r
+ }\r
+ };\r
+}\r
+\r
+// Add getElementsByName if usable\r
+if ( assertUsableName ) {\r
+ Expr.order.push("NAME");\r
+ Expr.find["NAME"] = function( name, context ) {\r
+ if ( typeof context.getElementsByName !== strundefined ) {\r
+ return context.getElementsByName( name );\r
+ }\r
+ };\r
+}\r
+\r
+// Add getElementsByClassName if usable\r
+if ( assertUsableClassName ) {\r
+ Expr.order.splice( 1, 0, "CLASS" );\r
+ Expr.find["CLASS"] = function( className, context, xml ) {\r
+ if ( typeof context.getElementsByClassName !== strundefined && !xml ) {\r
+ return context.getElementsByClassName( className );\r
+ }\r
+ };\r
+}\r
+\r
+// If slice is not available, provide a backup\r
+try {\r
+ slice.call( docElem.childNodes, 0 )[0].nodeType;\r
+} catch ( e ) {\r
+ slice = function( i ) {\r
+ var elem, results = [];\r
+ for ( ; (elem = this[i]); i++ ) {\r
+ results.push( elem );\r
+ }\r
+ return results;\r
+ };\r
+}\r
+\r
+var isXML = Sizzle.isXML = function( elem ) {\r
+ // documentElement is verified for cases where it doesn't yet exist\r
+ // (such as loading iframes in IE - #4833)\r
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;\r
+ return documentElement ? documentElement.nodeName !== "HTML" : false;\r
+};\r
+\r
+// Element contains another\r
+var contains = Sizzle.contains = docElem.compareDocumentPosition ?\r
+ function( a, b ) {\r
+ return !!( a.compareDocumentPosition( b ) & 16 );\r
+ } :\r
+ docElem.contains ?\r
+ function( a, b ) {\r
+ var adown = a.nodeType === 9 ? a.documentElement : a,\r
+ bup = b.parentNode;\r
+ return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );\r
+ } :\r
+ function( a, b ) {\r
+ while ( (b = b.parentNode) ) {\r
+ if ( b === a ) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ };\r
+\r
+/**\r
+ * Utility function for retrieving the text value of an array of DOM nodes\r
+ * @param {Array|Element} elem\r
+ */\r
+var getText = Sizzle.getText = function( elem ) {\r
+ var node,\r
+ ret = "",\r
+ i = 0,\r
+ nodeType = elem.nodeType;\r
+\r
+ if ( nodeType ) {\r
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\r
+ // Use textContent for elements\r
+ // innerText usage removed for consistency of new lines (see #11153)\r
+ if ( typeof elem.textContent === "string" ) {\r
+ return elem.textContent;\r
+ } else {\r
+ // Traverse its children\r
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\r
+ ret += getText( elem );\r
+ }\r
+ }\r
+ } else if ( nodeType === 3 || nodeType === 4 ) {\r
+ return elem.nodeValue;\r
+ }\r
+ // Do not include comment or processing instruction nodes\r
+ } else {\r
+\r
+ // If no nodeType, this is expected to be an array\r
+ for ( ; (node = elem[i]); i++ ) {\r
+ // Do not traverse comment nodes\r
+ ret += getText( node );\r
+ }\r
+ }\r
+ return ret;\r
+};\r
+\r
+Sizzle.attr = function( elem, name ) {\r
+ var attr,\r
+ xml = isXML( elem );\r
+\r
+ if ( !xml ) {\r
+ name = name.toLowerCase();\r
+ }\r
+ if ( Expr.attrHandle[ name ] ) {\r
+ return Expr.attrHandle[ name ]( elem );\r
+ }\r
+ if ( assertAttributes || xml ) {\r
+ return elem.getAttribute( name );\r
+ }\r
+ attr = elem.getAttributeNode( name );\r
+ return attr ?\r
+ typeof elem[ name ] === "boolean" ?\r
+ elem[ name ] ? name : null :\r
+ attr.specified ? attr.value : null :\r
+ null;\r
+};\r
+\r
+Sizzle.error = function( msg ) {\r
+ throw new Error( "Syntax error, unrecognized expression: " + msg );\r
+};\r
+\r
+// Check if the JavaScript engine is using some sort of\r
+// optimization where it does not always call our comparision\r
+// function. If that is the case, discard the hasDuplicate value.\r
+// Thus far that includes Google Chrome.\r
+[0, 0].sort(function() {\r
+ return (baseHasDuplicate = 0);\r
+});\r
+\r
+\r
+if ( docElem.compareDocumentPosition ) {\r
+ sortOrder = function( a, b ) {\r
+ if ( a === b ) {\r
+ hasDuplicate = true;\r
+ return 0;\r
+ }\r
+\r
+ return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?\r
+ a.compareDocumentPosition :\r
+ a.compareDocumentPosition(b) & 4\r
+ ) ? -1 : 1;\r
+ };\r
+\r
+} else {\r
+ sortOrder = function( a, b ) {\r
+ // The nodes are identical, we can exit early\r
+ if ( a === b ) {\r
+ hasDuplicate = true;\r
+ return 0;\r
+\r
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes\r
+ } else if ( a.sourceIndex && b.sourceIndex ) {\r
+ return a.sourceIndex - b.sourceIndex;\r
+ }\r
+\r
+ var al, bl,\r
+ ap = [],\r
+ bp = [],\r
+ aup = a.parentNode,\r
+ bup = b.parentNode,\r
+ cur = aup;\r
+\r
+ // If the nodes are siblings (or identical) we can do a quick check\r
+ if ( aup === bup ) {\r
+ return siblingCheck( a, b );\r
+\r
+ // If no parents were found then the nodes are disconnected\r
+ } else if ( !aup ) {\r
+ return -1;\r
+\r
+ } else if ( !bup ) {\r
+ return 1;\r
+ }\r
+\r
+ // Otherwise they're somewhere else in the tree so we need\r
+ // to build up a full list of the parentNodes for comparison\r
+ while ( cur ) {\r
+ ap.unshift( cur );\r
+ cur = cur.parentNode;\r
+ }\r
+\r
+ cur = bup;\r
+\r
+ while ( cur ) {\r
+ bp.unshift( cur );\r
+ cur = cur.parentNode;\r
+ }\r
+\r
+ al = ap.length;\r
+ bl = bp.length;\r
+\r
+ // Start walking down the tree looking for a discrepancy\r
+ for ( var i = 0; i < al && i < bl; i++ ) {\r
+ if ( ap[i] !== bp[i] ) {\r
+ return siblingCheck( ap[i], bp[i] );\r
+ }\r
+ }\r
+\r
+ // We ended someplace up the tree so do a sibling check\r
+ return i === al ?\r
+ siblingCheck( a, bp[i], -1 ) :\r
+ siblingCheck( ap[i], b, 1 );\r
+ };\r
+\r
+ siblingCheck = function( a, b, ret ) {\r
+ if ( a === b ) {\r
+ return ret;\r
+ }\r
+\r
+ var cur = a.nextSibling;\r
+\r
+ while ( cur ) {\r
+ if ( cur === b ) {\r
+ return -1;\r
+ }\r
+\r
+ cur = cur.nextSibling;\r
+ }\r
+\r
+ return 1;\r
+ };\r
+}\r
+\r
+// Document sorting and removing duplicates\r
+Sizzle.uniqueSort = function( results ) {\r
+ var elem,\r
+ i = 1;\r
+\r
+ if ( sortOrder ) {\r
+ hasDuplicate = baseHasDuplicate;\r
+ results.sort( sortOrder );\r
+\r
+ if ( hasDuplicate ) {\r
+ for ( ; (elem = results[i]); i++ ) {\r
+ if ( elem === results[ i - 1 ] ) {\r
+ results.splice( i--, 1 );\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return results;\r
+};\r
+\r
+function multipleContexts( selector, contexts, results, seed ) {\r
+ var i = 0,\r
+ len = contexts.length;\r
+ for ( ; i < len; i++ ) {\r
+ Sizzle( selector, contexts[i], results, seed );\r
+ }\r
+}\r
+\r
+function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) {\r
+ var results,\r
+ fn = Expr.setFilters[ posfilter.toLowerCase() ];\r
+\r
+ if ( !fn ) {\r
+ Sizzle.error( posfilter );\r
+ }\r
+\r
+ if ( selector || !(results = seed) ) {\r
+ multipleContexts( selector || "*", contexts, (results = []), seed );\r
+ }\r
+\r
+ return results.length > 0 ? fn( results, argument, not ) : [];\r
+}\r
+\r
+function handlePOS( selector, context, results, seed, groups ) {\r
+ var match, not, anchor, ret, elements, currentContexts, part, lastIndex,\r
+ i = 0,\r
+ len = groups.length,\r
+ rpos = matchExpr["POS"],\r
+ // This is generated here in case matchExpr["POS"] is extended\r
+ rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ),\r
+ // This is for making sure non-participating\r
+ // matching groups are represented cross-browser (IE6-8)\r
+ setUndefined = function() {\r
+ var i = 1,\r
+ len = arguments.length - 2;\r
+ for ( ; i < len; i++ ) {\r
+ if ( arguments[i] === undefined ) {\r
+ match[i] = undefined;\r
+ }\r
+ }\r
+ };\r
+\r
+ for ( ; i < len; i++ ) {\r
+ // Reset regex index to 0\r
+ rpos.exec("");\r
+ selector = groups[i];\r
+ ret = [];\r
+ anchor = 0;\r
+ elements = seed;\r
+ while ( (match = rpos.exec( selector )) ) {\r
+ lastIndex = rpos.lastIndex = match.index + match[0].length;\r
+ if ( lastIndex > anchor ) {\r
+ part = selector.slice( anchor, match.index );\r
+ anchor = lastIndex;\r
+ currentContexts = [ context ];\r
+\r
+ if ( rcombinators.test(part) ) {\r
+ if ( elements ) {\r
+ currentContexts = elements;\r
+ }\r
+ elements = seed;\r
+ }\r
+\r
+ if ( (not = rendsWithNot.test( part )) ) {\r
+ part = part.slice( 0, -5 ).replace( rcombinators, "$&*" );\r
+ }\r
+\r
+ if ( match.length > 1 ) {\r
+ match[0].replace( rposgroups, setUndefined );\r
+ }\r
+ elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not );\r
+ }\r
+ }\r
+\r
+ if ( elements ) {\r
+ ret = ret.concat( elements );\r
+\r
+ if ( (part = selector.slice( anchor )) && part !== ")" ) {\r
+ if ( rcombinators.test(part) ) {\r
+ multipleContexts( part, ret, results, seed );\r
+ } else {\r
+ Sizzle( part, context, results, seed ? seed.concat(elements) : elements );\r
+ }\r
+ } else {\r
+ push.apply( results, ret );\r
+ }\r
+ } else {\r
+ Sizzle( selector, context, results, seed );\r
+ }\r
+ }\r
+\r
+ // Do not sort if this is a single filter\r
+ return len === 1 ? results : Sizzle.uniqueSort( results );\r
+}\r
+\r
+function tokenize( selector, context, xml ) {\r
+ var tokens, soFar, type,\r
+ groups = [],\r
+ i = 0,\r
+\r
+ // Catch obvious selector issues: terminal ")"; nonempty fallback match\r
+ // rselector never fails to match *something*\r
+ match = rselector.exec( selector ),\r
+ matched = !match.pop() && !match.pop(),\r
+ selectorGroups = matched && selector.match( rgroups ) || [""],\r
+\r
+ preFilters = Expr.preFilter,\r
+ filters = Expr.filter,\r
+ checkContext = !xml && context !== document;\r
+\r
+ for ( ; (soFar = selectorGroups[i]) != null && matched; i++ ) {\r
+ groups.push( tokens = [] );\r
+\r
+ // Need to make sure we're within a narrower context if necessary\r
+ // Adding a descendant combinator will generate what is needed\r
+ if ( checkContext ) {\r
+ soFar = " " + soFar;\r
+ }\r
+\r
+ while ( soFar ) {\r
+ matched = false;\r
+\r
+ // Combinators\r
+ if ( (match = rcombinators.exec( soFar )) ) {\r
+ soFar = soFar.slice( match[0].length );\r
+\r
+ // Cast descendant combinators to space\r
+ matched = tokens.push({ part: match.pop().replace( rtrim, " " ), captures: match });\r
+ }\r
+\r
+ // Filters\r
+ for ( type in filters ) {\r
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||\r
+ (match = preFilters[ type ]( match, context, xml )) ) ) {\r
+\r
+ soFar = soFar.slice( match.shift().length );\r
+ matched = tokens.push({ part: type, captures: match });\r
+ }\r
+ }\r
+\r
+ if ( !matched ) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if ( !matched ) {\r
+ Sizzle.error( selector );\r
+ }\r
+\r
+ return groups;\r
+}\r
+\r
+function addCombinator( matcher, combinator, context ) {\r
+ var dir = combinator.dir,\r
+ doneName = done++;\r
+\r
+ if ( !matcher ) {\r
+ // If there is no matcher to check, check against the context\r
+ matcher = function( elem ) {\r
+ return elem === context;\r
+ };\r
+ }\r
+ return combinator.first ?\r
+ function( elem, context ) {\r
+ while ( (elem = elem[ dir ]) ) {\r
+ if ( elem.nodeType === 1 ) {\r
+ return matcher( elem, context ) && elem;\r
+ }\r
+ }\r
+ } :\r
+ function( elem, context ) {\r
+ var cache,\r
+ dirkey = doneName + "." + dirruns,\r
+ cachedkey = dirkey + "." + cachedruns;\r
+ while ( (elem = elem[ dir ]) ) {\r
+ if ( elem.nodeType === 1 ) {\r
+ if ( (cache = elem[ expando ]) === cachedkey ) {\r
+ return elem.sizset;\r
+ } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {\r
+ if ( elem.sizset ) {\r
+ return elem;\r
+ }\r
+ } else {\r
+ elem[ expando ] = cachedkey;\r
+ if ( matcher( elem, context ) ) {\r
+ elem.sizset = true;\r
+ return elem;\r
+ }\r
+ elem.sizset = false;\r
+ }\r
+ }\r
+ }\r
+ };\r
+}\r
+\r
+function addMatcher( higher, deeper ) {\r
+ return higher ?\r
+ function( elem, context ) {\r
+ var result = deeper( elem, context );\r
+ return result && higher( result === true ? elem : result, context );\r
+ } :\r
+ deeper;\r
+}\r
+\r
+// ["TAG", ">", "ID", " ", "CLASS"]\r
+function matcherFromTokens( tokens, context, xml ) {\r
+ var token, matcher,\r
+ i = 0;\r
+\r
+ for ( ; (token = tokens[i]); i++ ) {\r
+ if ( Expr.relative[ token.part ] ) {\r
+ matcher = addCombinator( matcher, Expr.relative[ token.part ], context );\r
+ } else {\r
+ token.captures.push( context, xml );\r
+ matcher = addMatcher( matcher, Expr.filter[ token.part ].apply( null, token.captures ) );\r
+ }\r
+ }\r
+\r
+ return matcher;\r
+}\r
+\r
+function matcherFromGroupMatchers( matchers ) {\r
+ return function( elem, context ) {\r
+ var matcher,\r
+ j = 0;\r
+ for ( ; (matcher = matchers[j]); j++ ) {\r
+ if ( matcher(elem, context) ) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ };\r
+}\r
+\r
+var compile = Sizzle.compile = function( selector, context, xml ) {\r
+ var tokens, group, i,\r
+ cached = compilerCache[ selector ];\r
+\r
+ // Return a cached group function if already generated (context dependent)\r
+ if ( cached && cached.context === context ) {\r
+ return cached;\r
+ }\r
+\r
+ // Generate a function of recursive functions that can be used to check each element\r
+ group = tokenize( selector, context, xml );\r
+ for ( i = 0; (tokens = group[i]); i++ ) {\r
+ group[i] = matcherFromTokens( tokens, context, xml );\r
+ }\r
+\r
+ // Cache the compiled function\r
+ cached = compilerCache[ selector ] = matcherFromGroupMatchers( group );\r
+ cached.context = context;\r
+ cached.runs = cached.dirruns = 0;\r
+ cachedSelectors.push( selector );\r
+ // Ensure only the most recent are cached\r
+ if ( cachedSelectors.length > Expr.cacheLength ) {\r
+ delete compilerCache[ cachedSelectors.shift() ];\r
+ }\r
+ return cached;\r
+};\r
+\r
+Sizzle.matches = function( expr, elements ) {\r
+ return Sizzle( expr, null, null, elements );\r
+};\r
+\r
+Sizzle.matchesSelector = function( elem, expr ) {\r
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;\r
+};\r
+\r
+var select = function( selector, context, results, seed, xml ) {\r
+ // Remove excessive whitespace\r
+ selector = selector.replace( rtrim, "$1" );\r
+ var elements, matcher, i, len, elem, token,\r
+ type, findContext, notTokens,\r
+ match = selector.match( rgroups ),\r
+ tokens = selector.match( rtokens ),\r
+ contextNodeType = context.nodeType;\r
+\r
+ // POS handling\r
+ if ( matchExpr["POS"].test(selector) ) {\r
+ return handlePOS( selector, context, results, seed, match );\r
+ }\r
+\r
+ if ( seed ) {\r
+ elements = slice.call( seed, 0 );\r
+\r
+ // To maintain document order, only narrow the\r
+ // set if there is one group\r
+ } else if ( match && match.length === 1 ) {\r
+\r
+ // Take a shortcut and set the context if the root selector is an ID\r
+ if ( tokens.length > 1 && contextNodeType === 9 && !xml &&\r
+ (match = matchExpr["ID"].exec( tokens[0] )) ) {\r
+\r
+ context = Expr.find["ID"]( match[1], context, xml )[0];\r
+ if ( !context ) {\r
+ return results;\r
+ }\r
+\r
+ selector = selector.slice( tokens.shift().length );\r
+ }\r
+\r
+ findContext = ( (match = rsibling.exec( tokens[0] )) && !match.index && context.parentNode ) || context;\r
+\r
+ // Get the last token, excluding :not\r
+ notTokens = tokens.pop();\r
+ token = notTokens.split(":not")[0];\r
+\r
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {\r
+ type = Expr.order[i];\r
+\r
+ if ( (match = matchExpr[ type ].exec( token )) ) {\r
+ elements = Expr.find[ type ]( (match[1] || "").replace( rbackslash, "" ), findContext, xml );\r
+\r
+ if ( elements == null ) {\r
+ continue;\r
+ }\r
+\r
+ if ( token === notTokens ) {\r
+ selector = selector.slice( 0, selector.length - notTokens.length ) +\r
+ token.replace( matchExpr[ type ], "" );\r
+\r
+ if ( !selector ) {\r
+ push.apply( results, slice.call(elements, 0) );\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Only loop over the given elements once\r
+ // If selector is empty, we're already done\r
+ if ( selector ) {\r
+ matcher = compile( selector, context, xml );\r
+ dirruns = matcher.dirruns++;\r
+\r
+ if ( elements == null ) {\r
+ elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context );\r
+ }\r
+ for ( i = 0; (elem = elements[i]); i++ ) {\r
+ cachedruns = matcher.runs++;\r
+ if ( matcher(elem, context) ) {\r
+ results.push( elem );\r
+ }\r
+ }\r
+ }\r
+\r
+ return results;\r
+};\r
+\r
+if ( document.querySelectorAll ) {\r
+ (function() {\r
+ var disconnectedMatch,\r
+ oldSelect = select,\r
+ rescape = /'|\\/g,\r
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,\r
+ rbuggyQSA = [],\r
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)\r
+ // A support test would require too much code (would include document ready)\r
+ // just skip matchesSelector for :active\r
+ rbuggyMatches = [":active"],\r
+ matches = docElem.matchesSelector ||\r
+ docElem.mozMatchesSelector ||\r
+ docElem.webkitMatchesSelector ||\r
+ docElem.oMatchesSelector ||\r
+ docElem.msMatchesSelector;\r
+\r
+ // Build QSA regex\r
+ // Regex strategy adopted from Diego Perini\r
+ assert(function( div ) {\r
+ div.innerHTML = "<select><option selected></option></select>";\r
+\r
+ // IE8 - Some boolean attributes are not treated correctly\r
+ if ( !div.querySelectorAll("[selected]").length ) {\r
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );\r
+ }\r
+\r
+ // Webkit/Opera - :checked should return selected option elements\r
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\r
+ // IE8 throws error here (do not put tests after this one)\r
+ if ( !div.querySelectorAll(":checked").length ) {\r
+ rbuggyQSA.push(":checked");\r
+ }\r
+ });\r
+\r
+ assert(function( div ) {\r
+\r
+ // Opera 10-12/IE9 - ^= $= *= and empty values\r
+ // Should not select anything\r
+ div.innerHTML = "<p test=''></p>";\r
+ if ( div.querySelectorAll("[test^='']").length ) {\r
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );\r
+ }\r
+\r
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\r
+ // IE8 throws error here (do not put tests after this one)\r
+ div.innerHTML = "<input type='hidden'>";\r
+ if ( !div.querySelectorAll(":enabled").length ) {\r
+ rbuggyQSA.push(":enabled", ":disabled");\r
+ }\r
+ });\r
+\r
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );\r
+\r
+ select = function( selector, context, results, seed, xml ) {\r
+ // Only use querySelectorAll when not filtering,\r
+ // when this is not xml,\r
+ // and when no QSA bugs apply\r
+ if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {\r
+ if ( context.nodeType === 9 ) {\r
+ try {\r
+ push.apply( results, slice.call(context.querySelectorAll( selector ), 0) );\r
+ return results;\r
+ } catch(qsaError) {}\r
+ // qSA works strangely on Element-rooted queries\r
+ // We can work around this by specifying an extra ID on the root\r
+ // and working up from there (Thanks to Andrew Dupont for the technique)\r
+ // IE 8 doesn't work on object elements\r
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {\r
+ var old = context.getAttribute("id"),\r
+ nid = old || expando,\r
+ newContext = rsibling.test( selector ) && context.parentNode || context;\r
+\r
+ if ( old ) {\r
+ nid = nid.replace( rescape, "\\$&" );\r
+ } else {\r
+ context.setAttribute( "id", nid );\r
+ }\r
+\r
+ try {\r
+ push.apply( results, slice.call( newContext.querySelectorAll(\r
+ selector.replace( rgroups, "[id='" + nid + "'] $&" )\r
+ ), 0 ) );\r
+ return results;\r
+ } catch(qsaError) {\r
+ } finally {\r
+ if ( !old ) {\r
+ context.removeAttribute("id");\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return oldSelect( selector, context, results, seed, xml );\r
+ };\r
+\r
+ if ( matches ) {\r
+ assert(function( div ) {\r
+ // Check to see if it's possible to do matchesSelector\r
+ // on a disconnected node (IE 9)\r
+ disconnectedMatch = matches.call( div, "div" );\r
+\r
+ // This should fail with an exception\r
+ // Gecko does not error, returns false instead\r
+ try {\r
+ matches.call( div, "[test!='']:sizzle" );\r
+ rbuggyMatches.push( Expr.match.PSEUDO );\r
+ } catch ( e ) {}\r
+ });\r
+\r
+ // rbuggyMatches always contains :active, so no need for a length check\r
+ rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );\r
+\r
+ Sizzle.matchesSelector = function( elem, expr ) {\r
+ // Make sure that attribute selectors are quoted\r
+ expr = expr.replace( rattributeQuotes, "='$1']" );\r
+\r
+ // rbuggyMatches always contains :active, so no need for an existence check\r
+ if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) {\r
+ try {\r
+ var ret = matches.call( elem, expr );\r
+\r
+ // IE 9's matchesSelector returns false on disconnected nodes\r
+ if ( ret || disconnectedMatch ||\r
+ // As well, disconnected nodes are said to be in a document\r
+ // fragment in IE 9\r
+ elem.document && elem.document.nodeType !== 11 ) {\r
+ return ret;\r
+ }\r
+ } catch(e) {}\r
+ }\r
+\r
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;\r
+ };\r
+ }\r
+ })();\r
+}\r
+\r
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+\r
+\r
+})( window );\r
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ rneedsContext = jQuery.expr.match.needsContext,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i, l, length, n, r, ret,
+ self = this;
-
-/*!
- * Sizzle CSS Selector Engine
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- * More information: http://sizzlejs.com/
- */
-(function(){
-
-var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
- expando = "sizcache" + (Math.random() + '').replace('.', ''),
- done = 0,
- toString = Object.prototype.toString,
- hasDuplicate = false,
- baseHasDuplicate = true,
- rBackslash = /\\/g,
- rReturn = /\r\n/g,
- rNonWord = /\W/;
-
-// Here we check if the JavaScript engine is using some sort of
-// optimization where it does not always call our comparision
-// function. If that is the case, discard the hasDuplicate value.
-// Thus far that includes Google Chrome.
-[0, 0].sort(function() {
- baseHasDuplicate = false;
- return 0;
-});
-
-var Sizzle = function( selector, context, results, seed ) {
- results = results || [];
- context = context || document;
-
- var origContext = context;
-
- if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
- return [];
- }
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- var m, set, checkSet, extra, ret, cur, pop, i,
- prune = true,
- contextXML = Sizzle.isXML( context ),
- parts = [],
- soFar = selector;
-
- // Reset the position of the chunker regexp (start from head)
- do {
- chunker.exec( "" );
- m = chunker.exec( soFar );
-
- if ( m ) {
- soFar = m[3];
-
- parts.push( m[1] );
-
- if ( m[2] ) {
- extra = m[3];
- break;
- }
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
}
- } while ( m );
-
- if ( parts.length > 1 && origPOS.exec( selector ) ) {
- if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- set = posProcess( parts[0] + parts[1], context, seed );
+ ret = this.pushStack( "", "find", selector );
- } else {
- set = Expr.relative[ parts[0] ] ?
- [ context ] :
- Sizzle( parts.shift(), context );
-
- while ( parts.length ) {
- selector = parts.shift();
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
- if ( Expr.relative[ selector ] ) {
- selector += parts.shift();
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
}
-
- set = posProcess( selector, set, seed );
}
}
- } else {
- // Take a shortcut and set the context if the root selector is an ID
- // (but not if it'll be faster if the inner selector is an ID)
- if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
- Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
-
- ret = Sizzle.find( parts.shift(), context, contextXML );
- context = ret.expr ?
- Sizzle.filter( ret.expr, ret.set )[0] :
- ret.set[0];
- }
-
- if ( context ) {
- ret = seed ?
- { expr: parts.pop(), set: makeArray(seed) } :
- Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
-
- set = ret.expr ?
- Sizzle.filter( ret.expr, ret.set ) :
- ret.set;
-
- if ( parts.length > 0 ) {
- checkSet = makeArray( set );
-
- } else {
- prune = false;
- }
-
- while ( parts.length ) {
- cur = parts.pop();
- pop = cur;
+ return ret;
+ },
- if ( !Expr.relative[ cur ] ) {
- cur = "";
- } else {
- pop = parts.pop();
- }
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
- if ( pop == null ) {
- pop = context;
+ return this.filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
}
-
- Expr.relative[ cur ]( checkSet, pop, contextXML );
}
+ });
+ },
- } else {
- checkSet = parts = [];
- }
- }
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
- if ( !checkSet ) {
- checkSet = set;
- }
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
- if ( !checkSet ) {
- Sizzle.error( cur || selector );
- }
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ rneedsContext.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
- if ( toString.call(checkSet) === "[object Array]" ) {
- if ( !prune ) {
- results.push.apply( results, checkSet );
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ ret = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
- } else if ( context && context.nodeType === 1 ) {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
- results.push( set[i] );
- }
- }
+ for ( ; i < l; i++ ) {
+ cur = this[i];
- } else {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
- results.push( set[i] );
+ while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
}
+ cur = cur.parentNode;
}
}
- } else {
- makeArray( checkSet, results );
- }
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
- if ( extra ) {
- Sizzle( extra, origContext, results, seed );
- Sizzle.uniqueSort( results );
- }
+ return this.pushStack( ret, "closest", selectors );
+ },
- return results;
-};
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
-Sizzle.uniqueSort = function( results ) {
- if ( sortOrder ) {
- hasDuplicate = baseHasDuplicate;
- results.sort( sortOrder );
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
- if ( hasDuplicate ) {
- for ( var i = 1; i < results.length; i++ ) {
- if ( results[i] === results[ i - 1 ] ) {
- results.splice( i--, 1 );
- }
- }
- }
- }
-
- return results;
-};
-
-Sizzle.matches = function( expr, set ) {
- return Sizzle( expr, null, null, set );
-};
-
-Sizzle.matchesSelector = function( node, expr ) {
- return Sizzle( expr, null, null, [node] ).length > 0;
-};
-
-Sizzle.find = function( expr, context, isXML ) {
- var set, i, len, match, type, left;
-
- if ( !expr ) {
- return [];
- }
-
- for ( i = 0, len = Expr.order.length; i < len; i++ ) {
- type = Expr.order[i];
-
- if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
- left = match[1];
- match.splice( 1, 1 );
-
- if ( left.substr( left.length - 1 ) !== "\\" ) {
- match[1] = (match[1] || "").replace( rBackslash, "" );
- set = Expr.find[ type ]( match, context, isXML );
-
- if ( set != null ) {
- expr = expr.replace( Expr.match[ type ], "" );
- break;
- }
- }
- }
- }
-
- if ( !set ) {
- set = typeof context.getElementsByTagName !== "undefined" ?
- context.getElementsByTagName( "*" ) :
- [];
- }
-
- return { set: set, expr: expr };
-};
-
-Sizzle.filter = function( expr, set, inplace, not ) {
- var match, anyFound,
- type, found, item, filter, left,
- i, pass,
- old = expr,
- result = [],
- curLoop = set,
- isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
-
- while ( expr && set.length ) {
- for ( type in Expr.filter ) {
- if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- filter = Expr.filter[ type ];
- left = match[1];
-
- anyFound = false;
-
- match.splice(1,1);
-
- if ( left.substr( left.length - 1 ) === "\\" ) {
- continue;
- }
-
- if ( curLoop === result ) {
- result = [];
- }
-
- if ( Expr.preFilter[ type ] ) {
- match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
-
- if ( !match ) {
- anyFound = found = true;
-
- } else if ( match === true ) {
- continue;
- }
- }
-
- if ( match ) {
- for ( i = 0; (item = curLoop[i]) != null; i++ ) {
- if ( item ) {
- found = filter( item, match, i, curLoop );
- pass = not ^ found;
-
- if ( inplace && found != null ) {
- if ( pass ) {
- anyFound = true;
-
- } else {
- curLoop[i] = false;
- }
-
- } else if ( pass ) {
- result.push( item );
- anyFound = true;
- }
- }
- }
- }
-
- if ( found !== undefined ) {
- if ( !inplace ) {
- curLoop = result;
- }
-
- expr = expr.replace( Expr.match[ type ], "" );
-
- if ( !anyFound ) {
- return [];
- }
-
- break;
- }
- }
- }
-
- // Improper expression
- if ( expr === old ) {
- if ( anyFound == null ) {
- Sizzle.error( expr );
-
- } else {
- break;
- }
- }
-
- old = expr;
- }
-
- return curLoop;
-};
-
-Sizzle.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-/**
- * Utility function for retreiving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-var getText = Sizzle.getText = function( elem ) {
- var i, node,
- nodeType = elem.nodeType,
- ret = "";
-
- if ( nodeType ) {
- if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
- // Use textContent || innerText for elements
- if ( typeof elem.textContent === 'string' ) {
- return elem.textContent;
- } else if ( typeof elem.innerText === 'string' ) {
- // Replace IE's carriage returns
- return elem.innerText.replace( rReturn, '' );
- } else {
- // Traverse it's children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
- ret += getText( elem );
- }
- }
- } else if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
- } else {
-
- // If no nodeType, this is expected to be an array
- for ( i = 0; (node = elem[i]); i++ ) {
- // Do not traverse comment nodes
- if ( node.nodeType !== 8 ) {
- ret += getText( node );
- }
- }
- }
- return ret;
-};
-
-var Expr = Sizzle.selectors = {
- order: [ "ID", "NAME", "TAG" ],
-
- match: {
- ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
- PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
- },
-
- leftMatch: {},
-
- attrMap: {
- "class": "className",
- "for": "htmlFor"
- },
-
- attrHandle: {
- href: function( elem ) {
- return elem.getAttribute( "href" );
- },
- type: function( elem ) {
- return elem.getAttribute( "type" );
- }
- },
-
- relative: {
- "+": function(checkSet, part){
- var isPartStr = typeof part === "string",
- isTag = isPartStr && !rNonWord.test( part ),
- isPartStrNotTag = isPartStr && !isTag;
-
- if ( isTag ) {
- part = part.toLowerCase();
- }
-
- for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
- if ( (elem = checkSet[i]) ) {
- while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
-
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
- elem || false :
- elem === part;
- }
- }
-
- if ( isPartStrNotTag ) {
- Sizzle.filter( part, checkSet, true );
- }
- },
-
- ">": function( checkSet, part ) {
- var elem,
- isPartStr = typeof part === "string",
- i = 0,
- l = checkSet.length;
-
- if ( isPartStr && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
-
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- var parent = elem.parentNode;
- checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
- }
- }
-
- } else {
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- checkSet[i] = isPartStr ?
- elem.parentNode :
- elem.parentNode === part;
- }
- }
-
- if ( isPartStr ) {
- Sizzle.filter( part, checkSet, true );
- }
- }
- },
-
- "": function(checkSet, part, isXML){
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
- },
-
- "~": function( checkSet, part, isXML ) {
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
- }
- },
-
- find: {
- ID: function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- },
-
- NAME: function( match, context ) {
- if ( typeof context.getElementsByName !== "undefined" ) {
- var ret = [],
- results = context.getElementsByName( match[1] );
-
- for ( var i = 0, l = results.length; i < l; i++ ) {
- if ( results[i].getAttribute("name") === match[1] ) {
- ret.push( results[i] );
- }
- }
-
- return ret.length === 0 ? null : ret;
- }
- },
-
- TAG: function( match, context ) {
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- return context.getElementsByTagName( match[1] );
- }
- }
- },
- preFilter: {
- CLASS: function( match, curLoop, inplace, result, not, isXML ) {
- match = " " + match[1].replace( rBackslash, "" ) + " ";
-
- if ( isXML ) {
- return match;
- }
-
- for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
- if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
- if ( !inplace ) {
- result.push( elem );
- }
-
- } else if ( inplace ) {
- curLoop[i] = false;
- }
- }
- }
-
- return false;
- },
-
- ID: function( match ) {
- return match[1].replace( rBackslash, "" );
- },
-
- TAG: function( match, curLoop ) {
- return match[1].replace( rBackslash, "" ).toLowerCase();
- },
-
- CHILD: function( match ) {
- if ( match[1] === "nth" ) {
- if ( !match[2] ) {
- Sizzle.error( match[0] );
- }
-
- match[2] = match[2].replace(/^\+|\s*/g, '');
-
- // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
- var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
- match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
- !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
-
- // calculate the numbers (first)n+(last) including if they are negative
- match[2] = (test[1] + (test[2] || 1)) - 0;
- match[3] = test[3] - 0;
- }
- else if ( match[2] ) {
- Sizzle.error( match[0] );
- }
-
- // TODO: Move to normal caching system
- match[0] = done++;
-
- return match;
- },
-
- ATTR: function( match, curLoop, inplace, result, not, isXML ) {
- var name = match[1] = match[1].replace( rBackslash, "" );
-
- if ( !isXML && Expr.attrMap[name] ) {
- match[1] = Expr.attrMap[name];
- }
-
- // Handle if an un-quoted value was used
- match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
-
- if ( match[2] === "~=" ) {
- match[4] = " " + match[4] + " ";
- }
-
- return match;
- },
-
- PSEUDO: function( match, curLoop, inplace, result, not ) {
- if ( match[1] === "not" ) {
- // If we're dealing with a complex expression, or a simple one
- if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
- match[3] = Sizzle(match[3], null, null, curLoop);
-
- } else {
- var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
-
- if ( !inplace ) {
- result.push.apply( result, ret );
- }
-
- return false;
- }
-
- } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
- return true;
- }
-
- return match;
- },
-
- POS: function( match ) {
- match.unshift( true );
-
- return match;
- }
- },
-
- filters: {
- enabled: function( elem ) {
- return elem.disabled === false && elem.type !== "hidden";
- },
-
- disabled: function( elem ) {
- return elem.disabled === true;
- },
-
- checked: function( elem ) {
- return elem.checked === true;
- },
-
- selected: function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- parent: function( elem ) {
- return !!elem.firstChild;
- },
-
- empty: function( elem ) {
- return !elem.firstChild;
- },
-
- has: function( elem, i, match ) {
- return !!Sizzle( match[3], elem ).length;
- },
-
- header: function( elem ) {
- return (/h\d/i).test( elem.nodeName );
- },
-
- text: function( elem ) {
- var attr = elem.getAttribute( "type" ), type = elem.type;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
- },
-
- radio: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
- },
-
- checkbox: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
- },
-
- file: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
- },
-
- password: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
- },
-
- submit: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "submit" === elem.type;
- },
-
- image: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
- },
-
- reset: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "reset" === elem.type;
- },
-
- button: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && "button" === elem.type || name === "button";
- },
-
- input: function( elem ) {
- return (/input|select|textarea|button/i).test( elem.nodeName );
- },
-
- focus: function( elem ) {
- return elem === elem.ownerDocument.activeElement;
- }
- },
- setFilters: {
- first: function( elem, i ) {
- return i === 0;
- },
-
- last: function( elem, i, match, array ) {
- return i === array.length - 1;
- },
-
- even: function( elem, i ) {
- return i % 2 === 0;
- },
-
- odd: function( elem, i ) {
- return i % 2 === 1;
- },
-
- lt: function( elem, i, match ) {
- return i < match[3] - 0;
- },
-
- gt: function( elem, i, match ) {
- return i > match[3] - 0;
- },
-
- nth: function( elem, i, match ) {
- return match[3] - 0 === i;
- },
-
- eq: function( elem, i, match ) {
- return match[3] - 0 === i;
- }
- },
- filter: {
- PSEUDO: function( elem, match, i, array ) {
- var name = match[1],
- filter = Expr.filters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
-
- } else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
-
- } else if ( name === "not" ) {
- var not = match[3];
-
- for ( var j = 0, l = not.length; j < l; j++ ) {
- if ( not[j] === elem ) {
- return false;
- }
- }
-
- return true;
-
- } else {
- Sizzle.error( name );
- }
- },
-
- CHILD: function( elem, match ) {
- var first, last,
- doneName, parent, cache,
- count, diff,
- type = match[1],
- node = elem;
-
- switch ( type ) {
- case "only":
- case "first":
- while ( (node = node.previousSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- if ( type === "first" ) {
- return true;
- }
-
- node = elem;
-
- /* falls through */
- case "last":
- while ( (node = node.nextSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- return true;
-
- case "nth":
- first = match[2];
- last = match[3];
-
- if ( first === 1 && last === 0 ) {
- return true;
- }
-
- doneName = match[0];
- parent = elem.parentNode;
-
- if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
- count = 0;
-
- for ( node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType === 1 ) {
- node.nodeIndex = ++count;
- }
- }
-
- parent[ expando ] = doneName;
- }
-
- diff = elem.nodeIndex - last;
-
- if ( first === 0 ) {
- return diff === 0;
-
- } else {
- return ( diff % first === 0 && diff / first >= 0 );
- }
- }
- },
-
- ID: function( elem, match ) {
- return elem.nodeType === 1 && elem.getAttribute("id") === match;
- },
-
- TAG: function( elem, match ) {
- return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
- },
-
- CLASS: function( elem, match ) {
- return (" " + (elem.className || elem.getAttribute("class")) + " ")
- .indexOf( match ) > -1;
- },
-
- ATTR: function( elem, match ) {
- var name = match[1],
- result = Sizzle.attr ?
- Sizzle.attr( elem, name ) :
- Expr.attrHandle[ name ] ?
- Expr.attrHandle[ name ]( elem ) :
- elem[ name ] != null ?
- elem[ name ] :
- elem.getAttribute( name ),
- value = result + "",
- type = match[2],
- check = match[4];
-
- return result == null ?
- type === "!=" :
- !type && Sizzle.attr ?
- result != null :
- type === "=" ?
- value === check :
- type === "*=" ?
- value.indexOf(check) >= 0 :
- type === "~=" ?
- (" " + value + " ").indexOf(check) >= 0 :
- !check ?
- value && result !== false :
- type === "!=" ?
- value !== check :
- type === "^=" ?
- value.indexOf(check) === 0 :
- type === "$=" ?
- value.substr(value.length - check.length) === check :
- type === "|=" ?
- value === check || value.substr(0, check.length + 1) === check + "-" :
- false;
- },
-
- POS: function( elem, match, i, array ) {
- var name = match[2],
- filter = Expr.setFilters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
- }
- }
- }
-};
-
-var origPOS = Expr.match.POS,
- fescape = function(all, num){
- return "\\" + (num - 0 + 1);
- };
-
-for ( var type in Expr.match ) {
- Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
- Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
-}
-// Expose origPOS
-// "global" as in regardless of relation to brackets/parens
-Expr.match.globalPOS = origPOS;
-
-var makeArray = function( array, results ) {
- array = Array.prototype.slice.call( array, 0 );
-
- if ( results ) {
- results.push.apply( results, array );
- return results;
- }
-
- return array;
-};
-
-// Perform a simple check to determine if the browser is capable of
-// converting a NodeList to an array using builtin methods.
-// Also verifies that the returned array holds DOM nodes
-// (which is not the case in the Blackberry browser)
-try {
- Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
-
-// Provide a fallback method if it does not work
-} catch( e ) {
- makeArray = function( array, results ) {
- var i = 0,
- ret = results || [];
-
- if ( toString.call(array) === "[object Array]" ) {
- Array.prototype.push.apply( ret, array );
-
- } else {
- if ( typeof array.length === "number" ) {
- for ( var l = array.length; i < l; i++ ) {
- ret.push( array[i] );
- }
-
- } else {
- for ( ; array[i]; i++ ) {
- ret.push( array[i] );
- }
- }
- }
-
- return ret;
- };
-}
-
-var sortOrder, siblingCheck;
-
-if ( document.documentElement.compareDocumentPosition ) {
- sortOrder = function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
- return a.compareDocumentPosition ? -1 : 1;
- }
-
- return a.compareDocumentPosition(b) & 4 ? -1 : 1;
- };
-
-} else {
- sortOrder = function( a, b ) {
- // The nodes are identical, we can exit early
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
-
- // Fallback to using sourceIndex (in IE) if it's available on both nodes
- } else if ( a.sourceIndex && b.sourceIndex ) {
- return a.sourceIndex - b.sourceIndex;
- }
-
- var al, bl,
- ap = [],
- bp = [],
- aup = a.parentNode,
- bup = b.parentNode,
- cur = aup;
-
- // If the nodes are siblings (or identical) we can do a quick check
- if ( aup === bup ) {
- return siblingCheck( a, b );
-
- // If no parents were found then the nodes are disconnected
- } else if ( !aup ) {
- return -1;
-
- } else if ( !bup ) {
- return 1;
- }
-
- // Otherwise they're somewhere else in the tree so we need
- // to build up a full list of the parentNodes for comparison
- while ( cur ) {
- ap.unshift( cur );
- cur = cur.parentNode;
- }
-
- cur = bup;
-
- while ( cur ) {
- bp.unshift( cur );
- cur = cur.parentNode;
- }
-
- al = ap.length;
- bl = bp.length;
-
- // Start walking down the tree looking for a discrepancy
- for ( var i = 0; i < al && i < bl; i++ ) {
- if ( ap[i] !== bp[i] ) {
- return siblingCheck( ap[i], bp[i] );
- }
- }
-
- // We ended someplace up the tree so do a sibling check
- return i === al ?
- siblingCheck( a, bp[i], -1 ) :
- siblingCheck( ap[i], b, 1 );
- };
-
- siblingCheck = function( a, b, ret ) {
- if ( a === b ) {
- return ret;
- }
-
- var cur = a.nextSibling;
-
- while ( cur ) {
- if ( cur === b ) {
- return -1;
- }
-
- cur = cur.nextSibling;
- }
-
- return 1;
- };
-}
-
-// Check to see if the browser returns elements by name when
-// querying by getElementById (and provide a workaround)
-(function(){
- // We're going to inject a fake input element with a specified name
- var form = document.createElement("div"),
- id = "script" + (new Date()).getTime(),
- root = document.documentElement;
-
- form.innerHTML = "<a name='" + id + "'/>";
-
- // Inject it into the root element, check its status, and remove it quickly
- root.insertBefore( form, root.firstChild );
-
- // The workaround has to do additional checks after a getElementById
- // Which slows things down for other browsers (hence the branching)
- if ( document.getElementById( id ) ) {
- Expr.find.ID = function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
-
- return m ?
- m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
- [m] :
- undefined :
- [];
- }
- };
-
- Expr.filter.ID = function( elem, match ) {
- var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
-
- return elem.nodeType === 1 && node && node.nodeValue === match;
- };
- }
-
- root.removeChild( form );
-
- // release memory in IE
- root = form = null;
-})();
-
-(function(){
- // Check to see if the browser returns only elements
- // when doing getElementsByTagName("*")
-
- // Create a fake element
- var div = document.createElement("div");
- div.appendChild( document.createComment("") );
-
- // Make sure no comments are found
- if ( div.getElementsByTagName("*").length > 0 ) {
- Expr.find.TAG = function( match, context ) {
- var results = context.getElementsByTagName( match[1] );
-
- // Filter out possible comments
- if ( match[1] === "*" ) {
- var tmp = [];
-
- for ( var i = 0; results[i]; i++ ) {
- if ( results[i].nodeType === 1 ) {
- tmp.push( results[i] );
- }
- }
-
- results = tmp;
- }
-
- return results;
- };
- }
-
- // Check to see if an attribute returns normalized href attributes
- div.innerHTML = "<a href='#'></a>";
-
- if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
- div.firstChild.getAttribute("href") !== "#" ) {
-
- Expr.attrHandle.href = function( elem ) {
- return elem.getAttribute( "href", 2 );
- };
- }
-
- // release memory in IE
- div = null;
-})();
-
-if ( document.querySelectorAll ) {
- (function(){
- var oldSizzle = Sizzle,
- div = document.createElement("div"),
- id = "__sizzle__";
-
- div.innerHTML = "<p class='TEST'></p>";
-
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
-
- Sizzle = function( query, context, extra, seed ) {
- context = context || document;
-
- // Only use querySelectorAll on non-XML documents
- // (ID selectors don't work in non-HTML documents)
- if ( !seed && !Sizzle.isXML(context) ) {
- // See if we find a selector to speed up
- var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
-
- if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
- // Speed-up: Sizzle("TAG")
- if ( match[1] ) {
- return makeArray( context.getElementsByTagName( query ), extra );
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
- return makeArray( context.getElementsByClassName( match[2] ), extra );
- }
- }
-
- if ( context.nodeType === 9 ) {
- // Speed-up: Sizzle("body")
- // The body element only exists once, optimize finding it
- if ( query === "body" && context.body ) {
- return makeArray( [ context.body ], extra );
-
- // Speed-up: Sizzle("#ID")
- } else if ( match && match[3] ) {
- var elem = context.getElementById( match[3] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id === match[3] ) {
- return makeArray( [ elem ], extra );
- }
-
- } else {
- return makeArray( [], extra );
- }
- }
-
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(qsaError) {}
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- var oldContext = context,
- old = context.getAttribute( "id" ),
- nid = old || id,
- hasParent = context.parentNode,
- relativeHierarchySelector = /^\s*[+~]/.test( query );
-
- if ( !old ) {
- context.setAttribute( "id", nid );
- } else {
- nid = nid.replace( /'/g, "\\$&" );
- }
- if ( relativeHierarchySelector && hasParent ) {
- context = context.parentNode;
- }
-
- try {
- if ( !relativeHierarchySelector || hasParent ) {
- return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
- }
-
- } catch(pseudoError) {
- } finally {
- if ( !old ) {
- oldContext.removeAttribute( "id" );
- }
- }
- }
- }
-
- return oldSizzle(query, context, extra, seed);
- };
-
- for ( var prop in oldSizzle ) {
- Sizzle[ prop ] = oldSizzle[ prop ];
- }
-
- // release memory in IE
- div = null;
- })();
-}
-
-(function(){
- var html = document.documentElement,
- matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
-
- if ( matches ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9 fails this)
- var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
- pseudoWorks = false;
-
- try {
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( document.documentElement, "[test!='']:sizzle" );
-
- } catch( pseudoError ) {
- pseudoWorks = true;
- }
-
- Sizzle.matchesSelector = function( node, expr ) {
- // Make sure that attribute selectors are quoted
- expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
-
- if ( !Sizzle.isXML( node ) ) {
- try {
- if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
- var ret = matches.call( node, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || !disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9, so check for that
- node.document && node.document.nodeType !== 11 ) {
- return ret;
- }
- }
- } catch(e) {}
- }
-
- return Sizzle(expr, null, null, [node]).length > 0;
- };
- }
-})();
-
-(function(){
- var div = document.createElement("div");
-
- div.innerHTML = "<div class='test e'></div><div class='test'></div>";
-
- // Opera can't find a second classname (in 9.6)
- // Also, make sure that getElementsByClassName actually exists
- if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
- return;
- }
-
- // Safari caches class attributes, doesn't catch changes (in 3.2)
- div.lastChild.className = "e";
-
- if ( div.getElementsByClassName("e").length === 1 ) {
- return;
- }
-
- Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function( match, context, isXML ) {
- if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
- return context.getElementsByClassName(match[1]);
- }
- };
-
- // release memory in IE
- div = null;
-})();
-
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem[ expando ] === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 && !isXML ){
- elem[ expando ] = doneName;
- elem.sizset = i;
- }
-
- if ( elem.nodeName.toLowerCase() === cur ) {
- match = elem;
- break;
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem[ expando ] === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 ) {
- if ( !isXML ) {
- elem[ expando ] = doneName;
- elem.sizset = i;
- }
-
- if ( typeof cur !== "string" ) {
- if ( elem === cur ) {
- match = true;
- break;
- }
-
- } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
- match = elem;
- break;
- }
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-if ( document.documentElement.contains ) {
- Sizzle.contains = function( a, b ) {
- return a !== b && (a.contains ? a.contains(b) : true);
- };
-
-} else if ( document.documentElement.compareDocumentPosition ) {
- Sizzle.contains = function( a, b ) {
- return !!(a.compareDocumentPosition(b) & 16);
- };
-
-} else {
- Sizzle.contains = function() {
- return false;
- };
-}
-
-Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
-
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-var posProcess = function( selector, context, seed ) {
- var match,
- tmpSet = [],
- later = "",
- root = context.nodeType ? [context] : context;
-
- // Position selectors must be done after the filter
- // And so must :not(positional) so we move all PSEUDOs to the end
- while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
- later += match[0];
- selector = selector.replace( Expr.match.PSEUDO, "" );
- }
-
- selector = Expr.relative[selector] ? selector + "*" : selector;
-
- for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet, seed );
- }
-
- return Sizzle.filter( later, tmpSet );
-};
-
-// EXPOSE
-// Override sizzle attribute retrieval
-Sizzle.attr = jQuery.attr;
-Sizzle.selectors.attrMap = {};
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.filters;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-})();
-
-
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prevUntil|prevAll)/,
- // Note: This RegExp should be improved, or likely pulled from Sizzle
- rmultiselector = /,/,
- isSimple = /^.[^:#\[\.,]*$/,
- slice = Array.prototype.slice,
- POS = jQuery.expr.match.globalPOS,
- // methods guaranteed to produce a unique set when starting from a unique set
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.fn.extend({
- find: function( selector ) {
- var self = this,
- i, l;
-
- if ( typeof selector !== "string" ) {
- return jQuery( selector ).filter(function() {
- for ( i = 0, l = self.length; i < l; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- });
- }
-
- var ret = this.pushStack( "", "find", selector ),
- length, n, r;
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- length = ret.length;
- jQuery.find( selector, this[i], ret );
-
- if ( i > 0 ) {
- // Make sure that the results are unique
- for ( n = length; n < ret.length; n++ ) {
- for ( r = 0; r < length; r++ ) {
- if ( ret[r] === ret[n] ) {
- ret.splice(n--, 1);
- break;
- }
- }
- }
- }
- }
-
- return ret;
- },
-
- has: function( target ) {
- var targets = jQuery( target );
- return this.filter(function() {
- for ( var i = 0, l = targets.length; i < l; i++ ) {
- if ( jQuery.contains( this, targets[i] ) ) {
- return true;
- }
- }
- });
- },
-
- not: function( selector ) {
- return this.pushStack( winnow(this, selector, false), "not", selector);
- },
-
- filter: function( selector ) {
- return this.pushStack( winnow(this, selector, true), "filter", selector );
- },
-
- is: function( selector ) {
- return !!selector && (
- typeof selector === "string" ?
- // If this is a positional selector, check membership in the returned set
- // so $("p:first").is("p:last") won't return true for a doc with two "p".
- POS.test( selector ) ?
- jQuery( selector, this.context ).index( this[0] ) >= 0 :
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
- },
-
- closest: function( selectors, context ) {
- var ret = [], i, l, cur = this[0];
-
- // Array (deprecated as of jQuery 1.7)
- if ( jQuery.isArray( selectors ) ) {
- var level = 1;
-
- while ( cur && cur.ownerDocument && cur !== context ) {
- for ( i = 0; i < selectors.length; i++ ) {
-
- if ( jQuery( cur ).is( selectors[ i ] ) ) {
- ret.push({ selector: selectors[ i ], elem: cur, level: level });
- }
- }
-
- cur = cur.parentNode;
- level++;
- }
-
- return ret;
- }
-
- // String
- var pos = POS.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- cur = this[i];
-
- while ( cur ) {
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- ret.push( cur );
- break;
-
- } else {
- cur = cur.parentNode;
- if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
- break;
- }
- }
- }
- }
-
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
-
- return this.pushStack( ret, "closest", selectors );
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
-
- // No argument, return index in parent
- if ( !elem ) {
- return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
- }
-
- // index in selector
- if ( typeof elem === "string" ) {
- return jQuery.inArray( this[0], jQuery( elem ) );
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
}
// Locate the position of the desired element
jQuery.unique( all ) );
},
- andSelf: function() {
- return this.add( this.prevObject );
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
}
});
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
function isDisconnected( node ) {
return !node || !node.parentNode || node.parentNode.nodeType === 11;
}
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+}
+
jQuery.each({
parent: function( elem ) {
var parent = elem.parentNode;
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
- return jQuery.nth( elem, 2, "nextSibling" );
+ return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
- return jQuery.nth( elem, 2, "previousSibling" );
+ return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
contents: function( elem ) {
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
- jQuery.makeArray( elem.childNodes );
+ jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
- if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ if ( this.length > 1 && rparentsprev.test( name ) ) {
ret = ret.reverse();
}
- return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+ return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
};
});
return matched;
},
- nth: function( cur, result, dir, elem ) {
- result = result || 1;
- var num = 0;
-
- for ( ; cur; cur = cur[dir] ) {
- if ( cur.nodeType === 1 && ++num === result ) {
- break;
- }
- }
-
- return cur;
- },
-
sibling: function( n, elem ) {
var r = [];
return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
});
}
-
-
-
-
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
- rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
- rnoInnerhtml = /<(?:script|style)/i,
+ rnoInnerhtml = /<(?:script|style|link)/i,
rnocache = /<(?:script|object|embed|option|style)/i,
rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+ rcheckableType = /^(?:checkbox|radio)$/,
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /\/(java|ecma)script/i,
- rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,
wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ]
},
- safeFragment = createSafeFragment( document );
+ safeFragment = createSafeFragment( document ),
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
-// IE can't serialize <link> and <script> tags normally
+// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+// unless wrapped in a div with non-breaking characters in front of it.
if ( !jQuery.support.htmlSerialize ) {
- wrapMap._default = [ 1, "div<div>", "</div>" ];
+ wrapMap._default = [ 1, "X<div>", "</div>" ];
}
jQuery.fn.extend({
append: function() {
return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
this.appendChild( elem );
}
});
prepend: function() {
return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
this.insertBefore( elem, this.firstChild );
}
});
},
before: function() {
- if ( this[0] && this[0].parentNode ) {
+ if ( !isDisconnected( this[0] ) ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this );
});
- } else if ( arguments.length ) {
+ }
+
+ if ( arguments.length ) {
var set = jQuery.clean( arguments );
- set.push.apply( set, this.toArray() );
- return this.pushStack( set, "before", arguments );
+ return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
}
},
after: function() {
- if ( this[0] && this[0].parentNode ) {
+ if ( !isDisconnected( this[0] ) ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this.nextSibling );
});
- } else if ( arguments.length ) {
- var set = this.pushStack( this, "after", arguments );
- set.push.apply( set, jQuery.clean(arguments) );
- return set;
+ }
+
+ if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
}
},
// keepData is for internal use only--do not document
remove: function( selector, keepData ) {
- for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
if ( !keepData && elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
},
empty: function() {
- for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
if ( value === undefined ) {
return elem.nodeType === 1 ?
elem.innerHTML.replace( rinlinejQuery, "" ) :
- null;
+ undefined;
}
-
+ // See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
},
replaceWith: function( value ) {
- if ( this[0] && this[0].parentNode ) {
+ if ( !isDisconnected( this[0] ) ) {
// Make sure that the elements are removed from the DOM before they are inserted
// this can help fix replacing a parent with child elements
if ( jQuery.isFunction( value ) ) {
jQuery(parent).append( value );
}
});
- } else {
- return this.length ?
- this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
- this;
}
+
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
},
detach: function( selector ) {
},
domManip: function( args, table, callback ) {
- var results, first, fragment, parent,
+
+ // Flatten any nested arrays
+ args = [].concat.apply( [], args );
+
+ var results, first, fragment, iNoClone,
+ i = 0,
value = args[0],
- scripts = [];
+ scripts = [],
+ l = this.length;
// We can't cloneNode fragments that contain checked, in WebKit
- if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
return this.each(function() {
- jQuery(this).domManip( args, table, callback, true );
+ jQuery(this).domManip( args, table, callback );
});
}
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
- args[0] = value.call(this, i, table ? self.html() : undefined);
+ args[0] = value.call( this, i, table ? self.html() : undefined );
self.domManip( args, table, callback );
});
}
if ( this[0] ) {
- parent = value && value.parentNode;
-
- // If we're in a fragment, just use that instead of building a new one
- if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
- results = { fragment: parent };
-
- } else {
- results = jQuery.buildFragment( args, this, scripts );
- }
-
+ results = jQuery.buildFragment( args, this, scripts );
fragment = results.fragment;
+ first = fragment.firstChild;
if ( fragment.childNodes.length === 1 ) {
- first = fragment = fragment.firstChild;
- } else {
- first = fragment.firstChild;
+ fragment = first;
}
if ( first ) {
table = table && jQuery.nodeName( first, "tr" );
- for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ // Use the original fragment for the last item instead of the first because it can end up
+ // being emptied incorrectly in certain situations (#8070).
+ // Fragments from the fragment cache must always be cloned and never used in place.
+ for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
callback.call(
- table ?
- root(this[i], first) :
+ table && jQuery.nodeName( this[i], "table" ) ?
+ findOrAppend( this[i], "tbody" ) :
this[i],
- // Make sure that we do not leak memory by inadvertently discarding
- // the original fragment (which might have attached data) instead of
- // using it; in addition, use the original fragment object for the last
- // item instead of first because it can end up being emptied incorrectly
- // in certain situations (Bug #8070).
- // Fragments from the fragment cache must always be cloned and never used
- // in place.
- results.cacheable || ( l > 1 && i < lastIndex ) ?
- jQuery.clone( fragment, true, true ) :
- fragment
+ i === iNoClone ?
+ fragment :
+ jQuery.clone( fragment, true, true )
);
}
}
+ // Fix #11809: Avoid leaking memory
+ fragment = first = null;
+
if ( scripts.length ) {
jQuery.each( scripts, function( i, elem ) {
if ( elem.src ) {
- jQuery.ajax({
- type: "GET",
- global: false,
- url: elem.src,
- async: false,
- dataType: "script"
- });
+ if ( jQuery.ajax ) {
+ jQuery.ajax({
+ url: elem.src,
+ type: "GET",
+ dataType: "script",
+ async: false,
+ global: false,
+ "throws": true
+ });
+ } else {
+ jQuery.error("no ajax");
+ }
} else {
- jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
}
if ( elem.parentNode ) {
}
});
-function root( elem, cur ) {
- return jQuery.nodeName(elem, "table") ?
- (elem.getElementsByTagName("tbody")[0] ||
- elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
- elem;
+function findOrAppend( elem, tag ) {
+ return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
}
function cloneCopyEvent( src, dest ) {
nodeName = dest.nodeName.toLowerCase();
- // IE6-8 fail to clone children inside object elements that use
- // the proprietary classid attribute value (rather than the type
- // attribute) to identify the type of content to display
if ( nodeName === "object" ) {
- dest.outerHTML = src.outerHTML;
+ // IE6-10 improperly clones children of object elements using classid.
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
+ if ( dest.parentNode ) {
+ dest.outerHTML = src.outerHTML;
+ }
+
+ // This path appears unavoidable for IE9. When cloning an object
+ // element in IE9, the outerHTML strategy above is not sufficient.
+ // If the src has innerHTML and the destination does not,
+ // copy the src.innerHTML into the dest.innerHTML. #10324
+ if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
+ dest.innerHTML = src.innerHTML;
+ }
- } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
// IE6-8 fails to persist the checked state of a cloned checkbox
// or radio button. Worse, IE6-7 fail to give the cloned element
// a checked appearance if the defaultChecked value isn't also set
- if ( src.checked ) {
- dest.defaultChecked = dest.checked = src.checked;
- }
+
+ dest.defaultChecked = dest.checked = src.checked;
// IE6-7 get confused and end up setting the value of a cloned
// checkbox/radio button to an empty string instead of "on"
// Event data gets referenced instead of copied if the expando
// gets copied too
dest.removeAttribute( jQuery.expando );
-
- // Clear flags for bubbling special change/submit events, they must
- // be reattached when the newly cloned events are first activated
- dest.removeAttribute( "_submit_attached" );
- dest.removeAttribute( "_change_attached" );
}
-jQuery.buildFragment = function( args, nodes, scripts ) {
- var fragment, cacheable, cacheresults, doc,
- first = args[ 0 ];
+jQuery.buildFragment = function( args, context, scripts ) {
+ var fragment, cacheable, cachehit,
+ first = args[ 0 ];
- // nodes may contain either an explicit document object,
- // a jQuery collection or context object.
- // If nodes[0] contains a valid object to assign to doc
- if ( nodes && nodes[0] ) {
- doc = nodes[0].ownerDocument || nodes[0];
- }
+ // Set context from what may come in as undefined or a jQuery collection or a node
+ context = context || document;
+ context = (context[0] || context).ownerDocument || context[0] || context;
// Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
- if ( !doc.createDocumentFragment ) {
- doc = document;
+ if ( typeof context.createDocumentFragment === "undefined" ) {
+ context = document;
}
// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
- if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document &&
first.charAt(0) === "<" && !rnocache.test( first ) &&
(jQuery.support.checkClone || !rchecked.test( first )) &&
(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+ // Mark cacheable and look for a hit
cacheable = true;
-
- cacheresults = jQuery.fragments[ first ];
- if ( cacheresults && cacheresults !== 1 ) {
- fragment = cacheresults;
- }
+ fragment = jQuery.fragments[ first ];
+ cachehit = fragment !== undefined;
}
if ( !fragment ) {
- fragment = doc.createDocumentFragment();
- jQuery.clean( args, doc, fragment, scripts );
- }
+ fragment = context.createDocumentFragment();
+ jQuery.clean( args, context, fragment, scripts );
- if ( cacheable ) {
- jQuery.fragments[ first ] = cacheresults ? fragment : 1;
+ // Update the cache, but only store false
+ // unless this is a second parsing of the same content
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cachehit && fragment;
+ }
}
return { fragment: fragment, cacheable: cacheable };
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
- var ret = [],
+ var elems,
+ i = 0,
+ ret = [],
insert = jQuery( selector ),
+ l = insert.length,
parent = this.length === 1 && this[0].parentNode;
- if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {
insert[ original ]( this[0] );
return this;
-
} else {
- for ( var i = 0, l = insert.length; i < l; i++ ) {
- var elems = ( i > 0 ? this.clone(true) : this ).get();
+ for ( ; i < l; i++ ) {
+ elems = ( i > 0 ? this.clone(true) : this ).get();
jQuery( insert[i] )[ original ]( elems );
ret = ret.concat( elems );
}
// Used in clean, fixes the defaultChecked property
function fixDefaultChecked( elem ) {
- if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ if ( rcheckableType.test( elem.type ) ) {
elem.defaultChecked = elem.checked;
}
}
-// Finds all inputs and passes them to fixDefaultChecked
-function findInputs( elem ) {
- var nodeName = ( elem.nodeName || "" ).toLowerCase();
- if ( nodeName === "input" ) {
- fixDefaultChecked( elem );
- // Skip scripts, get other children
- } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
- jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
- }
-}
-
-// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
-function shimCloneNode( elem ) {
- var div = document.createElement( "div" );
- safeFragment.appendChild( div );
-
- div.innerHTML = elem.outerHTML;
- return div.firstChild;
-}
jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var srcElements,
destElements,
i,
- // IE<=8 does not properly clone detached, unknown element nodes
- clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ?
- elem.cloneNode( true ) :
- shimCloneNode( elem );
+ clone;
+
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+ clone = elem.cloneNode( true );
+
+ // IE<=8 does not properly clone detached, unknown element nodes
+ } else {
+ fragmentDiv.innerHTML = elem.outerHTML;
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+ }
if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
},
clean: function( elems, context, fragment, scripts ) {
- var checkScriptType, script, j,
- ret = [];
-
- context = context || document;
+ var j, safe, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
+ i = 0,
+ ret = [];
- // !context.createElement fails in IE with an error but returns typeof 'object'
- if ( typeof context.createElement === "undefined" ) {
- context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ // Ensure that context is a document
+ if ( !context || typeof context.createDocumentFragment === "undefined" ) {
+ context = document;
}
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ // Use the already-created safe fragment if context permits
+ for ( safe = context === document && safeFragment; (elem = elems[i]) != null; i++ ) {
if ( typeof elem === "number" ) {
elem += "";
}
if ( !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );
} else {
+ // Ensure a safe container in which to render the html
+ safe = safe || createSafeFragment( context );
+ div = div || safe.appendChild( context.createElement("div") );
+
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, "<$1></$2>");
- // Trim whitespace, otherwise indexOf won't work as expected
- var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
- wrap = wrapMap[ tag ] || wrapMap._default,
- depth = wrap[0],
- div = context.createElement("div"),
- safeChildNodes = safeFragment.childNodes,
- remove;
-
- // Append wrapper element to unknown element safe doc fragment
- if ( context === document ) {
- // Use the fragment we've already created for this document
- safeFragment.appendChild( div );
- } else {
- // Use a fragment created with the owner document
- createSafeFragment( context ).appendChild( div );
- }
-
// Go to html and back, then peel off extra wrappers
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+ depth = wrap[0];
div.innerHTML = wrap[1] + elem + wrap[2];
// Move to the right depth
if ( !jQuery.support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
- var hasBody = rtbody.test(elem),
+ hasBody = rtbody.test(elem);
tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes :
elem = div.childNodes;
- // Clear elements from DocumentFragment (safeFragment or otherwise)
- // to avoid hoarding elements. Fixes #11356
- if ( div ) {
- div.parentNode.removeChild( div );
-
- // Guard against -1 index exceptions in FF3.6
- if ( safeChildNodes.length > 0 ) {
- remove = safeChildNodes[ safeChildNodes.length - 1 ];
-
- if ( remove && remove.parentNode ) {
- remove.parentNode.removeChild( remove );
- }
- }
- }
- }
- }
-
- // Resets defaultChecked for any radios and checkboxes
- // about to be appended to the DOM in IE 6/7 (#8060)
- var len;
- if ( !jQuery.support.appendChecked ) {
- if ( elem[0] && typeof (len = elem.length) === "number" ) {
- for ( j = 0; j < len; j++ ) {
- findInputs( elem[j] );
- }
- } else {
- findInputs( elem );
+ // Remember the top-level container for proper cleanup
+ div = safe.lastChild;
}
}
}
}
+ // Fix #11356: Clear elements from safeFragment
+ if ( div ) {
+ safe.removeChild( div );
+ elem = div = safe = null;
+ }
+
+ // Reset defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ if ( !jQuery.support.appendChecked ) {
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+ }
+ }
+
+ // Append elements to a provided document fragment
if ( fragment ) {
- checkScriptType = function( elem ) {
- return !elem.type || rscriptType.test( elem.type );
+ // Special handling of each script element
+ handleScript = function( elem ) {
+ // Check if we consider it executable
+ if ( !elem.type || rscriptType.test( elem.type ) ) {
+ // Detach the script and store it in the scripts array (if provided) or the fragment
+ // Return truthy to indicate that it has been handled
+ return scripts ?
+ scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
+ fragment.appendChild( elem );
+ }
};
- for ( i = 0; ret[i]; i++ ) {
- script = ret[i];
- if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
- scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
- } else {
- if ( script.nodeType === 1 ) {
- var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ // Check if we're done after handling an executable script
+ if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
+ // Append to fragment and handle embedded scripts
+ fragment.appendChild( elem );
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+ jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
+ // Splice the scripts into ret after their former ancestor and advance our index beyond them
ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ i += jsTags.length;
}
- fragment.appendChild( script );
}
}
}
return ret;
},
- cleanData: function( elems ) {
- var data, id,
+ cleanData: function( elems, /* internal */ acceptData ) {
+ var data, id, elem, type,
+ i = 0,
+ internalKey = jQuery.expando,
cache = jQuery.cache,
- special = jQuery.event.special,
- deleteExpando = jQuery.support.deleteExpando;
+ deleteExpando = jQuery.support.deleteExpando,
+ special = jQuery.event.special;
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
- continue;
- }
+ for ( ; (elem = elems[i]) != null; i++ ) {
- id = elem[ jQuery.expando ];
+ if ( acceptData || jQuery.acceptData( elem ) ) {
- if ( id ) {
- data = cache[ id ];
+ id = elem[ internalKey ];
+ data = id && cache[ id ];
- if ( data && data.events ) {
- for ( var type in data.events ) {
- if ( special[ type ] ) {
- jQuery.event.remove( elem, type );
+ if ( data ) {
+ if ( data.events ) {
+ for ( type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
- // This is a shortcut to avoid jQuery.event.remove's overhead
- } else {
- jQuery.removeEvent( elem, type, data.handle );
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
}
}
- // Null the DOM reference to avoid IE6/7/8 leak (#7054)
- if ( data.handle ) {
- data.handle.elem = null;
- }
- }
+ // Remove cache only if it was not already removed by jQuery.event.remove
+ if ( cache[ id ] ) {
- if ( deleteExpando ) {
- delete elem[ jQuery.expando ];
+ delete cache[ id ];
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- }
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( deleteExpando ) {
+ delete elem[ internalKey ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+
+ } else {
+ elem[ internalKey ] = null;
+ }
- delete cache[ id ];
+ jQuery.deletedIds.push( id );
+ }
+ }
}
}
}
});
+// Limit scope pollution from any deprecated API
+(function() {
+
+var matched, browser;
+
+// Use of jQuery.browser is frowned upon.
+// More details: http://api.jquery.com/jQuery.browser
+// jQuery.uaMatch maintained for back-compat
+jQuery.uaMatch = function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+};
+matched = jQuery.uaMatch( navigator.userAgent );
+browser = {};
+
+if ( matched.browser ) {
+ browser[ matched.browser ] = true;
+ browser.version = matched.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+// Maintained for back-compat only
+if ( browser.webkit ) {
+ browser.safari = true;
+}
+jQuery.browser = browser;
+jQuery.sub = function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
-var ralpha = /alpha\([^)]*\)/i,
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+};
+
+})();
+var curCSS, iframe, iframeDoc,
+ ralpha = /alpha\([^)]*\)/i,
ropacity = /opacity=([^)]*)/,
- // fixed for IE9, see #8346
- rupper = /([A-Z]|^ms)/g,
- rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
- rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
- rrelNum = /^([\-+])=([\-+.\de]+)/,
+ rposition = /^(top|right|bottom|left)$/,
rmargin = /^margin/,
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+ rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
+ elemdisplay = {},
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssNormalTransform = {
+ letterSpacing: 0,
+ fontWeight: 400,
+ lineHeight: 1
+ },
- // order is important!
cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
- curCSS,
+ eventsToggle = jQuery.fn.toggle;
- getComputedStyle,
- currentStyle;
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
-jQuery.fn.css = function( name, value ) {
- return jQuery.access( this, function( elem, name, value ) {
- return value !== undefined ?
- jQuery.style( elem, name, value ) :
- jQuery.css( elem, name );
- }, name, value, arguments.length > 1 );
-};
+ // shortcut for names that are not vendor prefixed
+ if ( name in style ) {
+ return name;
+ }
+
+ // check for vendor prefixed names
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
+ origName = name,
+ i = cssPrefixes.length;
+
+ while ( i-- ) {
+ name = cssPrefixes[ i ] + capName;
+ if ( name in style ) {
+ return name;
+ }
+ }
+
+ return origName;
+}
+
+function isHidden( elem, el ) {
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+ var elem, display,
+ values = [],
+ index = 0,
+ length = elements.length;
+
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ values[ index ] = jQuery._data( elem, "olddisplay" );
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && elem.style.display === "none" ) {
+ elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( elem.style.display === "" && isHidden( elem ) ) {
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+ }
+ } else {
+ display = curCSS( elem, "display" );
+
+ if ( !values[ index ] && display !== "none" ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
+
+ return elements;
+}
+
+jQuery.fn.extend({
+ css: function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+ },
+ show: function() {
+ return showHide( this, true );
+ },
+ hide: function() {
+ return showHide( this );
+ },
+ toggle: function( state, fn2 ) {
+ var bool = typeof state === "boolean";
+
+ if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
+ return eventsToggle.apply( this, arguments );
+ }
+
+ return this.each(function() {
+ if ( bool ? state : isHidden( this ) ) {
+ jQuery( this ).show();
+ } else {
+ jQuery( this ).hide();
+ }
+ });
+ }
+});
jQuery.extend({
// Add in style property hooks for overriding the default
var ret = curCSS( elem, "opacity" );
return ret === "" ? "1" : ret;
- } else {
- return elem.style.opacity;
}
}
}
}
// Make sure that we're working with the right name
- var ret, type, origName = jQuery.camelCase( name ),
- style = elem.style, hooks = jQuery.cssHooks[ origName ];
+ var ret, type, hooks,
+ origName = jQuery.camelCase( name ),
+ style = elem.style;
+
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
- name = jQuery.cssProps[ origName ] || origName;
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// Check if we're setting a value
if ( value !== undefined ) {
// convert relative number strings (+= or -=) to relative numbers. #7345
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
- value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
// Fixes bug #9237
type = "number";
}
}
// If a hook was provided, use that value, otherwise just set the specified value
- if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
// Fixes bug #5509
try {
}
},
- css: function( elem, name, extra ) {
- var ret, hooks;
+ css: function( elem, name, numeric, extra ) {
+ var val, num, hooks,
+ origName = jQuery.camelCase( name );
// Make sure that we're working with the right name
- name = jQuery.camelCase( name );
- hooks = jQuery.cssHooks[ name ];
- name = jQuery.cssProps[ name ] || name;
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
- // cssFloat needs a special treatment
- if ( name === "cssFloat" ) {
- name = "float";
- }
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// If a hook was provided get the computed value from there
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
- return ret;
+ if ( hooks && "get" in hooks ) {
+ val = hooks.get( elem, true, extra );
+ }
// Otherwise, if a way to get the computed value exists, use that
- } else if ( curCSS ) {
- return curCSS( elem, name );
+ if ( val === undefined ) {
+ val = curCSS( elem, name );
+ }
+
+ //convert "normal" to computed value
+ if ( val === "normal" && name in cssNormalTransform ) {
+ val = cssNormalTransform[ name ];
+ }
+
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
+ if ( numeric || extra !== undefined ) {
+ num = parseFloat( val );
+ return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
}
+ return val;
},
// A method for quickly swapping in/out CSS properties to get correct calculations
swap: function( elem, options, callback ) {
- var old = {},
- ret, name;
+ var ret, name,
+ old = {};
// Remember the old values, and insert the new ones
for ( name in options ) {
}
});
-// DEPRECATED in 1.3, Use jQuery.css() instead
-jQuery.curCSS = jQuery.css;
-
-if ( document.defaultView && document.defaultView.getComputedStyle ) {
- getComputedStyle = function( elem, name ) {
- var ret, defaultView, computedStyle, width,
+// NOTE: To any future maintainer, we've used both window.getComputedStyle
+// and getComputedStyle here to produce a better gzip size
+if ( window.getComputedStyle ) {
+ curCSS = function( elem, name ) {
+ var ret, width, minWidth, maxWidth,
+ computed = getComputedStyle( elem, null ),
style = elem.style;
- name = name.replace( rupper, "-$1" ).toLowerCase();
+ if ( computed ) {
- if ( (defaultView = elem.ownerDocument.defaultView) &&
- (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
-
- ret = computedStyle.getPropertyValue( name );
+ ret = computed[ name ];
if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
ret = jQuery.style( elem, name );
}
- }
- // A tribute to the "awesome hack by Dean Edwards"
- // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins
- // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
- if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) {
- width = style.width;
- style.width = ret;
- ret = computedStyle.width;
- style.width = width;
+ // A tribute to the "awesome hack by Dean Edwards"
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+ width = style.width;
+ minWidth = style.minWidth;
+ maxWidth = style.maxWidth;
+
+ style.minWidth = style.maxWidth = style.width = ret;
+ ret = computed.width;
+
+ style.width = width;
+ style.minWidth = minWidth;
+ style.maxWidth = maxWidth;
+ }
}
return ret;
};
-}
-
-if ( document.documentElement.currentStyle ) {
- currentStyle = function( elem, name ) {
- var left, rsLeft, uncomputed,
+} else if ( document.documentElement.currentStyle ) {
+ curCSS = function( elem, name ) {
+ var left, rsLeft,
ret = elem.currentStyle && elem.currentStyle[ name ],
style = elem.style;
// Avoid setting ret to empty string here
// so we don't default to auto
- if ( ret == null && style && (uncomputed = style[ name ]) ) {
- ret = uncomputed;
+ if ( ret == null && style && style[ name ] ) {
+ ret = style[ name ];
}
// From the awesome hack by Dean Edwards
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
- if ( rnumnonpx.test( ret ) ) {
+ // but not position css attributes, as those are proportional to the parent element instead
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
// Remember the original values
left = style.left;
if ( rsLeft ) {
elem.runtimeStyle.left = elem.currentStyle.left;
}
- style.left = name === "fontSize" ? "1em" : ret;
- ret = style.pixelLeft + "px";
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+ value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
+ // If we already have the right measurement, avoid augmentation
+ 4 :
+ // Otherwise initialize for horizontal or vertical properties
+ name === "width" ? 1 : 0,
+
+ val = 0;
+
+ for ( ; i < 4; i += 2 ) {
+ // both box models exclude margin, so add it if we want it
+ if ( extra === "margin" ) {
+ // we use jQuery.css instead of curCSS here
+ // because of the reliableMarginRight CSS hook!
+ val += jQuery.css( elem, extra + cssExpand[ i ], true );
+ }
+
+ // From this point on we use curCSS for maximum performance (relevant in animations)
+ if ( isBorderBox ) {
+ // border-box includes padding, so remove it if we want content
+ if ( extra === "content" ) {
+ val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ }
+
+ // at this point, extra isn't border nor margin, so remove border
+ if ( extra !== "margin" ) {
+ val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ } else {
+ // at this point, extra isn't content, so add padding
+ val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
- // Revert the changed values
- style.left = left;
- if ( rsLeft ) {
- elem.runtimeStyle.left = rsLeft;
+ // at this point, extra isn't content nor padding, so add border
+ if ( extra !== "padding" ) {
+ val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
}
}
+ }
- return ret === "" ? "auto" : ret;
- };
+ return val;
}
-curCSS = getComputedStyle || currentStyle;
-
function getWidthOrHeight( elem, name, extra ) {
- // Start with offset property
+ // Start with offset property, which is equivalent to the border-box value
var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
- i = name === "width" ? 1 : 0,
- len = 4;
-
- if ( val > 0 ) {
- if ( extra !== "border" ) {
- for ( ; i < len; i += 2 ) {
- if ( !extra ) {
- val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
- }
- if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0;
- } else {
- val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
- }
- }
+ valueIsBorderBox = true,
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
+
+ if ( val <= 0 ) {
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
}
- return val + "px";
- }
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // we need the check for style in case a browser which returns unreliable values
+ // for getComputedStyle silently falls back to the reliable elem.style
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
- // Fall back to computed then uncomputed css if necessary
- val = curCSS( elem, name );
- if ( val < 0 || val == null ) {
- val = elem.style[ name ];
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
}
- // Computed unit is not pixels. Stop here and return.
- if ( rnumnonpx.test(val) ) {
- return val;
+ // use the active box-sizing model to add/subtract irrelevant styles
+ return ( val +
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra || ( isBorderBox ? "border" : "content" ),
+ valueIsBorderBox
+ )
+ ) + "px";
+}
+
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+ if ( elemdisplay[ nodeName ] ) {
+ return elemdisplay[ nodeName ];
}
- // Normalize "", auto, and prepare for extra
- val = parseFloat( val ) || 0;
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
+ display = elem.css("display");
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // Use the already-created iframe if possible
+ iframe = document.body.appendChild(
+ iframe || jQuery.extend( document.createElement("iframe"), {
+ frameBorder: 0,
+ width: 0,
+ height: 0
+ })
+ );
- // Add padding, border, margin
- if ( extra ) {
- for ( ; i < len; i += 2 ) {
- val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
- if ( extra !== "padding" ) {
- val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
- }
- if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0;
- }
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write("<!doctype html><html><body>");
+ iframeDoc.close();
}
+
+ elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
+
+ display = curCSS( elem, "display" );
+ document.body.removeChild( iframe );
}
- return val + "px";
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+
+ return display;
}
jQuery.each([ "height", "width" ], function( i, name ) {
jQuery.cssHooks[ name ] = {
get: function( elem, computed, extra ) {
if ( computed ) {
- if ( elem.offsetWidth !== 0 ) {
+ if ( elem.offsetWidth !== 0 || curCSS( elem, "display" ) !== "none" ) {
return getWidthOrHeight( elem, name, extra );
} else {
return jQuery.swap( elem, cssShow, function() {
}
},
- set: function( elem, value ) {
- return rnum.test( value ) ?
- value + "px" :
- value;
+ set: function( elem, value, extra ) {
+ return setPositiveNumber( elem, value, extra ?
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra,
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
+ ) : 0
+ );
}
};
});
get: function( elem, computed ) {
// IE uses filters for opacity
return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
- ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
computed ? "1" : "";
},
style.zoom = 1;
// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
- if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+ style.removeAttribute ) {
// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
// if "filter:" is present at all, clearType is disabled, we want to avoid this
};
}
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
jQuery(function() {
- // This hook cannot be added until DOM ready because the support test
- // for it is not run until after DOM ready
if ( !jQuery.support.reliableMarginRight ) {
jQuery.cssHooks.marginRight = {
get: function( elem, computed ) {
// Work around by temporarily setting element display to inline-block
return jQuery.swap( elem, { "display": "inline-block" }, function() {
if ( computed ) {
- return curCSS( elem, "margin-right" );
- } else {
- return elem.style.marginRight;
+ return curCSS( elem, "marginRight" );
}
});
}
};
}
+
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+ // getComputedStyle returns percent when specified for top/left/bottom/right
+ // rather than make the css module depend on the offset module, we just check for it here
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ var ret = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
+ }
+ }
+ };
+ });
+ }
+
});
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.hidden = function( elem ) {
- var width = elem.offsetWidth,
- height = elem.offsetHeight;
-
- return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+ return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
padding: "",
border: "Width"
}, function( prefix, suffix ) {
-
jQuery.cssHooks[ prefix + suffix ] = {
expand: function( value ) {
var i,
return expanded;
}
};
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
+});
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ rselectTextarea = /^(?:select|textarea)/i;
+
+jQuery.fn.extend({
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
});
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+ var prefix,
+ s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+ var name;
+
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+var // Document location
+ ajaxLocation,
+ // Document location segments
+ ajaxLocParts,
-var r20 = /%20/g,
- rbracket = /\[\]$/,
- rCRLF = /\r?\n/g,
rhash = /#.*$/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
- rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
// #7653, #8125, #8152: local protocol detection
rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
rnoContent = /^(?:GET|HEAD)$/,
rprotocol = /^\/\//,
rquery = /\?/,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
- rselectTextarea = /^(?:select|textarea)/i,
- rspacesAjax = /\s+/,
rts = /([?&])_=[^&]*/,
- rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
// Keep a copy of the old load method
_load = jQuery.fn.load,
*/
transports = {},
- // Document location
- ajaxLocation,
-
- // Document location segments
- ajaxLocParts,
-
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
allTypes = ["*/"] + ["*"];
dataTypeExpression = "*";
}
- if ( jQuery.isFunction( func ) ) {
- var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
- i = 0,
- length = dataTypes.length,
- dataType,
- list,
- placeBefore;
+ var dataType, list, placeBefore,
+ dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ),
+ i = 0,
+ length = dataTypes.length;
+ if ( jQuery.isFunction( func ) ) {
// For each dataType in the dataTypeExpression
for ( ; i < length; i++ ) {
dataType = dataTypes[ i ];
inspected[ dataType ] = true;
- var list = structure[ dataType ],
+ var selection,
+ list = structure[ dataType ],
i = 0,
length = list ? list.length : 0,
- executeOnly = ( structure === prefilters ),
- selection;
+ executeOnly = ( structure === prefilters );
for ( ; i < length && ( executeOnly || !selection ); i++ ) {
selection = list[ i ]( options, originalOptions, jqXHR );
}
}
-jQuery.fn.extend({
- load: function( url, params, callback ) {
- if ( typeof url !== "string" && _load ) {
- return _load.apply( this, arguments );
+jQuery.fn.load = function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+ }
- // Don't do a request if no elements are being requested
- } else if ( !this.length ) {
- return this;
- }
+ // Don't do a request if no elements are being requested
+ if ( !this.length ) {
+ return this;
+ }
- var off = url.indexOf( " " );
- if ( off >= 0 ) {
- var selector = url.slice( off, url.length );
- url = url.slice( 0, off );
- }
+ var selector, type, response,
+ self = this,
+ off = url.indexOf(" ");
+
+ if ( off >= 0 ) {
+ selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
- // Default to a GET request
- var type = "GET";
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
- // If the second parameter was provided
- if ( params ) {
- // If it's a function
- if ( jQuery.isFunction( params ) ) {
- // We assume that it's the callback
- callback = params;
- params = undefined;
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ type = "POST";
+ }
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
- // Otherwise, build a param string
- } else if ( typeof params === "object" ) {
- params = jQuery.param( params, jQuery.ajaxSettings.traditional );
- type = "POST";
+ // if "type" variable is undefined, then "GET" method will be used
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function( jqXHR, status ) {
+ if ( callback ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
}
}
+ }).done(function( responseText ) {
- var self = this;
+ // Save response for use in complete callback
+ response = arguments;
- // Request the remote document
- jQuery.ajax({
- url: url,
- type: type,
- dataType: "html",
- data: params,
- // Complete callback (responseText is used internally)
- complete: function( jqXHR, status, responseText ) {
- // Store the response as specified by the jqXHR object
- responseText = jqXHR.responseText;
- // If successful, inject the HTML into all the matched elements
- if ( jqXHR.isResolved() ) {
- // #4825: Get the actual response in case
- // a dataFilter is present in ajaxSettings
- jqXHR.done(function( r ) {
- responseText = r;
- });
- // See if a selector was specified
- self.html( selector ?
- // Create a dummy div to hold the results
- jQuery("<div>")
- // inject the contents of the document in, removing the scripts
- // to avoid any 'Permission Denied' errors in IE
- .append(responseText.replace(rscript, ""))
-
- // Locate the specified elements
- .find(selector) :
-
- // If not, just inject the full result
- responseText );
- }
+ // See if a selector was specified
+ self.html( selector ?
- if ( callback ) {
- self.each( callback, [ responseText, status, jqXHR ] );
- }
- }
- });
+ // Create a dummy div to hold the results
+ jQuery("<div>")
- return this;
- },
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append( responseText.replace( rscript, "" ) )
- serialize: function() {
- return jQuery.param( this.serializeArray() );
- },
+ // Locate the specified elements
+ .find( selector ) :
- serializeArray: function() {
- return this.map(function(){
- return this.elements ? jQuery.makeArray( this.elements ) : this;
- })
- .filter(function(){
- return this.name && !this.disabled &&
- ( this.checked || rselectTextarea.test( this.nodeName ) ||
- rinput.test( this.type ) );
- })
- .map(function( i, elem ){
- var val = jQuery( this ).val();
+ // If not, just inject the full result
+ responseText );
- return val == null ?
- null :
- jQuery.isArray( val ) ?
- jQuery.map( val, function( val, i ){
- return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }) :
- { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }).get();
- }
-});
+ });
+
+ return this;
+};
// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
username: null,
password: null,
cache: null,
+ throws: false,
traditional: false,
headers: {},
*/
// Force options to be an object
options = options || {};
- var // Create the final options object
+ var // ifModified key
+ ifModifiedKey,
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Create the final options object
s = jQuery.ajaxSetup( {}, options ),
// Callbacks context
callbackContext = s.context || s,
completeDeferred = jQuery.Callbacks( "once memory" ),
// Status-dependent callbacks
statusCode = s.statusCode || {},
- // ifModified key
- ifModifiedKey,
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
- // Response headers
- responseHeadersString,
- responseHeaders,
- // transport
- transport,
- // timeout handle
- timeoutTimer,
- // Cross-domain detection vars
- parts,
// The jqXHR state
state = 0,
- // To know if global events are to be dispatched
- fireGlobals,
- // Loop variable
- i,
+ // Default abort message
+ strAbort = "canceled",
// Fake xhr
jqXHR = {
// Cancel the request
abort: function( statusText ) {
- statusText = statusText || "abort";
+ statusText = statusText || strAbort;
if ( transport ) {
transport.abort( statusText );
}
// It is defined here because jslint complains if it is declared
// at the end of the function (which would be more logical and readable)
function done( status, nativeStatusText, responses, headers ) {
+ var isSuccess, success, error, response, modified,
+ statusText = nativeStatusText;
// Called once
if ( state === 2 ) {
// Set readyState
jqXHR.readyState = status > 0 ? 4 : 0;
- var isSuccess,
- success,
- error,
- statusText = nativeStatusText,
- response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
- lastModified,
- etag;
+ // Get response data
+ if ( responses ) {
+ response = ajaxHandleResponses( s, jqXHR, responses );
+ }
// If successful, handle type chaining
if ( status >= 200 && status < 300 || status === 304 ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
- if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
- jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ modified = jqXHR.getResponseHeader("Last-Modified");
+ if ( modified ) {
+ jQuery.lastModified[ ifModifiedKey ] = modified;
}
- if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
- jQuery.etag[ ifModifiedKey ] = etag;
+ modified = jqXHR.getResponseHeader("Etag");
+ if ( modified ) {
+ jQuery.etag[ ifModifiedKey ] = modified;
}
}
// If we have data
} else {
- try {
- success = ajaxConvert( s, response );
- statusText = "success";
- isSuccess = true;
- } catch(e) {
- // We have a parsererror
- statusText = "parsererror";
- error = e;
- }
+ isSuccess = ajaxConvert( s, response );
+ statusText = isSuccess.state;
+ success = isSuccess.data;
+ error = isSuccess.error;
+ isSuccess = !error;
}
} else {
// We extract error from statusText
}
} else {
tmp = map[ jqXHR.status ];
- jqXHR.then( tmp, tmp );
+ jqXHR.always( tmp );
}
}
return this;
s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
// Extract dataTypes list
- s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace );
// Determine if a cross-domain request is in order
if ( s.crossDomain == null ) {
// If request was aborted inside a prefilter, stop there
if ( state === 2 ) {
- return false;
+ return jqXHR;
}
// We can fire global events as of now if asked to
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
- // Abort if not done already
- jqXHR.abort();
- return false;
+ // Abort if not done already and return
+ return jqXHR.abort();
}
+ // aborting is no longer a cancellation
+ strAbort = "abort";
+
// Install callbacks on deferreds
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
}
}
- return jqXHR;
- },
-
- // Serialize an array of form elements or a set of
- // key/values into a query string
- param: function( a, traditional ) {
- var s = [],
- add = function( key, value ) {
- // If value is a function, invoke it and return its value
- value = jQuery.isFunction( value ) ? value() : value;
- s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
- };
-
- // Set traditional to true for jQuery <= 1.3.2 behavior.
- if ( traditional === undefined ) {
- traditional = jQuery.ajaxSettings.traditional;
- }
-
- // If an array was passed in, assume that it is an array of form elements.
- if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
- // Serialize the form elements
- jQuery.each( a, function() {
- add( this.name, this.value );
- });
-
- } else {
- // If traditional, encode the "old" way (the way 1.3.2 or older
- // did it), otherwise encode params recursively.
- for ( var prefix in a ) {
- buildParams( prefix, a[ prefix ], traditional, add );
- }
- }
-
- // Return the resulting serialization
- return s.join( "&" ).replace( r20, "+" );
- }
-});
-
-function buildParams( prefix, obj, traditional, add ) {
- if ( jQuery.isArray( obj ) ) {
- // Serialize array item.
- jQuery.each( obj, function( i, v ) {
- if ( traditional || rbracket.test( prefix ) ) {
- // Treat each array item as a scalar.
- add( prefix, v );
-
- } else {
- // If array item is non-scalar (array or object), encode its
- // numeric index to resolve deserialization ambiguity issues.
- // Note that rack (as of 1.0.0) can't currently deserialize
- // nested arrays properly, and attempting to do so may cause
- // a server error. Possible fixes are to modify rack's
- // deserialization algorithm or to provide an option or flag
- // to force array serialization to be shallow.
- buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
- }
- });
-
- } else if ( !traditional && jQuery.type( obj ) === "object" ) {
- // Serialize object item.
- for ( var name in obj ) {
- buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
- }
-
- } else {
- // Serialize scalar item.
- add( prefix, obj );
- }
-}
-
-// This is still on the jQuery object... for now
-// Want to move this to jQuery.ajax some day
-jQuery.extend({
+ return jqXHR;
+ },
// Counter for holding the number of active queries
active: 0,
*/
function ajaxHandleResponses( s, jqXHR, responses ) {
- var contents = s.contents,
+ var ct, type, finalDataType, firstDataType,
+ contents = s.contents,
dataTypes = s.dataTypes,
- responseFields = s.responseFields,
- ct,
- type,
- finalDataType,
- firstDataType;
+ responseFields = s.responseFields;
// Fill responseXXX fields
for ( type in responseFields ) {
// Chain conversions given the request and the original response
function ajaxConvert( s, response ) {
+ var conv, conv2, current, tmp,
+ // Work with a copy of dataTypes in case we need to modify it for conversion
+ dataTypes = s.dataTypes.slice(),
+ prev = dataTypes[ 0 ],
+ converters = {},
+ i = 0;
+
// Apply the dataFilter if provided
if ( s.dataFilter ) {
response = s.dataFilter( response, s.dataType );
}
- var dataTypes = s.dataTypes,
- converters = {},
- i,
- key,
- length = dataTypes.length,
- tmp,
- // Current and previous dataTypes
- current = dataTypes[ 0 ],
- prev,
- // Conversion expression
- conversion,
- // Conversion function
- conv,
- // Conversion functions (transitive conversion)
- conv1,
- conv2;
-
- // For each dataType in the chain
- for ( i = 1; i < length; i++ ) {
-
- // Create converters map
- // with lowercased keys
- if ( i === 1 ) {
- for ( key in s.converters ) {
- if ( typeof key === "string" ) {
- converters[ key.toLowerCase() ] = s.converters[ key ];
- }
- }
+ // Create converters map with lowercased keys
+ if ( dataTypes[ 1 ] ) {
+ for ( conv in s.converters ) {
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
}
+ }
- // Get the dataTypes
- prev = current;
- current = dataTypes[ i ];
-
- // If current is auto dataType, update it to prev
- if ( current === "*" ) {
- current = prev;
- // If no auto and dataTypes are actually different
- } else if ( prev !== "*" && prev !== current ) {
-
- // Get the converter
- conversion = prev + " " + current;
- conv = converters[ conversion ] || converters[ "* " + current ];
-
- // If there is no direct converter, search transitively
- if ( !conv ) {
- conv2 = undefined;
- for ( conv1 in converters ) {
- tmp = conv1.split( " " );
- if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
- conv2 = converters[ tmp[1] + " " + current ];
- if ( conv2 ) {
- conv1 = converters[ conv1 ];
- if ( conv1 === true ) {
- conv = conv2;
- } else if ( conv2 === true ) {
- conv = conv1;
+ // Convert to each sequential dataType, tolerating list modification
+ for ( ; (current = dataTypes[++i]); ) {
+
+ // There's only work to do if current dataType is non-auto
+ if ( current !== "*" ) {
+
+ // Convert response if prev dataType is non-auto and differs from current
+ if ( prev !== "*" && prev !== current ) {
+
+ // Seek a direct converter
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+ // If none found, seek a pair
+ if ( !conv ) {
+ for ( conv2 in converters ) {
+
+ // If conv2 outputs current
+ tmp = conv2.split(" ");
+ if ( tmp[ 1 ] === current ) {
+
+ // If prev can be converted to accepted input
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ converters[ "* " + tmp[ 0 ] ];
+ if ( conv ) {
+ // Condense equivalence converters
+ if ( conv === true ) {
+ conv = converters[ conv2 ];
+
+ // Otherwise, insert the intermediate dataType
+ } else if ( converters[ conv2 ] !== true ) {
+ current = tmp[ 0 ];
+ dataTypes.splice( i--, 0, current );
+ }
+
+ break;
}
- break;
}
}
}
- }
- // If we found no converter, dispatch an error
- if ( !( conv || conv2 ) ) {
- jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
- }
- // If found converter is not an equivalence
- if ( conv !== true ) {
- // Convert with 1 or 2 converters accordingly
- response = conv ? conv( response ) : conv2( conv1(response) );
- }
- }
- }
- return response;
-}
+ // Apply converter (if not an equivalence)
+ if ( conv !== true ) {
+ // Unless errors are allowed to bubble, catch and return them
+ if ( conv && s["throws"] ) {
+ response = conv( response );
+ } else {
+ try {
+ response = conv( response );
+ } catch ( e ) {
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+ }
+ }
+ }
+ }
+ // Update prev for next iteration
+ prev = current;
+ }
+ }
-var jsc = jQuery.now(),
- jsre = /(\=)\?(&|$)|\?\?/i;
+ return { state: "success", data: response };
+}
+var oldCallbacks = [],
+ rquestion = /\?/,
+ rjsonp = /(=)\?(?=&|$)|\?\?/,
+ nonce = jQuery.now();
// Default jsonp settings
jQuery.ajaxSetup({
jsonp: "callback",
jsonpCallback: function() {
- return jQuery.expando + "_" + ( jsc++ );
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+ this[ callback ] = true;
+ return callback;
}
});
// Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
- var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
-
- if ( s.dataTypes[ 0 ] === "jsonp" ||
- s.jsonp !== false && ( jsre.test( s.url ) ||
- inspectData && jsre.test( s.data ) ) ) {
-
- var responseContainer,
- jsonpCallback = s.jsonpCallback =
- jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
- previous = window[ jsonpCallback ],
- url = s.url,
- data = s.data,
- replace = "$1" + jsonpCallback + "$2";
-
- if ( s.jsonp !== false ) {
- url = url.replace( jsre, replace );
- if ( s.url === url ) {
- if ( inspectData ) {
- data = data.replace( jsre, replace );
- }
- if ( s.data === data ) {
- // Add callback manually
- url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
- }
- }
+ var callbackName, overwritten, responseContainer,
+ data = s.data,
+ url = s.url,
+ hasCallback = s.jsonp !== false,
+ replaceInUrl = hasCallback && rjsonp.test( url ),
+ replaceInData = hasCallback && !replaceInUrl && typeof data === "string" &&
+ !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") &&
+ rjsonp.test( data );
+
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
+ if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) {
+
+ // Get callback name, remembering preexisting value associated with it
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+ s.jsonpCallback() :
+ s.jsonpCallback;
+ overwritten = window[ callbackName ];
+
+ // Insert callback into url or form data
+ if ( replaceInUrl ) {
+ s.url = url.replace( rjsonp, "$1" + callbackName );
+ } else if ( replaceInData ) {
+ s.data = data.replace( rjsonp, "$1" + callbackName );
+ } else if ( hasCallback ) {
+ s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
}
- s.url = url;
- s.data = data;
-
- // Install callback
- window[ jsonpCallback ] = function( response ) {
- responseContainer = [ response ];
- };
-
- // Clean-up function
- jqXHR.always(function() {
- // Set callback back to previous value
- window[ jsonpCallback ] = previous;
- // Call if it was a function and we have a response
- if ( responseContainer && jQuery.isFunction( previous ) ) {
- window[ jsonpCallback ]( responseContainer[ 0 ] );
- }
- });
-
// Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( !responseContainer ) {
- jQuery.error( jsonpCallback + " was not called" );
+ jQuery.error( callbackName + " was not called" );
}
return responseContainer[ 0 ];
};
// force json dataType
s.dataTypes[ 0 ] = "json";
- // Delegate to script
- return "script";
- }
-});
+ // Install callback
+ window[ callbackName ] = function() {
+ responseContainer = arguments;
+ };
+
+ // Clean-up function (fires after converters)
+ jqXHR.always(function() {
+ // Restore preexisting value
+ window[ callbackName ] = overwritten;
+
+ // Save back as free
+ if ( s[ callbackName ] ) {
+ // make sure that re-using the options doesn't screw things around
+ s.jsonpCallback = originalSettings.jsonpCallback;
+ // save the callback name for future use
+ oldCallbacks.push( callbackName );
+ }
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+ overwritten( responseContainer[ 0 ] );
+ }
+ responseContainer = overwritten = undefined;
+ });
+ // Delegate to script
+ return "script";
+ }
+});
// Install script dataType
jQuery.ajaxSetup({
accepts: {
};
}
});
-
-
-
-
-var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+var xhrCallbacks,
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
xhrOnUnloadAbort = window.ActiveXObject ? function() {
// Abort all pending requests
for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ]( 0, 1 );
}
} : false,
- xhrId = 0,
- xhrCallbacks;
+ xhrId = 0;
// Functions to create xhrs
function createStandardXHR() {
send: function( headers, complete ) {
// Get a new xhr
- var xhr = s.xhr(),
- handle,
- i;
+ var handle, i,
+ xhr = s.xhr();
// Open the socket
// Passing null username, generates a login popup on Opera (#2865)
xml;
// Firefox throws exceptions when accessing properties
- // of an xhr when a network error occured
+ // of an xhr when a network error occurred
// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
try {
}
};
- // if we're in sync mode or it's in cache
- // and has been retrieved directly (IE6 & IE7)
- // we need to manually fire the callback
- if ( !s.async || xhr.readyState === 4 ) {
+ if ( !s.async ) {
+ // if we're in sync mode we fire the callback
callback();
+ } else if ( xhr.readyState === 4 ) {
+ // (IE6 & IE7) if it's in cache and has been
+ // retrieved directly we need to fire the callback
+ setTimeout( callback, 0 );
} else {
handle = ++xhrId;
if ( xhrOnUnloadAbort ) {
}
};
}
- });
-}
-
-
-
-
-var elemdisplay = {},
- iframe, iframeDoc,
- rfxtypes = /^(?:toggle|show|hide)$/,
- rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
- timerId,
- fxAttrs = [
- // height animations
- [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
- // width animations
- [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
- // opacity animations
- [ "opacity" ]
- ],
- fxNow;
-
-jQuery.fn.extend({
- show: function( speed, easing, callback ) {
- var elem, display;
-
- if ( speed || speed === 0 ) {
- return this.animate( genFx("show", 3), speed, easing, callback );
-
- } else {
- for ( var i = 0, j = this.length; i < j; i++ ) {
- elem = this[ i ];
-
- if ( elem.style ) {
- display = elem.style.display;
-
- // Reset the inline display of this element to learn if it is
- // being hidden by cascaded rules or not
- if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
- display = elem.style.display = "";
- }
-
- // Set elements which have been overridden with display: none
- // in a stylesheet to whatever the default browser style is
- // for such an element
- if ( (display === "" && jQuery.css(elem, "display") === "none") ||
- !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
- jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
- }
- }
- }
-
- // Set the display of most of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- elem = this[ i ];
-
- if ( elem.style ) {
- display = elem.style.display;
-
- if ( display === "" || display === "none" ) {
- elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
- }
- }
- }
-
- return this;
- }
- },
-
- hide: function( speed, easing, callback ) {
- if ( speed || speed === 0 ) {
- return this.animate( genFx("hide", 3), speed, easing, callback);
-
- } else {
- var elem, display,
- i = 0,
- j = this.length;
-
- for ( ; i < j; i++ ) {
- elem = this[i];
- if ( elem.style ) {
- display = jQuery.css( elem, "display" );
-
- if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
- jQuery._data( elem, "olddisplay", display );
- }
- }
- }
-
- // Set the display of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- if ( this[i].style ) {
- this[i].style.display = "none";
- }
- }
-
- return this;
- }
- },
-
- // Save the old toggle function
- _toggle: jQuery.fn.toggle,
-
- toggle: function( fn, fn2, callback ) {
- var bool = typeof fn === "boolean";
-
- if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
- this._toggle.apply( this, arguments );
-
- } else if ( fn == null || bool ) {
- this.each(function() {
- var state = bool ? fn : jQuery(this).is(":hidden");
- jQuery(this)[ state ? "show" : "hide" ]();
- });
-
- } else {
- this.animate(genFx("toggle", 3), fn, fn2, callback);
- }
-
- return this;
- },
-
- fadeTo: function( speed, to, easing, callback ) {
- return this.filter(":hidden").css("opacity", 0).show().end()
- .animate({opacity: to}, speed, easing, callback);
- },
-
- animate: function( prop, speed, easing, callback ) {
- var optall = jQuery.speed( speed, easing, callback );
-
- if ( jQuery.isEmptyObject( prop ) ) {
- return this.each( optall.complete, [ false ] );
- }
-
- // Do not change referenced properties as per-property easing will be lost
- prop = jQuery.extend( {}, prop );
-
- function doAnimation() {
- // XXX 'this' does not always have a nodeName when running the
- // test suite
-
- if ( optall.queue === false ) {
- jQuery._mark( this );
- }
-
- var opt = jQuery.extend( {}, optall ),
- isElement = this.nodeType === 1,
- hidden = isElement && jQuery(this).is(":hidden"),
- name, val, p, e, hooks, replace,
- parts, start, end, unit,
- method;
-
- // will store per property easing and be used to determine when an animation is complete
- opt.animatedProperties = {};
-
- // first pass over propertys to expand / normalize
- for ( p in prop ) {
- name = jQuery.camelCase( p );
- if ( p !== name ) {
- prop[ name ] = prop[ p ];
- delete prop[ p ];
- }
-
- if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
- replace = hooks.expand( prop[ name ] );
- delete prop[ name ];
-
- // not quite $.extend, this wont overwrite keys already present.
- // also - reusing 'p' from above because we have the correct "name"
- for ( p in replace ) {
- if ( ! ( p in prop ) ) {
- prop[ p ] = replace[ p ];
- }
- }
- }
- }
-
- for ( name in prop ) {
- val = prop[ name ];
- // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
- if ( jQuery.isArray( val ) ) {
- opt.animatedProperties[ name ] = val[ 1 ];
- val = prop[ name ] = val[ 0 ];
- } else {
- opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
- }
-
- if ( val === "hide" && hidden || val === "show" && !hidden ) {
- return opt.complete.call( this );
- }
-
- if ( isElement && ( name === "height" || name === "width" ) ) {
- // Make sure that nothing sneaks out
- // Record all 3 overflow attributes because IE does not
- // change the overflow attribute when overflowX and
- // overflowY are set to the same value
- opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
-
- // Set display property to inline-block for height/width
- // animations on inline elements that are having width/height animated
- if ( jQuery.css( this, "display" ) === "inline" &&
- jQuery.css( this, "float" ) === "none" ) {
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
- this.style.display = "inline-block";
-
- } else {
- this.style.zoom = 1;
- }
- }
- }
- }
-
- if ( opt.overflow != null ) {
- this.style.overflow = "hidden";
- }
-
- for ( p in prop ) {
- e = new jQuery.fx( this, opt, p );
- val = prop[ p ];
-
- if ( rfxtypes.test( val ) ) {
-
- // Tracks whether to show or hide based on private
- // data attached to the element
- method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
- if ( method ) {
- jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
- e[ method ]();
- } else {
- e[ val ]();
- }
-
- } else {
- parts = rfxnum.exec( val );
- start = e.cur();
-
- if ( parts ) {
- end = parseFloat( parts[2] );
- unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
-
- // We need to compute starting value
- if ( unit !== "px" ) {
- jQuery.style( this, p, (end || 1) + unit);
- start = ( (end || 1) / e.cur() ) * start;
- jQuery.style( this, p, start + unit);
- }
-
- // If a +=/-= token was provided, we're doing a relative animation
- if ( parts[1] ) {
- end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
- }
-
- e.custom( start, end, unit );
-
- } else {
- e.custom( start, val, "" );
- }
- }
- }
-
- // For JS strict compliance
- return true;
- }
-
- return optall.queue === false ?
- this.each( doAnimation ) :
- this.queue( optall.queue, doAnimation );
- },
-
- stop: function( type, clearQueue, gotoEnd ) {
- if ( typeof type !== "string" ) {
- gotoEnd = clearQueue;
- clearQueue = type;
- type = undefined;
- }
- if ( clearQueue && type !== false ) {
- this.queue( type || "fx", [] );
- }
-
- return this.each(function() {
- var index,
- hadTimers = false,
- timers = jQuery.timers,
- data = jQuery._data( this );
-
- // clear marker counters if we know they won't be
- if ( !gotoEnd ) {
- jQuery._unmark( true, this );
- }
-
- function stopQueue( elem, data, index ) {
- var hooks = data[ index ];
- jQuery.removeData( elem, index, true );
- hooks.stop( gotoEnd );
- }
-
- if ( type == null ) {
- for ( index in data ) {
- if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
- stopQueue( this, data, index );
- }
- }
- } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
- stopQueue( this, data, index );
- }
-
- for ( index = timers.length; index--; ) {
- if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
- if ( gotoEnd ) {
-
- // force the next step to be the last
- timers[ index ]( true );
- } else {
- timers[ index ].saveState();
- }
- hadTimers = true;
- timers.splice( index, 1 );
- }
- }
-
- // start the next in the queue if the last step wasn't forced
- // timers currently will call their complete callbacks, which will dequeue
- // but only if they were gotoEnd
- if ( !( gotoEnd && hadTimers ) ) {
- jQuery.dequeue( this, type );
- }
- });
- }
-
-});
-
-// Animations created synchronously will run synchronously
-function createFxNow() {
- setTimeout( clearFxNow, 0 );
- return ( fxNow = jQuery.now() );
-}
-
-function clearFxNow() {
- fxNow = undefined;
-}
-
-// Generate parameters to create a standard animation
-function genFx( type, num ) {
- var obj = {};
-
- jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
- obj[ this ] = type;
- });
-
- return obj;
-}
-
-// Generate shortcuts for custom animations
-jQuery.each({
- slideDown: genFx( "show", 1 ),
- slideUp: genFx( "hide", 1 ),
- slideToggle: genFx( "toggle", 1 ),
- fadeIn: { opacity: "show" },
- fadeOut: { opacity: "hide" },
- fadeToggle: { opacity: "toggle" }
-}, function( name, props ) {
- jQuery.fn[ name ] = function( speed, easing, callback ) {
- return this.animate( props, speed, easing, callback );
+ });
+}
+var fxNow, timerId,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+ rrun = /queueHooks$/,
+ animationPrefilters = [ defaultPrefilter ],
+ tweeners = {
+ "*": [function( prop, value ) {
+ var end, unit, prevScale,
+ tween = this.createTween( prop, value ),
+ parts = rfxnum.exec( value ),
+ target = tween.cur(),
+ start = +target || 0,
+ scale = 1;
+
+ if ( parts ) {
+ end = +parts[2];
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" && start ) {
+ // Iteratively approximate from a nonzero starting point
+ // Prefer the current property, because this process will be trivial if it uses the same units
+ // Fallback to end or a simple constant
+ start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+ do {
+ // If previous iteration zeroed out, double until we get *something*
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+ prevScale = scale = scale || ".5";
+
+ // Adjust and apply
+ start = start / scale;
+ jQuery.style( tween.elem, prop, start + unit );
+
+ // Update scale, tolerating zeroes from tween.cur()
+ scale = tween.cur() / target;
+
+ // Stop looping if we've hit the mark or scale is unchanged
+ } while ( scale !== 1 && scale !== prevScale );
+ }
+
+ tween.unit = unit;
+ tween.start = start;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+ }
+ return tween;
+ }]
};
-});
-jQuery.extend({
- speed: function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
- complete: fn || !fn && easing ||
- jQuery.isFunction( speed ) && speed,
- duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
- };
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ }, 0 );
+ return ( fxNow = jQuery.now() );
+}
- opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+function createTweens( animation, props ) {
+ jQuery.each( props, function( prop, value ) {
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( collection[ index ].call( animation, prop, value ) ) {
- // normalize opt.queue - true/undefined/null -> "fx"
- if ( opt.queue == null || opt.queue === true ) {
- opt.queue = "fx";
+ // we're done with this property
+ return;
+ }
}
+ });
+}
- // Queueing
- opt.old = opt.complete;
-
- opt.complete = function( noUnmark ) {
- if ( jQuery.isFunction( opt.old ) ) {
- opt.old.call( this );
+function Animation( elem, properties, options ) {
+ var result,
+ index = 0,
+ tweenerIndex = 0,
+ length = animationPrefilters.length,
+ deferred = jQuery.Deferred().always( function() {
+ // don't match elem in the :animated selector
+ delete tick.elem;
+ }),
+ tick = function() {
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ percent = 1 - ( remaining / animation.duration || 0 ),
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
+
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ animation ] );
+ return false;
}
+ },
+ animation = deferred.promise({
+ elem: elem,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
+ originalProperties: properties,
+ originalOptions: options,
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ tweens: [],
+ createTween: function( prop, end, easing ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // if we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
+ }
- if ( opt.queue ) {
- jQuery.dequeue( this, opt.queue );
- } else if ( noUnmark !== false ) {
- jQuery._unmark( this );
+ // resolve when we played the last frame
+ // otherwise, reject
+ if ( gotoEnd ) {
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
+ } else {
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
+ }
+ return this;
}
- };
+ }),
+ props = animation.props;
- return opt;
- },
+ propFilter( props, animation.opts.specialEasing );
- easing: {
- linear: function( p ) {
- return p;
- },
- swing: function( p ) {
- return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+ if ( result ) {
+ return result;
}
- },
-
- timers: [],
+ }
- fx: function( elem, options, prop ) {
- this.options = options;
- this.elem = elem;
- this.prop = prop;
+ createTweens( animation, props );
- options.orig = options.orig || {};
+ if ( jQuery.isFunction( animation.opts.start ) ) {
+ animation.opts.start.call( elem, animation );
}
-});
+ jQuery.fx.timer(
+ jQuery.extend( tick, {
+ anim: animation,
+ queue: animation.opts.queue,
+ elem: elem
+ })
+ );
-jQuery.fx.prototype = {
- // Simple function for setting a style value
- update: function() {
- if ( this.options.step ) {
- this.options.step.call( this.elem, this.now, this );
- }
+ // attach callbacks from options
+ return animation.progress( animation.opts.progress )
+ .done( animation.opts.done, animation.opts.complete )
+ .fail( animation.opts.fail )
+ .always( animation.opts.always );
+}
- ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
- },
+function propFilter( props, specialEasing ) {
+ var index, name, easing, value, hooks;
- // Get the current size
- cur: function() {
- if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
- return this.elem[ this.prop ];
+ // camelCase, specialEasing and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ easing = specialEasing[ name ];
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ easing = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
}
- var parsed,
- r = jQuery.css( this.elem, this.prop );
- // Empty strings, null, undefined and "auto" are converted to 0,
- // complex values such as "rotate(1rad)" are returned as is,
- // simple values such as "10px" are parsed to Float.
- return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
- },
-
- // Start an animation from one number to another
- custom: function( from, to, unit ) {
- var self = this,
- fx = jQuery.fx;
-
- this.startTime = fxNow || createFxNow();
- this.end = to;
- this.now = this.start = from;
- this.pos = this.state = 0;
- this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
-
- function t( gotoEnd ) {
- return self.step( gotoEnd );
+ if ( index !== name ) {
+ props[ name ] = value;
+ delete props[ index ];
}
- t.queue = this.options.queue;
- t.elem = this.elem;
- t.saveState = function() {
- if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
- if ( self.options.hide ) {
- jQuery._data( self.elem, "fxshow" + self.prop, self.start );
- } else if ( self.options.show ) {
- jQuery._data( self.elem, "fxshow" + self.prop, self.end );
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ value = hooks.expand( value );
+ delete props[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'index' from above because we have the correct "name"
+ for ( index in value ) {
+ if ( !( index in props ) ) {
+ props[ index ] = value[ index ];
+ specialEasing[ index ] = easing;
}
}
- };
-
- if ( t() && jQuery.timers.push(t) && !timerId ) {
- timerId = setInterval( fx.tick, fx.interval );
+ } else {
+ specialEasing[ name ] = easing;
}
- },
-
- // Simple 'show' function
- show: function() {
- var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+ }
+}
- // Remember where we started, so that we can go back to it later
- this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
- this.options.show = true;
+jQuery.Animation = jQuery.extend( Animation, {
- // Begin the animation
- // Make sure that we start at a small width/height to avoid any flash of content
- if ( dataShow !== undefined ) {
- // This show is picking up where a previous hide or show left off
- this.custom( this.cur(), dataShow );
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
} else {
- this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+ props = props.split(" ");
}
- // Start by showing the element
- jQuery( this.elem ).show();
- },
-
- // Simple 'hide' function
- hide: function() {
- // Remember where we started, so that we can go back to it later
- this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
- this.options.hide = true;
+ var prop,
+ index = 0,
+ length = props.length;
- // Begin the animation
- this.custom( this.cur(), 0 );
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
},
- // Each step of an animation
- step: function( gotoEnd ) {
- var p, n, complete,
- t = fxNow || createFxNow(),
- done = true,
- elem = this.elem,
- options = this.options;
-
- if ( gotoEnd || t >= options.duration + this.startTime ) {
- this.now = this.end;
- this.pos = this.state = 1;
- this.update();
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
- options.animatedProperties[ this.prop ] = true;
+function defaultPrefilter( elem, props, opts ) {
+ var index, prop, value, length, dataShow, tween, hooks, oldfire,
+ anim = this,
+ style = elem.style,
+ orig = {},
+ handled = [],
+ hidden = elem.nodeType && isHidden( elem );
+
+ // handle queue: false promises
+ if ( !opts.queue ) {
+ hooks = jQuery._queueHooks( elem, "fx" );
+ if ( hooks.unqueued == null ) {
+ hooks.unqueued = 0;
+ oldfire = hooks.empty.fire;
+ hooks.empty.fire = function() {
+ if ( !hooks.unqueued ) {
+ oldfire();
+ }
+ };
+ }
+ hooks.unqueued++;
- for ( p in options.animatedProperties ) {
- if ( options.animatedProperties[ p ] !== true ) {
- done = false;
+ anim.always(function() {
+ // doing this makes sure that the complete handler will be called
+ // before this completes
+ anim.always(function() {
+ hooks.unqueued--;
+ if ( !jQuery.queue( elem, "fx" ).length ) {
+ hooks.empty.fire();
}
- }
+ });
+ });
+ }
- if ( done ) {
- // Reset the overflow
- if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+ // height/width overflow pass
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
- jQuery.each( [ "", "X", "Y" ], function( index, value ) {
- elem.style[ "overflow" + value ] = options.overflow[ index ];
- });
- }
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( elem, "display" ) === "inline" &&
+ jQuery.css( elem, "float" ) === "none" ) {
- // Hide the element if the "hide" operation was done
- if ( options.hide ) {
- jQuery( elem ).hide();
- }
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+ style.display = "inline-block";
- // Reset the properties, if the item has been hidden or shown
- if ( options.hide || options.show ) {
- for ( p in options.animatedProperties ) {
- jQuery.style( elem, p, options.orig[ p ] );
- jQuery.removeData( elem, "fxshow" + p, true );
- // Toggle data is no longer needed
- jQuery.removeData( elem, "toggle" + p, true );
- }
- }
+ } else {
+ style.zoom = 1;
+ }
+ }
+ }
- // Execute the complete function
- // in the event that the complete function throws an exception
- // we must ensure it won't be called twice. #5684
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ if ( !jQuery.support.shrinkWrapBlocks ) {
+ anim.done(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+ }
- complete = options.complete;
- if ( complete ) {
- options.complete = false;
- complete.call( elem );
- }
+ // show/hide pass
+ for ( index in props ) {
+ value = props[ index ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ index ];
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+ continue;
}
+ handled.push( index );
+ }
+ }
- return false;
-
+ length = handled.length;
+ if ( length ) {
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+ if ( hidden ) {
+ jQuery( elem ).show();
} else {
- // classical easing cannot be used with an Infinity duration
- if ( options.duration == Infinity ) {
- this.now = t;
- } else {
- n = t - this.startTime;
- this.state = n / options.duration;
+ anim.done(function() {
+ jQuery( elem ).hide();
+ });
+ }
+ anim.done(function() {
+ var prop;
+ jQuery.removeData( elem, "fxshow", true );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( index = 0 ; index < length ; index++ ) {
+ prop = handled[ index ];
+ tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
- // Perform the easing function, defaults to swing
- this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
- this.now = this.start + ( (this.end - this.start) * this.pos );
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
}
- // Perform the next step of the animation
- this.update();
}
-
- return true;
}
-};
+}
-jQuery.extend( jQuery.fx, {
- tick: function() {
- var timer,
- timers = jQuery.timers,
- i = 0;
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
- for ( ; i < timers.length; i++ ) {
- timer = timers[ i ];
- // Checks the timer has not already been removed
- if ( !timer() && timers[ i ] === timer ) {
- timers.splice( i--, 1 );
- }
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+ },
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
+
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
+ },
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
+
+ this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration );
+ this.now = ( this.end - this.start ) * eased + this.start;
+
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
}
- if ( !timers.length ) {
- jQuery.fx.stop();
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
}
- },
+ return this;
+ }
+};
- interval: 13,
+Tween.prototype.init.prototype = Tween.prototype;
- stop: function() {
- clearInterval( timerId );
- timerId = null;
- },
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var result;
- speeds: {
- slow: 600,
- fast: 200,
- // Default speed
- _default: 400
- },
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
+ }
- step: {
- opacity: function( fx ) {
- jQuery.style( fx.elem, "opacity", fx.now );
+ // passing any value as a 4th parameter to .css will automatically
+ // attempt a parseFloat and fallback to a string if the parse fails
+ // so, simple values such as "10px" are parsed to Float.
+ // complex values such as "rotate(1rad)" are returned as is.
+ result = jQuery.css( tween.elem, tween.prop, false, "" );
+ // Empty strings, null, undefined and "auto" are converted to 0.
+ return !result || result === "auto" ? 0 : result;
},
-
- _default: function( fx ) {
- if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
- fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ set: function( tween ) {
+ // use step hook for back compat - use cssHook if its there - use .style if its
+ // available and use plain properties where available
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
} else {
- fx.elem[ fx.prop ] = fx.now;
+ tween.elem[ tween.prop ] = tween.now;
}
}
}
-});
+};
-// Ensure props that can't be negative don't go there on undershoot easing
-jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
- // exclude marginTop, marginLeft, marginBottom and marginRight from this list
- if ( prop.indexOf( "margin" ) ) {
- jQuery.fx.step[ prop ] = function( fx ) {
- jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
- };
+// Remove in 2.0 - this supports IE8's panic based approach
+// to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+ set: function( tween ) {
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
+ tween.elem[ tween.prop ] = tween.now;
+ }
}
-});
+};
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.animated = function( elem ) {
- return jQuery.grep(jQuery.timers, function( fn ) {
- return elem === fn.elem;
- }).length;
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+ var cssFn = jQuery.fn[ name ];
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return speed == null || typeof speed === "boolean" ||
+ // special check for .toggle( handler, handler, ... )
+ ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ?
+ cssFn.apply( this, arguments ) :
+ this.animate( genFx( name, true ), speed, easing, callback );
};
-}
+});
-// Try to restore the default display value of an element
-function defaultDisplay( nodeName ) {
+jQuery.fn.extend({
+ fadeTo: function( speed, to, easing, callback ) {
- if ( !elemdisplay[ nodeName ] ) {
+ // show any hidden elements after setting opacity to 0
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
- var body = document.body,
- elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
- display = elem.css( "display" );
- elem.remove();
+ // animate to the value specified
+ .end().animate({ opacity: to }, speed, easing, callback );
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var empty = jQuery.isEmptyObject( prop ),
+ optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ // Operate on a copy of prop so per-property easing won't be lost
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
- // If the simple way fails,
- // get element's real default display by attaching it to a temp iframe
- if ( display === "none" || display === "" ) {
- // No iframe to use yet, so create it
- if ( !iframe ) {
- iframe = document.createElement( "iframe" );
- iframe.frameBorder = iframe.width = iframe.height = 0;
- }
+ // Empty animations resolve immediately
+ if ( empty ) {
+ anim.stop( true );
+ }
+ };
- body.appendChild( iframe );
+ return empty || optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+ stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( hooks ) {
+ var stop = hooks.stop;
+ delete hooks.stop;
+ stop( gotoEnd );
+ };
+
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var dequeue = true,
+ index = type != null && type + "queueHooks",
+ timers = jQuery.timers,
+ data = jQuery._data( this );
- // Create a cacheable copy of the iframe document on first call.
- // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
- // document to it; WebKit & Firefox won't allow reusing the iframe document.
- if ( !iframeDoc || !iframe.createElement ) {
- iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
- iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
- iframeDoc.close();
+ if ( index ) {
+ if ( data[ index ] && data[ index ].stop ) {
+ stopQueue( data[ index ] );
+ }
+ } else {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+ stopQueue( data[ index ] );
+ }
+ }
}
- elem = iframeDoc.createElement( nodeName );
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ timers[ index ].anim.stop( gotoEnd );
+ dequeue = false;
+ timers.splice( index, 1 );
+ }
+ }
- iframeDoc.body.appendChild( elem );
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( dequeue || !gotoEnd ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ }
+});
- display = jQuery.css( elem, "display" );
- body.removeChild( iframe );
- }
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+ var which,
+ attrs = { height: type },
+ i = 0;
+
+ // if we include width, step value is 1 to do all cssExpand values,
+ // if we don't include width, step value is 2 to skip over Left and Right
+ for( ; i < 4 ; i += 2 - includeWidth ) {
+ which = cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
- // Store the correct default display
- elemdisplay[ nodeName ] = display;
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
}
- return elemdisplay[ nodeName ];
+ return attrs;
}
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
-var getOffset,
- rtable = /^t(?:able|d|h)$/i,
- rroot = /^(?:body|html)$/i;
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
-if ( "getBoundingClientRect" in document.documentElement ) {
- getOffset = function( elem, doc, docElem, box ) {
- try {
- box = elem.getBoundingClientRect();
- } catch(e) {}
+ // Queueing
+ opt.old = opt.complete;
- // Make sure we're not dealing with a disconnected DOM node
- if ( !box || !jQuery.contains( docElem, elem ) ) {
- return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ opt.complete = function() {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
}
- var body = doc.body,
- win = getWindow( doc ),
- clientTop = docElem.clientTop || body.clientTop || 0,
- clientLeft = docElem.clientLeft || body.clientLeft || 0,
- scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
- scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
- top = box.top + scrollTop - clientTop,
- left = box.left + scrollLeft - clientLeft;
-
- return { top: top, left: left };
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ }
};
-} else {
- getOffset = function( elem, doc, docElem ) {
- var computedStyle,
- offsetParent = elem.offsetParent,
- prevOffsetParent = elem,
- body = doc.body,
- defaultView = doc.defaultView,
- prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
- top = elem.offsetTop,
- left = elem.offsetLeft;
-
- while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
- if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
- break;
- }
+ return opt;
+};
- computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
- top -= elem.scrollTop;
- left -= elem.scrollLeft;
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
+ }
+};
- if ( elem === offsetParent ) {
- top += elem.offsetTop;
- left += elem.offsetLeft;
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
- if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
- top += parseFloat( computedStyle.borderTopWidth ) || 0;
- left += parseFloat( computedStyle.borderLeftWidth ) || 0;
- }
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
- prevOffsetParent = offsetParent;
- offsetParent = elem.offsetParent;
- }
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+};
- if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
- top += parseFloat( computedStyle.borderTopWidth ) || 0;
- left += parseFloat( computedStyle.borderLeftWidth ) || 0;
- }
+jQuery.fx.timer = function( timer ) {
+ if ( timer() && jQuery.timers.push( timer ) && !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
- prevComputedStyle = computedStyle;
- }
+jQuery.fx.interval = 13;
- if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
- top += body.offsetTop;
- left += body.offsetLeft;
- }
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
- if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
- top += Math.max( docElem.scrollTop, body.scrollTop );
- left += Math.max( docElem.scrollLeft, body.scrollLeft );
- }
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
- return { top: top, left: left };
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
};
}
+var rroot = /^(?:body|html)$/i;
jQuery.fn.offset = function( options ) {
if ( arguments.length ) {
});
}
- var elem = this[0],
+ var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left,
+ elem = this[ 0 ],
doc = elem && elem.ownerDocument;
if ( !doc ) {
- return null;
+ return;
}
- if ( elem === doc.body ) {
+ if ( (body = doc.body) === elem ) {
return jQuery.offset.bodyOffset( elem );
}
- return getOffset( elem, doc, doc.documentElement );
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return { top: 0, left: 0 };
+ }
+
+ box = elem.getBoundingClientRect();
+ win = getWindow( doc );
+ clientTop = docElem.clientTop || body.clientTop || 0;
+ clientLeft = docElem.clientLeft || body.clientLeft || 0;
+ scrollTop = win.pageYOffset || docElem.scrollTop;
+ scrollLeft = win.pageXOffset || docElem.scrollLeft;
+ top = box.top + scrollTop - clientTop;
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
};
jQuery.offset = {
position: function() {
if ( !this[0] ) {
- return null;
+ return;
}
var elem = this[0],
while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
offsetParent = offsetParent.offsetParent;
}
- return offsetParent;
+ return offsetParent || document.body;
});
}
});
if ( val === undefined ) {
return win ? (prop in win) ? win[ prop ] :
- jQuery.support.boxModel && win.document.documentElement[ method ] ||
- win.document.body[ method ] :
+ win.document.documentElement[ method ] :
elem[ method ];
}
elem.defaultView || elem.parentWindow :
false;
}
-
-
-
-
-// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
- var clientProp = "client" + name,
- scrollProp = "scroll" + name,
- offsetProp = "offset" + name;
-
- // innerHeight and innerWidth
- jQuery.fn[ "inner" + name ] = function() {
- var elem = this[0];
- return elem ?
- elem.style ?
- parseFloat( jQuery.css( elem, type, "padding" ) ) :
- this[ type ]() :
- null;
- };
-
- // outerHeight and outerWidth
- jQuery.fn[ "outer" + name ] = function( margin ) {
- var elem = this[0];
- return elem ?
- elem.style ?
- parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
- this[ type ]() :
- null;
- };
-
- jQuery.fn[ type ] = function( value ) {
- return jQuery.access( this, function( elem, type, value ) {
- var doc, docElemProp, orig, ret;
-
- if ( jQuery.isWindow( elem ) ) {
- // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
- doc = elem.document;
- docElemProp = doc.documentElement[ clientProp ];
- return jQuery.support.boxModel && docElemProp ||
- doc.body && doc.body[ clientProp ] || docElemProp;
- }
-
- // Get document width or height
- if ( elem.nodeType === 9 ) {
- // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
- doc = elem.documentElement;
-
- // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height]
- // so we can't use max, as it'll choose the incorrect offset[Width/Height]
- // instead we use the correct client[Width/Height]
- // support:IE6
- if ( doc[ clientProp ] >= doc[ scrollProp ] ) {
- return doc[ clientProp ];
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
}
- return Math.max(
- elem.body[ scrollProp ], doc[ scrollProp ],
- elem.body[ offsetProp ], doc[ offsetProp ]
- );
- }
-
- // Get width or height on the element
- if ( value === undefined ) {
- orig = jQuery.css( elem, type );
- ret = parseFloat( orig );
- return jQuery.isNumeric( ret ) ? ret : orig;
- }
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, value, extra ) :
- // Set the width or height on the element
- jQuery( elem ).css( type, value );
- }, type, value, arguments.length, null );
- };
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable );
+ };
+ });
});
-
-
-
-
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
define( "jquery", [], function () { return jQuery; } );
}
-
-
})( window );
/**
* Simple Placeholder-based Localization
*
- * Call on a selection of HTML which contains <html:msg key="message-key" /> elements or elements with
- * title-msg="message-key" or alt-msg="message-key" attributes. <html:msg /> elements will be replaced
- * with localized text, elements with title-msg and alt-msg attributes will receive localized title
- * and alt attributes.
- * *
+ * Call on a selection of HTML which contains <html:msg key="message-key" /> elements or elements
+ * with title-msg="message-key", alt-msg="message-key" or placeholder-msg="message-key" attributes.
+ * <html:msg /> elements will be replaced with localized text, *-msg attributes will be replaced
+ * with attributes that do not have the "-msg" suffix and contain a localized message.
+ *
+ * Example:
+ * // Messages: { 'title': 'Awesome', 'desc': 'Cat doing backflip' 'search' contains 'Search' }
+ * var html = '\
+ * <p>\
+ * <html:msg key="title" />\
+ * <img src="something.jpg" title-msg="title" alt-msg="desc" />\
+ * <input type="text" placeholder-msg="search" />\
+ * </p>';
+ * $( 'body' ).append( $( html ).localize() );
+ *
+ * Appends something like this to the body...
+ * <p>
+ * Awesome
+ * <img src="something.jpg" title="Awesome" alt="Cat doing backflip" />
+ * <input type="text" placeholder="Search" />
+ * </p>
+ *
+ * Arguments can be passed into uses of a message using the params property of the options object
+ * given to .localize(). Multiple messages can be given parameters, because the params property is
+ * an object keyed by the message key to apply the parameters to, each containing an array of
+ * parameters to use. The limitation is that you can not use different parameters to individual uses
+ * of a message in the same selection being localized - they will all recieve the same parameters.
+ *
+ * Example:
+ * // Messages: { 'easy-as': 'Easy as $1 $2 $3.' }
+ * var html = '<p><html:msg key="easy-as" /></p>';
+ * $( 'body' ).append( $( html ).localize( { 'params': { 'easy-as': ['a', 'b', 'c'] } } ) );
+ *
+ * Appends something like this to the body...
+ * <p>Easy as a, b, c</p>
+ *
+ * Raw HTML content can be used, instead of it being escaped as text. To do this, just use the raw
+ * attribute on a msg element.
+ *
+ * Example:
+ * // Messages: { 'hello': '<b><i>Hello</i> $1!</b>' }
+ * var html = '\
+ * <p>\
+ * <!-- escaped: --><html:msg key="hello" />\
+ * <!-- raw: --><html:msg key="hello" raw />\
+ * </p>';
+ * $( 'body' ).append( $( html ).localize( { 'params': { 'hello': ['world'] } } ) );
+ *
+ * Appends something like this to the body...
+ * <p>
+ * <!-- escaped: --><b><i>Hello</i> world!</b>
+ * <!-- raw: --><b><i>Hello</i> world!</b>
+ * </p>
+ *
+ * Message keys can also be remapped, allowing the same generic template to be used with a variety
+ * of messages. This is important for improving re-usability of templates.
+ *
* Example:
- * <p class="somethingCool">
- * <html:msg key="my-message" />
- * <img src="something.jpg" title-msg="my-title-message" alt-msg="my-alt-message" />
- * </p>
- *
- * Localizes to...
- * <p class="somethingCool">
- * My Message
- * <img src="something.jpg" title="My Title Message" alt="My Alt Message" />
- * </p>
+ * // Messages: { 'good-afternoon': 'Good afternoon' }
+ * var html = '<p><html:msg key="greeting" /></p>';
+ * $( 'body' ).append( $( html ).localize( { 'keys': { 'greeting': 'good-afternoon' } } ) );
+ *
+ * Appends something like this to the body...
+ * <p>Good afternoon</p>
+ *
+ * Message keys can also be prefixed globally, which is handy when writing extensions, where by
+ * convention all messages are prefixed with the extension's name.
+ *
+ * Example:
+ * // Messages: { 'teleportation-warning': 'You may not get there all in one piece.' }
+ * var html = '<p><html:msg key="warning" /></p>';
+ * $( 'body' ).append( $( html ).localize( { 'prefix': 'teleportation-' } ) );
+ *
+ * Appends something like this to the body...
+ * <p>You may not get there all in one piece.</p>
+ *
*/
( function ( $, mw ) {
+
+/**
+ * Gets a localized message, using parameters from options if present.
+ *
+ * @function
+ * @param {String} key Message key to get localized message for
+ * @returns {String} Localized message
+ */
+function msg( options, key ) {
+ var args = options.params[key] || [];
+ // Format: mw.msg( key [, p1, p2, ...] )
+ args.unshift( options.prefix + ( options.keys[key] || key ) );
+ return mw.msg.apply( mw, args );
+}
+
/**
* Localizes a DOM selection by replacing <html:msg /> elements with localized text and adding
* localized title and alt attributes to elements with title-msg and alt-msg attributes
* respectively.
*
- * @param Object: options Map of options
- * * prefix: Message prefix to use when localizing elements and attributes
+ * @method
+ * @param {Object} options Map of options to be used while localizing
+ * @param {String} options.prefix String to prepend to all message keys
+ * @param {Object} options.keys Message key aliases, used for remapping keys to a template
+ * @param {Object} options.params Lists of parameters to use with certain message keys
+ * @returns {jQuery} This selection
*/
-
$.fn.localize = function ( options ) {
+ var $target = this,
+ attributes = ['title', 'alt', 'placeholder'];
+
+ // Extend options
options = $.extend( {
prefix: '',
keys: {},
params: {}
}, options );
- function msg( key ) {
- var args = key in options.params ? options.params[key] : [];
- // Format: mw.msg( key [, p1, p2, ...] )
- args.unshift( options.prefix + ( key in options.keys ? options.keys[key] : key ) );
- return mw.msg.apply( mw, args );
- }
+ // Elements
+ // Ok, so here's the story on this selector. In IE 6/7, searching for 'msg' turns up the
+ // 'html:msg', but searching for 'html:msg' doesn't. In later IE and other browsers, searching
+ // for 'html:msg' turns up the 'html:msg', but searching for 'msg' doesn't. So searching for
+ // both 'msg' and 'html:msg' seems to get the job done. This feels pretty icky, though.
+ $target.find( 'msg,html\\:msg' ).each( function () {
+ var $el = $(this);
+ // Escape by default
+ if ( $el.attr( 'raw' ) ) {
+ $el.html( msg( options, $el.attr( 'key' ) ) );
+ } else {
+ $el.text( msg( options, $el.attr( 'key' ) ) );
+ }
+ // Remove wrapper
+ $el.replaceWith( $el.html() );
+ } );
- return $(this)
- // Ok, so here's the story on this selector.
- // In IE 6/7, searching for 'msg' turns up the 'html:msg', but searching for 'html:msg' does not.
- // In later IE and other browsers, searching for 'html:msg' turns up the 'html:msg', but searching for 'msg' does not.
- // So searching for both 'msg' and 'html:msg' seems to get the job done.
- // This feels pretty icky, though.
- .find( 'msg,html\\:msg' )
- .each( function () {
- var $el = $(this);
- var msgText = msg( $el.attr( 'key' ) );
+ // Attributes
+ // Note: there's no way to prevent escaping of values being injected into attributes, this is
+ // on purpose, not a design flaw.
+ $.each( attributes, function ( i, attr ) {
+ var msgAttr = attr + '-msg';
+ $target.find( '[' + msgAttr + ']' ).each( function () {
+ var $el = $(this);
+ $el.attr( attr, msg( options, $el.attr( msgAttr ) ) ).removeAttr( msgAttr );
+ } );
+ } );
- if ( $el.attr( 'raw' ) ) {
- $el.html(msgText);
- } else {
- $el.text(msgText);
- }
-
- $el
- .replaceWith( $el.html() );
- } )
- .end()
- .find( '[title-msg]' )
- .each( function () {
- var $el = $(this);
- $el
- .attr( 'title', msg( $el.attr( 'title-msg' ) ) )
- .removeAttr( 'title-msg' );
- } )
- .end()
- .find( '[alt-msg]' )
- .each( function () {
- var $el = $(this);
- $el
- .attr( 'alt', msg( $el.attr( 'alt-msg' ) ) )
- .removeAttr( 'alt-msg' );
- } )
- .end();
+ return $target;
};
// Let IE know about the msg tag before it's used...
$.fn.makeCollapsible = function () {
return this.each(function () {
- var _fn = 'jquery.makeCollapsible> ';
+ var lpx = 'jquery.makeCollapsible> ';
// Define reused variables and functions
var $toggle,
var thatId = $that.attr( 'id' ),
$customTogglers = $( '.' + thatId.replace( 'mw-customcollapsible', 'mw-customtoggle' ) );
- mw.log( _fn + 'Found custom collapsible: #' + thatId );
+ mw.log( lpx + 'Found custom collapsible: #' + thatId );
// Double check that there is actually a customtoggle link
if ( $customTogglers.length ) {
toggleLinkCustom( $(this), e, $that );
} );
} else {
- mw.log( _fn + '#' + thatId + ': Missing toggler!' );
+ mw.log( lpx + '#' + thatId + ': Missing toggler!' );
}
// Initial state
/**
- * QUnit v1.8.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.9.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
line-height: 1em;
font-weight: normal;
- border-radius: 15px 15px 0 0;
- -moz-border-radius: 15px 15px 0 0;
- -webkit-border-top-right-radius: 15px;
- -webkit-border-top-left-radius: 15px;
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ -webkit-border-top-right-radius: 5px;
+ -webkit-border-top-left-radius: 5px;
}
#qunit-header a {
color: #fff;
}
-#qunit-header label {
+#qunit-testrunner-toolbar label {
display: inline-block;
- padding-left: 0.5em;
+ padding: 0 .5em 0 .1em;
}
#qunit-banner {
background-color: #fff;
- border-radius: 15px;
- -moz-border-radius: 15px;
- -webkit-border-radius: 15px;
-
- box-shadow: inset 0px 2px 13px #999;
- -moz-box-shadow: inset 0px 2px 13px #999;
- -webkit-box-shadow: inset 0px 2px 13px #999;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
}
#qunit-tests table {
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
- margin: 0.5em;
- padding: 0.4em 0.5em 0.4em 0.5em;
+ padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
/*** Passing Styles */
#qunit-tests li li.pass {
- color: #5E740B;
+ color: #3c510c;
background-color: #fff;
- border-left: 26px solid #C6E746;
+ border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
- border-left: 26px solid #EE5757;
+ border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
- border-radius: 0 0 15px 15px;
- -moz-border-radius: 0 0 15px 15px;
- -webkit-border-bottom-right-radius: 15px;
- -webkit-border-bottom-left-radius: 15px;
+ border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
/**
- * QUnit v1.8.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.9.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
QUnit.assert = {
/**
* Asserts rough true-ish result.
+ * @name ok
+ * @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function( result, msg ) {
/**
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
+ * @name equal
+ * @function
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function( actual, expected, message ) {
QUnit.push( expected == actual, actual, expected, message );
},
+ /**
+ * @name notEqual
+ * @function
+ */
notEqual: function( actual, expected, message ) {
QUnit.push( expected != actual, actual, expected, message );
},
+ /**
+ * @name deepEqual
+ * @function
+ */
deepEqual: function( actual, expected, message ) {
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
},
+ /**
+ * @name notDeepEqual
+ * @function
+ */
notDeepEqual: function( actual, expected, message ) {
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
},
+ /**
+ * @name strictEqual
+ * @function
+ */
strictEqual: function( actual, expected, message ) {
QUnit.push( expected === actual, actual, expected, message );
},
+ /**
+ * @name notStrictEqual
+ * @function
+ */
notStrictEqual: function( actual, expected, message ) {
QUnit.push( expected !== actual, actual, expected, message );
},
- raises: function( block, expected, message ) {
+ throws: function( block, expected, message ) {
var actual,
ok = false;
+ // 'expected' is optional
if ( typeof expected === "string" ) {
message = expected;
expected = null;
} else if ( expected.call( {}, actual ) === true ) {
ok = true;
}
- }
- QUnit.push( ok, actual, null, message );
+ QUnit.push( ok, actual, null, message );
+ } else {
+ QUnit.pushFailure( message, null, 'No exception was thrown.' );
+ }
}
};
-// @deprecated: Kept assertion helpers in root for backwards compatibility
+/**
+ * @deprecate since 1.8.0
+ * Kept assertion helpers in root for backwards compatibility
+ */
extend( QUnit, QUnit.assert );
/**
- * @deprecated: Kept for backwards compatibility
- * next step: remove entirely
+ * @deprecated since 1.9.0
+ * Kept global "raises()" for backwards compatibility
+ */
+QUnit.raises = QUnit.assert.throws;
+
+/**
+ * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
+ * Kept to avoid TypeErrors for undefined methods.
*/
QUnit.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
// when enabled, all tests must call expect()
requireExpects: false,
- urlConfig: [ "noglobals", "notrycatch" ],
+ // add checkboxes that are persisted in the query-string
+ // when enabled, the id is set to `true` as a `QUnit.config` property
+ urlConfig: [
+ {
+ id: "noglobals",
+ label: "Check for Globals",
+ tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
+ },
+ {
+ id: "notrycatch",
+ label: "No try-catch",
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
+ }
+ ],
// logging callback queues
begin: [],
});
},
- pushFailure: function( message, source ) {
+ pushFailure: function( message, source, actual ) {
if ( !config.current ) {
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
}
message: message
};
- message = escapeInnerText(message ) || "error";
+ message = escapeInnerText( message ) || "error";
message = "<span class='test-message'>" + message + "</span>";
output = message;
+ output += "<table>";
+
+ if ( actual ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>";
+ }
+
if ( source ) {
details.source = source;
- output += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
}
+ output += "</table>";
+
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue
- var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes,
urlConfigHtml = "",
oldconfig = extend( {}, config );
for ( i = 0; i < len; i++ ) {
val = config.urlConfig[i];
- config[val] = QUnit.urlParams[val];
- urlConfigHtml += "<label><input name='" + val + "' type='checkbox'" + ( config[val] ? " checked='checked'" : "" ) + ">" + val + "</label>";
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val,
+ tooltip: "[no tooltip available]"
+ };
+ }
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
}
// `userAgent` initialized at top of scope
// `banner` initialized at top of scope
banner = id( "qunit-header" );
if ( banner ) {
- banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined }) + "'>" + banner.innerHTML + "</a> " + urlConfigHtml;
- addEvent( banner, "change", function( event ) {
- var params = {};
- params[ event.target.name ] = event.target.checked ? true : undefined;
- window.location = QUnit.url( params );
- });
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
}
// `toolbar` initialized at top of scope
// `label` initialized at top of scope
label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" );
+ label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
+
+ urlConfigCheckboxes = document.createElement( 'span' );
+ urlConfigCheckboxes.innerHTML = urlConfigHtml;
+ addEvent( urlConfigCheckboxes, "change", function( event ) {
+ var params = {};
+ params[ event.target.name ] = event.target.checked ? true : undefined;
+ window.location = QUnit.url( params );
+ });
+ toolbar.appendChild( urlConfigCheckboxes );
}
// `main` initialized at top of scope
function validTest( test ) {
var include,
filter = config.filter && config.filter.toLowerCase(),
- module = config.module,
+ module = config.module && config.module.toLowerCase(),
fullName = (test.module + ": " + test.testName).toLowerCase();
if ( config.testNumber ) {
return test.testNumber === config.testNumber;
}
- if ( module && test.module !== module ) {
+ if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
return false;
}
}
function sortText( a, b ) {
- return ( (a < b) ? false : ((a > b) ? true : 0) );
+ return ( (a < b) ? -1 : ((a > b) ? 1 : 0) );
}
function sortTextDesc( a, b ) {
- return ( (b < a) ? false : ((b > a) ? true : 0) );
+ return ( (b < a) ? -1 : ((b > a) ? 1 : 0) );
}
- function checkSorting( array1, array2, sortList ) {
- var col, fn, ret;
- for ( var i = 0, len = sortList.length; i < len; i++ ) {
- col = sortList[i][0];
- fn = ( sortList[i][1] ) ? sortTextDesc : sortText;
- ret = fn.call( this, array1[col], array2[col] );
- if ( ret !== 0 ) {
- return ret;
- }
+ function multisort( table, sortList, cache ) {
+ var sortFn = [];
+ var len = sortList.length;
+ for ( var i = 0; i < len; i++ ) {
+ sortFn[i] = ( sortList[i][1] ) ? sortTextDesc : sortText;
}
- return ret;
- }
-
- // Merge sort algorithm
- // Based on http://en.literateprograms.org/Merge_sort_(JavaScript)
- function mergeSortHelper( array, begin, beginRight, end, sortList ) {
- for ( ; begin < beginRight; ++begin ) {
- if ( checkSorting( array[begin], array[beginRight], sortList ) ) {
- var v = array[begin];
- array[begin] = array[beginRight];
- var begin2 = beginRight;
- while ( begin2 + 1 < end && checkSorting( v, array[begin2 + 1], sortList ) ) {
- var tmp = array[begin2];
- array[begin2] = array[begin2 + 1];
- array[begin2 + 1] = tmp;
- ++begin2;
+ cache.normalized.sort( function ( array1, array2 ) {
+ var col, ret;
+ for ( var i = 0; i < len; i++ ) {
+ col = sortList[i][0];
+ ret = sortFn[i].call( this, array1[col], array2[col] );
+ if ( ret !== 0 ) {
+ return ret;
}
- array[begin2] = v;
}
- }
- }
-
- function mergeSort(array, begin, end, sortList) {
- var size = end - begin;
- if ( size < 2 ) {
- return;
- }
-
- var beginRight = begin + Math.floor(size / 2);
-
- mergeSort( array, begin, beginRight, sortList );
- mergeSort( array, beginRight, end, sortList );
- mergeSortHelper( array, begin, beginRight, end, sortList );
- }
-
- function multisort( table, sortList, cache ) {
- var i = sortList.length;
- mergeSort( cache.normalized, 0, cache.normalized.length, sortList );
-
+ // Fall back to index number column to ensure stable sort
+ return sortText.call( this, array1[array1.length - 1], array2[array2.length - 1] );
+ } );
return cache;
}
// On IE, patch the focus() method to restore the windows' scroll position
// (bug 32241)
$.fn.extend({
- focus : ( function ( _focus ) {
+ focus: ( function ( jqFocus ) {
return function () {
if ( arguments.length === 0 ) {
var $w = $( window );
var state = {top: $w.scrollTop(), left: $w.scrollLeft()};
- var result = _focus.apply( this, arguments );
+ var result = jqFocus.apply( this, arguments );
window.scrollTo( state.top, state.left );
return result;
}
- return _focus.apply( this, arguments );
+ return jqFocus.apply( this, arguments );
};
}( $.fn.focus ) )
});
selectText: selectText
};
}
- var $image = $('<img>', {
+ var $image = $( '<img>', {
width : 23,
height: 22,
src : b.imageFile,
mw.toolbar = toolbar;
$( document ).ready( function () {
- var buttons, i, b, iframe;
+ var buttons, i, b, $iframe;
// currentFocus is used to determine where to insert tags
currentFocused = $( '#wpTextbox1' );
// HACK: make currentFocused work with the usability iframe
// With proper focus detection support (HTML 5!) this'll be much cleaner
- iframe = $( '.wikiEditor-ui-text iframe' );
- if ( iframe.length > 0 ) {
- $( iframe.get( 0 ).contentWindow.document )
+ // TODO: Get rid of this WikiEditor code from MediaWiki core!
+ $iframe = $( '.wikiEditor-ui-text iframe' );
+ if ( $iframe.length > 0 ) {
+ $( $iframe.get( 0 ).contentWindow.document )
// for IE
- .add( iframe.get( 0 ).contentWindow.document.body )
+ .add( $iframe.get( 0 ).contentWindow.document.body )
.focus( function () {
- currentFocused = iframe;
+ currentFocused = $iframe;
} );
}
});
return true;
}
- if ( $oldidRadio.prop( 'checked' ) ) {
+ if ( $oldidRadio.prop( 'checked' ) ) {
oldLi = true;
$li.addClass( 'selected' );
$oldidRadio.css( 'visibility', 'visible' );
$diffRadio.css( 'visibility', 'hidden' );
- } else if ( $diffRadio.prop( 'checked' ) ) {
+ } else if ( $diffRadio.prop( 'checked' ) ) {
diffLi = true;
$li.addClass( 'selected' );
$oldidRadio.css( 'visibility', 'hidden' );
$diffRadio.css( 'visibility', 'visible' );
// This list item has neither checked
- } else {
+ } else {
// We're below the selected radios
if ( diffLi && oldLi ) {
$oldidRadio.css( 'visibility', 'visible' );
/**
* This module enables double-click-to-edit functionality
*/
-jQuery( document ).ready( function( $ ) {
- var url = $( '#ca-edit a' ).attr( 'href' );
- if ( url ) {
- mw.util.$content.dblclick( function( e ) {
- e.preventDefault();
- window.location = url;
- } );
- }
-} );
+( function ( mw, $ ) {
+ $( function () {
+ var url = $( '#ca-edit a' ).attr( 'href' );
+ if ( url ) {
+ mw.util.$content.dblclick( function ( e ) {
+ e.preventDefault();
+ window.location = url;
+ } );
+ }
+ } );
+}( mediaWiki, jQuery ) );
-// Exif metadata display for MediaWiki file uploads
-//
-// Add an expand/collapse link and collapse by default if set to
-// (with JS disabled, user will see all items)
-//
+/**
+ * Exif metadata display for MediaWiki file uploads
+ *
+ * Add an expand/collapse link and collapse by default if set to
+ * (with JS disabled, user will see all items)
+ */
+( function ( mw, $ ) {
+ $( function () {
+ var $row, $col, $link,
+ showText = mw.msg( 'metadata-expand' ),
+ hideText = mw.msg( 'metadata-collapse' ),
+ $table = $( '#mw_metadata' ),
+ $tbody = $table.find( 'tbody' );
-jQuery( document ).ready( function( $ ) {
- var showText = mw.msg( 'metadata-expand' );
- var hideText = mw.msg( 'metadata-collapse' );
+ if ( !$tbody.length ) {
+ return;
+ }
- var $table = $( '#mw_metadata' );
- var $tbody = $table.find( 'tbody' );
- if ( !$tbody.length ) {
- return;
- }
+ $row = $( '<tr class="mw-metadata-show-hide-extended"></tr>' );
+ $col = $( '<td colspan="2"></td>' );
- var $row = $( '<tr class="mw-metadata-show-hide-extended"></tr>' );
- var $col = $( '<td colspan="2"></td>' );
+ $link = $( '<a>', {
+ text: showText,
+ href: '#'
+ }).click(function () {
+ if ( $table.hasClass( 'collapsed' ) ) {
+ $( this ).text( hideText );
+ } else {
+ $( this ).text( showText );
+ }
+ $table.toggleClass( 'expanded collapsed' );
+ return false;
+ });
- var $link = $( '<a></a>', {
- 'text': showText,
- 'href': '#'
- }).click(function() {
- if ( $table.hasClass( 'collapsed' ) ) {
- $( this ).text( hideText );
- } else {
- $( this ).text( showText );
- }
- $table.toggleClass( 'expanded collapsed' );
- return false;
- });
+ $col.append( $link );
+ $row.append( $col );
+ $tbody.append( $row );
- $col.append( $link );
- $row.append( $col );
- $tbody.append( $row );
+ // And collapse!
+ $table.addClass( 'collapsed' );
+ } );
- // And collapse!
- $table.addClass( 'collapsed' );
-} );
+}( mediaWiki, jQuery ) );
\ No newline at end of file
'file-too-large',
'filetype-missing',
'filetype-banned',
+ 'filetype-banned-type',
'filename-tooshort',
'illegal-filename',
'verification-error',
* Base language object with methods for storing and getting
* language data.
*/
-( function( $, mw ) {
+( function ( mw, $ ) {
var language = {
/**
mw.language = language;
-} )( jQuery, mediaWiki );
+}( mediaWiki, jQuery ) );
* Language.php in MediaWiki.
* This adds methods for transforming message text.
*/
-( function( $, mw ) {
+( function ( mw, $ ) {
var language = {
*
* @param {object} template Template object
* @format template
- * {
- * 'title': [title of template],
- * 'parameters': [template parameters]
- * }
+ * {
+ * 'title': [title of template],
+ * 'parameters': [template parameters]
+ * }
* @example {{Template:title|params}}
*/
- 'procPLURAL': function( template ) {
+ procPLURAL: function ( template ) {
if ( template.title && template.parameters && mw.language.convertPlural ) {
// Check if we have forms to replace
if ( template.parameters.length === 0 ) {
}
return '';
},
+
/**
* Plural form transformations, needed for some languages.
*
* @param forms array List of plural forms
* @return string Correct form for quantifier in this language
*/
- 'convertPlural': function( count, forms ){
+ convertPlural: function ( count, forms ){
if ( !forms || forms.length === 0 ) {
return '';
}
- return ( parseInt( count, 10 ) == 1 ) ? forms[0] : forms[1];
+ return ( parseInt( count, 10 ) === 1 ) ? forms[0] : forms[1];
},
+
/**
* Pads an array to a specific length by copying the last one element.
*
* @param count integer Number of forms required
* @return array Padded array of forms
*/
- 'preConvertPlural': function( forms, count ) {
+ preConvertPlural: function ( forms, count ) {
while ( forms.length < count ) {
forms.push( forms[ forms.length-1 ] );
}
return forms;
},
+
/**
* Converts a number using digitTransformTable.
*
* @param {num} number Value to be converted
* @param {boolean} integer Convert the return value to an integer
*/
- 'convertNumber': function( num, integer ) {
+ convertNumber: function( num, integer ) {
+ var i, tmp, transformTable;
+
if ( !mw.language.digitTransformTable ) {
return num;
}
// Set the target Transform table:
- var transformTable = mw.language.digitTransformTable;
+ transformTable = mw.language.digitTransformTable;
// Check if the "restore" to Latin number flag is set:
if ( integer ) {
- if ( parseInt( num, 10 ) == num ) {
+ if ( parseInt( num, 10 ) === num ) {
return num;
}
- var tmp = [];
- for ( var i in transformTable ) {
+ tmp = [];
+ for ( i in transformTable ) {
tmp[ transformTable[ i ] ] = i;
}
transformTable = tmp;
}
- var numberString = '' + num;
+ var numberString = '' + num;
var convertedNumber = '';
- for ( var i = 0; i < numberString.length; i++ ) {
+ for ( i = 0; i < numberString.length; i++ ) {
if ( transformTable[ numberString[i] ] ) {
convertedNumber += transformTable[numberString[i]];
} else {
}
return integer ? parseInt( convertedNumber, 10 ) : convertedNumber;
},
+
/**
* Provides an alternative text depending on specified gender.
* Usage {{gender:[gender|user object]|masculine|feminine|neutral}}.
*
* @return string
*/
- 'gender': function( gender, forms ) {
+ gender: function( gender, forms ) {
if ( !forms || forms.length === 0 ) {
return '';
}
},
// Digit Transform Table, populated by language classes where applicable
- 'digitTransformTable': mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'digitTransformTable' )
+ digitTransformTable: mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'digitTransformTable' )
};
$.extend( mw.language, language );
-} )( jQuery, mediaWiki );
+}( mediaWiki, jQuery ) );
-( function ( $ ) {
+( function ( mw, $ ) {
mw.page = {};
// is defined for them.
$( mw.util.init );
-} )( jQuery );
+}( mediaWiki, jQuery ) );
* Animate watch/unwatch links to use asynchronous API requests to
* watch pages, rather than navigating to a different URI.
*/
-( function ( $, mw, undefined ) {
+( function ( mw, $ ) {
/**
* The name of the page to watch or unwatch.
*/
var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
- // Expose local methods
- mw.page.watch = {
- 'updateWatchLink': updateWatchLink
- };
/**
* Update the link text, link href attribute and (if applicable)
* "loading" class.
return 'view';
}
+ // Expose local methods
+ mw.page.watch = {
+ 'updateWatchLink': updateWatchLink
+ };
+
$( document ).ready( function () {
var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
'#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
});
});
-}( jQuery, mediaWiki ) );
+}( mediaWiki, jQuery ) );
/*
* JavaScript for Special:ChangeEmail
*/
-( function( $, mw ) {
+( function ( mw, $ ) {
/**
* Given an email validity status (true, false, null) update the label CSS class
*/
-var updateMailValidityLabel = function( mail ) {
+function updateMailValidityLabel( mail ) {
var isValid = mw.util.validateEmail( mail ),
$label = $( '#mw-emailaddress-validity' );
} else {
$label.text( mw.msg( 'email-address-validity-invalid' ) ).addClass( 'invalid' ).removeClass( 'valid' );
}
-};
+}
-$( document ).ready( function() {
+$( document ).ready( function () {
// Lame tip to let user know if its email is valid. See bug 22449
// Only bind once for 'blur' so that the user can fill it in without errors
// After that look at every keypress for direct feedback if it was invalid onblur
- $( '#wpNewEmail' ).one( 'blur', function() {
+ $( '#wpNewEmail' ).one( 'blur', function () {
if ( $( '#mw-emailaddress-validity' ).length === 0 ) {
$(this).after( '<label for="wpNewEmail" id="mw-emailaddress-validity"></label>' );
}
updateMailValidityLabel( $(this).val() );
- $(this).keyup( function() {
+ $(this).keyup( function () {
updateMailValidityLabel( $(this).val() );
} );
} );
} );
-} )( jQuery, mediaWiki );
+}( mediaWiki, jQuery ) );
-/*
+/**
* JavaScript for Special:JavaScriptTest
*/
-jQuery( document ).ready( function( $ ) {
+( function ( mw, $ ) {
+ $( function () {
- // Create useskin dropdown menu and reload onchange to the selected skin
- // (only if a framework was found, not on error pages).
- $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function() {
+ // Create useskin dropdown menu and reload onchange to the selected skin
+ // (only if a framework was found, not on error pages).
+ $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function () {
- var $html = $( '<p><label for="useskin">'
- + mw.message( 'javascripttest-pagetext-skins' ).escaped()
- + ' '
- + '</label></p>' ),
- select = '<select name="useskin" id="useskin">';
+ var $html = $( '<p><label for="useskin">'
+ + mw.message( 'javascripttest-pagetext-skins' ).escaped()
+ + ' '
+ + '</label></p>' ),
+ select = '<select name="useskin" id="useskin">';
- // Build <select> further
- $.each( mw.config.get( 'wgAvailableSkins' ), function( id ) {
- select += '<option value="' + id + '"'
- + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
- + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
- } );
- select += '</select>';
+ // Build <select> further
+ $.each( mw.config.get( 'wgAvailableSkins' ), function ( id ) {
+ select += '<option value="' + id + '"'
+ + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
+ + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
+ } );
+ select += '</select>';
- // Bind onchange event handler and append to form
- $html.append(
- $( select ).change( function() {
- window.location = QUnit.url( { useskin: $(this).val() } );
- } )
- );
+ // Bind onchange event handler and append to form
+ $html.append(
+ $( select ).change( function () {
+ window.location = QUnit.url( { useskin: $(this).val() } );
+ } )
+ );
- return $html;
+ return $html;
+ } );
} );
-} );
+
+}( mediaWiki, jQuery ) );
+
/* JavaScript for Special:RecentChanges */
-( function( $ ) {
+( function ( mw, $ ) {
var checkboxes = [ 'nsassociated', 'nsinvert' ];
* Handler to disable/enable the namespace selector checkboxes when the
* special 'all' namespace is selected/unselected respectively.
*/
- updateCheckboxes: function() {
+ updateCheckboxes: function () {
// The option element for the 'all' namespace has an empty value
- var isAllNS = ('' === $select.find('option:selected').val() );
+ var isAllNS = $select.find('option:selected').val() === '';
// Iterates over checkboxes and propagate the selected option
- $.each( checkboxes, function( i, id ) {
+ $.each( checkboxes, function ( i, id ) {
$( '#' + id ).prop( 'disabled', isAllNS );
});
},
- init: function() {
+ init: function () {
// Populate
$select = $( '#namespace' );
// Run when document is ready
$( rc.init );
-})( jQuery );
+}( mediaWiki, jQuery ) );
-/*
+/**
* JavaScript for Special:Upload
* Note that additional code still lives in skins/common/upload.js
*/
-
-/**
- * Add a preview to the upload form
- */
-jQuery( function( $ ) {
- /**
- * Is the FileAPI available with sufficient functionality?
- */
- function hasFileAPI(){
- return typeof window.FileReader !== 'undefined';
- }
-
+( function ( mw, $ ) {
/**
- * Check if this is a recognizable image type...
- * Also excludes files over 10M to avoid going insane on memory usage.
- *
- * @todo is there a way we can ask the browser what's supported in <img>s?
- * @todo put SVG back after working around Firefox 7 bug <https://bugzilla.wikimedia.org/show_bug.cgi?id=31643>
- *
- * @param {File} file
- * @return boolean
+ * Add a preview to the upload form
*/
- function fileIsPreviewable( file ) {
- var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'],
- tooHuge = 10 * 1024 * 1024;
- return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge;
- }
+ $( function ( $ ) {
+ /**
+ * Is the FileAPI available with sufficient functionality?
+ */
+ function hasFileAPI() {
+ return typeof window.FileReader !== 'undefined';
+ }
- /**
- * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload
- * in browsers supporting HTML5 FileAPI.
- *
- * As of this writing, known good:
- * - Firefox 3.6+
- * - Chrome 7.something
- *
- * @todo check file size limits and warn of likely failures
- *
- * @param {File} file
- */
- function showPreview( file ) {
- var previewSize = 180,
- thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
- '<div class="thumbinner">' +
- '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' +
- '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
- '</div>' +
- '</div>' );
- thumb.find( '.filename' ).text( file.name ).end()
- .find( '.fileinfo' ).text( prettySize( file.size ) ).end();
+ /**
+ * Check if this is a recognizable image type...
+ * Also excludes files over 10M to avoid going insane on memory usage.
+ *
+ * @todo is there a way we can ask the browser what's supported in <img>s?
+ * @todo put SVG back after working around Firefox 7 bug <https://bugzilla.wikimedia.org/show_bug.cgi?id=31643>
+ *
+ * @param {File} file
+ * @return boolean
+ */
+ function fileIsPreviewable( file ) {
+ var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'],
+ tooHuge = 10 * 1024 * 1024;
+ return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge;
+ }
- var $canvas = $('<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>'),
- ctx = $canvas[0].getContext( '2d' );
- $( '#mw-htmlform-source' ).parent().prepend( thumb );
+ /**
+ * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload
+ * in browsers supporting HTML5 FileAPI.
+ *
+ * As of this writing, known good:
+ * - Firefox 3.6+
+ * - Chrome 7.something
+ *
+ * @todo check file size limits and warn of likely failures
+ *
+ * @param {File} file
+ */
+ function showPreview( file ) {
+ var previewSize = 180,
+ thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
+ '<div class="thumbinner">' +
+ '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' +
+ '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
+ '</div>' +
+ '</div>' );
+ thumb.find( '.filename' ).text( file.name ).end()
+ .find( '.fileinfo' ).text( prettySize( file.size ) ).end();
- var meta;
- fetchPreview( file, function( dataURL ) {
- var img = new Image(),
- rotation = 0;
+ var $canvas = $('<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>'),
+ ctx = $canvas[0].getContext( '2d' );
+ $( '#mw-htmlform-source' ).parent().prepend( thumb );
- if ( meta && meta.tiff && meta.tiff.Orientation ) {
- rotation = (360 - function () {
- // See includes/media/Bitmap.php
- switch ( meta.tiff.Orientation.value ) {
- case 8:
- return 90;
- case 3:
- return 180;
- case 6:
- return 270;
- default:
- return 0;
- }
- }() ) % 360;
- }
+ var meta;
+ fetchPreview( file, function( dataURL ) {
+ var img = new Image(),
+ rotation = 0;
- img.onload = function() {
- var width, height, x, y, dx, dy, logicalWidth, logicalHeight;
- // Fit the image within the previewSizexpreviewSize box
- if ( img.width > img.height ) {
- width = previewSize;
- height = img.height / img.width * previewSize;
- } else {
- height = previewSize;
- width = img.width / img.height * previewSize;
+ if ( meta && meta.tiff && meta.tiff.Orientation ) {
+ rotation = ( 360 - ( function () {
+ // See includes/media/Bitmap.php
+ switch ( meta.tiff.Orientation.value ) {
+ case 8:
+ return 90;
+ case 3:
+ return 180;
+ case 6:
+ return 270;
+ default:
+ return 0;
+ }
+ }() ) ) % 360;
}
- // Determine the offset required to center the image
- dx = (180 - width) / 2;
- dy = (180 - height) / 2;
- switch ( rotation ) {
- // If a rotation is applied, the direction of the axis
- // changes as well. You can derive the values below by
- // drawing on paper an axis system, rotate it and see
- // where the positive axis direction is
- case 0:
- x = dx;
- y = dy;
- logicalWidth = img.width;
- logicalHeight = img.height;
- break;
- case 90:
- x = dx;
- y = dy - previewSize;
- logicalWidth = img.height;
- logicalHeight = img.width;
- break;
- case 180:
- x = dx - previewSize;
- y = dy - previewSize;
- logicalWidth = img.width;
- logicalHeight = img.height;
- break;
- case 270:
- x = dx - previewSize;
- y = dy;
- logicalWidth = img.height;
- logicalHeight = img.width;
- break;
- }
-
- ctx.clearRect( 0, 0, 180, 180 );
- ctx.rotate( rotation / 180 * Math.PI );
- ctx.drawImage( img, x, y, width, height );
- thumb.find('.mw-small-spinner').replaceWith($canvas);
+ img.onload = function () {
+ var width, height, x, y, dx, dy, logicalWidth, logicalHeight;
+ // Fit the image within the previewSizexpreviewSize box
+ if ( img.width > img.height ) {
+ width = previewSize;
+ height = img.height / img.width * previewSize;
+ } else {
+ height = previewSize;
+ width = img.width / img.height * previewSize;
+ }
+ // Determine the offset required to center the image
+ dx = (180 - width) / 2;
+ dy = (180 - height) / 2;
+ switch ( rotation ) {
+ // If a rotation is applied, the direction of the axis
+ // changes as well. You can derive the values below by
+ // drawing on paper an axis system, rotate it and see
+ // where the positive axis direction is
+ case 0:
+ x = dx;
+ y = dy;
+ logicalWidth = img.width;
+ logicalHeight = img.height;
+ break;
+ case 90:
- // Image size
- var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
- ', ' + prettySize( file.size );
- $( '#mw-upload-thumbnail .fileinfo' ).text( info );
- };
- img.src = dataURL;
- }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
- try {
- meta = mw.libs.jpegmeta( data, file.fileName );
- meta._binary_data = null;
- } catch ( e ) {
- meta = null;
- }
- } : null );
- }
+ x = dx;
+ y = dy - previewSize;
+ logicalWidth = img.height;
+ logicalHeight = img.width;
+ break;
+ case 180:
+ x = dx - previewSize;
+ y = dy - previewSize;
+ logicalWidth = img.width;
+ logicalHeight = img.height;
+ break;
+ case 270:
+ x = dx - previewSize;
+ y = dy;
+ logicalWidth = img.height;
+ logicalHeight = img.width;
+ break;
+ }
- /**
- * Start loading a file into memory; when complete, pass it as a
- * data URL to the callback function. If the callbackBinary is set it will
- * first be read as binary and afterwards as data URL. Useful if you want
- * to do preprocessing on the binary data first.
- *
- * @param {File} file
- * @param {function} callback
- * @param {function} callbackBinary
- */
- function fetchPreview( file, callback, callbackBinary ) {
- var reader = new FileReader();
- if ( callbackBinary && 'readAsBinaryString' in reader ) {
- // To fetch JPEG metadata we need a binary string; start there.
- // todo:
- reader.onload = function() {
- callbackBinary( reader.result );
+ ctx.clearRect( 0, 0, 180, 180 );
+ ctx.rotate( rotation / 180 * Math.PI );
+ ctx.drawImage( img, x, y, width, height );
+ thumb.find('.mw-small-spinner').replaceWith($canvas);
- // Now run back through the regular code path.
- fetchPreview( file, callback );
- };
- reader.readAsBinaryString( file );
- } else if ( callbackBinary && 'readAsArrayBuffer' in reader ) {
- // readAsArrayBuffer replaces readAsBinaryString
- // However, our JPEG metadata library wants a string.
- // So, this is going to be an ugly conversion.
- reader.onload = function() {
- var buffer = new Uint8Array( reader.result ),
- string = '';
- for ( var i = 0; i < buffer.byteLength; i++ ) {
- string += String.fromCharCode( buffer[i] );
+ // Image size
+ var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
+ ', ' + prettySize( file.size );
+ $( '#mw-upload-thumbnail .fileinfo' ).text( info );
+ };
+ img.src = dataURL;
+ }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
+ try {
+ meta = mw.libs.jpegmeta( data, file.fileName );
+ meta._binary_data = null;
+ } catch ( e ) {
+ meta = null;
}
- callbackBinary( string );
-
- // Now run back through the regular code path.
- fetchPreview( file, callback );
- };
- reader.readAsArrayBuffer( file );
- } else if ( 'URL' in window && 'createObjectURL' in window.URL ) {
- // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en/DOM/window.URL.createObjectURL>
- // WebKit has it in a namespace for now but that's ok. ;)
- //
- // Lifetime of this URL is until document close, which is fine
- // for Special:Upload -- if this code gets used on longer-running
- // pages, add a revokeObjectURL() when it's no longer needed.
- //
- // Prefer this over readAsDataURL for Firefox 7 due to bug reading
- // some SVG files from data URIs <https://bugzilla.mozilla.org/show_bug.cgi?id=694165>
- callback( window.URL.createObjectURL( file ) );
- } else {
- // This ends up decoding the file to base-64 and back again, which
- // feels horribly inefficient.
- reader.onload = function() {
- callback( reader.result );
- };
- reader.readAsDataURL( file );
+ } : null );
}
- }
- /**
- * Format a file size attractively.
- * @todo match numeric formatting
- *
- * @param {number} s
- * @return string
- */
- function prettySize( s ) {
- var sizes = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes'];
- while ( s >= 1024 && sizes.length > 1 ) {
- s /= 1024;
- sizes = sizes.slice( 1 );
- }
- return mw.msg( sizes[0], Math.round( s ) );
- }
+ /**
+ * Start loading a file into memory; when complete, pass it as a
+ * data URL to the callback function. If the callbackBinary is set it will
+ * first be read as binary and afterwards as data URL. Useful if you want
+ * to do preprocessing on the binary data first.
+ *
+ * @param {File} file
+ * @param {function} callback
+ * @param {function} callbackBinary
+ */
+ function fetchPreview( file, callback, callbackBinary ) {
+ var reader = new FileReader();
+ if ( callbackBinary && 'readAsBinaryString' in reader ) {
+ // To fetch JPEG metadata we need a binary string; start there.
+ // todo:
+ reader.onload = function() {
+ callbackBinary( reader.result );
- /**
- * Clear the file upload preview area.
- */
- function clearPreview() {
- $( '#mw-upload-thumbnail' ).remove();
- }
+ // Now run back through the regular code path.
+ fetchPreview( file, callback );
+ };
+ reader.readAsBinaryString( file );
+ } else if ( callbackBinary && 'readAsArrayBuffer' in reader ) {
+ // readAsArrayBuffer replaces readAsBinaryString
+ // However, our JPEG metadata library wants a string.
+ // So, this is going to be an ugly conversion.
+ reader.onload = function() {
+ var buffer = new Uint8Array( reader.result ),
+ string = '';
+ for ( var i = 0; i < buffer.byteLength; i++ ) {
+ string += String.fromCharCode( buffer[i] );
+ }
+ callbackBinary( string );
- /**
- * Check if the file does not exceed the maximum size
- */
- function checkMaxUploadSize( file ) {
- function getMaxUploadSize( type ) {
- var sizes = mw.config.get( 'wgMaxUploadSize' );
- if ( sizes[type] !== undefined ) {
- return sizes[type];
+ // Now run back through the regular code path.
+ fetchPreview( file, callback );
+ };
+ reader.readAsArrayBuffer( file );
+ } else if ( 'URL' in window && 'createObjectURL' in window.URL ) {
+ // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en/DOM/window.URL.createObjectURL>
+ // WebKit has it in a namespace for now but that's ok. ;)
+ //
+ // Lifetime of this URL is until document close, which is fine
+ // for Special:Upload -- if this code gets used on longer-running
+ // pages, add a revokeObjectURL() when it's no longer needed.
+ //
+ // Prefer this over readAsDataURL for Firefox 7 due to bug reading
+ // some SVG files from data URIs <https://bugzilla.mozilla.org/show_bug.cgi?id=694165>
+ callback( window.URL.createObjectURL( file ) );
+ } else {
+ // This ends up decoding the file to base-64 and back again, which
+ // feels horribly inefficient.
+ reader.onload = function() {
+ callback( reader.result );
+ };
+ reader.readAsDataURL( file );
}
- return sizes['*'];
}
- $( '.mw-upload-source-error' ).remove();
- var maxSize = getMaxUploadSize( 'file' );
- if ( file.size > maxSize ) {
- var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
- mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
- $( '#wpUploadFile' ).after( error );
- return false;
+ /**
+ * Format a file size attractively.
+ * @todo match numeric formatting
+ *
+ * @param {number} s
+ * @return string
+ */
+ function prettySize( s ) {
+ var sizeMsgs = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes'];
+ while ( s >= 1024 && sizeMsgs.length > 1 ) {
+ s /= 1024;
+ sizeMsgs = sizeMsgs.slice( 1 );
+ }
+ return mw.msg( sizeMsgs[0], Math.round( s ) );
}
- return true;
- }
-
- /**
- * Initialization
- */
- if ( hasFileAPI() ) {
- // Update thumbnail when the file selection control is updated.
- $( '#wpUploadFile' ).change( function() {
- clearPreview();
- if ( this.files && this.files.length ) {
- // Note: would need to be updated to handle multiple files.
- var file = this.files[0];
+ /**
+ * Clear the file upload preview area.
+ */
+ function clearPreview() {
+ $( '#mw-upload-thumbnail' ).remove();
+ }
- if ( !checkMaxUploadSize( file ) ) {
- return;
+ /**
+ * Check if the file does not exceed the maximum size
+ */
+ function checkMaxUploadSize( file ) {
+ function getMaxUploadSize( type ) {
+ var sizes = mw.config.get( 'wgMaxUploadSize' );
+ if ( sizes[type] !== undefined ) {
+ return sizes[type];
}
+ return sizes['*'];
+ }
+ $( '.mw-upload-source-error' ).remove();
- if ( fileIsPreviewable( file ) ) {
- showPreview( file );
- }
+ var maxSize = getMaxUploadSize( 'file' );
+ if ( file.size > maxSize ) {
+ var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
+ mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
+ $( '#wpUploadFile' ).after( error );
+ return false;
}
- } );
- }
-} );
+ return true;
+ }
-/**
- * Disable all upload source fields except the selected one
- */
-jQuery( function ( $ ) {
- var rows = $( '.mw-htmlform-field-UploadSourceField' );
- for ( var i = rows.length; i; i-- ) {
- var row = rows[i - 1];
- $( 'input[name="wpSourceType"]', row ).change( function () {
- var currentRow = row; // Store current row in our own scope
- return function () {
- $( '.mw-upload-source-error' ).remove();
- if ( this.checked ) {
- // Disable all inputs
- $( 'input[name!="wpSourceType"]', rows ).prop( 'disabled', 'disabled' );
- // Re-enable the current one
- $( 'input', currentRow ).prop( 'disabled', false );
+
+ /**
+ * Initialization
+ */
+ if ( hasFileAPI() ) {
+ // Update thumbnail when the file selection control is updated.
+ $( '#wpUploadFile' ).change( function () {
+ clearPreview();
+ if ( this.files && this.files.length ) {
+ // Note: would need to be updated to handle multiple files.
+ var file = this.files[0];
+
+ if ( !checkMaxUploadSize( file ) ) {
+ return;
+ }
+
+ if ( fileIsPreviewable( file ) ) {
+ showPreview( file );
+ }
}
- };
- }() );
- }
-} );
+ } );
+ }
+ } );
+
+ /**
+ * Disable all upload source fields except the selected one
+ */
+ $( function ( $ ) {
+ var i, row,
+ rows = $( '.mw-htmlform-field-UploadSourceField' );
+ for ( i = rows.length; i; i-- ) {
+ row = rows[i - 1];
+ $( 'input[name="wpSourceType"]', row ).change( ( function () {
+ var currentRow = row; // Store current row in our own scope
+ return function () {
+ $( '.mw-upload-source-error' ).remove();
+ if ( this.checked ) {
+ // Disable all inputs
+ $( 'input[name!="wpSourceType"]', rows ).prop( 'disabled', true );
+ // Re-enable the current one
+ $( 'input', currentRow ).prop( 'disabled', false );
+ }
+ };
+ }() ) );
+ }
+ } );
+}( mediaWiki, jQuery ) );
*
* Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink
*/
-( function( $ ) {
+( function ( mw, $ ) {
/* Local space */
* @param namespace {Number} (optional) Namespace id. If given, title will be taken as-is.
* @return {Title} this
*/
-var Title = function( title, namespace ) {
- this._ns = 0; // integer namespace id
- this._name = null; // name in canonical 'database' form
- this._ext = null; // extension
+ function Title( title, namespace ) {
+ this.ns = 0; // integer namespace id
+ this.name = null; // name in canonical 'database' form
+ this.ext = null; // extension
if ( arguments.length === 2 ) {
setNameAndExtension( this, title );
- this._ns = fixNsId( namespace );
+ this.ns = fixNsId( namespace );
} else if ( arguments.length === 1 ) {
setAll( this, title );
}
return this;
- },
+ }
+var
/**
* Strip some illegal chars: control chars, colon, less than, greater than,
* brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
* @param s {String}
* @return {String}
*/
- clean = function( s ) {
+ clean = function ( s ) {
if ( s !== undefined ) {
return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
}
/**
* Sanitize name.
*/
- fixName = function( s ) {
+ fixName = function ( s ) {
return clean( $.trim( s ) );
},
/**
* Sanitize name.
*/
- fixExt = function( s ) {
+ fixExt = function ( s ) {
return clean( s );
},
* @param id {Number} Namespace id.
* @return {Number|Boolean} The id as-is or boolean false if invalid.
*/
- fixNsId = function( id ) {
+ fixNsId = function ( id ) {
// wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] )
var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()];
* @param ns {String} Namespace name (case insensitive, leading/trailing space ignored).
* @return {Number|Boolean} Namespace id or boolean false if unrecognized.
*/
- getNsIdByName = function( ns ) {
- // toLowerCase throws exception on null/undefined. Return early.
- if ( ns == null ) {
+ getNsIdByName = function ( ns ) {
+ // Don't cast non-strings to strings, because null or undefined
+ // should not result in returning the id of a potential namespace
+ // called "Null:" (e.g. on nullwiki.example.org)
+ // Also, toLowerCase throws exception on null/undefined, because
+ // it is a String.prototype method.
+ if ( typeof ns !== 'string' ) {
return false;
}
ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize
* @param raw {String}
* @return {mw.Title}
*/
- setAll = function( title, s ) {
+ setAll = function ( title, s ) {
// In normal browsers the match-array contains null/undefined if there's no match,
// IE returns an empty string.
var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ),
// Namespace must be valid, and title must be a non-empty string.
if ( ns_match && typeof matches[2] === 'string' && matches[2] !== '' ) {
- title._ns = ns_match;
- title._name = fixName( matches[2] );
+ title.ns = ns_match;
+ title.name = fixName( matches[2] );
if ( typeof matches[3] === 'string' && matches[3] !== '' ) {
- title._ext = fixExt( matches[3] );
+ title.ext = fixExt( matches[3] );
}
} else {
// Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace.
- title._ns = 0;
+ title.ns = 0;
setNameAndExtension( title, s );
}
return title;
* @param raw {String}
* @return {mw.Title}
*/
- setNameAndExtension = function( title, raw ) {
+ setNameAndExtension = function ( title, raw ) {
// In normal browsers the match-array contains null/undefined if there's no match,
// IE returns an empty string.
var matches = raw.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ );
// Title must be a non-empty string.
if ( typeof matches[1] === 'string' && matches[1] !== '' ) {
- title._name = fixName( matches[1] );
+ title.name = fixName( matches[1] );
if ( typeof matches[2] === 'string' && matches[2] !== '' ) {
- title._ext = fixExt( matches[2] );
+ title.ext = fixExt( matches[2] );
}
} else {
throw new Error( 'mw.Title: Could not parse title "' + raw + '"' );
* @param title {mixed} prefixed db-key name (string) or instance of Title
* @return {mixed} Boolean true/false if the information is available. Otherwise null.
*/
- Title.exists = function( title ) {
+ Title.exists = function ( title ) {
var type = $.type( title ), obj = Title.exist.pages, match;
if ( type === 'string' ) {
match = obj[title];
* @param state {Boolean} (optional) State of the given titles. Defaults to true.
* @return {Boolean}
*/
- set: function( titles, state ) {
+ set: function ( titles, state ) {
titles = $.isArray( titles ) ? titles : [titles];
state = state === undefined ? true : !!state;
var pages = this.pages, i, len = titles.length;
* Get the namespace number.
* @return {Number}
*/
- getNamespaceId: function(){
- return this._ns;
+ getNamespaceId: function (){
+ return this.ns;
},
/**
* In NS_MAIN this is '', otherwise namespace name plus ':'
* @return {String}
*/
- getNamespacePrefix: function(){
- return mw.config.get( 'wgFormattedNamespaces' )[this._ns].replace( / /g, '_' ) + (this._ns === 0 ? '' : ':');
+ getNamespacePrefix: function (){
+ return mw.config.get( 'wgFormattedNamespaces' )[this.ns].replace( / /g, '_' ) + (this.ns === 0 ? '' : ':');
},
/**
* The name, like "Foo_bar"
* @return {String}
*/
- getName: function() {
- if ( $.inArray( this._ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
- return this._name;
+ getName: function () {
+ if ( $.inArray( this.ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
+ return this.name;
} else {
- return $.ucFirst( this._name );
+ return $.ucFirst( this.name );
}
},
* The name, like "Foo bar"
* @return {String}
*/
- getNameText: function() {
+ getNameText: function () {
return text( this.getName() );
},
* Get full name in prefixed DB form, like File:Foo_bar.jpg,
* most useful for API calls, anything that must identify the "title".
*/
- getPrefixedDb: function() {
+ getPrefixedDb: function () {
return this.getNamespacePrefix() + this.getMain();
},
* Get full name in text form, like "File:Foo bar.jpg".
* @return {String}
*/
- getPrefixedText: function() {
+ getPrefixedText: function () {
return text( this.getPrefixedDb() );
},
* The main title (without namespace), like "Foo_bar.jpg"
* @return {String}
*/
- getMain: function() {
+ getMain: function () {
return this.getName() + this.getDotExtension();
},
* The "text" form, like "Foo bar.jpg"
* @return {String}
*/
- getMainText: function() {
+ getMainText: function () {
return text( this.getMain() );
},
* Get the extension (returns null if there was none)
* @return {String|null} extension
*/
- getExtension: function() {
- return this._ext;
+ getExtension: function () {
+ return this.ext;
},
/**
* Convenience method: return string like ".jpg", or "" if no extension
* @return {String}
*/
- getDotExtension: function() {
- return this._ext === null ? '' : '.' + this._ext;
+ getDotExtension: function () {
+ return this.ext === null ? '' : '.' + this.ext;
},
/**
* Return the URL to this title
* @return {String}
*/
- getUrl: function() {
+ getUrl: function () {
return mw.util.wikiGetlink( this.toString() );
},
* Whether this title exists on the wiki.
* @return {mixed} Boolean true/false if the information is available. Otherwise null.
*/
- exists: function() {
+ exists: function () {
return Title.exists( this );
}
};
// Expose
mw.Title = Title;
-})(jQuery);
+}( mediaWiki, jQuery ) );
var args = [];
$.each( this.query, function ( key, val ) {
var k = Uri.encode( key ),
- vals = val === null ? [ null ] : $.makeArray( val );
+ vals = $.isArray( val ) ? val : [ val ];
$.each( vals, function ( i, v ) {
args.push( k + ( v === null ? '' : '=' + Uri.encode( v ) ) );
} );
defaultUri = new Uri( documentLocation );
- return Uri;
+ return Uri;
};
// if we are running in a browser, inject the current document location, for relative URLs
- if ( document && document.location && document.location.href ) {
+ if ( document && document.location && document.location.href ) {
mw.Uri = mw.UriRelative( document.location.href );
}
* @since 1.19
*/
-( function ( $, mw, undefined ) {
-"use strict";
+( function ( mw, $ ) {
+ 'use strict';
- var hovzer = $.getFootHovzer();
+ var debug,
+ hovzer = $.getFootHovzer();
- var debug = mw.Debug = {
+ debug = mw.Debug = {
/**
* Toolbar container element
*
paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length );
var gitInfo = '';
- if ( this.data.gitRevision != false ) {
+ if ( this.data.gitRevision !== false ) {
gitInfo = '(' + this.data.gitRevision.substring( 0, 7 ) + ')';
- if ( this.data.gitViewUrl != false ) {
- gitInfo = $( '<a></a>' ).attr( 'href', this.data.gitViewUrl ).text( gitInfo );
+ if ( this.data.gitViewUrl !== false ) {
+ gitInfo = $( '<a>' ).attr( 'href', this.data.gitViewUrl ).text( gitInfo );
}
}
.append( ': ' + this.data.mwVersion + ' ' )
.append( gitInfo );
- if ( this.data.gitBranch != false ) {
+ if ( this.data.gitBranch !== false ) {
bitDiv( 'gitbranch' ).text( 'Git branch: ' + this.data.gitBranch );
}
$table = $( '<table id="mw-debug-console">' );
- $('<colgroup>').css( 'width', /*padding=*/20 + ( 10*/*fontSize*/11 ) ).appendTo( $table );
+ $('<colgroup>').css( 'width', /*padding=*/20 + ( 10 * /*fontSize*/11 ) ).appendTo( $table );
$('<colgroup>').appendTo( $table );
$('<colgroup>').css( 'width', 350 ).appendTo( $table );
}
};
-} )( jQuery, mediaWiki );
+}( mediaWiki, jQuery ) );
* Minimal example in how to use it:
*
* var feedback = new mw.Feedback();
- * $( '#myButton' ).click( function() { feedback.launch(); } );
+ * $( '#myButton' ).click( function () { feedback.launch(); } );
*
* You can also launch the feedback form with a prefilled subject and body.
* See the docs for the launch() method.
*/
-( function( mw, $, undefined ) {
+( function ( mw, $ ) {
/**
* Thingy for collecting user feedback on a wiki page
* @param {Array} options -- optional, all properties optional.
- * api: {mw.Api} if omitted, will just create a standard API
- * title: {mw.Title} the title of the page where you collect feedback. Defaults to "Feedback".
- * dialogTitleMessageKey: {String} message key for the title of the dialog box
- * bugsLink: {mw.Uri|String} url where bugs can be posted
- * bugsListLink: {mw.Uri|String} url where bugs can be listed
+ * api: {mw.Api} if omitted, will just create a standard API
+ * title: {mw.Title} the title of the page where you collect feedback. Defaults to "Feedback".
+ * dialogTitleMessageKey: {String} message key for the title of the dialog box
+ * bugsLink: {mw.Uri|String} url where bugs can be posted
+ * bugsListLink: {mw.Uri|String} url where bugs can be listed
*/
- mw.Feedback = function( options ) {
+ mw.Feedback = function ( options ) {
if ( options === undefined ) {
options = {};
}
};
mw.Feedback.prototype = {
- setup: function() {
- var _this = this;
+ setup: function () {
+ var fb = this;
- var $feedbackPageLink = $( '<a></a>' )
- .attr( { 'href': _this.title.getUrl(), 'target': '_blank' } )
+ var $feedbackPageLink = $( '<a>' )
+ .attr( { 'href': fb.title.getUrl(), 'target': '_blank' } )
.css( { 'white-space': 'nowrap' } );
- var $bugNoteLink = $( '<a></a>' ).attr( { 'href': '#' } ).click( function() { _this.displayBugs(); } );
+ var $bugNoteLink = $( '<a>' ).attr( { 'href': '#' } ).click( function () {
+ fb.displayBugs();
+ } );
- var $bugsListLink = $( '<a></a>' ).attr( { 'href': _this.bugsListLink, 'target': '_blank' } );
+ var $bugsListLink = $( '<a>' ).attr( { 'href': fb.bugsListLink, 'target': '_blank' } );
+ // TODO: Use a stylesheet instead of these inline styles
this.$dialog =
- $( '<div style="position:relative;"></div>' ).append(
+ $( '<div style="position: relative;"></div>' ).append(
$( '<div class="feedback-mode feedback-form"></div>' ).append(
- $( '<small></small>' ).append(
- $( '<p></p>' ).msg(
+ $( '<small>' ).append(
+ $( '<p>' ).msg(
'feedback-bugornote',
$bugNoteLink,
- _this.title.getNameText(),
+ fb.title.getNameText(),
$feedbackPageLink.clone()
)
),
- $( '<div style="margin-top:1em;"></div>' ).append(
+ $( '<div style="margin-top: 1em;"></div>' ).append(
mw.msg( 'feedback-subject' ),
- $( '<br/>' ),
- $( '<input type="text" class="feedback-subject" name="subject" maxlength="60" style="width:99%;"/>' )
+ $( '<br>' ),
+ $( '<input type="text" class="feedback-subject" name="subject" maxlength="60" style="width: 99%;"/>' )
),
- $( '<div style="margin-top:0.4em;"></div>' ).append(
+ $( '<div style="margin-top: 0.4em;"></div>' ).append(
mw.msg( 'feedback-message' ),
- $( '<br/>' ),
- $( '<textarea name="message" class="feedback-message" style="width:99%;" rows="5" cols="60"></textarea>' )
+ $( '<br>' ),
+ $( '<textarea name="message" class="feedback-message" style="width: 99%;" rows="5" cols="60"></textarea>' )
)
),
$( '<div class="feedback-mode feedback-bugs"></div>' ).append(
$( '<p>' ).msg( 'feedback-bugcheck', $bugsListLink )
),
- $( '<div class="feedback-mode feedback-submitting" style="text-align:center;margin:3em 0;"></div>' ).append(
+ $( '<div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;"></div>' ).append(
mw.msg( 'feedback-adding' ),
$( '<br/>' ),
$( '<span class="feedback-spinner"></span>' )
),
- $( '<div class="feedback-mode feedback-thanks" style="text-align:center;margin:1em"></div>' ).msg(
- 'feedback-thanks', _this.title.getNameText(), $feedbackPageLink.clone()
+ $( '<div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div>' ).msg(
+ 'feedback-thanks', fb.title.getNameText(), $feedbackPageLink.clone()
),
- $( '<div class="feedback-mode feedback-error" style="position:relative;"></div>' ).append(
- $( '<div class="feedback-error-msg style="color:#990000;margin-top:0.4em;"></div>' )
+ $( '<div class="feedback-mode feedback-error" style="position: relative;"></div>' ).append(
+ $( '<div class="feedback-error-msg style="color: #990000; margin-top: 0.4em;"></div>' )
)
);
// undo some damage from dialog css
- this.$dialog.find( 'a' ).css( { 'color': '#0645ad' } );
+ this.$dialog.find( 'a' ).css( {
+ color: '#0645ad'
+ } );
this.$dialog.dialog({
width: 500,
autoOpen: false,
title: mw.msg( this.dialogTitleMessageKey ),
modal: true,
- buttons: _this.buttons
+ buttons: fb.buttons
});
this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get(0);
},
- display: function( s ) {
+ display: function ( s ) {
this.$dialog.dialog( { buttons:{} } ); // hide the buttons
this.$dialog.find( '.feedback-mode' ).hide(); // hide everything
this.$dialog.find( '.feedback-' + s ).show(); // show the desired div
},
- displaySubmitting: function() {
+ displaySubmitting: function () {
this.display( 'submitting' );
},
- displayBugs: function() {
- var _this = this;
+ displayBugs: function () {
+ var fb = this;
this.display( 'bugs' );
var bugsButtons = {};
- bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function() { window.open( _this.bugsLink, '_blank' ); };
- bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); };
- this.$dialog.dialog( { buttons: bugsButtons } );
+ bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () {
+ window.open( fb.bugsLink, '_blank' );
+ };
+ bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
+ fb.cancel();
+ };
+ this.$dialog.dialog( {
+ buttons: bugsButtons
+ } );
},
- displayThanks: function() {
- var _this = this;
+ displayThanks: function () {
+ var fb = this;
this.display( 'thanks' );
var closeButton = {};
- closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); };
- this.$dialog.dialog( { buttons: closeButton } );
+ closeButton[ mw.msg( 'feedback-close' ) ] = function () {
+ fb.$dialog.dialog( 'close' );
+ };
+ this.$dialog.dialog( {
+ buttons: closeButton
+ } );
},
/**
* Display the feedback form
* @param {Object} optional prefilled contents for the feedback form. Object with properties:
- * subject: {String}
- * message: {String}
+ * subject: {String}
+ * message: {String}
*/
- displayForm: function( contents ) {
- var _this = this;
- this.subjectInput.value = (contents && contents.subject) ? contents.subject : '';
- this.messageInput.value = (contents && contents.message) ? contents.message : '';
+ displayForm: function ( contents ) {
+ var fb = this;
+ this.subjectInput.value = ( contents && contents.subject ) ? contents.subject : '';
+ this.messageInput.value = ( contents && contents.message ) ? contents.message : '';
this.display( 'form' );
// Set up buttons for dialog box. We have to do it the hard way since the json keys are localized
var formButtons = {};
- formButtons[ mw.msg( 'feedback-submit' ) ] = function() { _this.submit(); };
- formButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); };
+ formButtons[ mw.msg( 'feedback-submit' ) ] = function () {
+ fb.submit();
+ };
+ formButtons[ mw.msg( 'feedback-cancel' ) ] = function () {
+ fb.cancel();
+ };
this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back
},
- displayError: function( message ) {
- var _this = this;
+ displayError: function ( message ) {
+ var fb = this;
this.display( 'error' );
this.$dialog.find( '.feedback-error-msg' ).msg( message );
var closeButton = {};
- closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); };
+ closeButton[ mw.msg( 'feedback-close' ) ] = function () {
+ fb.$dialog.dialog( 'close' );
+ };
this.$dialog.dialog( { buttons: closeButton } );
},
- cancel: function() {
+ cancel: function () {
this.$dialog.dialog( 'close' );
},
- submit: function() {
- var _this = this;
+ submit: function () {
+ var fb = this;
// get the values to submit
var subject = this.subjectInput.value;
- var message = "<small>User agent: " + navigator.userAgent + "</small>\n\n"
+ var message = '<small>User agent: ' + mw.html.escape( navigator.userAgent ) + '</small>\n\n'
+ this.messageInput.value;
- if ( message.indexOf( '~~~' ) == -1 ) {
- message += " ~~~~";
+ if ( message.indexOf( '~~~' ) === -1 ) {
+ message += ' ~~~~';
}
this.displaySubmitting();
- var ok = function( result ) {
+ var ok = function ( result ) {
if ( result.edit !== undefined ) {
if ( result.edit.result === 'Success' ) {
- _this.displayThanks();
+ fb.displayThanks();
} else {
- _this.displayError( 'feedback-error1' ); // unknown API result
+ // unknown API result
+ fb.displayError( 'feedback-error1' );
}
} else {
- _this.displayError( 'feedback-error2' ); // edit failed
+ // edit failed
+ fb.displayError( 'feedback-error2' );
}
};
- var err = function( code, info ) {
- _this.displayError( 'feedback-error3' ); // ajax request failed
+ var err = function ( code, info ) {
+ // ajax request failed
+ fb.displayError( 'feedback-error3' );
};
this.api.newSection( this.title, subject, message, ok, err );
* subject: {String}
* message: {String}
*/
- launch: function( contents ) {
+ launch: function ( contents ) {
this.displayForm( contents );
this.$dialog.dialog( 'open' );
this.subjectInput.focus();
};
-} )( window.mediaWiki, jQuery );
+}( mediaWiki, jQuery ) );
/**
* Utility functions for jazzing up HTMLForm elements
*/
-( function( $ ) {
+( function ( $ ) {
/**
* jQuery plugin to fade or snap to visible state.
* @param boolean instantToggle (optional)
* @return jQuery
*/
-$.fn.goIn = function( instantToggle ) {
+$.fn.goIn = function ( instantToggle ) {
if ( instantToggle === true ) {
return $(this).show();
}
* @param boolean instantToggle (optional)
* @return jQuery
*/
-$.fn.goOut = function( instantToggle ) {
+$.fn.goOut = function ( instantToggle ) {
if ( instantToggle === true ) {
return $(this).hide();
}
* @param callback function taking one paramter, which is Bool true when the event
* is called immediately, and the EventArgs object when triggered from an event
*/
-$.fn.liveAndTestAtStart = function( callback ){
+$.fn.liveAndTestAtStart = function ( callback ){
$(this)
.live( 'change', callback )
- .each( function( index, element ){
+ .each( function ( index, element ){
callback.call( this, true );
} );
};
// Document ready:
-$( function() {
+$( function () {
// Animate the SelectOrOther fields, to only show the text field when
// 'other' is selected.
- $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function( instant ) {
+ $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function ( instant ) {
var $other = $( '#' + $(this).attr( 'id' ) + '-other' );
$other = $other.add( $other.siblings( 'br' ) );
if ( $(this).val() === 'other' ) {
});
-})( jQuery );
+}( jQuery ) );
/**
- * Experimental advanced wikitext parser-emitter.
- * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
- *
- * @author neilk@wikimedia.org
- */
-
-( function( mw, $, undefined ) {
-
- mw.jqueryMsg = {};
+* Experimental advanced wikitext parser-emitter.
+* See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
+*
+* @author neilk@wikimedia.org
+*/
+( function ( mw, $ ) {
+ var slice = Array.prototype.slice,
+ parserDefaults = {
+ magic : {
+ 'SITENAME' : mw.config.get( 'wgSiteName' )
+ },
+ messages : mw.messages,
+ language : mw.language
+ };
/**
* Given parser options, return a function that parses a key and replacements, returning jQuery object
* @param {Object} parser options
* @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery}
*/
- function getFailableParserFn( options ) {
- var parser = new mw.jqueryMsg.parser( options );
- /**
+ function getFailableParserFn( options ) {
+ var parser = new mw.jqueryMsg.parser( options );
+ /**
* Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes.
* If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into
* the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it.
* @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements.
* @return {jQuery}
*/
- return function( args ) {
+ return function ( args ) {
var key = args[0];
- var argsArray = $.isArray( args[1] ) ? args[1] : $.makeArray( args ).slice( 1 );
+ var argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 );
try {
return parser.parse( key, argsArray );
} catch ( e ) {
};
}
+ mw.jqueryMsg = {};
+
/**
- * Class method.
+ * Class method.
* Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements).
- * e.g.
+ * e.g.
* window.gM = mediaWiki.parser.getMessageFunction( options );
* $( 'p#headline' ).html( gM( 'hello-user', username ) );
*
* @param {Array} parser options
* @return {Function} function suitable for assigning to window.gM
*/
- mw.jqueryMsg.getMessageFunction = function( options ) {
+ mw.jqueryMsg.getMessageFunction = function ( options ) {
var failableParserFn = getFailableParserFn( options );
- /**
+ /**
* N.B. replacements are variadic arguments or an array in second parameter. In other words:
- * somefunction(a, b, c, d)
- * is equivalent to
+ * somefunction(a, b, c, d)
+ * is equivalent to
* somefunction(a, [b, c, d])
*
* @param {String} message key
* @param {Array} optional replacements (can also specify variadically)
* @return {String} rendered HTML as string
*/
- return function( /* key, replacements */ ) {
+ return function ( /* key, replacements */ ) {
return failableParserFn( arguments ).html();
};
};
/**
- * Class method.
- * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to
- * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links.
- * e.g.
+ * Class method.
+ * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to
+ * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links.
+ * e.g.
* $.fn.msg = mediaWiki.parser.getJqueryPlugin( options );
- * var userlink = $( '<a>' ).click( function() { alert( "hello!!") } );
+ * var userlink = $( '<a>' ).click( function () { alert( "hello!!") } );
* $( 'p#headline' ).msg( 'hello-user', userlink );
*
* @param {Array} parser options
* @return {Function} function suitable for assigning to jQuery plugin, such as $.fn.msg
*/
- mw.jqueryMsg.getPlugin = function( options ) {
+ mw.jqueryMsg.getPlugin = function ( options ) {
var failableParserFn = getFailableParserFn( options );
- /**
+ /**
* N.B. replacements are variadic arguments or an array in second parameter. In other words:
- * somefunction(a, b, c, d)
- * is equivalent to
+ * somefunction(a, b, c, d)
+ * is equivalent to
* somefunction(a, [b, c, d])
- *
+ *
* We append to 'this', which in a jQuery plugin context will be the selected elements.
* @param {String} message key
* @param {Array} optional replacements (can also specify variadically)
* @return {jQuery} this
*/
- return function( /* key, replacements */ ) {
+ return function ( /* key, replacements */ ) {
var $target = this.empty();
- $.each( failableParserFn( arguments ).contents(), function( i, node ) {
+ $.each( failableParserFn( arguments ).contents(), function ( i, node ) {
$target.append( node );
} );
return $target;
};
};
- var parserDefaults = {
- 'magic' : {
- 'SITENAME' : mw.config.get( 'wgSiteName' )
- },
- 'messages' : mw.messages,
- 'language' : mw.language
- };
-
/**
* The parser itself.
* Describes an object, whose primary duty is to .parse() message keys.
* @param {Array} options
*/
- mw.jqueryMsg.parser = function( options ) {
+ mw.jqueryMsg.parser = function ( options ) {
this.settings = $.extend( {}, parserDefaults, options );
this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic );
};
mw.jqueryMsg.parser.prototype = {
-
// cache, map of mediaWiki message key to the AST of the message. In most cases, the message is a string so this is identical.
// (This is why we would like to move this functionality server-side).
astCache: {},
* @param {Array} replacements for $1, $2... $n
* @return {jQuery}
*/
- parse: function( key, replacements ) {
+ parse: function ( key, replacements ) {
return this.emitter.emit( this.getAst( key ), replacements );
},
-
/**
* Fetch the message string associated with a key, return parsed structure. Memoized.
- * Note that we pass '[' + key + ']' back for a missing message here.
+ * Note that we pass '[' + key + ']' back for a missing message here.
* @param {String} key
* @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
*/
- getAst: function( key ) {
- if ( this.astCache[ key ] === undefined ) {
+ getAst: function ( key ) {
+ if ( this.astCache[ key ] === undefined ) {
var wikiText = this.settings.messages.get( key );
if ( typeof wikiText !== 'string' ) {
wikiText = "\\[" + key + "\\]";
}
this.astCache[ key ] = this.wikiTextToAst( wikiText );
}
- return this.astCache[ key ];
+ return this.astCache[ key ];
},
-
/*
* Parses the input wikiText into an abstract syntax tree, essentially an s-expression.
*
* CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already.
* n.b. We want to move this functionality to the server. Nothing here is required to be on the client.
- *
+ *
* @param {String} message string wikitext
* @throws Error
* @return {Mixed} abstract syntax tree
*/
- wikiTextToAst: function( input ) {
-
- // Indicates current position in input as we parse through it.
- // Shared among all parsing functions below.
- var pos = 0;
+ wikiTextToAst: function ( input ) {
+ // Indicates current position in input as we parse through it.
+ // Shared among all parsing functions below.
+ var pos = 0;
// =========================================================
// parsing combinators - could be a library on its own
// =========================================================
-
-
- // Try parsers until one works, if none work return null
+ // Try parsers until one works, if none work return null
function choice( ps ) {
- return function() {
+ return function () {
for ( var i = 0; i < ps.length; i++ ) {
var result = ps[i]();
if ( result !== null ) {
return null;
};
}
-
// try several ps in a row, all must succeed or return null
// this is the only eager one
function sequence( ps ) {
var originalPos = pos;
var result = [];
- for ( var i = 0; i < ps.length; i++ ) {
+ for ( var i = 0; i < ps.length; i++ ) {
var res = ps[i]();
if ( res === null ) {
pos = originalPos;
return null;
- }
+ }
result.push( res );
}
return result;
}
-
// run the same parser over and over until it fails.
// must succeed a minimum of n times or return null
function nOrMore( n, p ) {
- return function() {
+ return function () {
var originalPos = pos;
var result = [];
var parsed = p();
if ( result.length < n ) {
pos = originalPos;
return null;
- }
+ }
return result;
};
}
-
// There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null.
// But using this as a combinator seems to cause problems when combined with nOrMore().
// May be some scoping issue
function transform( p, fn ) {
- return function() {
+ return function () {
var result = p();
return result === null ? null : fn( result );
};
}
-
// Helpers -- just make ps out of simpler JS builtin types
-
- function makeStringParser( s ) {
+ function makeStringParser( s ) {
var len = s.length;
- return function() {
+ return function () {
var result = null;
if ( input.substr( pos, len ) === s ) {
result = s;
return result;
};
}
-
function makeRegexParser( regex ) {
- return function() {
+ return function () {
var matches = input.substr( pos ).match( regex );
- if ( matches === null ) {
+ if ( matches === null ) {
return null;
- }
+ }
pos += matches[0].length;
return matches[0];
};
}
-
- /**
- * ===================================================================
+ /**
+ * ===================================================================
* General patterns above this line -- wikitext specific parsers below
- * ===================================================================
+ * ===================================================================
*/
-
// Parsing functions follow. All parsing functions work like this:
// They don't accept any arguments.
// Instead, they just operate non destructively on the string 'input'
// As they can consume parts of the string, they advance the shared variable pos,
// and return tokens (or whatever else they want to return).
-
// some things are defined as closures and other things as ordinary functions
// converting everything to a closure makes it a lot harder to debug... errors pop up
// but some debuggers can't tell you exactly where they come from. Also the mutually
// recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
// This may be because, to save code, memoization was removed
-
-
- var regularLiteral = makeRegexParser( /^[^{}[\]$\\]/ );
- var regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
- var regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
-
+ var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
+ var regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/);
+ var regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/);
var backslash = makeStringParser( "\\" );
var anyCharacter = makeRegexParser( /^./ );
-
function escapedLiteral() {
var result = sequence( [
- backslash,
+ backslash,
anyCharacter
] );
return result === null ? null : result[1];
}
-
var escapedOrLiteralWithoutSpace = choice( [
escapedLiteral,
regularLiteralWithoutSpace
] );
-
var escapedOrLiteralWithoutBar = choice( [
escapedLiteral,
regularLiteralWithoutBar
] );
-
- var escapedOrRegularLiteral = choice( [
+ var escapedOrRegularLiteral = choice( [
escapedLiteral,
regularLiteral
] );
-
// Used to define "literals" without spaces, in space-delimited situations
function literalWithoutSpace() {
var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
return result === null ? null : result.join('');
}
-
- // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
+ // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
// it is not a literal in the parameter
function literalWithoutBar() {
var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
return result === null ? null : result.join('');
}
-
function literal() {
var result = nOrMore( 1, escapedOrRegularLiteral )();
return result === null ? null : result.join('');
}
-
- var whitespace = makeRegexParser( /^\s+/ );
+ var whitespace = makeRegexParser( /^\s+/ );
var dollar = makeStringParser( '$' );
- var digits = makeRegexParser( /^\d+/ );
+ var digits = makeRegexParser( /^\d+/ );
function replacement() {
var result = sequence( [
dollar,
digits
] );
- if ( result === null ) {
+ if ( result === null ) {
return null;
}
return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
}
-
-
var openExtlink = makeStringParser( '[' );
var closeExtlink = makeStringParser( ']' );
-
// this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed
function extlink() {
var result = null;
}
return result;
}
-
// this is the same as the above extlink, except that the url is being passed on as a parameter
function extLinkParam() {
var result = sequence( [
}
return [ 'LINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ];
}
-
var openLink = makeStringParser( '[[' );
var closeLink = makeStringParser( ']]' );
-
function link() {
var result = null;
var parsedResult = sequence( [
}
return result;
}
-
- var templateName = transform(
+ var templateName = transform(
// see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1"
- makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+-]+/ ),
- function( result ) { return result.toString(); }
+ makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
+ function ( result ) { return result.toString(); }
);
-
function templateParam() {
- var result = sequence( [
+ var result = sequence( [
pipe,
nOrMore( 0, paramExpression )
] );
// use a "CONCAT" operator if there are multiple nodes, otherwise return the first node, raw.
return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0];
}
-
var pipe = makeStringParser( '|' );
-
function templateWithReplacement() {
var result = sequence( [
templateName,
] );
return result === null ? null : [ result[0], result[2] ];
}
-
function templateWithOutReplacement() {
var result = sequence( [
templateName,
] );
return result === null ? null : [ result[0], result[2] ];
}
-
var colon = makeStringParser(':');
-
var templateContents = choice( [
- function() {
+ function () {
var res = sequence( [
// templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}}
// or no placeholders eg: {{GRAMMAR:genitive|{{SITENAME}}}
] );
return res === null ? null : res[0].concat( res[1] );
},
- function() {
+ function () {
var res = sequence( [
templateName,
- nOrMore( 0, templateParam )
+ nOrMore( 0, templateParam )
] );
if ( res === null ) {
return null;
return [ res[0] ].concat( res[1] );
}
] );
-
var openTemplate = makeStringParser('{{');
var closeTemplate = makeStringParser('}}');
-
function template() {
var result = sequence( [
openTemplate,
] );
return result === null ? null : result[1];
}
-
var nonWhitespaceExpression = choice( [
template,
link,
replacement,
literalWithoutSpace
] );
-
var paramExpression = choice( [
template,
link,
replacement,
literalWithoutBar
] );
-
- var expression = choice( [
+ var expression = choice( [
template,
link,
extLinkParam,
extlink,
replacement,
- literal
+ literal
] );
-
function start() {
var result = nOrMore( 0, expression )();
if ( result === null ) {
}
return [ "CONCAT" ].concat( result );
}
-
// everything above this point is supposed to be stateless/static, but
// I am deferring the work of turning it into prototypes & objects. It's quite fast enough
-
// finally let's do some actual work...
-
var result = start();
-
+
/*
- * For success, the p must have gotten to the end of the input
+ * For success, the p must have gotten to the end of the input
* and returned a non-null.
* n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
*/
}
return result;
}
-
- };
+ };
/**
* htmlEmitter - object which primarily exists to emit HTML from parser ASTs
*/
- mw.jqueryMsg.htmlEmitter = function( language, magic ) {
+ mw.jqueryMsg.htmlEmitter = function ( language, magic ) {
this.language = language;
- var _this = this;
-
- $.each( magic, function( key, val ) {
- _this[ key.toLowerCase() ] = function() { return val; };
+ var jmsg = this;
+ $.each( magic, function ( key, val ) {
+ jmsg[ key.toLowerCase() ] = function () {
+ return val;
+ };
} );
-
/**
* (We put this method definition here, and not in prototype, to make sure it's not overwritten by any magic.)
* Walk entire node structure, applying replacements and template functions when appropriate
* @param {Array} replacements for $1, $2, ... $n
* @return {Mixed} single-string node or array of nodes suitable for jQuery appending
*/
- this.emit = function( node, replacements ) {
+ this.emit = function ( node, replacements ) {
var ret = null;
- var _this = this;
- switch( typeof node ) {
+ var jmsg = this;
+ switch ( typeof node ) {
case 'string':
case 'number':
ret = node;
break;
case 'object': // node is an array of nodes
- var subnodes = $.map( node.slice( 1 ), function( n ) {
- return _this.emit( n, replacements );
+ var subnodes = $.map( node.slice( 1 ), function ( n ) {
+ return jmsg.emit( n, replacements );
} );
var operation = node[0].toLowerCase();
- if ( typeof _this[operation] === 'function' ) {
- ret = _this[ operation ]( subnodes, replacements );
+ if ( typeof jmsg[operation] === 'function' ) {
+ ret = jmsg[ operation ]( subnodes, replacements );
} else {
- throw new Error( 'unknown operation "' + operation + '"' );
+ throw new Error( 'Unknown operation "' + operation + '"' );
}
break;
case 'undefined':
ret = '';
break;
default:
- throw new Error( 'unexpected type in AST: ' + typeof node );
+ throw new Error( 'Unexpected type in AST: ' + typeof node );
}
return ret;
};
-
};
-
// For everything in input that follows double-open-curly braces, there should be an equivalent parser
- // function. For instance {{PLURAL ... }} will be processed by 'plural'.
+ // function. For instance {{PLURAL ... }} will be processed by 'plural'.
// If you have 'magic words' then configure the parser to have them upon creation.
//
// An emitter method takes the parent node, the array of subnodes and the array of replacements (the values that $1, $2... should translate to).
// Note: all such functions must be pure, with the exception of referring to other pure functions via this.language (convertPlural and so on)
mw.jqueryMsg.htmlEmitter.prototype = {
-
/**
* Parsing has been applied depth-first we can assume that all nodes here are single nodes
* Must return a single node to parents -- a jQuery with synthetic span
* @param {Array} nodes - mixed, some single nodes, some arrays of nodes
* @return {jQuery}
*/
- concat: function( nodes ) {
- var span = $( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
- $.each( nodes, function( i, node ) {
+ concat: function ( nodes ) {
+ var $span = $( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
+ $.each( nodes, function ( i, node ) {
if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) {
- $.each( node.contents(), function( j, childNode ) {
- span.append( childNode );
+ $.each( node.contents(), function ( j, childNode ) {
+ $span.append( childNode );
} );
} else {
// strings, integers, anything else
- span.append( node );
+ $span.append( node );
}
} );
- return span;
+ return $span;
},
/**
* @param {Array} of one element, integer, n >= 0
* @return {String} replacement
*/
- replace: function( nodes, replacements ) {
+ replace: function ( nodes, replacements ) {
var index = parseInt( nodes[0], 10 );
-
+
if ( index < replacements.length ) {
if ( typeof arg === 'string' ) {
// replacement is a string, escape it
}
},
- /**
+ /**
* Transform wiki-link
- * TODO unimplemented
+ * TODO unimplemented
*/
- wlink: function( nodes ) {
- return "unimplemented";
+ wlink: function ( nodes ) {
+ return 'unimplemented';
},
/**
* If the href is a jQuery object, treat it as "enclosing" the link text.
* ... function, treat it as the click handler
* ... string, treat it as a URI
- * TODO: throw an error if nodes.length > 2 ?
+ * TODO: throw an error if nodes.length > 2 ?
* @param {Array} of two elements, {jQuery|Function|String} and {String}
* @return {jQuery}
*/
- link: function( nodes ) {
+ link: function ( nodes ) {
var arg = nodes[0];
var contents = nodes[1];
- var $el;
+ var $el;
if ( arg instanceof jQuery ) {
$el = arg;
} else {
$el.attr( 'href', arg.toString() );
}
}
- $el.append( contents );
+ $el.append( contents );
return $el;
},
* @param {Array} of one element, integer, n >= 0
* @return {String} replacement
*/
- linkparam: function( nodes, replacements ) {
+ linkparam: function ( nodes, replacements ) {
var replacement,
index = parseInt( nodes[0], 10 );
if ( index < replacements.length) {
* Transform parsed structure into pluralization
* n.b. The first node may be a non-integer (for instance, a string representing an Arabic number).
* So convert it back with the current language's convertNumber.
- * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ]
+ * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ]
* @return {String} selected pluralized form according to current language
*/
- plural: function( nodes ) {
+ plural: function ( nodes ) {
var count = parseInt( this.language.convertNumber( nodes[0], true ), 10 );
var forms = nodes.slice(1);
return forms.length ? this.language.convertPlural( count, forms ) : '';
/**
* Transform parsed structure into gender
* Usage {{gender:[gender| mw.user object ] | masculine|feminine|neutral}}.
- * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ]
+ * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ]
* @return {String} selected gender form according to current language
*/
- gender: function( nodes ) {
+ gender: function ( nodes ) {
var gender;
if ( nodes[0] && nodes[0].options instanceof mw.Map ){
gender = nodes[0].options.get( 'gender' );
* @param {Array} of nodes [{Grammar case eg: genitive}, {String word}]
* @return {String} selected grammatical form according to current language
*/
- grammar: function( nodes ) {
+ grammar: function ( nodes ) {
var form = nodes[0];
var word = nodes[1];
return word && form && this.language.convertGrammar( word, form );
}
};
-
- // deprecated! don't rely on gM existing.
- // the window.gM ought not to be required - or if required, not required here. But moving it to extensions breaks it (?!)
+ // Deprecated! don't rely on gM existing.
+ // The window.gM ought not to be required - or if required, not required here.
+ // But moving it to extensions breaks it (?!)
// Need to fix plugin so it could do attributes as well, then will be okay to remove this.
- window.gM = mw.jqueryMsg.getMessageFunction();
-
+ window.gM = mw.jqueryMsg.getMessageFunction();
$.fn.msg = mw.jqueryMsg.getPlugin();
-
+
// Replace the default message parser with jqueryMsg
var oldParser = mw.Message.prototype.parser;
- mw.Message.prototype.parser = function() {
+ mw.Message.prototype.parser = function () {
// TODO: should we cache the message function so we don't create a new one every time? Benchmark this maybe?
// Caching is somewhat problematic, because we do need different message functions for different maps, so
// we'd have to cache the parser as a member of this.map, which sounds a bit ugly.
-
// Do not use mw.jqueryMsg unless required
if ( this.map.get( this.key ).indexOf( '{{' ) < 0 ) {
// Fall back to mw.msg's simple parser
return oldParser.apply( this );
}
-
var messageFunction = mw.jqueryMsg.getMessageFunction( { 'messages': this.map } );
return messageFunction( this.key, this.parameters );
};
-} )( mediaWiki, jQuery );
+}( mediaWiki, jQuery ) );
-/*jslint browser: true, continue: true, white: true, forin: true*/
-/*global jQuery*/
/*
* Core MediaWiki JavaScript Library
*/
/* Private Members */
- var hasOwn = Object.prototype.hasOwnProperty;
+ var hasOwn = Object.prototype.hasOwnProperty,
+ slice = Array.prototype.slice;
+
/* Object constructors */
/**
var results, i;
if ( $.isArray( selection ) ) {
- selection = $.makeArray( selection );
+ selection = slice.call( selection );
results = {};
for ( i = 0; i < selection.length; i += 1 ) {
results[selection[i]] = this.get( selection[i], fallback );
this.format = 'plain';
this.map = map;
this.key = key;
- this.parameters = parameters === undefined ? [] : $.makeArray( parameters );
+ this.parameters = parameters === undefined ? [] : slice.call( parameters );
return this;
}
*
* This function will not be called for nonexistent messages.
*/
- parser: function() {
+ parser: function () {
var parameters = this.parameters;
return this.map.get( this.key ).replace( /\$(\d+)/g, function ( str, match ) {
var index = parseInt( match, 10 ) - 1;
*
* @return string Message as a string in the current form or <key> if key does not exist.
*/
- toString: function() {
+ toString: function () {
var text;
if ( !this.exists() ) {
*
* @return {string} String form of parsed message
*/
- parse: function() {
+ parse: function () {
this.format = 'parse';
return this.toString();
},
*
* @return {string} String form of plain message
*/
- plain: function() {
+ plain: function () {
this.format = 'plain';
return this.toString();
},
*
* @return {string} String form of html escaped message
*/
- escaped: function() {
+ escaped: function () {
this.format = 'escaped';
return this.toString();
},
*
* @return {string} String form of parsed message
*/
- exists: function() {
+ exists: function () {
return this.map.exists( this.key );
}
};
* Dummy function which in debug mode can be replaced with a function that
* emulates console.log in console-less environments.
*/
- log: function() { },
+ log: function () { },
/**
* @var constructor Make the Map constructor publicly available.
var parameters;
// Support variadic arguments
if ( parameter_1 !== undefined ) {
- parameters = $.makeArray( arguments );
+ parameters = slice.call( arguments );
parameters.shift();
} else {
parameters = [];
* {
* 'moduleName': {
* 'version': ############## (unix timestamp),
- * 'dependencies': ['required.foo', 'bar.also', ...], (or) function() {}
+ * 'dependencies': ['required.foo', 'bar.also', ...], (or) function () {}
* 'group': 'somegroup', (or) null,
* 'source': 'local', 'someforeignwiki', (or) null
* 'state': 'registered', 'loading', 'loaded', 'ready', 'error' or 'missing'
* @param callback Function: Optional callback which will be run when the script is done
*/
function addScript( src, callback, async ) {
+ /*jshint evil:true */
var script, head,
done = false;
script.setAttribute( 'type', 'text/javascript' );
if ( $.isFunction( callback ) ) {
// Attach handlers for all browsers (based on jQuery.ajax)
- script.onload = script.onreadystatechange = function() {
+ script.onload = script.onreadystatechange = function () {
if (
!done
// scripts only start loading after the document has been rendered,
// but so be it. Opera users don't deserve faster web pages if their
// browser makes it impossible
- $( function() { document.body.appendChild( script ); } );
+ $( function () { document.body.appendChild( script ); } );
} else {
// IE-safe way of getting the <head> . document.documentElement.head doesn't
// work in scripts that run in the <head>
// Execute script
try {
script = registry[module].script;
- markModuleReady = function() {
+ markModuleReady = function () {
registry[module].state = 'ready';
handlePending( module );
};
return;
}
- addScript( arr[i], function() {
+ addScript( arr[i], function () {
nestedAddScript( arr, callback, async, i + 1 );
}, async );
};
registry[module].state = 'loading';
nestedAddScript( script, markModuleReady, registry[module].async, 0 );
} else if ( $.isFunction( script ) ) {
+ registry[module].state = 'ready';
script( $ );
- markModuleReady();
+ handlePending( module );
}
} catch ( e ) {
// This needs to NOT use mw.log because these errors are common in production mode
* @author Trevor Parscal <tparscal@wikimedia.org>
*/
-( function ( $ ) {
+( function ( mw, $ ) {
/**
* Logs a message to the console.
*
* @param {String} First in list of variadic messages to output to console.
*/
- mw.log = function( /* logmsg, logmsg, */ ) {
+ mw.log = function ( /* logmsg, logmsg, */ ) {
// Turn arguments into an array
var args = Array.prototype.slice.call( arguments ),
// Allow log messages to use a configured prefix to identify the source window (ie. frame)
} );
};
-})( jQuery );
+}( mediaWiki, jQuery ) );
* Implementation for mediaWiki.user
*/
-(function( $ ) {
+( function ( mw, $ ) {
/**
* User object
/* Private Members */
var that = this;
+ var callbacks = {};
+
+ /**
+ * Gets the current user's groups or rights.
+ * @param {String} info: One of 'groups' or 'rights'.
+ * @param {Function} callback
+ */
+ function getUserInfo( info, callback ) {
+ var api;
+ if ( callbacks[info] ) {
+ callbacks[info].add( callback );
+ return;
+ }
+ callbacks.rights = $.Callbacks('once memory');
+ callbacks.groups = $.Callbacks('once memory');
+ callbacks[info].add( callback );
+ api = new mw.Api();
+ api.get( {
+ action: 'query',
+ meta: 'userinfo',
+ uiprop: 'rights|groups'
+ } ).always( function ( data ) {
+ var rights, groups;
+ if ( data.query && data.query.userinfo ) {
+ rights = data.query.userinfo.rights;
+ groups = data.query.userinfo.groups;
+ }
+ callbacks.rights.fire( rights || [] );
+ callbacks.groups.fire( groups || [] );
+ } );
+ }
/* Public Members */
*
* @return Mixed: User name string or null if users is anonymous
*/
- this.name = function() {
+ this.getName = function () {
return mw.config.get( 'wgUserName' );
};
+ /**
+ * @deprecated since 1.20 use mw.user.getName() instead
+ */
+ this.name = function () {
+ return this.getName();
+ };
+
/**
* Checks if the current user is anonymous.
*
* @return Boolean
*/
- this.anonymous = function() {
- return that.name() ? false : true;
+ this.isAnon = function () {
+ return that.getName() === null;
+ };
+
+ /**
+ * @deprecated since 1.20 use mw.user.isAnon() instead
+ */
+ this.anonymous = function () {
+ return that.isAnon();
};
/**
*/
this.sessionId = function () {
var sessionId = $.cookie( 'mediaWiki.user.sessionId' );
- if ( typeof sessionId == 'undefined' || sessionId === null ) {
+ if ( typeof sessionId === 'undefined' || sessionId === null ) {
sessionId = generateId();
$.cookie( 'mediaWiki.user.sessionId', sessionId, { 'expires': null, 'path': '/' } );
}
* @return String: User name or random session ID
*/
this.id = function() {
- var name = that.name();
+ var name = that.getName();
if ( name ) {
return name;
}
var id = $.cookie( 'mediaWiki.user.id' );
- if ( typeof id == 'undefined' || id === null ) {
+ if ( typeof id === 'undefined' || id === null ) {
id = generateId();
}
// Set cookie if not set, or renew it if already set
- $.cookie( 'mediaWiki.user.id', id, { 'expires': 365, 'path': '/' } );
+ $.cookie( 'mediaWiki.user.id', id, {
+ expires: 365,
+ path: '/'
+ } );
return id;
};
* 'expires': 7
* } );
*/
- this.bucket = function( key, options ) {
+ this.bucket = function ( key, options ) {
options = $.extend( {
'buckets': {},
'version': 0,
// Bucket information is stored as 2 integers, together as version:bucket like: "1:2"
if ( typeof cookie === 'string' && cookie.length > 2 && cookie.indexOf( ':' ) > 0 ) {
var parts = cookie.split( ':' );
- if ( parts.length > 1 && parts[0] == options.version ) {
+ if ( parts.length > 1 && Number( parts[0] ) === options.version ) {
version = Number( parts[0] );
bucket = String( parts[1] );
}
}
}
if ( options.tracked ) {
- mw.loader.using( 'jquery.clickTracking', function() {
+ mw.loader.using( 'jquery.clickTracking', function () {
$.trackAction(
'mediaWiki.user.bucket:' + key + '@' + version + ':' + bucket
);
}
return bucket;
};
+
+ /**
+ * Gets the current user's groups.
+ */
+ this.getGroups = function ( callback ) {
+ getUserInfo( 'groups', callback );
+ };
+
+ /**
+ * Gets the current user's rights.
+ */
+ this.getRights = function ( callback ) {
+ getUserInfo( 'rights', callback );
+ };
}
// Extend the skeleton mw.user from mediawiki.js
// This is kind of ugly but we're stuck with this for b/c reasons
mw.user = new User( mw.user.options, mw.user.tokens );
-})(jQuery);
+}( mediaWiki, jQuery ) );
/**
* Implements mediaWiki.util library
*/
-( function ( $, mw ) {
- "use strict";
+( function ( mw, $ ) {
+ 'use strict';
// Local cache and alias
- var util = {
+ var hideMessageTimeout,
+ messageBoxEvents = false,
+ util = {
/**
* Initialisation
* @return {Boolean} True on success, false on failure.
*/
jsMessage: function ( message, className ) {
+ var $messageDiv = $( '#mw-js-message' );
+
if ( !arguments.length || message === '' || message === null ) {
- $( '#mw-js-message' ).empty().hide();
+ $messageDiv.empty().hide();
+ stopHideMessageTimeout();
return true; // Emptying and hiding message is intended behaviour, return true
-
} else {
// We special-case skin structures provided by the software. Skins that
// choose to abandon or significantly modify our formatting can just define
// an mw-js-message div to start with.
- var $messageDiv = $( '#mw-js-message' );
if ( !$messageDiv.length ) {
$messageDiv = $( '<div id="mw-js-message"></div>' );
if ( util.$content.parent().length ) {
}
}
+ if ( !messageBoxEvents ) {
+ messageBoxEvents = true;
+ $messageDiv
+ .on( {
+ 'mouseenter': stopHideMessageTimeout,
+ 'mouseleave': startHideMessageTimeout,
+ 'click': hideMessage
+ } )
+ .on( 'click', 'a', function ( e ) {
+ // Prevent links, even those that don't exist yet, from causing the
+ // message box to close when clicked
+ e.stopPropagation();
+ } );
+ }
+
if ( className ) {
- $messageDiv.prop( 'class', 'mw-js-message-' + className );
+ $messageDiv.prop( 'className', 'mw-js-message-' + className );
}
if ( typeof message === 'object' ) {
}
$messageDiv.slideDown();
+ startHideMessageTimeout();
return true;
}
},
}
};
+ // Message auto-hide helpers
+ function hideMessage() {
+ $( '#mw-js-message' ).fadeOut( 'slow' );
+ }
+ function stopHideMessageTimeout() {
+ clearTimeout( hideMessageTimeout );
+ }
+ function startHideMessageTimeout() {
+ clearTimeout( hideMessageTimeout );
+ hideMessageTimeout = setTimeout( hideMessage, 5000 );
+ }
+
mw.util = util;
-} )( jQuery, mediaWiki );
+}( mediaWiki, jQuery ) );
<div id="contentSub"<?php $this->html('userlangattributes') ?>><?php $this->html('subtitle') ?></div>
<?php if($this->data['undelete']) { ?><div id="contentSub2"><?php $this->html('undelete') ?></div><?php } ?>
- <?php if($this->data['showjumplinks']) { ?><div id="jump-to-nav"><?php $this->msg('jumpto') ?> <a href="#mw_portlets"><?php $this->msg('jumptonavigation') ?></a>, <a href="#searchInput"><?php $this->msg('jumptosearch') ?></a></div><?php } ?>
+ <?php if($this->data['showjumplinks']) { ?><div id="jump-to-nav"><?php $this->msg('jumpto') ?> <a href="#mw_portlets"><?php $this->msg('jumptonavigation') ?></a><?php $this->msg( 'comma-separator' ) ?><a href="#searchInput"><?php $this->msg('jumptosearch') ?></a></div><?php } ?>
<?php $this->html('bodytext') ?>
<div class='mw_clear'></div>
<?php } ?><?php if($this->data['newtalk'] ) { ?>
<div class="usermessage"><?php $this->html('newtalk') ?></div>
<?php } ?><?php if($this->data['showjumplinks']) { ?>
- <div id="jump-to-nav" class="mw-jump"><?php $this->msg('jumpto') ?> <a href="#column-one"><?php $this->msg('jumptonavigation') ?></a>, <a href="#searchInput"><?php $this->msg('jumptosearch') ?></a></div>
+ <div id="jump-to-nav" class="mw-jump"><?php $this->msg('jumpto') ?> <a href="#column-one"><?php $this->msg('jumptonavigation') ?></a><?php $this->msg( 'comma-separator' ) ?><a href="#searchInput"><?php $this->msg('jumptosearch') ?></a></div>
<?php } ?>
<!-- start content -->
<?php $this->html('bodytext') ?>
<?php if ( $this->data['showjumplinks'] ): ?>
<!-- jumpto -->
<div id="jump-to-nav" class="mw-jump">
- <?php $this->msg( 'jumpto' ) ?> <a href="#mw-head"><?php $this->msg( 'jumptonavigation' ) ?></a>,
+ <?php $this->msg( 'jumpto' ) ?>
+ <a href="#mw-head"><?php $this->msg( 'jumptonavigation' ) ?></a><?php $this->msg( 'comma-separator' ) ?>
<a href="#p-search"><?php $this->msg( 'jumptosearch' ) ?></a>
</div>
<!-- /jumpto -->
border: 1px dashed #2f6fab;
color: black;
background-color: #f9f9f9;
-
- /*
- * Wrap properly.
- * - pre-wrap: causes the browser to naturally wrap by displaying
- * words on the next line if they don't fit on the same line
- * within the box (does not cut off words).
- * - break-word: forces the browser to wrap anywhere (even within
- * a word) if it is (still) too long for the line.
- * When only using break-word in a <pre>, the browser only uses
- * the force behavior and as a result almost always cuts half-way
- * a word. When only using pre-wrap, too-long words will still
- * cause the page layout to break. The combination is magic :).
- * See also https://bugzilla.wikimedia.org/show_bug.cgi?id=260#c20
- */
- white-space: pre-wrap;
- word-wrap: break-word;
}
/* Tables */
#wpTextbox1 {
clear: both;
}
+
#toolbar img {
cursor: pointer;
}
+
div#mw-js-message {
margin: 1em 5%;
padding: 0.5em 2.5%;
border: solid 1px #ddd;
background-color: #fcfcfc;
+ /* Message hides on-click */
+ /* See also mw.util.jsMessage */
+ cursor: pointer;
}
/* Edit section links */
/**
* MediaWiki legacy wikibits
*/
-(function(){
+( function ( mw ) {
window.clientPC = navigator.userAgent.toLowerCase(); // Get client info
window.is_gecko = /gecko/.test( clientPC ) &&
* Add a cute little box at the top of the screen to inform the user of
* something, replacing any preexisting message.
*
- * @param message String -or- Dom Object HTML to be put inside the right div
- * @param className String Used in adding a class; should be different for each
- * call to allow CSS/JS to hide different boxes. null = no class used.
- * @return Boolean True on success, false on failure
+ * @deprecated since 1.17 Use mw.util.jsMessage instead.
+ * @param {String|HTMLElement} message To be put inside the message box.
+ * @param {String} className Used in adding a class; Can be used to selectively
+ * apply CSS to a certain category of messages. null = no class used.
+ * @return {Boolean} True on success, false on failure
*/
-window.jsMsg = function( message, className ) {
- if ( !document.getElementById ) {
- return false;
- }
- // We special-case skin structures provided by the software. Skins that
- // choose to abandon or significantly modify our formatting can just define
- // an mw-js-message div to start with.
- var messageDiv = document.getElementById( 'mw-js-message' );
- if ( !messageDiv ) {
- messageDiv = document.createElement( 'div' );
- if ( document.getElementById( 'column-content' )
- && document.getElementById( 'content' ) ) {
- // MonoBook, presumably
- document.getElementById( 'content' ).insertBefore(
- messageDiv,
- document.getElementById( 'content' ).firstChild
- );
- } else if ( document.getElementById( 'content' )
- && document.getElementById( 'article' ) ) {
- // Non-Monobook but still recognizable (old-style)
- document.getElementById( 'article').insertBefore(
- messageDiv,
- document.getElementById( 'article' ).firstChild
- );
- } else {
- return false;
- }
- }
-
- messageDiv.setAttribute( 'id', 'mw-js-message' );
- messageDiv.style.display = 'block';
- if( className ) {
- messageDiv.setAttribute( 'class', 'mw-js-message-' + className );
- }
-
- if ( typeof message === 'object' ) {
- while ( messageDiv.hasChildNodes() ) { // Remove old content
- messageDiv.removeChild( messageDiv.firstChild );
- }
- messageDiv.appendChild( message ); // Append new content
- } else {
- messageDiv.innerHTML = message;
- }
- return true;
+window.jsMsg = function () {
+ return mw.util.jsMessage.apply( mw.util, arguments );
};
/**
importScriptURI( mw.config.get( 'stylepath' ) + '/common/IEFixes.js' );
}
-})();
+}( mediaWiki ) );
.tipsy {
font-size: 127%;
}
+
+/* jsMessage */
+
+div#mw-js-message {
+ position: absolute;
+ margin: 0;
+ padding: 0.25em 1em;
+ right: 1em;
+ top: 1em;
+ width: 20em;
+ z-index: 10000;
+ -webkit-box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.125);
+ box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.125);
+}
}
div.vectorTabs li.selected a,
div.vectorTabs li.selected a:visited{
- color: #333333;
+ color: #333;
text-decoration: none;
}
div.vectorTabs li.new a,
}
div.vectorMenu li.selected a,
div.vectorMenu li.selected a:visited {
- color: #333333;
+ color: #333;
text-decoration: none;
}
/* Search */
margin-top: 0.65em;
position: relative;
min-height: 1px; /* Gotta trigger hasLayout for IE7 */
- border: solid 1px #AAAAAA;
+ border: solid 1px #aaa;
color: black;
background-color: white;
/* @embed */
outline: none;
}
div#simpleSearch input.placeholder {
- color: #999999;
+ color: #999;
}
div#simpleSearch input::-webkit-input-placeholder {
- color: #999999;
+ color: #999;
}
div#simpleSearch input#searchInput {
position: absolute;
}
div#mw-panel div.portal h5 {
font-weight: normal;
- color: #444444;
+ color: #444;
padding: 0.25em;
padding-top: 0;
padding-left: 1.75em;
padding: 0;
padding-bottom: 0.5em;
margin: 0;
- overflow: hidden;
font-size: 0.75em;
+ word-wrap: break-word;
}
div#mw-panel div.portal div.body ul li a {
color: #0645ad;
padding: 0;
padding-top: 0.5em;
padding-bottom: 0.5em;
- color: #333333;
+ color: #333;
font-size: 0.7em;
}
div#footer #footer-icons {
background-image: url(images/preferences-fade.png);
background-position: bottom;
background-repeat: repeat-x;
- color: #333333;
+ color: #333;
text-decoration: none;
}
#preferences {
margin: 0;
margin-top: -2px;
clear: both;
- border: solid 1px #cccccc;
+ border: solid 1px #ccc;
background-color: #f9f9f9;
/* @embed */
background-image: url(images/preferences-base.png);
}
#preferences fieldset {
border: none;
- border-top: solid 1px #cccccc;
+ border-top: solid 1px #ccc;
}
#preferences fieldset.prefsection {
border: none;
margin: 1em;
}
#preferences legend {
- color: #666666;
+ color: #666;
}
#preferences fieldset.prefsection legend.mainLegend {
display: none;
.htmlform-tip {
font-size: x-small;
padding: .2em 2em;
- color: #666666;
+ color: #666;
}
#preferences div.mw-prefs-buttons {
padding: 1em;
position: relative;
width: 100%;
}
-#mw-js-message {
+div#mw-js-message {
+ background-color: #fff;
+ background-color: rgba(255, 255, 255, 0.93);
font-size: 0.8em;
+ position: absolute;
+ margin: 0;
+ padding: 1em 2em;
+ right: 1em;
+ top: 7em;
+ width: 20em;
+ z-index: 10000;
+ border: solid 1px #a7d7f9;
+ border-radius: 0.75em;
+ -webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.125);
+ box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.125);
}
div#bodyContent {
line-height: 1.5em;
-/*
+/**
* Vector-specific scripts
*/
-jQuery( function( $ ) {
- $( 'div.vectorMenu' ).each( function() {
- var self = this;
- $( 'h5:first a:first', this )
+jQuery( function ( $ ) {
+ $( 'div.vectorMenu' ).each( function () {
+ var $el = $( this );
+ $el.find( 'h5:first a:first' )
// For accessibility, show the menu when the hidden link in the menu is clicked (bug 24298)
- .click( function( e ) {
- $( '.menu:first', self ).toggleClass( 'menuForceShow' );
+ .click( function ( e ) {
+ $el.find( '.menu:first' ).toggleClass( 'menuForceShow' );
e.preventDefault();
- })
+ } )
// When the hidden link has focus, also set a class that will change the arrow icon
- .focus( function() {
- $( self ).addClass( 'vectorMenuFocus' );
- })
- .blur( function() {
- $( self ).removeClass( 'vectorMenuFocus' );
- });
- });
-});
+ .focus( function () {
+ $el.addClass( 'vectorMenuFocus' );
+ } )
+ .blur( function () {
+ $el.removeClass( 'vectorMenuFocus' );
+ } );
+ } );
+} );
$this->savedGlobals = array();
+ /** @since 1.20 */
+ wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
+
foreach ( $settings as $var => $val ) {
if ( array_key_exists( $var, $GLOBALS ) ) {
$this->savedGlobals[$var] = $GLOBALS[$var];
</p>
!! end
+!! test
+Paragraphs with extra newline spacing
+!! input
+foo
+
+bar
+
+
+baz
+
+
+
+booz
+!! result
+<p>foo
+</p><p>bar
+</p><p><br />
+baz
+</p><p><br />
+</p><p>booz
+</p>
+!! end
+
!! test
Simple list
!! input
</p>
!! end
+!! test
+Headers with excess '=' characters
+(Are similar tests necessary beyond the 1st level?)
+!! input
+=foo==
+==foo=
+=''italic'' heading==
+==''italic'' heading=
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#foo.3D"><span class="tocnumber">1</span> <span class="toctext">foo=</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#.3Dfoo"><span class="tocnumber">2</span> <span class="toctext">=foo</span></a></li>
+<li class="toclevel-1 tocsection-3"><a href="#italic_heading.3D"><span class="tocnumber">3</span> <span class="toctext"><i>italic</i> heading=</span></a></li>
+<li class="toclevel-1 tocsection-4"><a href="#.3Ditalic_heading"><span class="tocnumber">4</span> <span class="toctext">=<i>italic</i> heading</span></a></li>
+</ul>
+</td></tr></table>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: foo=">edit</a>]</span> <span class="mw-headline" id="foo.3D">foo=</span></h1>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: =foo">edit</a>]</span> <span class="mw-headline" id=".3Dfoo">=foo</span></h1>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: italic heading=">edit</a>]</span> <span class="mw-headline" id="italic_heading.3D"><i>italic</i> heading=</span></h1>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: =italic heading">edit</a>]</span> <span class="mw-headline" id=".3Ditalic_heading">=<i>italic</i> heading</span></h1>
+
+!! end
+
!! test
BUG 1219 URL next to image (broken)
!! input
}
- public static function disableInterwikis( $prefix, &$data ) {
- return false;
- }
-
/**
* Don't throw a warning if $function is deprecated and called later
*
* the values given in the order of the columns in the $fields parameter.
* Note that the rows are sorted by the columns given in $fields.
*
+ * @since 1.20
+ *
* @param $table String|Array the table(s) to query
* @param $fields String|Array the columns to include in the result (and to sort by)
* @param $condition String|Array "where" condition(s)
$this->assertFalse( $r, "found extra row (after #$i)" );
}
+ /**
+ * Utility method taking an array of elements and wrapping
+ * each element in it's own array. Useful for data providers
+ * that only return a single argument.
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ *
+ * @return array
+ */
+ protected function arrayWrap( array $elements ) {
+ return array_map(
+ function( $element ) {
+ return array( $element );
+ },
+ $elements
+ );
+ }
+
/**
* Assert that two arrays are equal. By default this means that both arrays need to hold
* the same set of values. Using additional arguments, order and associated key can also
*/
protected function assertArrayEquals( array $expected, array $actual, $ordered = false, $named = false ) {
if ( !$ordered ) {
- asort( $expected );
- asort( $actual );
+ $this->objectAssociativeSort( $expected );
+ $this->objectAssociativeSort( $actual );
}
if ( !$named ) {
);
}
+ /**
+ * Does an associative sort that works for objects.
+ *
+ * @since 1.20
+ *
+ * @param array $array
+ */
+ protected function objectAssociativeSort( array &$array ) {
+ uasort(
+ $array,
+ function( $a, $b ) {
+ return serialize( $a ) > serialize( $b ) ? 1 : -1;
+ }
+ );
+ }
+
/**
* Utility function for eliminating all string keys from an array.
* Useful to turn a database result row as returned by fetchRow() into
* a pure indexed array.
*
- * @static
+ * @since 1.20
+ *
* @param $r mixed the array to remove string keys from.
*/
protected static function stripStringKeys( &$r ) {
- if ( !is_array( $r ) ) return;
+ if ( !is_array( $r ) ) {
+ return;
+ }
foreach ( $r as $k => $v ) {
- if ( is_string( $k ) ) unset( $r[$k] );
+ if ( is_string( $k ) ) {
+ unset( $r[$k] );
+ }
}
}
+
}
array( array( 'foo' => 1 ), 'foo=1' ), // number test
array( array( 'foo' => true ), 'foo=1' ), // true test
array( array( 'foo' => false ), '' ), // false test
- array( array( 'foo' => null ), '' ), // null test
+ array( array( 'foo' => null ), 'foo' ), // null test
array( array( 'foo' => 'A&B=5+6@!"\'' ), 'foo=A%26B%3D5%2B6%40%21%22%27' ), // urlencoding test
array( array( 'foo' => 'bar', 'baz' => 'is', 'asdf' => 'qwerty' ), 'foo=bar&baz=is&asdf=qwerty' ), // multi-item test
array( array( 'foo' => array( 'bar' => 'baz' ) ), 'foo%5Bbar%5D=baz' ),
--- /dev/null
+<?php
+
+class UriTest extends MediaWikiTestCase {
+
+ function setUp() {
+ AutoLoader::loadClass( 'Uri' );
+ }
+
+ function dataUris() {
+ return array(
+ array(
+ 'http://example.com/',
+ array(
+ 'scheme' => 'http',
+ 'delimiter' => '://',
+ 'user' => null,
+ 'pass' => null,
+ 'host' => 'example.com',
+ 'port' => null,
+ 'path' => '/',
+ 'query' => null,
+ 'fragment' => null,
+ ),
+ ),
+ array(
+ '//mediawiki.org/wiki/Main_Page',
+ array(
+ 'scheme' => null,
+ 'delimiter' => '//',
+ 'user' => null,
+ 'pass' => null,
+ 'host' => 'mediawiki.org',
+ 'port' => null,
+ 'path' => '/wiki/Main_Page',
+ 'query' => null,
+ 'fragment' => null,
+ ),
+ ),
+ array(
+ 'http://user:pass@example.com/',
+ array(
+ 'scheme' => 'http',
+ 'delimiter' => '://',
+ 'user' => 'user',
+ 'pass' => 'pass',
+ 'host' => 'example.com',
+ 'port' => null,
+ 'path' => '/',
+ 'query' => null,
+ 'fragment' => null,
+ ),
+ ),
+ array(
+ '/?asdf=asdf',
+ array(
+ 'scheme' => null,
+ 'delimiter' => null,
+ 'user' => null,
+ 'pass' => null,
+ 'host' => null,
+ 'port' => null,
+ 'path' => '/',
+ 'query' => 'asdf=asdf',
+ 'fragment' => null,
+ ),
+ ),
+ array(
+ '?asdf=asdf#asdf',
+ array(
+ 'scheme' => null,
+ 'delimiter' => null,
+ 'user' => null,
+ 'pass' => null,
+ 'host' => null,
+ 'port' => null,
+ 'path' => null,
+ 'query' => 'asdf=asdf',
+ 'fragment' => 'asdf',
+ ),
+ )
+ );
+ }
+
+ /**
+ * Ensure that get* methods properly match the appropriate getComponent( key ) value
+ * @dataProvider dataUris
+ */
+ function testGetters( $uri ) {
+ $uri = new Uri( $uri );
+ $getterMap = array(
+ 'getProtocol' => 'scheme',
+ 'getUser' => 'user',
+ 'getPassword' => 'pass',
+ 'getHost' => 'host',
+ 'getPort' => 'port',
+ 'getPath' => 'path',
+ 'getQueryString' => 'query',
+ 'getFragment' => 'fragment',
+ );
+ foreach ( $getterMap as $fn => $c ) {
+ $this->assertSame( $uri->{$fn}(), $uri->getComponent( $c ), "\$uri->{$fn}(); matches \$uri->getComponent( '$c' );" );
+ }
+ }
+
+ /**
+ * Ensure that Uri has the proper components for our example uris
+ * @dataProvider dataUris
+ */
+ function testComponents( $uri, $components ) {
+ $uri = new Uri( $uri );
+
+ $this->assertSame( $components['scheme'], $uri->getProtocol(), 'Correct scheme' );
+ $this->assertSame( $components['delimiter'], $uri->getDelimiter(), 'Correct delimiter' );
+ $this->assertSame( $components['user'], $uri->getUser(), 'Correct user' );
+ $this->assertSame( $components['pass'], $uri->getPassword(), 'Correct pass' );
+ $this->assertSame( $components['host'], $uri->getHost(), 'Correct host' );
+ $this->assertSame( $components['port'], $uri->getPort(), 'Correct port' );
+ $this->assertSame( $components['path'], $uri->getPath(), 'Correct path' );
+ $this->assertSame( $components['query'], $uri->getQueryString(), 'Correct query' );
+ $this->assertSame( $components['fragment'], $uri->getFragment(), 'Correct fragment' );
+ }
+
+ /**
+ * Ensure that the aliases work for various components.
+ */
+ function testAliases() {
+ $url = "//myuser@test.com";
+ $uri = new Uri( $url );
+
+ // Set the aliases.
+ $uri->setComponent( 'protocol', 'https' );
+ $uri->setComponent( 'password', 'mypass' );
+
+ // Now try getting them.
+ $this->assertSame( 'https', $uri->getComponent( 'protocol' ), 'Correct protocol (alias for scheme)' );
+ $this->assertSame( 'mypass', $uri->getComponent( 'password' ), 'Correct password (alias for pass)' );
+
+ // Finally check their actual names.
+ $this->assertSame( 'https', $uri->getProtocol(), 'Alias for scheme works' );
+ $this->assertSame( 'mypass', $uri->getPassword(), 'Alias for pass works' );
+ }
+
+ /**
+ * Ensure that Uri's helper methods return the correct data
+ */
+ function testHelpers() {
+ $uri = new Uri( 'http://a:b@example.com:8080/path?query=value' );
+
+ $this->assertSame( 'a:b', $uri->getUserInfo(), 'Correct getUserInfo' );
+ $this->assertSame( 'example.com:8080', $uri->getHostPort(), 'Correct getHostPort' );
+ $this->assertSame( 'a:b@example.com:8080', $uri->getAuthority(), 'Correct getAuthority' );
+ $this->assertSame( '/path?query=value', $uri->getRelativePath(), 'Correct getRelativePath' );
+ $this->assertSame( 'http://a:b@example.com:8080/path?query=value', $uri->toString(), 'Correct toString' );
+ }
+
+ /**
+ * Ensure that Uri's extend method properly overrides keys
+ */
+ function testExtend() {
+ $uri = new Uri( 'http://example.org/?a=b&hello=world' );
+ $uri->extendQuery( 'a=c&foo=bar' );
+ $this->assertSame( 'a=c&hello=world&foo=bar', $uri->getQueryString() );
+ }
+}
$data = $this->doApiRequest( array(
'action' => 'query',
'titles' => 'Main Page',
- 'intoken' => 'edit|delete|protect|move|block|unblock',
+ 'intoken' => 'edit|delete|protect|move|block|unblock|watch',
'prop' => 'info' ), $session, false, $user->user );
return $data;
}
}
function getTokens() {
- return $this->getTokenList( self::$users['sysop'] );
+ $data = $this->getTokenList( self::$users['sysop'] );
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ return $pageinfo;
}
/**
- * @group Broken
*/
function testWatchEdit() {
-
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'edit',
'title' => 'UTPage',
'text' => 'new text',
'token' => $pageinfo['edittoken'],
- 'watchlist' => 'watch' ), $data );
+ 'watchlist' => 'watch' ) );
$this->assertArrayHasKey( 'edit', $data[0] );
$this->assertArrayHasKey( 'result', $data[0]['edit'] );
$this->assertEquals( 'Success', $data[0]['edit']['result'] );
/**
* @depends testWatchEdit
- * @group Broken
*/
function testWatchClear() {
+ $pageinfo = $this->getTokens();
+
$data = $this->doApiRequest( array(
'action' => 'query',
- 'list' => 'watchlist' ), $data );
+ 'list' => 'watchlist' ) );
if ( isset( $data[0]['query']['watchlist'] ) ) {
$wl = $data[0]['query']['watchlist'];
$data = $this->doApiRequest( array(
'action' => 'watch',
'title' => $page['title'],
- 'unwatch' => true ), $data );
+ 'unwatch' => true,
+ 'token' => $pageinfo['watchtoken'] ) );
}
}
$data = $this->doApiRequest( array(
}
/**
- * @group Broken
*/
function testWatchProtect() {
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'protect',
'token' => $pageinfo['protecttoken'],
'title' => 'UTPage',
'protections' => 'edit=sysop',
- 'watchlist' => 'unwatch' ), $data );
+ 'watchlist' => 'unwatch' ) );
$this->assertArrayHasKey( 'protect', $data[0] );
$this->assertArrayHasKey( 'protections', $data[0]['protect'] );
}
/**
- * @group Broken
*/
function testGetRollbackToken() {
- $data = $this->getTokens();
+ $pageinfo = $this->getTokens();
if ( !Title::newFromText( 'UTPage' )->exists() ) {
- $this->markTestIncomplete( "The article [[UTPage]] does not exist" );
+ $this->markTestSkipped( "The article [[UTPage]] does not exist" ); //TODO: just create it?
}
$data = $this->doApiRequest( array(
'action' => 'query',
'prop' => 'revisions',
'titles' => 'UTPage',
- 'rvtoken' => 'rollback' ), $data );
+ 'rvtoken' => 'rollback' ) );
$this->assertArrayHasKey( 'query', $data[0] );
$this->assertArrayHasKey( 'pages', $data[0]['query'] );
$key = array_pop( $keys );
if ( isset( $data[0]['query']['pages'][$key]['missing'] ) ) {
- $this->markTestIncomplete( "Target page (UTPage) doesn't exist" );
+ $this->markTestSkipped( "Target page (UTPage) doesn't exist" );
}
$this->assertArrayHasKey( 'pageid', $data[0]['query']['pages'][$key] );
}
/**
- * @depends testGetRollbackToken
* @group Broken
+ * Broken because there is currently no revision info in the $pageinfo
+ *
+ * @depends testGetRollbackToken
*/
function testWatchRollback( $data ) {
$keys = array_keys( $data[0]['query']['pages'] );
$key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key]['revisions'][0];
+ $pageinfo = $data[0]['query']['pages'][$key];
+ $revinfo = $pageinfo['revisions'][0];
try {
$data = $this->doApiRequest( array(
'action' => 'rollback',
'title' => 'UTPage',
- 'user' => $pageinfo['user'],
+ 'user' => $revinfo['user'],
'token' => $pageinfo['rollbacktoken'],
- 'watchlist' => 'watch' ), $data );
+ 'watchlist' => 'watch' ) );
+
+ $this->assertArrayHasKey( 'rollback', $data[0] );
+ $this->assertArrayHasKey( 'title', $data[0]['rollback'] );
} catch( UsageException $ue ) {
if( $ue->getCodeString() == 'onlyauthor' ) {
$this->markTestIncomplete( "Only one author to 'UTPage', cannot test rollback" );
$this->fail( "Received error '" . $ue->getCodeString() . "'" );
}
}
-
- $this->assertArrayHasKey( 'rollback', $data[0] );
- $this->assertArrayHasKey( 'title', $data[0]['rollback'] );
}
/**
- * @group Broken
*/
function testWatchDelete() {
-
- $data = $this->getTokens();
-
- $keys = array_keys( $data[0]['query']['pages'] );
- $key = array_pop( $keys );
- $pageinfo = $data[0]['query']['pages'][$key];
+ $pageinfo = $this->getTokens();
$data = $this->doApiRequest( array(
'action' => 'delete',
'token' => $pageinfo['deletetoken'],
- 'title' => 'UTPage' ), $data );
+ 'title' => 'UTPage' ) );
$this->assertArrayHasKey( 'delete', $data[0] );
$this->assertArrayHasKey( 'title', $data[0]['delete'] );
$data = $this->doApiRequest( array(
'action' => 'query',
- 'list' => 'watchlist' ), $data );
+ 'list' => 'watchlist' ) );
$this->markTestIncomplete( 'This test needs to verify the deleted article was added to the users watchlist' );
}
$this->db->addQuotes( "string's cause trouble" ) );
}
+ private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) {
+ global $wgSharedDB, $wgSharedTables, $wgSharedPrefix;
+
+ $oldName = $wgSharedDB;
+ $oldTables = $wgSharedTables;
+ $oldPrefix = $wgSharedPrefix;
+
+ $wgSharedDB = $database;
+ $wgSharedTables = array( $table );
+ $wgSharedPrefix = $prefix;
+
+ $ret = $this->db->tableName( $table, $format );
+
+ $wgSharedDB = $oldName;
+ $wgSharedTables = $oldTables;
+ $wgSharedPrefix = $oldPrefix;
+
+ return $ret;
+ }
+
+ private function prefixAndQuote( $table, $database = null, $prefix = null, $format = 'quoted' ) {
+ if ( $this->db->getType() === 'sqlite' || $format !== 'quoted' ) {
+ $quote = '';
+ } elseif ( $this->db->getType() === 'mysql' ) {
+ $quote = '`';
+ } else {
+ $quote = '"';
+ }
+
+ if ( $database !== null ) {
+ $database = $quote . $database . $quote . '.';
+ }
+
+ if ( $prefix === null ) {
+ $prefix = $this->dbPrefix();
+ }
+
+ return $database . $quote . $prefix . $table . $quote;
+ }
+
+ function testTableNameLocal() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename' ),
+ $this->db->tableName( 'tablename' )
+ );
+ }
+
+ function testTableNameRawLocal() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', null, null, 'raw' ),
+ $this->db->tableName( 'tablename', 'raw' )
+ );
+ }
+
+ function testTableNameShared() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_' ),
+ $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_' )
+ );
+
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'sharedatabase', null ),
+ $this->getSharedTableName( 'tablename', 'sharedatabase', null )
+ );
+ }
+
+ function testTableNameRawShared() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_', 'raw' ),
+ $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_', 'raw' )
+ );
+
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'sharedatabase', null, 'raw' ),
+ $this->getSharedTableName( 'tablename', 'sharedatabase', null, 'raw' )
+ );
+ }
+
+ function testTableNameForeign() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'databasename', '' ),
+ $this->db->tableName( 'databasename.tablename' )
+ );
+ }
+
+ function testTableNameRawForeign() {
+ $this->assertEquals(
+ $this->prefixAndQuote( 'tablename', 'databasename', '', 'raw' ),
+ $this->db->tableName( 'databasename.tablename', 'raw' )
+ );
+ }
+
function testFillPreparedEmpty() {
$sql = $this->db->fillPrepared(
'SELECT * FROM interwiki', array() );
$idField = $isSqlite ? 'INTEGER' : 'INT unsigned';
$primaryKey = $isSqlite ? 'PRIMARY KEY AUTOINCREMENT' : 'auto_increment PRIMARY KEY';
- $dbw->safeQuery(
+ $dbw->query(
'CREATE TABLE IF NOT EXISTS ' . $dbw->tableName( 'orm_test' ) . '(
test_id ' . $idField . ' NOT NULL ' . $primaryKey . ',
test_name VARCHAR(255) NOT NULL,
--- /dev/null
+<?php
+/**
+ * Based on the test suite of the original Python
+ * CSSJanus libary:
+ * http://code.google.com/p/cssjanus/source/browse/trunk/cssjanus_test.py
+ * Ported to PHP for ResourceLoader and has been extended since.
+ */
+class CSSJanusTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider provideTransformCases
+ */
+ function testTransform( $cssA, $cssB = null ) {
+
+ if ( $cssB ) {
+ $transformedA = CSSJanus::transform( $cssA );
+ $this->assertEquals( $transformedA, $cssB, 'Test A-B transformation' );
+
+ $transformedB = CSSJanus::transform( $cssB );
+ $this->assertEquals( $transformedB, $cssA, 'Test B-A transformation' );
+
+ // If no B version is provided, it means
+ // the output should equal the input.
+ } else {
+ $transformedA = CSSJanus::transform( $cssA );
+ $this->assertEquals( $transformedA, $cssA, 'Nothing was flipped' );
+ }
+ }
+
+ /**
+ * @dataProvider provideTransformAdvancedCases
+ */
+ function testTransformAdvanced( $code, $expectedOutput, $options = array() ) {
+ $swapLtrRtlInURL = isset( $options['swapLtrRtlInURL'] ) ? $options['swapLtrRtlInURL'] : false;
+ $swapLeftRightInURL = isset( $options['swapLeftRightInURL'] ) ? $options['swapLeftRightInURL'] : false;
+
+ $flipped = CSSJanus::transform( $code, $swapLtrRtlInURL, $swapLeftRightInURL );
+
+ $this->assertEquals( $expectedOutput, $flipped,
+ 'Test flipping, options: url-ltr-rtl=' . ($swapLtrRtlInURL ? 'true' : 'false')
+ . ' url-left-right=' . ($swapLeftRightInURL ? 'true' : 'false')
+ );
+ }
+ /**
+ * @dataProvider provideTransformBrokenCases
+ * @group Broken
+ */
+ function testTransformBroken( $code, $expectedOutput ) {
+ $flipped = CSSJanus::transform( $code );
+
+ $this->assertEquals( $expectedOutput, $flipped, 'Test flipping' );
+ }
+
+ /**
+ * These transform cases are tested *in both directions*
+ * No need to declare a principle twice in both directions here.
+ */
+ function provideTransformCases() {
+ return array(
+ // Property keys
+ array(
+ '.foo { left: 0; }',
+ '.foo { right: 0; }'
+ ),
+ // Guard against partial keys
+ // (CSS currently doesn't have flippable properties
+ // that contain the direction as part of the key without
+ // dash separation)
+ array(
+ '.foo { alright: 0; }'
+ ),
+ array(
+ '.foo { balleft: 0; }'
+ ),
+
+ // Dashed property keys
+ array(
+ '.foo { padding-left: 0; }',
+ '.foo { padding-right: 0; }'
+ ),
+ array(
+ '.foo { margin-left: 0; }',
+ '.foo { margin-right: 0; }'
+ ),
+ array(
+ '.foo { border-left: 0; }',
+ '.foo { border-right: 0; }'
+ ),
+
+ // Double-dashed property keys
+ array(
+ '.foo { border-left-color: red; }',
+ '.foo { border-right-color: red; }'
+ ),
+ array(
+ // Includes unknown properties?
+ '.foo { x-left-y: 0; }',
+ '.foo { x-right-y: 0; }'
+ ),
+
+ // Multi-value properties
+ array(
+ '.foo { padding: 0; }'
+ ),
+ array(
+ '.foo { padding: 0 1px; }'
+ ),
+ array(
+ '.foo { padding: 0 1px 2px; }'
+ ),
+ array(
+ '.foo { padding: 0 1px 2px 3px; }',
+ '.foo { padding: 0 3px 2px 1px; }'
+ ),
+
+ // Shorthand / Four notation
+ array(
+ '.foo { padding: .25em 15px 0pt 0ex; }',
+ '.foo { padding: .25em 0ex 0pt 15px; }'
+ ),
+ array(
+ '.foo { margin: 1px -4px 3px 2px; }',
+ '.foo { margin: 1px 2px 3px -4px; }'
+ ),
+ array(
+ '.foo { padding: 0 15px .25em 0; }',
+ '.foo { padding: 0 0 .25em 15px; }'
+ ),
+ array(
+ '.foo { padding: 1px 4.1grad 3px 2%; }',
+ '.foo { padding: 1px 2% 3px 4.1grad; }'
+ ),
+ array(
+ '.foo { padding: 1px 2px 3px auto; }',
+ '.foo { padding: 1px auto 3px 2px; }'
+ ),
+ array(
+ '.foo { padding: 1px inherit 3px auto; }',
+ '.foo { padding: 1px auto 3px inherit; }'
+ ),
+ array(
+ '.foo { border-radius: .25em 15px 0pt 0ex; }',
+ '.foo { border-radius: .25em 0ex 0pt 15px; }'
+ ),
+ array(
+ '.foo { x-unknown: a b c d; }'
+ ),
+ array(
+ '.foo barpx 0 2% { opacity: 0; }'
+ ),
+ array(
+ '#settings td p strong'
+ ),
+ array(
+ # Not sure how 4+ values should behave,
+ # testing to make sure changes are detected
+ '.foo { x-unknown: 1 2 3 4 5; }',
+ '.foo { x-unknown: 1 4 3 2 5; }',
+ ),
+ array(
+ '.foo { x-unknown: 1 2 3 4 5 6; }',
+ '.foo { x-unknown: 1 4 3 2 5 6; }',
+ ),
+
+ // Shorthand / Three notation
+ array(
+ '.foo { margin: 1em 0 .25em; }'
+ ),
+ array(
+ '.foo { margin:-1.5em 0 -.75em; }'
+ ),
+
+ // Shorthand / Two notation
+ array(
+ '.foo { padding: 1px 2px; }'
+ ),
+
+ // Shorthand / One notation
+ array(
+ '.foo { padding: 1px; }'
+ ),
+
+ // Direction
+ // Note: This differs from the Python implementation,
+ // see also CSSJanus::fixDirection for more info.
+ array(
+ '.foo { direction: ltr; }',
+ '.foo { direction: rtl; }'
+ ),
+ array(
+ '.foo { direction: rtl; }',
+ '.foo { direction: ltr; }'
+ ),
+ array(
+ 'input { direction: ltr; }',
+ 'input { direction: rtl; }'
+ ),
+ array(
+ 'input { direction: rtl; }',
+ 'input { direction: ltr; }'
+ ),
+ array(
+ 'body { direction: ltr; }',
+ 'body { direction: rtl; }'
+ ),
+ array(
+ '.foo, body, input { direction: ltr; }',
+ '.foo, body, input { direction: rtl; }'
+ ),
+ array(
+ 'body { padding: 10px; direction: ltr; }',
+ 'body { padding: 10px; direction: rtl; }'
+ ),
+ array(
+ 'body { direction: ltr } .myClass { direction: ltr }',
+ 'body { direction: rtl } .myClass { direction: rtl }'
+ ),
+
+ // Left/right values
+ array(
+ '.foo { float: left; }',
+ '.foo { float: right; }'
+ ),
+ array(
+ '.foo { text-align: left; }',
+ '.foo { text-align: right; }'
+ ),
+ array(
+ '.foo { -x-unknown: left; }',
+ '.foo { -x-unknown: right; }'
+ ),
+ // Guard against selectors that look flippable
+ array(
+ '.column-left { width: 0; }'
+ ),
+ array(
+ 'a.left { width: 0; }'
+ ),
+ array(
+ 'a.leftification { width: 0; }'
+ ),
+ array(
+ 'a.ltr { width: 0; }'
+ ),
+ array(
+ # <div class="a-ltr png">
+ '.a-ltr.png { width: 0; }'
+ ),
+ array(
+ # <foo-ltr attr="x">
+ 'foo-ltr[attr="x"] { width: 0; }'
+ ),
+ array(
+ 'div.left > span.right+span.left { width: 0; }'
+ ),
+ array(
+ '.thisclass .left .myclass { width: 0; }'
+ ),
+ array(
+ '.thisclass .left .myclass #myid { width: 0; }'
+ ),
+
+ // Cursor values (east/west)
+ array(
+ '.foo { cursor: e-resize; }',
+ '.foo { cursor: w-resize; }'
+ ),
+ array(
+ '.foo { cursor: se-resize; }',
+ '.foo { cursor: sw-resize; }'
+ ),
+ array(
+ '.foo { cursor: ne-resize; }',
+ '.foo { cursor: nw-resize; }'
+ ),
+
+ // Background
+ array(
+ '.foo { background-position: top left; }',
+ '.foo { background-position: top right; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) top left; }',
+ '.foo { background: url(/foo/bar.png) top right; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) top left no-repeat; }',
+ '.foo { background: url(/foo/bar.png) top right no-repeat; }'
+ ),
+ array(
+ '.foo { background: url(/foo/bar.png) no-repeat top left; }',
+ '.foo { background: url(/foo/bar.png) no-repeat top right; }'
+ ),
+ array(
+ '.foo { background: #fff url(/foo/bar.png) no-repeat top left; }',
+ '.foo { background: #fff url(/foo/bar.png) no-repeat top right; }'
+ ),
+ array(
+ '.foo { background-position: 100% 40%; }',
+ '.foo { background-position: 0% 40%; }'
+ ),
+ array(
+ '.foo { background-position: 23% 0; }',
+ '.foo { background-position: 77% 0; }'
+ ),
+ array(
+ '.foo { background-position: 23% auto; }',
+ '.foo { background-position: 77% auto; }'
+ ),
+ array(
+ '.foo { background-position-x: 23%; }',
+ '.foo { background-position-x: 77%; }'
+ ),
+ array(
+ '.foo { background-position-y: 23%; }',
+ '.foo { background-position-y: 23%; }'
+ ),
+ array(
+ '.foo { background:url(../foo.png) no-repeat 75% 50%; }',
+ '.foo { background:url(../foo.png) no-repeat 25% 50%; }'
+ ),
+ array(
+ '.foo { background: 10% 20% } .bar { background: 40% 30% }',
+ '.foo { background: 90% 20% } .bar { background: 60% 30% }'
+ ),
+
+ // Multiple rules
+ array(
+ 'body { direction: rtl; float: right; } .foo { direction: ltr; float: right; }',
+ 'body { direction: ltr; float: left; } .foo { direction: rtl; float: left; }',
+ ),
+
+ // Duplicate properties
+ array(
+ '.foo { float: left; float: right; float: left; }',
+ '.foo { float: right; float: left; float: right; }',
+ ),
+
+ // Preserve comments
+ array(
+ '/* left /* right */left: 10px',
+ '/* left /* right */right: 10px'
+ ),
+ array(
+ '/*left*//*left*/left: 10px',
+ '/*left*//*left*/right: 10px'
+ ),
+ array(
+ '/* Going right is cool */ .foo { width: 0 }',
+ ),
+ array(
+ "/* padding-right 1 2 3 4 */\n#test { width: 0}\n/*right*/"
+ ),
+ array(
+ "/** Two line comment\n * left\n \*/\n#test {width: 0}"
+ ),
+
+ // @noflip annotation
+ array(
+ // before selector (single)
+ '/* @noflip */ div { float: left; }'
+ ),
+ array(
+ // before selector (multiple)
+ '/* @noflip */ div, .notme { float: left; }'
+ ),
+ array(
+ // inside selector
+ 'div, /* @noflip */ .foo { float: left; }'
+ ),
+ array(
+ // after selector
+ 'div, .notme /* @noflip */ { float: left; }'
+ ),
+ array(
+ // before multiple rules
+ '/* @noflip */ div { float: left; } .foo { float: left; }',
+ '/* @noflip */ div { float: left; } .foo { float: right; }'
+ ),
+ array(
+ // after multiple rules
+ '.foo { float: left; } /* @noflip */ div { float: left; }',
+ '.foo { float: right; } /* @noflip */ div { float: left; }'
+ ),
+ array(
+ // before multiple properties
+ 'div { /* @noflip */ float: left; text-align: left; }',
+ 'div { /* @noflip */ float: left; text-align: right; }'
+ ),
+ array(
+ // after multiple properties
+ 'div { float: left; /* @noflip */ text-align: left; }',
+ 'div { float: right; /* @noflip */ text-align: left; }'
+ ),
+
+ // Guard against css3 stuff
+ array(
+ 'background-image: -moz-linear-gradient(#326cc1, #234e8c);'
+ ),
+ array(
+ 'background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#666666), to(#ffffff));'
+ ),
+
+ // CSS syntax / white-space variations
+ // spaces, no spaces, tabs, new lines, omitting semi-colons
+ array(
+ ".foo { left: 0; }",
+ ".foo { right: 0; }"
+ ),
+ array(
+ ".foo{ left: 0; }",
+ ".foo{ right: 0; }"
+ ),
+ array(
+ ".foo{ left: 0 }",
+ ".foo{ right: 0 }"
+ ),
+ array(
+ ".foo{left:0 }",
+ ".foo{right:0 }"
+ ),
+ array(
+ ".foo{left:0}",
+ ".foo{right:0}"
+ ),
+ array(
+ ".foo { left : 0 ; }",
+ ".foo { right : 0 ; }"
+ ),
+ array(
+ ".foo\n { left : 0 ; }",
+ ".foo\n { right : 0 ; }"
+ ),
+ array(
+ ".foo\n { \nleft : 0 ; }",
+ ".foo\n { \nright : 0 ; }"
+ ),
+ array(
+ ".foo\n { \n left : 0 ; }",
+ ".foo\n { \n right : 0 ; }"
+ ),
+ array(
+ ".foo\n { \n left\n : 0; }",
+ ".foo\n { \n right\n : 0; }"
+ ),
+ array(
+ ".foo \n { \n left\n : 0; }",
+ ".foo \n { \n right\n : 0; }"
+ ),
+ array(
+ ".foo\n{\nleft\n:\n0;}",
+ ".foo\n{\nright\n:\n0;}"
+ ),
+ array(
+ ".foo\n.bar {\n\tleft: 0;\n}",
+ ".foo\n.bar {\n\tright: 0;\n}"
+ ),
+ array(
+ ".foo\t{\tleft\t:\t0;}",
+ ".foo\t{\tright\t:\t0;}"
+ ),
+ );
+ }
+
+ /**
+ * These cases are tested in one way only (format: actual, expected, msg).
+ * If both ways can be tested, either put both versions in here or move
+ * it to provideTransformCases().
+ */
+ function provideTransformAdvancedCases() {
+ $bgPairs = array(
+ # [ - _ . ] <-> [ left right ltr rtl ]
+ 'foo.jpg' => 'foo.jpg',
+ 'left.jpg' => 'right.jpg',
+ 'ltr.jpg' => 'rtl.jpg',
+
+ 'foo-left.png' => 'foo-right.png',
+ 'foo_left.png' => 'foo_right.png',
+ 'foo.left.png' => 'foo.right.png',
+
+ 'foo-ltr.png' => 'foo-rtl.png',
+ 'foo_ltr.png' => 'foo_rtl.png',
+ 'foo.ltr.png' => 'foo.rtl.png',
+
+ 'left-foo.png' => 'right-foo.png',
+ 'left_foo.png' => 'right_foo.png',
+ 'left.foo.png' => 'right.foo.png',
+
+ 'ltr-foo.png' => 'rtl-foo.png',
+ 'ltr_foo.png' => 'rtl_foo.png',
+ 'ltr.foo.png' => 'rtl.foo.png',
+
+ 'foo-ltr-left.gif' => 'foo-rtl-right.gif',
+ 'foo_ltr_left.gif' => 'foo_rtl_right.gif',
+ 'foo.ltr.left.gif' => 'foo.rtl.right.gif',
+ 'foo-ltr_left.gif' => 'foo-rtl_right.gif',
+ 'foo_ltr.left.gif' => 'foo_rtl.right.gif',
+ );
+ $provider = array();
+ foreach ( $bgPairs as $left => $right ) {
+ # By default '-rtl' and '-left' etc. are not touched,
+ # Only when the appropiate parameter is set.
+ $provider[] = array(
+ ".foo { background: url(images/$left); }",
+ ".foo { background: url(images/$left); }"
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$right); }",
+ ".foo { background: url(images/$right); }"
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$left); }",
+ ".foo { background: url(images/$right); }",
+ array(
+ 'swapLtrRtlInURL' => true,
+ 'swapLeftRightInURL' => true,
+ )
+ );
+ $provider[] = array(
+ ".foo { background: url(images/$right); }",
+ ".foo { background: url(images/$left); }",
+ array(
+ 'swapLtrRtlInURL' => true,
+ 'swapLeftRightInURL' => true,
+ )
+ );
+ }
+
+ return $provider;
+ }
+
+ /**
+ * Cases that are currently failing, but
+ * should be looked at in the future as enhancements and/or bug fix
+ */
+ function provideTransformBrokenCases() {
+ return array(
+ // Guard against partial keys
+ array(
+ '.foo { leftxx: 0; }',
+ '.foo { leftxx: 0; }'
+ ),
+ array(
+ '.foo { rightxx: 0; }',
+ '.foo { rightxx: 0; }'
+ ),
+
+ // Guard against selectors that look flippable
+ array(
+ # <foo-left-x attr="x">
+ 'foo-left-x[attr="x"] { width: 0; }',
+ 'foo-left-x[attr="x"] { width: 0; }'
+ ),
+ array(
+ # <div class="foo" data-left="x">
+ '.foo[data-left="x"] { width: 0; }',
+ '.foo[data-left="x"] { width: 0; }'
+ ),
+ );
+ }
+}
--- /dev/null
+<?php
+
+
+/**
+ * Tests for the GenericArrayObject and deriving classes.
+ *
+ * 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
+ * @since 1.20
+ *
+ * @ingroup Test
+ * @group GenericArrayObject
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class GenericArrayObjectTest extends MediaWikiTestCase {
+
+ /**
+ * Returns objects that can serve as elements in the concrete GenericArrayObject deriving class being tested.
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ public abstract function elementInstancesProvider();
+
+ /**
+ * Provides instances of the concrete class being tested.
+ *
+ * @since 1.20
+ *
+ * @return array
+ */
+ public abstract function instanceProvider();
+
+ /**
+ * Returns the name of the concrete class being tested.
+ *
+ * @since 1.20
+ *
+ * @return string
+ */
+ public abstract function getInstanceClass();
+
+ /**
+ * @since 1.20
+ *
+ * @param array $elements
+ *
+ * @return GenericArrayObject
+ */
+ protected function getNew( array $elements = array() ) {
+ $class = $this->getInstanceClass();
+ return new $class( $elements );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testConstructor( array $elements ) {
+ $arrayObject = $this->getNew( $elements );
+
+ $this->assertEquals( count( $elements ), $arrayObject->count() );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testIsEmpty( array $elements ) {
+ $arrayObject = $this->getNew( $elements );
+
+ $this->assertEquals( $elements === array(), $arrayObject->isEmpty() );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @since 1.20
+ *
+ * @param GenericArrayObject $list
+ */
+ public function testUnset( GenericArrayObject $list ) {
+ if ( !$list->isEmpty() ) {
+ $offset = $list->getIterator()->key();
+ $count = $list->count();
+ $list->offsetUnset( $offset );
+ $this->assertEquals( $count - 1, $list->count() );
+ }
+
+ if ( !$list->isEmpty() ) {
+ $offset = $list->getIterator()->key();
+ $count = $list->count();
+ unset( $list[$offset] );
+ $this->assertEquals( $count - 1, $list->count() );
+ }
+
+ $exception = null;
+ try { $list->offsetUnset( 'sdfsedtgsrdysftu' ); } catch ( \Exception $exception ){}
+ $this->assertInstanceOf( '\Exception', $exception );
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testAppend( array $elements ) {
+ $list = $this->getNew();
+
+ $listSize = count( $elements );
+
+ foreach ( $elements as $element ) {
+ $list->append( $element );
+ }
+
+ $this->assertEquals( $listSize, $list->count() );
+
+ $list = $this->getNew();
+
+ foreach ( $elements as $element ) {
+ $list[] = $element;
+ }
+
+ $this->assertEquals( $listSize, $list->count() );
+
+ $this->checkTypeChecks( function( GenericArrayObject $list, $element ) {
+ $list->append( $element );
+ } );
+ }
+
+ /**
+ * @since 1.20
+ *
+ * @param callback $function
+ */
+ protected function checkTypeChecks( $function ) {
+ $excption = null;
+ $list = $this->getNew();
+
+ $elementClass = $list->getObjectType();
+
+ foreach ( array( 42, 'foo', array(), new \stdClass(), 4.2 ) as $element ) {
+ $validValid = $element instanceof $elementClass;
+
+ try{
+ call_user_func( $function, $list, $element );
+ $valid = true;
+ }
+ catch ( \MWException $exception ) {
+ $valid = false;
+ }
+
+ $this->assertEquals(
+ $validValid,
+ $valid,
+ 'Object of invalid type got successfully added to a GenericArrayObject'
+ );
+ }
+ }
+
+ /**
+ * @dataProvider elementInstancesProvider
+ *
+ * @since 1.20
+ *
+ * @param array $elements
+ */
+ public function testOffsetSet( array $elements ) {
+ if ( $elements === array() ) {
+ $this->assertTrue( true );
+ return;
+ }
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( 42, $element );
+ $this->assertEquals( $element, $list->offsetGet( 42 ) );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list['oHai'] = $element;
+ $this->assertEquals( $element, $list['oHai'] );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( 9001, $element );
+ $this->assertEquals( $element, $list[9001] );
+
+ $list = $this->getNew();
+
+ $element = reset( $elements );
+ $list->offsetSet( null, $element );
+ $this->assertEquals( $element, $list[0] );
+
+ $list = $this->getNew();
+ $offset = 0;
+
+ foreach ( $elements as $element ) {
+ $list->offsetSet( null, $element );
+ $this->assertEquals( $element, $list[$offset++] );
+ }
+
+ $this->assertEquals( count( $elements ), $list->count() );
+
+ $this->checkTypeChecks( function( GenericArrayObject $list, $element ) {
+ $list->offsetSet( mt_rand(), $element );
+ } );
+ }
+
+}
$this->savedGlobals = array();
+ /** @since 1.20 */
+ wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
+
foreach ( $settings as $var => $val ) {
if ( array_key_exists( $var, $GLOBALS ) ) {
$this->savedGlobals[$var] = $GLOBALS[$var];
$this->upload = new UploadTestHandler;
$this->hooks = $wgHooks;
- $wgHooks['InterwikiLoadPrefix'][] = 'MediaWikiTestCase::disableInterwikis';
+ $wgHooks['InterwikiLoadPrefix'][] = function( $prefix, &$data ) {
+ return false;
+ };
}
function tearDown() {
-start();
-ok( true, 'Successfully loaded!');
+QUnit.start();
+QUnit.assert.ok( true, 'Successfully loaded!');
( function ( $, mw, QUnit, undefined ) {
-"use strict";
+/*global CompletenessTest */
+/*jshint evil:true */
+'use strict';
var mwTestIgnore, mwTester, addons;
QUnit.config.testTimeout = 10 * 1000;
// Add a checkbox to QUnit header to toggle MediaWiki ResourceLoader debug mode.
-QUnit.config.urlConfig.push( 'debug' );
+QUnit.config.urlConfig.push( {
+ id: 'debug',
+ label: 'Enable ResourceLoaderDebug',
+ tooltip: 'Enable debug mode in ResourceLoader'
+} );
/**
* Load TestSwarm agent
* CompletenessTest
*/
// Adds toggle checkbox to header
-QUnit.config.urlConfig.push( 'completenesstest' );
+QUnit.config.urlConfig.push( {
+ id: 'completenesstest',
+ label: 'Run CompletenessTest',
+ tooltip: 'Run the completeness test'
+} );
// Initiate when enabled
if ( QUnit.urlParams.completenesstest ) {
mw.config.values = freshConfigCopy( localEnv.config );
mw.messages.values = freshMessagesCopy( localEnv.messages );
- localEnv.setup()
+ localEnv.setup();
},
teardown: function () {
// Expect boolean true
assertTrue: function ( actual, message ) {
- strictEqual( actual, true, message );
+ QUnit.push( actual === true, actual, true, message );
},
// Expect boolean false
assertFalse: function ( actual, message ) {
- strictEqual( actual, false, message );
+ QUnit.push( actual === false, actual, false, message );
},
// Expect numerical value less than X
// Expect numerical value greater than or equal to X
gtOrEq: function ( actual, expected, message ) {
QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message );
- },
-
- // Backwards compatible with new verions of QUnit
- equals: window.equal,
- same: window.deepEqual
+ }
};
-$.extend( QUnit, addons );
-$.extend( window, addons );
+$.extend( QUnit.assert, addons );
/**
* Small test suite to confirm proper functionality of the utilities and
* initializations in this file.
*/
var envExecCount = 0;
-module( 'mediawiki.tests.qunit.testrunner', QUnit.newMwEnvironment({
+QUnit.module( 'mediawiki.tests.qunit.testrunner', QUnit.newMwEnvironment({
setup: function () {
envExecCount += 1;
this.mwHtmlLive = mw.html;
}
}) );
-test( 'Setup', function () {
- expect( 3 );
-
- equal( mw.html.escape( 'foo' ), 'mocked-1', 'extra setup() callback was ran.' );
- equal( mw.config.get( 'testVar' ), 'foo', 'config object applied' );
- equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' );
+QUnit.test( 'Setup', 3, function ( assert ) {
+ assert.equal( mw.html.escape( 'foo' ), 'mocked-1', 'extra setup() callback was ran.' );
+ assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object applied' );
+ assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' );
mw.config.set( 'testVar', 'bar' );
mw.messages.set( 'testMsg', 'Bar.' );
});
-test( 'Teardown', function () {
- expect( 3 );
-
- equal( mw.html.escape( 'foo' ), 'mocked-2', 'extra setup() callback was re-ran.' );
- equal( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' );
- equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' );
+QUnit.test( 'Teardown', 3, function ( assert ) {
+ assert.equal( mw.html.escape( 'foo' ), 'mocked-2', 'extra setup() callback was re-ran.' );
+ assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' );
+ assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' );
});
-module( 'mediawiki.tests.qunit.testrunner-after', QUnit.newMwEnvironment() );
-
-test( 'Teardown', function () {
- expect( 3 );
+QUnit.module( 'mediawiki.tests.qunit.testrunner-after', QUnit.newMwEnvironment() );
- equal( mw.html.escape( '<' ), '<', 'extra teardown() callback was ran.' );
- equal( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' );
- equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' );
+QUnit.test( 'Teardown', 3, function ( assert ) {
+ assert.equal( mw.html.escape( '<' ), '<', 'extra teardown() callback was ran.' );
+ assert.equal( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' );
+ assert.equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' );
});
-})( jQuery, mediaWiki, QUnit );
+}( jQuery, mediaWiki, QUnit ) );
-module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() );
+( function ( mw, $ ) {
-test( '-- Initial check', function() {
- expect(1);
- ok( $.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' );
-});
+QUnit.module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() );
function createWrappedDiv( text, width ) {
var $wrapper = $( '<div>' ).css( 'width', width );
function findDivergenceIndex( a, b ) {
var i = 0;
- while ( i < a.length && i < b.length && a[i] == b[i] ) {
+ while ( i < a.length && i < b.length && a[i] === b[i] ) {
i++;
}
return i;
}
-test( 'Position right', function() {
- expect(4);
-
+QUnit.test( 'Position right', 4, function ( assert ) {
// We need this thing to be visible, so append it to the DOM
var origText = 'This is a really long random string and there is no way it fits in 100 pixels.';
var $wrapper = createWrappedDiv( origText, '100px' );
// Verify that, and only one, span element was created
var $span = $wrapper.find( '> span' );
- strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' );
+ assert.strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' );
// Check that the text fits by turning on word wrapping
$span.css( 'whiteSpace', 'nowrap' );
- ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" );
+ assert.ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" );
// Add two characters using scary black magic
var spanText = $span.text();
var d = findDivergenceIndex( origText, spanText );
var spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...';
- gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' );
+ assert.gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' );
// Put this text in the span and verify it doesn't fit
$span.text( spanTextNew );
// In IE6 width works like min-width, allow IE6's width to be "equal to"
if ( $.browser.msie && Number( $.browser.version ) === 6 ) {
- gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' );
+ assert.gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' );
} else {
- gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' );
+ assert.gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' );
}
});
+
+}( mediaWiki, jQuery ) );
-module( 'jquery.byteLength', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( $.byteLength, 'jQuery.byteLength defined' );
-} );
-
-test( 'Simple text', function() {
- expect(5);
+QUnit.module( 'jquery.byteLength', QUnit.newMwEnvironment() );
+QUnit.test( 'Simple text', 5, function ( assert ) {
var azLc = 'abcdefghijklmnopqrstuvwxyz',
azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
num = '0123456789',
x = '*',
space = ' ';
- equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
- equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
- equal( $.byteLength( num ), 10, 'Numbers 0-9' );
- equal( $.byteLength( x ), 1, 'An asterisk' );
- equal( $.byteLength( space ), 3, '3 spaces' );
+ assert.equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
+ assert.equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
+ assert.equal( $.byteLength( num ), 10, 'Numbers 0-9' );
+ assert.equal( $.byteLength( x ), 1, 'An asterisk' );
+ assert.equal( $.byteLength( space ), 3, '3 spaces' );
} );
-test( 'Special text', window.foo = function() {
- expect(5);
-
+QUnit.test( 'Special text', 5, function ( assert ) {
// http://en.wikipedia.org/wiki/UTF-8
var U_0024 = '\u0024',
U_00A2 = '\u00A2',
// according to http://www.fileformat.info/info/unicode/char/24B62/index.htm
U_024B62_alt = '\uD852\uDF62';
- strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' );
- strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' );
- strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' );
- strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' );
- strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' );
+ assert.strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' );
+ assert.strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' );
+ assert.strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' );
+ assert.strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' );
+ assert.strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' );
} );
( function ( $ ) {
var simpleSample, U_20AC, mbSample;
- module( 'jquery.byteLimit', QUnit.newMwEnvironment() );
+ QUnit.module( 'jquery.byteLimit', QUnit.newMwEnvironment() );
// Simple sample (20 chars, 20 bytes)
simpleSample = '12345678901234567890';
limit: null
}, options);
- test( opt.description, function () {
+ QUnit.test( opt.description, function ( assert ) {
var rawVal, fn, newVal;
opt.$input.appendTo( '#qunit-fixture' );
newVal = $.isFunction( fn ) ? fn( rawVal ) : rawVal;
if ( opt.hasLimit ) {
- expect(3);
+ QUnit.expect(3);
- QUnit.ltOrEq(
+ assert.ltOrEq(
$.byteLength( newVal ),
opt.limit,
'Prevent keypresses after byteLimit was reached, length never exceeded the limit'
);
- equal(
+ assert.equal(
$.byteLength( rawVal ),
$.byteLength( opt.expected ),
'Not preventing keypresses too early, length has reached the expected length'
);
- equal( rawVal, opt.expected, 'New value matches the expected string' );
+ assert.equal( rawVal, opt.expected, 'New value matches the expected string' );
} else {
- expect(2);
- equal( newVal, opt.expected, 'New value matches the expected string' );
- equal(
+ QUnit.expect(2);
+ assert.equal( newVal, opt.expected, 'New value matches the expected string' );
+ assert.equal(
$.byteLength( newVal ),
$.byteLength( opt.expected ),
'Unlimited scenarios are not affected, expected length reached'
} );
}
- test( '-- Initial check', function () {
- expect(1);
- ok( $.fn.byteLimit, 'jQuery.fn.byteLimit defined' );
- } );
-
byteLimitTest({
description: 'Plain text input',
$input: $( '<input>' )
expected: 'User:Sample'
});
- test( 'Confirm properties and attributes set', function () {
+ QUnit.test( 'Confirm properties and attributes set', 5, function ( assert ) {
var $el, $elA, $elB;
- expect(5);
-
$el = $( '<input>' )
.attr( 'type', 'text' )
.prop( 'maxLength', '7' )
.appendTo( '#qunit-fixture' )
.byteLimit();
- strictEqual( $el.prop( 'maxLength' ), 7, 'Pre-set maxLength property unchanged' );
+ assert.strictEqual( $el.prop( 'maxLength' ), 7, 'Pre-set maxLength property unchanged' );
$el = $( '<input>' )
.attr( 'type', 'text' )
.appendTo( '#qunit-fixture' )
.byteLimit( 12 );
- strictEqual( $el.prop( 'maxLength' ), 12, 'maxLength property updated if value was passed to $.fn.byteLimit' );
+ assert.strictEqual( $el.prop( 'maxLength' ), 12, 'maxLength property updated if value was passed to $.fn.byteLimit' );
$elA = $( '<input>' )
.addClass( 'mw-test-byteLimit-foo' )
$el = $( '.mw-test-byteLimit-foo' );
- strictEqual( $el.length, 2, 'Verify that there are no other elements clashing with this test suite' );
+ assert.strictEqual( $el.length, 2, 'Verify that there are no other elements clashing with this test suite' );
$el.byteLimit();
// because $.fn.byteLimit sets:
// `limit = limitArg || this.prop( 'maxLength' ); this.prop( 'maxLength', limit )`
// and did so outside the each() loop.
- strictEqual( $elA.prop( 'maxLength' ), 7, 'maxLength was not incorrectly set on #1 when calling byteLimit on multiple elements (bug 35294)' );
- strictEqual( $elB.prop( 'maxLength' ), 12, 'maxLength was not incorrectly set on #2 when calling byteLimit on multiple elements (bug 35294)' );
+ assert.strictEqual( $elA.prop( 'maxLength' ), 7, 'maxLength was not incorrectly set on #1 when calling byteLimit on multiple elements (bug 35294)' );
+ assert.strictEqual( $elB.prop( 'maxLength' ), 12, 'maxLength was not incorrectly set on #2 when calling byteLimit on multiple elements (bug 35294)' );
});
}( jQuery ) );
-module( 'jquery.client', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( jQuery.client, 'jQuery.client defined' );
-});
+QUnit.module( 'jquery.client', QUnit.newMwEnvironment() );
/** Number of user-agent defined */
var uacount = 0;
-var uas = (function() {
+var uas = (function () {
// Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
// Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
}
}
};
- $.each( uas, function() { uacount++ });
+ $.each( uas, function () {
+ uacount++;
+ });
return uas;
-})();
-
-test( 'profile userAgent support', function() {
- expect(uacount);
+}());
+QUnit.test( 'profile userAgent support', uacount, function ( assert ) {
// Generate a client profile object and compare recursively
var uaTest = function( rawUserAgent, data ) {
var ret = $.client.profile( {
userAgent: rawUserAgent,
platform: data.platform
} );
- deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
+ assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
};
// Loop through and run tests
$.each( uas, uaTest );
} );
-test( 'profile return validation for current user agent', function() {
- expect(7);
+QUnit.test( 'profile return validation for current user agent', 7, function ( assert ) {
var p = $.client.profile();
- var unknownOrType = function( val, type, summary ) {
- return ok( typeof val === type || val === 'unknown', summary );
- };
+ function unknownOrType( val, type, summary ) {
+ assert.ok( typeof val === type || val === 'unknown', summary );
+ }
- equal( typeof p, 'object', 'profile returns an object' );
+ assert.equal( typeof p, 'object', 'profile returns an object' );
unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' );
unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' );
unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' );
unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' );
unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' );
- equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
+ assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
});
// Example from WikiEditor
}
};
-test( 'test', function() {
- expect(1);
-
+QUnit.test( 'test', 1, function ( assert ) {
// .test() uses eval, make sure no exceptions are thrown
// then do a basic return value type check
var testMatch = $.client.test( testMap );
- equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
+ assert.equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
});
-test( 'User-agent matches against WikiEditor\'s compatibility map', function() {
- expect( uacount * 2 ); // double since we test both LTR and RTL
-
+QUnit.test( 'User-agent matches against WikiEditor\'s compatibility map', uacount * 2, function ( assert ) {
var $body = $( 'body' ),
bodyClasses = $body.attr( 'class' );
var testMatch = $.client.test( testMap, profile );
$body.removeClass( dir );
- equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent );
+ assert.equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent );
});
});
-module( 'jquery.colorUtil', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( $.colorUtil, '$.colorUtil defined' );
-});
-
-test( 'getRGB', function() {
- expect(18);
-
- strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' );
- strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' );
- deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' );
- deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' );
- deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' );
- deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' );
- deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' );
- deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' );
- deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' );
- deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' );
- deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' );
- deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' );
- deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' );
- deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' );
+QUnit.module( 'jquery.colorUtil', QUnit.newMwEnvironment() );
+
+QUnit.test( 'getRGB', 18, function ( assert ) {
+ assert.strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' );
+ assert.strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' );
+ assert.deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' );
+ assert.deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' );
+ assert.deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' );
+ assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' );
+ assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' );
+ assert.deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' );
+ assert.deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' );
+ assert.deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' );
+ assert.deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' );
+ assert.deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' );
+ assert.deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' );
+ assert.deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' );
// Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep
// track of it, so we will know in case it would ever change.
- strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' );
+ assert.strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' );
- deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' );
- deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' );
- strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' );
+ assert.deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' );
+ assert.deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' );
+ assert.strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' );
});
-test( 'rgbToHsl', function() {
- expect(1);
-
+QUnit.test( 'rgbToHsl', 1, function ( assert ) {
var hsl = $.colorUtil.rgbToHsl( 144, 238, 144 );
// Cross-browser differences in decimals...
// Re-create the rgbToHsl return array items, limited to two decimals.
var ret = [dualDecimals(hsl[0]), dualDecimals(hsl[1]), dualDecimals(hsl[2])];
- deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' );
+ assert.deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' );
});
-test( 'hslToRgb', function() {
- expect(1);
-
+QUnit.test( 'hslToRgb', 1, function ( assert ) {
var rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 );
// Cross-browser differences in decimals...
// Re-create the hslToRgb return array items, rounded to whole numbers.
var ret = [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])];
- deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' );
+ assert.deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' );
});
-test( 'getColorBrightness', function() {
- expect(2);
-
+QUnit.test( 'getColorBrightness', 2, function ( assert ) {
var a = $.colorUtil.getColorBrightness( 'red', +0.1 );
- equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' );
+ assert.equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' );
var b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
- equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
+ assert.equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
});
-test('jquery.delayedBind with data option', function() {
+QUnit.asyncTest('jquery.delayedBind with data option', 2, function ( assert ) {
var $fixture = $('<div>').appendTo('#qunit-fixture'),
data = { magic: "beeswax" },
delay = 50;
- $fixture.delayedBind(delay, 'testevent', data, function(event) {
- start(); // continue!
- ok(true, 'testevent fired');
- ok(event.data === data, 'data is passed through delayedBind');
+ $fixture.delayedBind(delay, 'testevent', data, function ( e ) {
+ QUnit.start(); // continue!
+ assert.ok( true, 'testevent fired');
+ assert.ok( e.data === data, 'data is passed through delayedBind');
});
- expect(2);
- stop(); // async!
-
// We'll trigger it thrice, but it should only happen once.
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
});
-test('jquery.delayedBind without data option', function() {
+QUnit.asyncTest('jquery.delayedBind without data option', 1, function ( assert ) {
var $fixture = $('<div>').appendTo('#qunit-fixture'),
data = { magic: "beeswax" },
delay = 50;
- $fixture.delayedBind(delay, 'testevent', function(event) {
- start(); // continue!
- ok(true, 'testevent fired');
+ $fixture.delayedBind(delay, 'testevent', function ( e ) {
+ QUnit.start(); // continue!
+ assert.ok(true, 'testevent fired');
});
- expect(1);
- stop(); // async!
-
// We'll trigger it thrice, but it should only happen once.
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
- $fixture.trigger('testevent', {});
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
+ $fixture.trigger( 'testevent', {} );
});
-module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
+QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
-test( '-- Initial check', function() {
- expect(1);
- ok( $.fn.getAttrs, 'jQuery.fn.getAttrs defined' );
-} );
-
-test( 'Check', function() {
- expect(1);
+QUnit.test( 'Check', 1, function ( assert ) {
var attrs = {
foo: 'bar',
'class': 'lorem'
},
- $el = $( '<div>', attrs );
+ $el = jQuery( '<div>', attrs );
- deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
+ assert.deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
} );
-module( 'jquery.highlightText', QUnit.newMwEnvironment() );
+QUnit.module( 'jquery.highlightText', QUnit.newMwEnvironment() );
-test( '-- Initial check', function() {
- expect(1);
- ok( $.fn.highlightText, 'jQuery.fn.highlightText defined' );
-} );
-
-test( 'Check', function() {
- var cases = [
+QUnit.test( 'Check', function ( assert ) {
+ var $fixture, cases = [
{
desc: 'Test 001',
text: 'Blue Öyster Cult',
expected: '<span class="highlight">بو</span>ل إيردوس'
}
];
- expect(cases.length);
- var $fixture;
+ QUnit.expect( cases.length );
- $.each(cases, function( i, item ) {
- $fixture = $( '<p></p>' ).text( item.text );
- $fixture.highlightText( item.highlight );
- equals(
+ $.each( cases, function ( i, item ) {
+ $fixture = $( '<p>' ).text( item.text ).highlightText( item.highlight );
+ assert.equal(
$fixture.html(),
- $('<p>' + item.expected + '</p>').html(), // re-parse to normalize!
+ $( '<p>' ).html( item.expected ).html(), // re-parse to normalize!
item.desc || undefined
- );
+ );
} );
} );
-module( 'jquery.localize', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( $.fn.localize, 'jQuery.fn.localize defined' );
-} );
-
-test( 'Handle basic replacements', function() {
- expect(3);
+QUnit.module( 'jquery.localize', QUnit.newMwEnvironment() );
+QUnit.test( 'Handle basic replacements', 4, function ( assert ) {
var html, $lc;
mw.messages.set( 'basic', 'Basic stuff' );
html = '<div><span><html:msg key="basic" /></span></div>';
$lc = $( html ).localize().find( 'span' );
- strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' );
+ assert.strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' );
// Attribute: title-msg
- html = '<div><span title-msg="basic" /></span></div>';
+ html = '<div><span title-msg="basic"></span></div>';
$lc = $( html ).localize().find( 'span' );
- strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' );
+ assert.strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' );
// Attribute: alt-msg
- html = '<div><span alt-msg="basic" /></span></div>';
+ html = '<div><span alt-msg="basic"></span></div>';
$lc = $( html ).localize().find( 'span' );
- strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' );
-} );
+ assert.strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' );
-test( 'Proper escaping', function() {
- expect(2);
+ // Attribute: placeholder-msg
+ html = '<div><input placeholder-msg="basic" /></div>';
+ $lc = $( html ).localize().find( 'input' );
+ assert.strictEqual( $lc.attr( 'placeholder' ), 'Basic stuff', 'Attribute: placeholder-msg' );
+} );
+
+QUnit.test( 'Proper escaping', 2, function ( assert ) {
var html, $lc;
mw.messages.set( 'properfoo', '<proper esc="test">' );
// making sure it is actually using text() and attr() (or something with the same effect)
// Text escaping
- html = '<div><span><html:msg key="properfoo" /></span></div>';
+ html = '<div><span><html:msg key="properfoo"></span></div>';
$lc = $( html ).localize().find( 'span' );
- strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
+ assert.strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
// Attribute escaping
- html = '<div><span title-msg="properfoo" /></span></div>';
+ html = '<div><span title-msg="properfoo"></span></div>';
$lc = $( html ).localize().find( 'span' );
- strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' );
+ assert.strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' );
} );
-test( 'Options', function() {
- expect(7);
-
+QUnit.test( 'Options', 7, function ( assert ) {
mw.messages.set( {
'foo-lorem': 'Lorem',
'foo-ipsum': 'Ipsum',
var html, $lc, attrs, x, sitename = 'Wikipedia';
// Message key prefix
- html = '<div><span title-msg="lorem"><html:msg key="ipsum" /></span></div>';
+ html = '<div><span title-msg="lorem"><html:msg key="ipsum"></span></div>';
$lc = $( html ).localize( {
prefix: 'foo-'
} ).find( 'span' );
- strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' );
- strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' );
+ assert.strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' );
+ assert.strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' );
// Variable keys mapping
x = 'bar';
- html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+ html = '<div><span title-msg="title"><html:msg key="label"></span></div>';
$lc = $( html ).localize( {
keys: {
'title': 'foo-' + x + '-title',
}
} ).find( 'span' );
- strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' );
- strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
+ assert.strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' );
+ assert.strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
// Passing parameteters to mw.msg
- html = '<div><span><html:msg key="foo-welcome" /></span></div>';
+ html = '<div><span><html:msg key="foo-welcome"></span></div>';
$lc = $( html ).localize( {
params: {
'foo-welcome': [sitename, 'yesterday']
}
} ).find( 'span' );
- strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' );
+ assert.strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' );
// Combination of options prefix, params and keys
x = 'bazz';
- html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+ html = '<div><span title-msg="title"><html:msg key="label"></span></div>';
$lc = $( html ).localize( {
prefix: 'foo-',
keys: {
}
} ).find( 'span' );
- strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' );
- strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' );
+ assert.strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' );
+ assert.strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' );
} );
-module( 'jquery.mwExtension', QUnit.newMwEnvironment() );
+QUnit.module( 'jquery.mwExtension', QUnit.newMwEnvironment() );
-test( 'String functions', function() {
+QUnit.test( 'String functions', function ( assert ) {
- equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' );
- equal( $.trimRight( ' foo bar ' ), ' foo bar', 'trimRight' );
- equal( $.ucFirst( 'foo' ), 'Foo', 'ucFirst' );
+ assert.equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' );
+ assert.equal( $.trimRight( ' foo bar ' ), ' foo bar', 'trimRight' );
+ assert.equal( $.ucFirst( 'foo' ), 'Foo', 'ucFirst' );
- equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ),
+ assert.equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ),
'<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
- equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
+ assert.equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
- equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
+ assert.equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
- equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
+ assert.equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
});
-test( 'Is functions', function() {
+QUnit.test( 'Is functions', function ( assert ) {
- strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
+ assert.strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
'isDomElement: #qunit-header Node' );
- strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
+ assert.strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
'isDomElement: #random-name (null)' );
- strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
+ assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
'isDomElement: getElementsByTagName Array' );
- strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
+ assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
'isDomElement: getElementsByTagName(..)[0] Node' );
- strictEqual( $.isDomElement( $( 'div' ) ), false,
+ assert.strictEqual( $.isDomElement( $( 'div' ) ), false,
'isDomElement: jQuery object' );
- strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
+ assert.strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
'isDomElement: jQuery object > Get node' );
- strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
+ assert.strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
'isDomElement: createElement' );
- strictEqual( $.isDomElement( { foo: 1 } ), false,
+ assert.strictEqual( $.isDomElement( { foo: 1 } ), false,
'isDomElement: Object' );
- strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
- strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
- strictEqual( $.isEmpty( '' ), true, 'isEmptry: ""' );
- strictEqual( $.isEmpty( 1 ), false, 'isEmptry: 1' );
- strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
- strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
+ assert.strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
+ assert.strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
+ assert.strictEqual( $.isEmpty( '' ), true, 'isEmptry: ""' );
+ assert.strictEqual( $.isEmpty( 1 ), false, 'isEmptry: 1' );
+ assert.strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
+ assert.strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
// Documented behaviour
- strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
+ assert.strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
});
-test( 'Comparison functions', function() {
+QUnit.test( 'Comparison functions', function ( assert ) {
- ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
+ assert.ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
'compareArray: Two deep arrays that are excactly the same' );
- ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
+ assert.ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
- ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
- ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
- ok( !$.compareObject( { bar: true }, { baz: false } ),
+ assert.ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
+ assert.ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
+ assert.ok( !$.compareObject( { bar: true }, { baz: false } ),
'compareObject: Two different objects (false)' );
});
-module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(2);
-
- ok( $.fn.firstTabIndex, '$.fn.firstTabIndex defined' );
- ok( $.fn.lastTabIndex, '$.fn.lastTabIndex defined' );
-});
-
-test( 'firstTabIndex', function() {
- expect(2);
+QUnit.module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
+QUnit.test( 'firstTabIndex', 2, function ( assert ) {
var testEnvironment =
'<form>' +
'<input tabindex="7" />' +
'</form>';
var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' );
- strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
+ assert.strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
var $testB = $( '<div>' );
- strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
+ assert.strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
});
-test( 'lastTabIndex', function() {
- expect(2);
-
+QUnit.test( 'lastTabIndex', 2, function ( assert ) {
var testEnvironment =
'<form>' +
'<input tabindex="7" />' +
'</form>';
var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' );
- strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
+ assert.strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
var $testB = $( '<div>' );
- strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
+ assert.strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
});
wgContentLanguage: 'en'
};
-module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( $.tablesorter, '$.tablesorter defined' );
-});
+QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
/**
* Create an HTML table from an array of row arrays containing text strings.
* @param {String[][]} data
* @return jQuery
*/
-var tableCreate = function( header, data ) {
- var $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
+function tableCreate( header, data ) {
+ var i,
+ $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
$thead = $table.find( 'thead' ),
$tbody = $table.find( 'tbody' ),
$tr = $( '<tr>' );
- $.each( header, function( i, str ) {
+ $.each( header, function ( i, str ) {
var $th = $( '<th>' );
$th.text( str ).appendTo( $tr );
});
$tr.appendTo( $thead );
- for (var i = 0; i < data.length; i++) {
+ for ( i = 0; i < data.length; i++ ) {
$tr = $( '<tr>' );
- $.each( data[i], function( j, str ) {
+ $.each( data[i], function ( j, str ) {
var $td = $( '<td>' );
$td.text( str ).appendTo( $tr );
});
$tr.appendTo( $tbody );
}
return $table;
-};
+}
/**
* Extract text from table.
* @param {jQuery} $table
* @return String[][]
*/
-var tableExtract = function( $table ) {
+function tableExtract( $table ) {
var data = [];
$table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
data.push( row );
});
return data;
-};
+}
/**
* Run a table test by building a table with the given data,
* @param {String[][]} expected rows/cols to compare against at end
* @param {function($table)} callback something to do with the table before we compare
*/
-var tableTest = function( msg, header, data, expected, callback ) {
- test( msg, function() {
- expect(1);
-
+function tableTest( msg, header, data, expected, callback ) {
+ QUnit.test( msg, 1, function ( assert ) {
var $table = tableCreate( header, data );
// Give caller a chance to set up sorting and manipulate the table.
// Table sorting is done synchronously; if it ever needs to change back
// to asynchronous, we'll need a timeout or a callback here.
var extracted = tableExtract( $table );
- deepEqual( extracted, expected, msg );
+ assert.deepEqual( extracted, expected, msg );
});
-};
+}
-var reversed = function(arr) {
+function reversed(arr) {
+ // Clone array
var arr2 = arr.slice(0);
+
arr2.reverse();
+
return arr2;
-};
+}
// Sample data set using planets named and their radius
var header = [ 'Planet' , 'Radius (km)'],
header,
planets,
ascendingName,
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
header,
planets,
ascendingName,
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
header,
planets,
reversed(ascendingName),
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click().click();
}
header,
planets,
ascendingRadius,
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click();
}
header,
planets,
reversed(ascendingRadius),
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click().click();
}
['09.11.2011'],
['11.11.2011']
],
- function( $table ) {
+ function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
mw.config.set( 'wgContentLanguage', 'de' );
['09.11.2011'],
['11.11.2011']
],
- function( $table ) {
+ function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
['IP'],
ipv4,
ipv4Sorted,
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
['IP'],
ipv4,
reversed(ipv4Sorted),
- function( $table ) {
+ function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click().click();
}
['Name'],
umlautWords,
umlautWordsSorted,
- function( $table ) {
+ function ( $table ) {
mw.config.set( 'tableSorterCollation', {
'ä': 'ae',
'ö': 'oe',
);
var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
-var planetsRowspanII = [jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']];
+var planetsRowspanII = [jupiter, mercury, saturn, venus, ['Venus', '6371.0'], ['Venus', '3390.0']];
tableTest(
'Basic planet table: same value for multiple rows via rowspan',
header,
planets,
planetsRowspan,
- function( $table ) {
+ function ( $table ) {
// Modify the table to have a multiuple-row-spanning cell:
// - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
$table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
header,
planets,
planetsRowspanII,
- function( $table ) {
+ function ( $table ) {
// Modify the table to have a multiuple-row-spanning cell:
// - Remove 1st cell of 4th row, and, 1st cell or 5th row.
$table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
['date'],
complexMDYDates,
complexMDYSorted,
- function( $table ) {
+ function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
);
/** FIXME: the diff output is not very readeable. */
-test( 'bug 32047 - caption must be before thead', function() {
+QUnit.test( 'bug 32047 - caption must be before thead', function ( assert ) {
var $table;
$table = $(
'<table class="sortable">' +
);
$table.tablesorter();
- equals(
+ assert.equal(
$table.children( ).get( 0 ).nodeName,
'CAPTION',
'First element after <thead> must be <caption> (bug 32047)'
);
});
-test( 'data-sort-value attribute, when available, should override sorting position', function() {
+QUnit.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert ) {
var $table, data;
// Example 1: All cells except one cell without data-sort-value,
});
});
- deepEqual( data, [
+ assert.deepEqual( data, [
{
data: 'Apple',
text: 'Bird'
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
data = [];
- $table.find( 'tbody > tr' ).each( function( i, tr ) {
- $( tr ).find( 'td' ).each( function( i, td ) {
+ $table.find( 'tbody > tr' ).each( function ( i, tr ) {
+ $( tr ).find( 'td' ).each( function ( i, td ) {
data.push( {
data: $( td ).data( 'sortValue' ),
text: $( td ).text()
});
});
- deepEqual( data, [
+ assert.deepEqual( data, [
{
data: undefined,
text: 'B'
});
});
- deepEqual( data, [
+ assert.deepEqual( data, [
{
data: 1,
text: "B"
);
// TODO add numbers sorting tests for bug 8115 with a different language
-test( 'bug 32888 - Tables inside a tableheader cell', function() {
- expect(2);
-
+QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
var $table;
$table = $(
'<table class="sortable" id="mw-bug-32888">' +
);
$table.tablesorter();
- equals(
+ assert.equal(
$table.find('> thead:eq(0) > tr > th.headerSort').length,
1,
'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
);
- equals(
+ assert.equal(
$( '#mw-bug-32888-2' ).find('th.headerSort').length,
0,
'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
var correctDateSorting1 = [
['01 January 2010'],
['05 February 2010'],
- ['16 January 2010'],
+ ['16 January 2010']
];
var correctDateSortingSorted1 = [
['01 January 2010'],
['16 January 2010'],
- ['05 February 2010'],
+ ['05 February 2010']
];
tableTest(
var correctDateSorting2 = [
['January 01 2010'],
['February 05 2010'],
- ['January 16 2010'],
+ ['January 16 2010']
];
var correctDateSortingSorted2 = [
['January 01 2010'],
['January 16 2010'],
- ['February 05 2010'],
+ ['February 05 2010']
];
tableTest(
}
);
-})( jQuery );
+}( jQuery ) );
-module( 'jquery.textSelection', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(1);
- ok( $.fn.textSelection, 'jQuery.fn.textSelection defined' );
-} );
+QUnit.module( 'jquery.textSelection', QUnit.newMwEnvironment() );
/**
* Test factory for $.fn.textSelection( 'encapsulateText' )
* end {int} ending char for selection
* params {object} add'l parameters for $().textSelection( 'encapsulateText' )
*/
-var encapsulateTest = function( options ) {
+function encapsulateTest( options ) {
var opt = $.extend({
description: '',
before: {},
selected: null
}, opt.after);
- test( opt.description, function() {
+ QUnit.test( opt.description, function ( assert ) {
var tests = 1;
if ( opt.after.selected !== null ) {
tests++;
}
- expect( tests );
+ QUnit.expect( tests );
var $textarea = $( '<textarea>' );
var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, "\n" );
- equal( text, opt.after.text, 'Checking full text after encapsulation' );
+ assert.equal( text, opt.after.text, 'Checking full text after encapsulation' );
if (opt.after.selected !== null) {
var selected = $textarea.textSelection( 'getSelection' );
- equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
+ assert.equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
}
} );
-};
+}
var sig = {
'pre': "--~~~~"
'peri': 'Heading 2',
'post': ' ==',
'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
- 'regexReplace': "\$1==\$3==\$4",
+ 'regexReplace': "$1==$3==$4",
'ownline': true
}, ulist = {
'pre': "* ",
});
-var caretTest = function(options) {
- test(options.description, function() {
- expect(2);
-
- var $textarea = $( '<textarea>' ).text(options.text);
+function caretTest( options ) {
+ QUnit.test( options.description, 2, function ( assert ) {
+ var $textarea = $( '<textarea>' ).text( options.text );
$( '#qunit-fixture' ).append( $textarea );
- if (options.mode == 'set') {
+ if ( options.mode === 'set' ) {
$textarea.textSelection('setSelection', {
start: options.start,
end: options.end
});
}
- var among = function(actual, expected, message) {
- if ($.isArray(expected)) {
- ok($.inArray(actual, expected) !== -1 , message + ' (got ' + actual + '; expected one of ' + expected.join(', ') + ')');
+ function among( actual, expected, message ) {
+ if ( $.isArray( expected ) ) {
+ assert.ok( $.inArray( actual, expected ) !== -1 , message + ' (got ' + actual + '; expected one of ' + expected.join(', ') + ')' );
} else {
- equal(actual, expected, message);
+ assert.equal( actual, expected, message );
}
- };
+ }
var pos = $textarea.textSelection('getCaretPosition', {startAndEnd: true});
among(pos[0], options.start, 'Caret start should be where we set it.');
QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment() );
-QUnit.asyncTest( 'Simple', function ( assert ) {
+QUnit.asyncTest( 'Hello world', function ( assert ) {
var api;
- QUnit.expect( 1 );
+ QUnit.expect( 6 );
api = new mw.Api();
api.parse( "'''Hello world'''" )
.done( function ( html ) {
- // Html also contains "NewPP report", so only check the first part
- assert.equal( html.substr( 0, 26 ), '<p><b>Hello world</b>\n</p>',
- 'Wikitext to html parsing works.'
- );
+ // Parse into a document fragment instead of comparing HTML, due to
+ // presence of Tidy influencing whitespace.
+ // Html also contains "NewPP report" comment.
+ var $res = $( '<div>' ).html( html ).children(),
+ res = $res.get( 0 );
+ assert.equal( $res.length, 1, 'Response contains 1 element' );
+ assert.equal( res.nodeName.toLowerCase(), 'p', 'Response is a paragraph' );
+ assert.equal( $res.children().length, 1, 'Response has 1 child element' );
+ assert.equal( $res.children().get( 0 ).nodeName.toLowerCase(), 'b', 'Child element is a bold tag' );
+ // Trim since Tidy may or may not mess with the spacing here
+ assert.equal( $.trim( $res.text() ), 'Hello world', 'Response contains given text' );
+ assert.equal( $res.find( 'b' ).text(), 'Hello world', 'Bold tag wraps the entire, same, text' );
QUnit.start();
});
-module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
+QUnit.module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
-test( '-- Initial check', function() {
- expect( 2 );
- ok( mw.special.recentchanges.init, 'mw.special.recentchanges.init defined' );
- ok( mw.special.recentchanges.updateCheckboxes, 'mw.special.recentchanges.updateCheckboxes defined' );
- // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
-});
+// TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
-test( '"all" namespace disable checkboxes', function() {
+QUnit.test( '"all" namespace disable checkboxes', function ( assert ) {
// from Special:Recentchanges
var select =
// TODO abstract the double strictEquals
// At first checkboxes are enabled
- strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
- strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
+ assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
+ assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
// Initiate the recentchanges module
mw.special.recentchanges.init();
// By default
- strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
- strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
+ assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
+ assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
// select second option...
var $options = $( '#namespace' ).find( 'option' );
$( '#namespace' ).change();
// ... and checkboxes should be enabled again
- strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
- strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
+ assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
+ assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
// select first option ( 'all' namespace)...
$options.eq(1).removeProp( 'selected' );
$( '#namespace' ).change();
// ... and checkboxes should now be disabled
- strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
- strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
+ assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
+ assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
// DOM cleanup
$env.remove();
"wgCaseSensitiveNamespaces": []
};
-module( 'mediawiki.Title', QUnit.newMwEnvironment({ config: config }) );
+QUnit.module( 'mediawiki.Title', QUnit.newMwEnvironment({ config: config }) );
-test( '-- Initial check', function () {
- expect(1);
- ok( mw.Title, 'mw.Title defined' );
-});
-
-test( 'Transformation', function () {
- expect(8);
+QUnit.test( 'Transformation', 8, function ( assert ) {
var title;
title = new mw.Title( 'File:quux pif.jpg' );
- equal( title.getName(), 'Quux_pif' );
+ assert.equal( title.getName(), 'Quux_pif' );
title = new mw.Title( 'File:Glarg_foo_glang.jpg' );
- equal( title.getNameText(), 'Glarg foo glang' );
+ assert.equal( title.getNameText(), 'Glarg foo glang' );
title = new mw.Title( 'User:ABC.DEF' );
- equal( title.toText(), 'User:ABC.DEF' );
- equal( title.getNamespaceId(), 2 );
- equal( title.getNamespacePrefix(), 'User:' );
+ assert.equal( title.toText(), 'User:ABC.DEF' );
+ assert.equal( title.getNamespaceId(), 2 );
+ assert.equal( title.getNamespacePrefix(), 'User:' );
title = new mw.Title( 'uSEr:hAshAr' );
- equal( title.toText(), 'User:HAshAr' );
- equal( title.getNamespaceId(), 2 );
+ assert.equal( title.toText(), 'User:HAshAr' );
+ assert.equal( title.getNamespaceId(), 2 );
title = new mw.Title( ' MediaWiki: Foo bar .js ' );
// Don't ask why, it's the way the backend works. One space is kept of each set
- equal( title.getName(), 'Foo_bar_.js', "Merge multiple spaces to a single space." );
+ assert.equal( title.getName(), 'Foo_bar_.js', "Merge multiple spaces to a single space." );
});
-test( 'Main text for filename', function () {
- expect(8);
-
+QUnit.test( 'Main text for filename', 8, function ( assert ) {
var title = new mw.Title( 'File:foo_bar.JPG' );
- equal( title.getNamespaceId(), 6 );
- equal( title.getNamespacePrefix(), 'File:' );
- equal( title.getName(), 'Foo_bar' );
- equal( title.getNameText(), 'Foo bar' );
- equal( title.getMain(), 'Foo_bar.JPG' );
- equal( title.getMainText(), 'Foo bar.JPG' );
- equal( title.getExtension(), 'JPG' );
- equal( title.getDotExtension(), '.JPG' );
+ assert.equal( title.getNamespaceId(), 6 );
+ assert.equal( title.getNamespacePrefix(), 'File:' );
+ assert.equal( title.getName(), 'Foo_bar' );
+ assert.equal( title.getNameText(), 'Foo bar' );
+ assert.equal( title.getMain(), 'Foo_bar.JPG' );
+ assert.equal( title.getMainText(), 'Foo bar.JPG' );
+ assert.equal( title.getExtension(), 'JPG' );
+ assert.equal( title.getDotExtension(), '.JPG' );
});
-test( 'Namespace detection and conversion', function () {
- expect(6);
-
+QUnit.test( 'Namespace detection and conversion', 6, function ( assert ) {
var title;
title = new mw.Title( 'something.PDF', 6 );
- equal( title.toString(), 'File:Something.PDF' );
+ assert.equal( title.toString(), 'File:Something.PDF' );
title = new mw.Title( 'NeilK', 3 );
- equal( title.toString(), 'User_talk:NeilK' );
- equal( title.toText(), 'User talk:NeilK' );
+ assert.equal( title.toString(), 'User_talk:NeilK' );
+ assert.equal( title.toText(), 'User talk:NeilK' );
title = new mw.Title( 'Frobisher', 100 );
- equal( title.toString(), 'Penguins:Frobisher' );
+ assert.equal( title.toString(), 'Penguins:Frobisher' );
title = new mw.Title( 'antarctic_waterfowl:flightless_yet_cute.jpg' );
- equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
+ assert.equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
title = new mw.Title( 'Penguins:flightless_yet_cute.jpg' );
- equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
+ assert.equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
});
-test( 'Throw error on invalid title', function () {
- expect(1);
-
- raises(function () {
+QUnit.test( 'Throw error on invalid title', 1, function ( assert ) {
+ assert.throws(function () {
var title = new mw.Title( '' );
}, 'Throw error on empty string' );
});
-test( 'Case-sensivity', function () {
- expect(3);
-
+QUnit.test( 'Case-sensivity', 3, function ( assert ) {
var title;
// Default config
mw.config.set( 'wgCaseSensitiveNamespaces', [] );
title = new mw.Title( 'article' );
- equal( title.toString(), 'Article', 'Default config: No sensitive namespaces by default. First-letter becomes uppercase' );
+ assert.equal( title.toString(), 'Article', 'Default config: No sensitive namespaces by default. First-letter becomes uppercase' );
// $wgCapitalLinks = false;
mw.config.set( 'wgCaseSensitiveNamespaces', [0, -2, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15] );
title = new mw.Title( 'article' );
- equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' );
+ assert.equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' );
title = new mw.Title( 'john', 2 );
- equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' );
+ assert.equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' );
});
-test( 'toString / toText', function () {
- expect(2);
-
+QUnit.test( 'toString / toText', 2, function ( assert ) {
var title = new mw.Title( 'Some random page' );
- equal( title.toString(), title.getPrefixedDb() );
- equal( title.toText(), title.getPrefixedText() );
+ assert.equal( title.toString(), title.getPrefixedDb() );
+ assert.equal( title.toText(), title.getPrefixedText() );
});
-test( 'Exists', function () {
- expect(3);
-
+QUnit.test( 'Exists', 3, function ( assert ) {
var title;
// Empty registry, checks default to null
title = new mw.Title( 'Some random page', 4 );
- strictEqual( title.exists(), null, 'Return null with empty existance registry' );
+ assert.strictEqual( title.exists(), null, 'Return null with empty existance registry' );
// Basic registry, checks default to boolean
mw.Title.exist.set( ['Does_exist', 'User_talk:NeilK', 'Wikipedia:Sandbox_rules'], true );
mw.Title.exist.set( ['Does_not_exist', 'User:John', 'Foobar'], false );
title = new mw.Title( 'Project:Sandbox rules' );
- assertTrue( title.exists(), 'Return true for page titles marked as existing' );
+ assert.assertTrue( title.exists(), 'Return true for page titles marked as existing' );
title = new mw.Title( 'Foobar' );
- assertFalse( title.exists(), 'Return false for page titles marked as nonexistent' );
+ assert.assertFalse( title.exists(), 'Return false for page titles marked as nonexistent' );
});
-test( 'Url', function () {
- expect(2);
-
+QUnit.test( 'Url', 2, function ( assert ) {
var title;
// Config
mw.config.set( 'wgArticlePath', '/wiki/$1' );
title = new mw.Title( 'Foobar' );
- equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' );
+ assert.equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' );
title = new mw.Title( 'John Doe', 3 );
- equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
+ assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
});
}() );
\ No newline at end of file
-module( 'mediawiki.Uri', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function () {
- expect( 2 );
-
- // Ensure we have a generic mw.Uri constructor. By default mediawiki.uri,
- // will use the currrent window ocation as base. But for testing we need
- // to have a generic one, so that it doens't return false negatives if
- // we run the test suite from an https server.
- mw.Uri = mw.UriRelative( 'http://example.org/w/index.php' );
-
- ok( mw.UriRelative, 'mw.UriRelative defined' );
- ok( mw.Uri, 'mw.Uri defined' );
-} );
+QUnit.module( 'mediawiki.Uri', QUnit.newMwEnvironment({
+ setup: function () {
+ this.mwUriOrg = mw.Uri;
+ mw.Uri = mw.UriRelative( 'http://example.org/w/index.php' );
+ },
+ teardown: function () {
+ mw.Uri = this.mwUriOrg;
+ delete this.mwUriOrg;
+ }
+}) );
$.each( [true, false], function ( i, strictMode ) {
- test( 'Basic mw.Uri object test in ' + ( strictMode ? '' : 'non-' ) + 'strict mode for a simple HTTP URI', function () {
+ QUnit.test( 'Basic mw.Uri object test in ' + ( strictMode ? '' : 'non-' ) + 'strict mode for a simple HTTP URI', 2, function ( assert ) {
var uriString, uri;
- expect( 2 );
-
uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
uri = new mw.Uri( uriString, {
strictMode: strictMode
});
- deepEqual(
+ assert.deepEqual(
{
protocol: uri.protocol,
host: uri.host,
'basic object properties'
);
- deepEqual(
+ assert.deepEqual(
{
userInfo: uri.getUserInfo(),
authority: uri.getAuthority(),
});
});
-test( 'Parse an ftp URI correctly with user and password', function () {
- var uri;
- expect( 1 );
-
- uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
+QUnit.test( 'Parse an ftp URI correctly with user and password', 1, function ( assert ) {
+ var uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
- deepEqual(
+ assert.deepEqual(
{
protocol: uri.protocol,
user: uri.user,
);
} );
-test( 'Parse a uri with simple querystring', function () {
- var uri;
- expect( 1 );
-
- uri = new mw.Uri( 'http://www.google.com/?q=uri' );
+QUnit.test( 'Parse a uri with simple querystring', 1, function ( assert ) {
+ var uri = new mw.Uri( 'http://www.google.com/?q=uri' );
- deepEqual(
+ assert.deepEqual(
{
protocol: uri.protocol,
host: uri.host,
);
} );
-test( 'Handle multiple query parameter (overrideKeys on)', function () {
- var uri;
- expect( 5 );
-
- uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+QUnit.test( 'Handle multiple query parameter (overrideKeys on)', 5, function ( assert ) {
+ var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
overrideKeys: true
});
- equal( uri.query.n, '1', 'multiple parameters are parsed' );
- equal( uri.query.m, 'bar', 'last key overrides earlier keys' );
+ assert.equal( uri.query.n, '1', 'multiple parameters are parsed' );
+ assert.equal( uri.query.m, 'bar', 'last key overrides earlier keys' );
uri.query.n = [ 'x', 'y', 'z' ];
// Verify parts and total length instead of entire string because order
// of iteration can vary.
- ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
- ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
- equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
+ assert.ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
+ assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
+ assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
} );
-test( 'Handle multiple query parameter (overrideKeys off)', function () {
- var uri;
- expect( 9 );
-
- uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+QUnit.test( 'Handle multiple query parameter (overrideKeys off)', 9, function ( assert ) {
+ var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
overrideKeys: false
});
// Strict comparison so that types are also verified (n should be string '1')
- strictEqual( uri.query.m.length, 2, 'multi-value query should be an array with 2 items' );
- strictEqual( uri.query.m[0], 'foo', 'order and value is correct' );
- strictEqual( uri.query.m[1], 'bar', 'order and value is correct' );
- strictEqual( uri.query.n, '1', 'n=1 is parsed with the correct value of the expected type' );
+ assert.strictEqual( uri.query.m.length, 2, 'multi-value query should be an array with 2 items' );
+ assert.strictEqual( uri.query.m[0], 'foo', 'order and value is correct' );
+ assert.strictEqual( uri.query.m[1], 'bar', 'order and value is correct' );
+ assert.strictEqual( uri.query.n, '1', 'n=1 is parsed with the correct value of the expected type' );
// Change query values
uri.query.n = [ 'x', 'y', 'z' ];
// Verify parts and total length instead of entire string because order
// of iteration can vary.
- ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
- ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
- equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
+ assert.ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
+ assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
+ assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
// Remove query values
uri.query.m.splice( 0, 1 );
delete uri.query.n;
- equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
+ assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
// Remove more query values, leaving an empty array
uri.query.m.splice( 0, 1 );
- equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
+ assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
} );
-test( 'All-dressed URI with everything', function () {
+QUnit.test( 'All-dressed URI with everything', 11, function ( assert ) {
var uri, queryString, relativePath;
- expect( 11 );
uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
- deepEqual(
+ assert.deepEqual(
{
protocol: uri.protocol,
user: uri.user,
'basic object properties'
);
- equal( uri.getUserInfo(), 'auth', 'user info' );
+ assert.equal( uri.getUserInfo(), 'auth', 'user info' );
- equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
+ assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
- equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
+ assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
queryString = uri.getQueryString();
- ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
- ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
- ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
- ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
+ assert.ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
+ assert.ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
+ assert.ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
+ assert.ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
relativePath = uri.getRelativePath();
- ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
- ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
- ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
+ assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
+ assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
+ assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
} );
-test( 'Cloning', function () {
+QUnit.test( 'Cloning', 6, function ( assert ) {
var original, clone;
- expect( 6 );
original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
clone = original.clone();
- deepEqual( clone, original, 'clone has equivalent properties' );
- equal( original.toString(), clone.toString(), 'toString matches original' );
+ assert.deepEqual( clone, original, 'clone has equivalent properties' );
+ assert.equal( original.toString(), clone.toString(), 'toString matches original' );
- notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
+ assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
clone.host = 'bar.example.org';
- notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
- notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
+ assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
+ assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
clone.query.three = 3;
- deepEqual(
+ assert.deepEqual(
original.query,
{ 'one': '1', 'two': '2' },
'Properties is deep cloned (bug 37708)'
);
} );
-test( 'Constructing mw.Uri from plain object', function () {
- var uri;
- expect( 3 );
-
- uri = new mw.Uri({
+QUnit.test( 'Constructing mw.Uri from plain object', 3, function ( assert ) {
+ var uri = new mw.Uri({
protocol: 'http',
host: 'www.foo.local',
path: '/this'
});
- equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
+ assert.equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
uri = new mw.Uri({
protocol: 'http',
query: { hi: 'there' },
fragment: 'blah'
});
- equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
+ assert.equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
- raises(
+ assert.throws(
function () {
var uri = new mw.Uri({
protocol: 'http',
);
} );
-test( 'Manipulate properties', function () {
+QUnit.test( 'Manipulate properties', 8, function ( assert ) {
var uriBase, uri;
- expect( 8 );
uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
uri = uriBase.clone();
uri.fragment = 'frag';
- equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
uri = uriBase.clone();
uri.host = 'fr.wiki.local';
uri.port = '8080';
- equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
+ assert.equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
uri = uriBase.clone();
uri.query.foo = 'bar';
- equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
delete uri.query.foo;
- equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
uri = uriBase.clone();
uri.query.foo = 'bar';
- equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
uri.extend({
foo: 'quux',
pif: 'paf'
});
- ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
- ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
- ok( uri.toString().indexOf( 'pif=paf' ) >= 0 , 'extend query arguments' );
+ assert.ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
+ assert.ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
+ assert.ok( uri.toString().indexOf( 'pif=paf' ) >= 0 , 'extend query arguments' );
} );
-test( 'Handle protocol-relative URLs', function () {
+QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
var UriRel, uri;
- expect( 5 );
UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
uri = new UriRel( '//en.wiki.local/w/api.php' );
- equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
+ assert.equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
uri = new UriRel( '/foo.com' );
- equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
+ assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
uri = new UriRel( 'http:/foo.com' );
- equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
uri = new UriRel( '/foo.com', true );
- equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
+ assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
uri = new UriRel( 'http:/foo.com', true );
- equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
+ assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
} );
-test( 'Bad calls', function () {
+QUnit.test( 'Bad calls', 3, function ( assert ) {
var uri;
- expect( 3 );
- raises(
+ assert.throws(
function () {
new mw.Uri( 'glaswegian penguins' );
},
'throw error on non-URI as argument to constructor'
);
- raises(
+ assert.throws(
function () {
new mw.Uri( 'foo.com/bar/baz', {
strictMode: true
uri = new mw.Uri( 'foo.com/bar/baz', {
strictMode: false
});
- equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
+ assert.equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
});
-test( 'bug 35658', function () {
- expect( 2 );
-
+QUnit.test( 'bug 35658', 2, function ( assert ) {
var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
testProtocol = 'https://';
UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
uri = new UriClass( testPath );
href = uri.toString();
- equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
+ assert.equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
uri = new UriClass( testPath );
href = uri.toString();
- equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
+ assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
} );
-QUnit.test( 'Constructor falls back to default location', function (assert) {
+QUnit.test( 'Constructor falls back to default location', 4, function ( assert ) {
var testuri, MyUri, uri;
- QUnit.expect( 4 );
testuri = 'http://example.org/w/index.php';
MyUri = mw.UriRelative( testuri );
-module( 'mediawiki.jqueryMsg' );
+QUnit.module( 'mediawiki.jqueryMsg' );
-test( '-- Initial check', function () {
- expect( 1 );
- ok( mw.jqueryMsg, 'mw.jqueryMsg defined' );
-} );
-
-test( 'mw.jqueryMsg Plural', function () {
- expect( 3 );
+QUnit.test( 'mw.jqueryMsg Plural', 3, function ( assert ) {
var parser = mw.jqueryMsg.getMessageFunction();
mw.messages.set( 'plural-msg', 'Found $1 {{PLURAL:$1|item|items}}' );
- equal( parser( 'plural-msg', 0 ), 'Found 0 items', 'Plural test for english with zero as count' );
- equal( parser( 'plural-msg', 1 ), 'Found 1 item', 'Singular test for english' );
- equal( parser( 'plural-msg', 2 ), 'Found 2 items', 'Plural test for english' );
+ assert.equal( parser( 'plural-msg', 0 ), 'Found 0 items', 'Plural test for english with zero as count' );
+ assert.equal( parser( 'plural-msg', 1 ), 'Found 1 item', 'Singular test for english' );
+ assert.equal( parser( 'plural-msg', 2 ), 'Found 2 items', 'Plural test for english' );
} );
-test( 'mw.jqueryMsg Gender', function () {
- expect( 11 );
+QUnit.test( 'mw.jqueryMsg Gender', 11, function ( assert ) {
// TODO: These tests should be for mw.msg once mw.msg integrated with mw.jqueryMsg
// TODO: English may not be the best language for these tests. Use a language like Arabic or Russian
var user = mw.user,
mw.messages.set( 'gender-msg', '$1: {{GENDER:$2|blue|pink|green}}' );
user.options.set( 'gender', 'male' );
- equal(
+ assert.equal(
parser( 'gender-msg', 'Bob', 'male' ),
'Bob: blue',
'Masculine from string "male"'
);
- equal(
+ assert.equal(
parser( 'gender-msg', 'Bob', user ),
'Bob: blue',
'Masculine from mw.user object'
);
user.options.set( 'gender', 'unknown' );
- equal(
+ assert.equal(
parser( 'gender-msg', 'Foo', user ),
'Foo: green',
'Neutral from mw.user object' );
- equal(
+ assert.equal(
parser( 'gender-msg', 'Alice', 'female' ),
'Alice: pink',
'Feminine from string "female"' );
- equal(
+ assert.equal(
parser( 'gender-msg', 'User' ),
'User: green',
'Neutral when no parameter given' );
- equal(
+ assert.equal(
parser( 'gender-msg', 'User', 'unknown' ),
'User: green',
'Neutral from string "unknown"'
mw.messages.set( 'gender-msg-one-form', '{{GENDER:$1|User}}: $2 {{PLURAL:$2|edit|edits}}' );
- equal(
+ assert.equal(
parser( 'gender-msg-one-form', 'male', 10 ),
'User: 10 edits',
'Gender neutral and plural form'
);
- equal(
+ assert.equal(
parser( 'gender-msg-one-form', 'female', 1 ),
'User: 1 edit',
'Gender neutral and singular form'
);
mw.messages.set( 'gender-msg-lowercase', '{{gender:$1|he|she}} is awesome' );
- equal(
+ assert.equal(
parser( 'gender-msg-lowercase', 'male' ),
'he is awesome',
'Gender masculine'
);
- equal(
+ assert.equal(
parser( 'gender-msg-lowercase', 'female' ),
'she is awesome',
'Gender feminine'
);
mw.messages.set( 'gender-msg-wrong', '{{gender}} test' );
- equal(
+ assert.equal(
parser( 'gender-msg-wrong', 'female' ),
' test',
'Invalid syntax should result in {{gender}} simply being stripped away'
} );
-test( 'mw.jqueryMsg Grammar', function () {
- expect( 2 );
+QUnit.test( 'mw.jqueryMsg Grammar', 2, function ( assert ) {
var parser = mw.jqueryMsg.getMessageFunction();
// Assume the grammar form grammar_case_foo is not valid in any language
mw.messages.set( 'grammar-msg', 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}' );
- equal( parser( 'grammar-msg' ), 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar Test with sitename' );
+ assert.equal( parser( 'grammar-msg' ), 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar Test with sitename' );
mw.messages.set( 'grammar-msg-wrong-syntax', 'Przeszukaj {{GRAMMAR:grammar_case_xyz}}' );
- equal( parser( 'grammar-msg-wrong-syntax' ), 'Przeszukaj ' , 'Grammar Test with wrong grammar template syntax' );
+ assert.equal( parser( 'grammar-msg-wrong-syntax' ), 'Przeszukaj ' , 'Grammar Test with wrong grammar template syntax' );
} );
/* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */
-module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
+QUnit.module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
-test( 'Variable with Unicode letter in name', function() {
- expect(3);
+QUnit.test( 'Variable with Unicode letter in name', 3, function ( assert ) {
var orig = "some token";
var ŝablono = orig;
- deepEqual( ŝablono, orig, 'ŝablono' );
- deepEqual( \u015dablono, orig, '\\u015dablono' );
- deepEqual( \u015Dablono, orig, '\\u015Dablono' );
+
+ assert.deepEqual( ŝablono, orig, 'ŝablono' );
+ assert.deepEqual( \u015dablono, orig, '\\u015dablono' );
+ assert.deepEqual( \u015Dablono, orig, '\\u015Dablono' );
});
/*
// Not that we need this. ;)
// This fails on IE 6-8
// Works on IE 9, Firefox 6, Chrome 14
-test( 'Keyword workaround: "if" as variable name using Unicode escapes', function() {
+QUnit.test( 'Keyword workaround: "if" as variable name using Unicode escapes', function ( assert ) {
var orig = "another token";
\u0069\u0066 = orig;
- deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' );
+ assert.deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' );
});
*/
// Not that we need this. ;)
// This fails on IE 6-9
// Works on Firefox 6, Chrome 14
-test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function() {
+QUnit.test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function ( assert ) {
var orig = "another token";
var foo = {};
foo.\u0069\u0066 = orig;
- deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' );
+ assert.deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' );
});
*/
-test( 'Stripping of single initial newline from textarea\'s literal contents (bug 12130)', function() {
+QUnit.test( 'Stripping of single initial newline from textarea\'s literal contents (bug 12130)', function ( assert ) {
var maxn = 4;
- expect(maxn * 2);
+ QUnit.expect( maxn * 2 );
- var repeat = function(str, n) {
- if (n <= 0) {
+ function repeat( str, n ) {
+ if ( n <= 0 ) {
return '';
} else {
- var out = Array(n);
- for (var i = 0; i < n; i++) {
+ var out = new Array(n);
+ for ( var i = 0; i < n; i++ ) {
out[i] = str;
}
return out.join('');
}
- };
+ }
- for (var n = 0; n < maxn; n++) {
+ for ( var n = 0; n < maxn; n++ ) {
var expected = repeat('\n', n) + 'some text';
var $textarea = $('<textarea>\n' + expected + '</textarea>');
- equal($textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')');
+ assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')' );
var $textarea2 = $('<textarea>').val(expected);
- equal($textarea2.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')');
+ assert.equal( $textarea2.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')' );
}
});
-module( 'mediawiki', QUnit.newMwEnvironment() );
-
-test( '-- Initial check', function() {
- expect(8);
-
- ok( window.jQuery, 'jQuery defined' );
- ok( window.$, '$j defined' );
- ok( window.$j, '$j defined' );
- strictEqual( window.$, window.jQuery, '$ alias to jQuery' );
- strictEqual( window.$j, window.jQuery, '$j alias to jQuery' );
-
- ok( window.mediaWiki, 'mediaWiki defined' );
- ok( window.mw, 'mw defined' );
- strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
+QUnit.module( 'mediawiki', QUnit.newMwEnvironment() );
+
+QUnit.test( '-- Initial check', 8, function ( assert ) {
+ assert.ok( window.jQuery, 'jQuery defined' );
+ assert.ok( window.$, '$j defined' );
+ assert.ok( window.$j, '$j defined' );
+ assert.strictEqual( window.$, window.jQuery, '$ alias to jQuery' );
+ assert.strictEqual( window.$j, window.jQuery, '$j alias to jQuery' );
+
+ assert.ok( window.mediaWiki, 'mediaWiki defined' );
+ assert.ok( window.mw, 'mw defined' );
+ assert.strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
});
-test( 'mw.Map', function() {
+QUnit.test( 'mw.Map', 17, function ( assert ) {
var arry, conf, funky, globalConf, nummy, someValues;
- expect(17);
- ok( mw.Map, 'mw.Map defined' );
+ assert.ok( mw.Map, 'mw.Map defined' );
conf = new mw.Map();
// Dummy variables
nummy = 7;
// Tests for input validation
- strictEqual( conf.get( 'inexistantKey' ), null, 'Map.get returns null if selection was a string and the key was not found' );
- strictEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set returns boolean true if a value was set for a valid key string' );
- strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
- strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
- strictEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set returns boolean false if key was invalid (Number)' );
- equal( conf.get( 'myKey' ), 'myValue', 'Map.get returns a single value value correctly' );
- strictEqual( conf.get( nummy ), null, 'Map.get ruturns null if selection was invalid (Number)' );
- strictEqual( conf.get( funky ), null, 'Map.get ruturns null if selection was invalid (Function)' );
+ assert.strictEqual( conf.get( 'inexistantKey' ), null, 'Map.get returns null if selection was a string and the key was not found' );
+ assert.strictEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set returns boolean true if a value was set for a valid key string' );
+ assert.strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
+ assert.strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
+ assert.strictEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set returns boolean false if key was invalid (Number)' );
+ assert.equal( conf.get( 'myKey' ), 'myValue', 'Map.get returns a single value value correctly' );
+ assert.strictEqual( conf.get( nummy ), null, 'Map.get ruturns null if selection was invalid (Number)' );
+ assert.strictEqual( conf.get( funky ), null, 'Map.get ruturns null if selection was invalid (Function)' );
// Multiple values at once
someValues = {
'lorem': 'ipsum',
'MediaWiki': true
};
- strictEqual( conf.set( someValues ), true, 'Map.set returns boolean true if multiple values were set by passing an object' );
- deepEqual( conf.get( ['foo', 'lorem'] ), {
+ assert.strictEqual( conf.set( someValues ), true, 'Map.set returns boolean true if multiple values were set by passing an object' );
+ assert.deepEqual( conf.get( ['foo', 'lorem'] ), {
'foo': 'bar',
'lorem': 'ipsum'
}, 'Map.get returns multiple values correctly as an object' );
- deepEqual( conf.get( ['foo', 'notExist'] ), {
+ assert.deepEqual( conf.get( ['foo', 'notExist'] ), {
'foo': 'bar',
'notExist': null
}, 'Map.get return includes keys that were not found as null values' );
- strictEqual( conf.exists( 'foo' ), true, 'Map.exists returns boolean true if a key exists' );
- strictEqual( conf.exists( 'notExist' ), false, 'Map.exists returns boolean false if a key does not exists' );
+ assert.strictEqual( conf.exists( 'foo' ), true, 'Map.exists returns boolean true if a key exists' );
+ assert.strictEqual( conf.exists( 'notExist' ), false, 'Map.exists returns boolean false if a key does not exists' );
// Interacting with globals and accessing the values object
- strictEqual( conf.get(), conf.values, 'Map.get returns the entire values object by reference (if called without arguments)' );
+ assert.strictEqual( conf.get(), conf.values, 'Map.get returns the entire values object by reference (if called without arguments)' );
conf.set( 'globalMapChecker', 'Hi' );
- ok( false === 'globalMapChecker' in window, 'new mw.Map did not store its values in the global window object by default' );
+ assert.ok( false === 'globalMapChecker' in window, 'new mw.Map did not store its values in the global window object by default' );
globalConf = new mw.Map( true );
globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
- ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
+ assert.ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
// Whitelist this global variable for QUnit's 'noglobal' mode
if ( QUnit.config.noglobals ) {
}
});
-test( 'mw.config', function() {
- expect(1);
-
- ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
+QUnit.test( 'mw.config', 1, function ( assert ) {
+ assert.ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
});
-test( 'mw.message & mw.messages', function() {
+QUnit.test( 'mw.message & mw.messages', 20, function ( assert ) {
var goodbye, hello, pluralMessage;
- expect(20);
- ok( mw.messages, 'messages defined' );
- ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
- ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
+ assert.ok( mw.messages, 'messages defined' );
+ assert.ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
+ assert.ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
hello = mw.message( 'hello' );
- equal( hello.format, 'plain', 'Message property "format" defaults to "plain"' );
- strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
- equal( hello.key, 'hello', 'Message property "key" (currect key)' );
- deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
+ assert.equal( hello.format, 'plain', 'Message property "format" defaults to "plain"' );
+ assert.strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
+ assert.equal( hello.key, 'hello', 'Message property "key" (currect key)' );
+ assert.deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
// Todo
- ok( hello.params, 'Message prototype "params"' );
+ assert.ok( hello.params, 'Message prototype "params"' );
hello.format = 'plain';
- equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString returns the message as a string with the current "format"' );
+ assert.equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString returns the message as a string with the current "format"' );
- equal( hello.escaped(), 'Hello <b>awesome</b> world', 'Message.escaped returns the escaped message' );
- equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
+ assert.equal( hello.escaped(), 'Hello <b>awesome</b> world', 'Message.escaped returns the escaped message' );
+ assert.equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
hello.parse();
- equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
+ assert.equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
hello.plain();
- equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
+ assert.equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
- strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
+ assert.strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
goodbye = mw.message( 'goodbye' );
- strictEqual( goodbye.exists(), false, 'Message.exists returns false for nonexistent messages' );
+ assert.strictEqual( goodbye.exists(), false, 'Message.exists returns false for nonexistent messages' );
- equal( goodbye.plain(), '<goodbye>', 'Message.toString returns plain <key> if format is "plain" and key does not exist' );
+ assert.equal( goodbye.plain(), '<goodbye>', 'Message.toString returns plain <key> if format is "plain" and key does not exist' );
// bug 30684
- equal( goodbye.escaped(), '<goodbye>', 'Message.toString returns properly escaped <key> if format is "escaped" and key does not exist' );
+ assert.equal( goodbye.escaped(), '<goodbye>', 'Message.toString returns properly escaped <key> if format is "escaped" and key does not exist' );
- ok( mw.messages.set( 'pluraltestmsg', 'There {{PLURAL:$1|is|are}} $1 {{PLURAL:$1|result|results}}' ), 'mw.messages.set: Register' );
+ assert.ok( mw.messages.set( 'pluraltestmsg', 'There {{PLURAL:$1|is|are}} $1 {{PLURAL:$1|result|results}}' ), 'mw.messages.set: Register' );
pluralMessage = mw.message( 'pluraltestmsg' , 6 );
- equal( pluralMessage.plain(), 'There are 6 results', 'plural get resolved when format is plain' );
- equal( pluralMessage.parse(), 'There are 6 results', 'plural get resolved when format is parse' );
+ assert.equal( pluralMessage.plain(), 'There are 6 results', 'plural get resolved when format is plain' );
+ assert.equal( pluralMessage.parse(), 'There are 6 results', 'plural get resolved when format is parse' );
});
-test( 'mw.msg', function() {
- expect(11);
+QUnit.test( 'mw.msg', 11, function ( assert ) {
+ assert.ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
+ assert.equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
+ assert.equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (nonexistent message)' );
- ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
- equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
- equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (nonexistent message)' );
+ assert.ok( mw.messages.set( 'plural-item' , 'Found $1 {{PLURAL:$1|item|items}}' ) );
+ assert.equal( mw.msg( 'plural-item', 5 ), 'Found 5 items', 'Apply plural for count 5' );
+ assert.equal( mw.msg( 'plural-item', 0 ), 'Found 0 items', 'Apply plural for count 0' );
+ assert.equal( mw.msg( 'plural-item', 1 ), 'Found 1 item', 'Apply plural for count 1' );
- ok( mw.messages.set( 'plural-item' , 'Found $1 {{PLURAL:$1|item|items}}' ) );
- equal( mw.msg( 'plural-item', 5 ), 'Found 5 items', 'Apply plural for count 5' );
- equal( mw.msg( 'plural-item', 0 ), 'Found 0 items', 'Apply plural for count 0' );
- equal( mw.msg( 'plural-item', 1 ), 'Found 1 item', 'Apply plural for count 1' );
-
- ok( mw.messages.set('gender-plural-msg' , '{{GENDER:$1|he|she|they}} {{PLURAL:$2|is|are}} awesome' ) );
- equal( mw.msg( 'gender-plural-msg', 'male', 1 ), 'he is awesome', 'Gender test for male, plural count 1' );
- equal( mw.msg( 'gender-plural-msg', 'female', '1' ), 'she is awesome', 'Gender test for female, plural count 1' );
- equal( mw.msg( 'gender-plural-msg', 'unknown', 10 ), 'they are awesome', 'Gender test for neutral, plural count 10' );
+ assert.ok( mw.messages.set('gender-plural-msg' , '{{GENDER:$1|he|she|they}} {{PLURAL:$2|is|are}} awesome' ) );
+ assert.equal( mw.msg( 'gender-plural-msg', 'male', 1 ), 'he is awesome', 'Gender test for male, plural count 1' );
+ assert.equal( mw.msg( 'gender-plural-msg', 'female', '1' ), 'she is awesome', 'Gender test for female, plural count 1' );
+ assert.equal( mw.msg( 'gender-plural-msg', 'unknown', 10 ), 'they are awesome', 'Gender test for neutral, plural count 10' );
});
-test( 'mw.loader', function() {
+QUnit.asyncTest( 'mw.loader', 2, function ( assert ) {
var isAwesomeDone;
- expect(2);
-
- // Async ahead
- stop();
mw.loader.testCallback = function () {
- start();
- strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined');
+ QUnit.start();
+ assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined');
isAwesomeDone = true;
};
// /sample/awesome.js declares the "mw.loader.testCallback" function
// which contains a call to start() and ok()
- strictEqual( isAwesomeDone, true, "test.callback module should've caused isAwesomeDone to be true" );
+ assert.strictEqual( isAwesomeDone, true, "test.callback module should've caused isAwesomeDone to be true" );
delete mw.loader.testCallback;
}, function () {
- start();
- ok( false, 'Error callback fired while loader.using "test.callback" module' );
+ QUnit.start();
+ assert.ok( false, 'Error callback fired while loader.using "test.callback" module' );
});
});
-test( 'mw.loader.implement', function () {
+QUnit.asyncTest( 'mw.loader.implement', 5, function ( assert ) {
var isJsExecuted, $element, styleTestUrl;
- expect(5);
-
- // Async ahead
- stop();
styleTestUrl = QUnit.fixurl(
mw.config.get( 'wgScriptPath' )
function () {
var styleTestTimeout, styleTestStart, styleTestSince;
- strictEqual( isJsExecuted, undefined, 'javascript not executed multiple times' );
+ assert.strictEqual( isJsExecuted, undefined, 'javascript not executed multiple times' );
isJsExecuted = true;
- equal( mw.loader.getState( 'test.implement' ), 'loaded', 'module state is "loaded" while implement() is executing javascript' );
+ assert.equal( mw.loader.getState( 'test.implement' ), 'ready', 'module state is "ready" while implement() is executing javascript' );
$element = $( '<div class="mw-test-loaderimplement">Foo bar</div>' ).appendTo( '#qunit-fixture' );
- equal( mw.msg( 'test-foobar' ), 'Hello Foobar, $1!', 'Messages are loaded before javascript execution' );
+ assert.equal( mw.msg( 'test-foobar' ), 'Hello Foobar, $1!', 'Messages are loaded before javascript execution' );
// The @import test. This is, in a way, also an open bug for ResourceLoader
// ("execute js after styles are loaded"), but browsers don't offer a way to
styleTestSince = new Date().getTime() - styleTestStart;
// If it is passing or if we timed out, run the real test and stop the loop
if ( isCssImportApplied() || styleTestSince > styleTestTimeout ) {
- equal( $element.css( 'float' ), 'right',
+ assert.equal( $element.css( 'float' ), 'right',
'CSS stylesheet via @import was applied (after ' + styleTestSince + 'ms) (bug 34669). ("float: right")'
);
- equal( $element.css( 'text-align' ),'center',
+ assert.equal( $element.css( 'text-align' ),'center',
'CSS styles after the @import are working ("text-align: center")'
);
// Async done
- start();
+ QUnit.start();
return;
}
});
-test( 'mw.loader erroneous indirect dependency', function() {
- expect( 3 );
+QUnit.test( 'mw.loader erroneous indirect dependency', 3, function ( assert ) {
mw.loader.register( [
['test.module1', '0'],
['test.module2', '0', ['test.module1']],
['test.module3', '0', ['test.module2']]
] );
- mw.loader.implement( 'test.module1', function() { throw new Error( 'expected' ); }, {}, {} );
- strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
- strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
- strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
+ mw.loader.implement( 'test.module1', function () { throw new Error( 'expected' ); }, {}, {} );
+ assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
+ assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
+ assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
} );
-test( 'mw.loader out-of-order implementation', function() {
- expect( 9 );
+QUnit.test( 'mw.loader out-of-order implementation', 9, function ( assert ) {
mw.loader.register( [
['test.module4', '0'],
['test.module5', '0', ['test.module4']],
['test.module6', '0', ['test.module5']]
] );
- mw.loader.implement( 'test.module4', function() {}, {}, {} );
- strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
- strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
- strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
- mw.loader.implement( 'test.module6', function() {}, {}, {} );
- strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
- strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
- strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
+ mw.loader.implement( 'test.module4', function () {}, {}, {} );
+ assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+ assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
+ assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
+ mw.loader.implement( 'test.module6', function () {}, {}, {} );
+ assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+ assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
+ assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
mw.loader.implement( 'test.module5', function() {}, {}, {} );
- strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
- strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
- strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
+ assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+ assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
+ assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
} );
-test( 'mw.loader missing dependency', function() {
- expect( 13 );
+QUnit.test( 'mw.loader missing dependency', 13, function ( assert ) {
mw.loader.register( [
['test.module7', '0'],
['test.module8', '0', ['test.module7']],
['test.module9', '0', ['test.module8']]
] );
- mw.loader.implement( 'test.module8', function() {}, {}, {} );
- strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
- strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
- strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
+ mw.loader.implement( 'test.module8', function () {}, {}, {} );
+ assert.strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
+ assert.strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
+ assert.strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
mw.loader.state( 'test.module7', 'missing' );
- strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
- strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
- strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
- mw.loader.implement( 'test.module9', function() {}, {}, {} );
- strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
- strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
- strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
+ assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
+ assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
+ assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
+ mw.loader.implement( 'test.module9', function () {}, {}, {} );
+ assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
+ assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
+ assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
mw.loader.using(
['test.module7'],
- function() {
- ok( false, "Success fired despite missing dependency" );
- ok( true , "QUnit expected() count dummy" );
+ function () {
+ assert.ok( false, "Success fired despite missing dependency" );
+ assert.ok( true , "QUnit expected() count dummy" );
},
- function( e, dependencies ) {
- strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
- deepEqual( dependencies, ['test.module7'], 'Error callback called with module test.module7' );
+ function ( e, dependencies ) {
+ assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
+ assert.deepEqual( dependencies, ['test.module7'], 'Error callback called with module test.module7' );
}
);
mw.loader.using(
['test.module9'],
- function() {
- ok( false, "Success fired despite missing dependency" );
- ok( true , "QUnit expected() count dummy" );
+ function () {
+ assert.ok( false, "Success fired despite missing dependency" );
+ assert.ok( true , "QUnit expected() count dummy" );
},
- function( e, dependencies ) {
- strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
+ function ( e, dependencies ) {
+ assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
dependencies.sort();
- deepEqual(
+ assert.deepEqual(
dependencies,
['test.module7', 'test.module8', 'test.module9'],
'Error callback called with all three modules as dependencies'
);
} );
-test( 'mw.loader dependency handling', function () {
- expect( 5 );
-
+QUnit.asyncTest( 'mw.loader dependency handling', 5, function ( assert ) {
mw.loader.addSource(
'testloader',
{
] );
function verifyModuleStates() {
- equal( mw.loader.getState( 'testMissing' ), 'missing', 'Module not known to server must have state "missing"' );
- equal( mw.loader.getState( 'testUsesMissing' ), 'error', 'Module with missing dependency must have state "error"' );
- equal( mw.loader.getState( 'testUsesNestedMissing' ), 'error', 'Module with indirect missing dependency must have state "error"' );
+ assert.equal( mw.loader.getState( 'testMissing' ), 'missing', 'Module not known to server must have state "missing"' );
+ assert.equal( mw.loader.getState( 'testUsesMissing' ), 'error', 'Module with missing dependency must have state "error"' );
+ assert.equal( mw.loader.getState( 'testUsesNestedMissing' ), 'error', 'Module with indirect missing dependency must have state "error"' );
}
- stop();
-
mw.loader.using( ['testUsesNestedMissing'],
function () {
- ok( false, 'Error handler should be invoked.' );
- ok( true ); // Dummy to reach QUnit expect()
+ assert.ok( false, 'Error handler should be invoked.' );
+ assert.ok( true ); // Dummy to reach QUnit expect()
verifyModuleStates();
- start();
+ QUnit.start();
},
function ( e, badmodules ) {
- ok( true, 'Error handler should be invoked.' );
+ assert.ok( true, 'Error handler should be invoked.' );
// As soon as server spits out state('testMissing', 'missing');
// it will bubble up and trigger the error callback.
// Therefor the badmodules array is not testUsesMissing or testUsesNestedMissing.
- deepEqual( badmodules, ['testMissing'], 'Bad modules as expected.' );
+ assert.deepEqual( badmodules, ['testMissing'], 'Bad modules as expected.' );
verifyModuleStates();
- start();
+ QUnit.start();
}
);
} );
-test( 'mw.loader bug29107' , function () {
- expect(2);
-
+QUnit.asyncTest( 'mw.loader bug29107' , 2, function ( assert ) {
// Message doesn't exist already
- ok( !mw.messages.exists( 'bug29107' ) );
-
- // Async! Failure in this test may lead to neither the success nor error callbacks getting called.
- // Due to QUnit's timeout feauture we won't hang here forever if this happends.
- stop();
+ assert.ok( !mw.messages.exists( 'bug29107' ) );
mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} );
mw.loader.using( 'bug29107.messages-only', function() {
- start();
- ok( mw.messages.exists( 'bug29107' ), 'Bug 29107: messages-only module should implement ok' );
+ QUnit.start();
+ assert.ok( mw.messages.exists( 'bug29107' ), 'Bug 29107: messages-only module should implement ok' );
}, function() {
- start();
- ok( false, 'Error callback fired while implementing "bug29107.messages-only" module' );
+ QUnit.start();
+ assert.ok( false, 'Error callback fired while implementing "bug29107.messages-only" module' );
});
});
-test( 'mw.loader.bug30825', function() {
+QUnit.asyncTest( 'mw.loader.bug30825', 2, function ( assert ) {
// This bug was actually already fixed in 1.18 and later when discovered in 1.17.
// Test is for regressions!
- expect(2);
-
// Forge an URL to the test callback script
var target = QUnit.fixurl(
mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
// Confirm that mw.loader.load() works with protocol-relative URLs
target = target.replace( /https?:/, '' );
- equal( target.substr( 0, 2 ), '//',
+ assert.equal( target.substr( 0, 2 ), '//',
'URL must be relative to test relative URLs!'
);
// Async!
- stop();
+ // The target calls QUnit.start
mw.loader.load( target );
});
-test( 'mw.html', function () {
- expect(13);
-
- raises( function () {
+QUnit.test( 'mw.html', 13, function ( assert ) {
+ assert.throws( function () {
mw.html.escape();
}, TypeError, 'html.escape throws a TypeError if argument given is not a string' );
- equal( mw.html.escape( '<mw awesome="awesome" value=\'test\' />' ),
+ assert.equal( mw.html.escape( '<mw awesome="awesome" value=\'test\' />' ),
'<mw awesome="awesome" value='test' />', 'escape() escapes special characters to html entities' );
- equal( mw.html.element(),
+ assert.equal( mw.html.element(),
'<undefined/>', 'element() always returns a valid html string (even without arguments)' );
- equal( mw.html.element( 'div' ), '<div/>', 'element() Plain DIV (simple)' );
+ assert.equal( mw.html.element( 'div' ), '<div/>', 'element() Plain DIV (simple)' );
- equal( mw.html.element( 'div', {}, '' ), '<div></div>', 'element() Basic DIV (simple)' );
+ assert.equal( mw.html.element( 'div', {}, '' ), '<div></div>', 'element() Basic DIV (simple)' );
- equal(
+ assert.equal(
mw.html.element(
'div', {
id: 'foobar'
'<div id="foobar"/>',
'html.element DIV (attribs)' );
- equal( mw.html.element( 'p', null, 12 ), '<p>12</p>', 'Numbers are valid content and should be casted to a string' );
+ assert.equal( mw.html.element( 'p', null, 12 ), '<p>12</p>', 'Numbers are valid content and should be casted to a string' );
- equal( mw.html.element( 'p', { title: 12 }, '' ), '<p title="12"></p>', 'Numbers are valid attribute values' );
+ assert.equal( mw.html.element( 'p', { title: 12 }, '' ), '<p title="12"></p>', 'Numbers are valid attribute values' );
// Example from https://www.mediawiki.org/wiki/ResourceLoader/Default_modules#mediaWiki.html
- equal(
+ assert.equal(
mw.html.element(
'div',
{},
'Raw inclusion of another element'
);
- equal(
+ assert.equal(
mw.html.element(
'option', {
selected: true
'Attributes may have boolean values. True copies the attribute name to the value.'
);
- equal(
+ assert.equal(
mw.html.element(
'option', {
value: 'foo',
'Attributes may have boolean values. False keeps the attribute from output.'
);
- equal( mw.html.element( 'div',
+ assert.equal( mw.html.element( 'div',
null, 'a' ),
'<div>a</div>',
'html.element DIV (content)' );
- equal( mw.html.element( 'a',
+ assert.equal( mw.html.element( 'a',
{ href: 'http://mediawiki.org/w/index.php?title=RL&action=history' }, 'a' ),
'<a href="http://mediawiki.org/w/index.php?title=RL&action=history">a</a>',
'html.element DIV (attribs + content)' );
-module( 'mediawiki.user', QUnit.newMwEnvironment() );
+( function ( mw ) {
-test( '-- Initial check', function() {
- expect(1);
+QUnit.module( 'mediawiki.user', QUnit.newMwEnvironment() );
- ok( mw.user, 'mw.user defined' );
+QUnit.test( 'options', 1, function ( assert ) {
+ assert.ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
});
-
-test( 'options', function() {
- expect(1);
-
- ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
-});
-
-test( 'User login status', function() {
- expect(5);
-
+QUnit.test( 'user status', 9, function ( assert ) {
/**
* Tests can be run under three different conditions:
* 1) From tests/qunit/index.html, user will be anonymous.
*/
// Forge an anonymous user:
- mw.config.set( 'wgUserName', null);
+ mw.config.set( 'wgUserName', null );
- strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' );
- ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' );
+ assert.strictEqual( mw.user.getName(), null, 'user.getName() returns null when anonymous' );
+ assert.strictEqual( mw.user.name(), null, 'user.name() compatibility' );
+ assert.assertTrue( mw.user.isAnon(), 'user.isAnon() returns true when anonymous' );
+ assert.assertTrue( mw.user.anonymous(), 'user.anonymous() compatibility' );
// Not part of startUp module
mw.config.set( 'wgUserName', 'John' );
- equal( mw.user.name(), 'John', 'user.name returns username when logged-in' );
- ok( !mw.user.anonymous(), 'user.anonymous returns false when logged-in' );
+ assert.equal( mw.user.getName(), 'John', 'user.getName() returns username when logged-in' );
+ assert.equal( mw.user.name(), 'John', 'user.name() compatibility' );
+ assert.assertFalse( mw.user.isAnon(), 'user.isAnon() returns false when logged-in' );
+ assert.assertFalse( mw.user.anonymous(), 'user.anonymous() compatibility' );
- equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
+ assert.equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
});
+
+QUnit.asyncTest( 'getGroups', 3, function ( assert ) {
+ mw.user.getGroups( function ( groups ) {
+ // First group should always be '*'
+ assert.equal( $.type( groups ), 'array', 'Callback gets an array' );
+ assert.equal( groups[0], '*', '"*"" is the first group' );
+ // Sort needed because of different methods if creating the arrays,
+ // only the content matters.
+ assert.deepEqual( groups.sort(), mw.config.get( 'wgUserGroups' ).sort(), 'Array contains all groups, just like wgUserGroups' );
+ QUnit.start();
+ });
+});
+
+QUnit.asyncTest( 'getRights', 1, function ( assert ) {
+ mw.user.getRights( function ( rights ) {
+ // First group should always be '*'
+ assert.equal( $.type( rights ), 'array', 'Callback gets an array' );
+ QUnit.start();
+ });
+});
+
+}( mediaWiki ) );
-module( 'mediawiki.util', QUnit.newMwEnvironment() );
+QUnit.module( 'mediawiki.util', QUnit.newMwEnvironment() );
-test( '-- Initial check', function() {
- expect(1);
-
- ok( mw.util, 'mw.util defined' );
+QUnit.test( 'rawurlencode', 1, function ( assert ) {
+ assert.equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
});
-test( 'rawurlencode', function() {
- expect(1);
-
- equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
-});
-
-test( 'wikiUrlencode', function() {
- expect(1);
-
- equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
+QUnit.test( 'wikiUrlencode', 1, function ( assert ) {
+ assert.equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
});
-test( 'wikiGetlink', function() {
- expect(3);
-
+QUnit.test( 'wikiGetlink', 3, function ( assert ) {
// Not part of startUp module
mw.config.set( 'wgArticlePath', '/wiki/$1' );
mw.config.set( 'wgPageName', 'Foobar' );
var hrefA = mw.util.wikiGetlink( 'Sandbox' );
- equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+ assert.equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
- equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
+ assert.equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
var hrefC = mw.util.wikiGetlink();
- equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+ assert.equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
});
-test( 'wikiScript', function() {
- expect(2);
-
+QUnit.test( 'wikiScript', 2, function ( assert ) {
mw.config.set({
'wgScript': '/w/index.php',
'wgScriptPath': '/w',
'wgScriptExtension': '.php'
});
- equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
- equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
+ assert.equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
+ assert.equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
});
-test( 'addCSS', function() {
- expect(3);
-
+QUnit.test( 'addCSS', 3, function ( assert ) {
var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( '#qunit-fixture' );
var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
- equal( typeof style, 'object', 'addCSS returned an object' );
- strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
+ assert.equal( typeof style, 'object', 'addCSS returned an object' );
+ assert.strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
- equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
+ assert.equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
// Clean up
$( style.ownerNode ).remove();
});
-test( 'toggleToc', function() {
- expect(4);
-
- strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
+QUnit.asyncTest( 'toggleToc', 4, function ( assert ) {
+ assert.strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
var tocHtml =
'<table id="toc" class="toc"><tr><td>' +
$toc = $(tocHtml).appendTo( '#qunit-fixture' ),
$toggleLink = $( '#togglelink' );
- strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
-
- // Toggle animation is asynchronous
- // QUnit should not finish this test() untill they are all done
- stop();
+ assert.strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
var actionC = function() {
- start();
+ QUnit.start();
};
var actionB = function() {
- start(); stop();
- strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
+ assert.strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
};
var actionA = function() {
- strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
+ assert.strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
};
actionA();
});
-test( 'getParamValue', function() {
- expect(5);
-
+QUnit.test( 'getParamValue', 5, function ( assert ) {
var url1 = 'http://example.org/?foo=wrong&foo=right#&foo=bad';
- equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
- strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
+ assert.equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
+ assert.strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
var url2 = 'http://example.org/#&foo=bad';
- strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
+ assert.strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
var url3 = 'example.org?' + $.param({ 'TEST': 'a b+c' });
- strictEqual( mw.util.getParamValue( 'TEST', url3 ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' );
+ assert.strictEqual( mw.util.getParamValue( 'TEST', url3 ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' );
var url4 = 'example.org?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :)
- strictEqual( mw.util.getParamValue( 'TEST', url4 ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
+ assert.strictEqual( mw.util.getParamValue( 'TEST', url4 ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
});
-test( 'tooltipAccessKey', function() {
- expect(3);
-
- equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
- ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
- ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
+QUnit.test( 'tooltipAccessKey', 3, function ( assert ) {
+ assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
+ assert.ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
+ assert.ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
});
-test( '$content', function() {
- expect(2);
-
- ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
- strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
+QUnit.test( '$content', 2, function ( assert ) {
+ assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
+ assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
});
* Portlet names are prefixed with 'p-test' to avoid conflict with core
* when running the test suite under a wiki page.
* Previously, test elements where invisible to the selector since only
- * one element can have a given id.
+ * one element can have a given id.
*/
-test( 'addPortletLink', function () {
+QUnit.test( 'addPortletLink', 8, function ( assert ) {
var pTestTb, pCustom, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
- expect( 8 );
-
pTestTb = '\
<div class="portlet" id="p-test-tb">\
<h5>Toolbox</h5>\
tbRL = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/ResourceLoader',
'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
- ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
+ assert.ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
tbMW = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/',
'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL );
$tbMW = $( tbMW );
- equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
- equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' );
- equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
+ assert.equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
+ assert.equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' );
+ assert.equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
cuQuux = mw.util.addPortletLink( 'p-test-custom', '#', 'Quux' );
$cuQuux = $(cuQuux);
- equal(
+ assert.equal(
$( '#p-test-custom #c-barmenu ul li' ).length,
1,
'addPortletLink did not add the item to all <ul> elements in the portlet (bug 35082)'
tbRLDM = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
- equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
+ assert.equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
caFoo = mw.util.addPortletLink( 'p-test-views', '#', 'Foo' );
- strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
- strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
+ assert.strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
+ assert.strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
});
-test( 'jsMessage', function() {
- expect(1);
-
+QUnit.test( 'jsMessage', 1, function ( assert ) {
var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
- ok( a, 'Basic checking of return value' );
+ assert.ok( a, 'Basic checking of return value' );
// Clean up
$( '#mw-js-message' ).remove();
});
-test( 'validateEmail', function() {
- expect(6);
-
- strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
- strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
+QUnit.test( 'validateEmail', 6, function ( assert ) {
+ assert.strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
+ assert.strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
// testEmailWithCommasAreInvalids
- strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
- strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
+ assert.strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
+ assert.strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
// testEmailWithHyphens
- strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
- strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
+ assert.strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
+ assert.strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
});
-test( 'isIPv6Address', function() {
- expect(40);
-
+QUnit.test( 'isIPv6Address', 40, function ( assert ) {
// Shortcuts
- var assertFalseIPv6 = function( addy, summary ) {
- return strictEqual( mw.util.isIPv6Address( addy ), false, summary );
- },
- assertTrueIPv6 = function( addy, summary ) {
- return strictEqual( mw.util.isIPv6Address( addy ), true, summary );
- };
+ function assertFalseIPv6( addy, summary ) {
+ return assert.strictEqual( mw.util.isIPv6Address( addy ), false, summary );
+ }
+ function assertTrueIPv6( addy, summary ) {
+ return assert.strictEqual( mw.util.isIPv6Address( addy ), true, summary );
+ }
// Based on IPTest.php > testisIPv6
assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
'fc:100:a:d::',
'fc:100:a:d:1::',
'fc:100:a:d:1:e::',
- 'fc:100:a:d:1:e:ac::'], function( i, addy ){
+ 'fc:100:a:d:1:e:ac::'], function ( i, addy ){
assertTrueIPv6( addy, addy + ' is a valid IP' );
});
'::fc:100:a:d:1:e',
'::fc:100:a:d:1:e:ac',
- 'fc:100:a:d:1:e:ac:0'], function( i, addy ){
+ 'fc:100:a:d:1:e:ac:0'], function ( i, addy ){
assertTrueIPv6( addy, addy + ' is a valid IP' );
});
assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
});
-test( 'isIPv4Address', function() {
- expect(11);
-
+QUnit.test( 'isIPv4Address', 11, function ( assert ) {
// Shortcuts
- var assertFalseIPv4 = function( addy, summary ) {
- return strictEqual( mw.util.isIPv4Address( addy ), false, summary );
- },
- assertTrueIPv4 = function( addy, summary ) {
- return strictEqual( mw.util.isIPv4Address( addy ), true, summary );
- };
+ function assertFalseIPv4( addy, summary ) {
+ assert.strictEqual( mw.util.isIPv4Address( addy ), false, summary );
+ }
+ function assertTrueIPv4( addy, summary ) {
+ assert.strictEqual( mw.util.isIPv4Address( addy ), true, summary );
+ }
// Based on IPTest.php > testisIPv4
assertFalseIPv4( false, 'Boolean false is not an IP' );