Use plugin to ensure all message keys are documented

Bug: T235502
Change-Id: If77ddf2caf667504bd0f7e0150649745ae31c4e2
This commit is contained in:
Ed Sanders 2019-10-21 17:05:40 +01:00
parent aaf0006a5a
commit 7b9bb6f7d6
22 changed files with 102 additions and 23 deletions

View file

@ -4,14 +4,16 @@
"wikimedia/jquery"
],
"globals": {
"require": false,
"module": false,
"mw": false,
"OO": false
"require": "readonly",
"module": "readonly",
"mw": "readonly",
"OO": "readonly"
},
"rules": {
"quote-props": [ "error", "as-needed" ],
"max-len": "off",
"no-jquery/no-global-selector": "off"
}
"no-jquery/no-global-selector": "off",
"mediawiki/msg-doc": "error"
},
"plugins": [ "mediawiki" ]
}

6
package-lock.json generated
View file

@ -3031,6 +3031,12 @@
"vscode-json-languageservice": "^3.2.1"
}
},
"eslint-plugin-mediawiki": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-mediawiki/-/eslint-plugin-mediawiki-0.1.0.tgz",
"integrity": "sha512-OiRH4axfR+TOFbw/7fKHakgleo0z9nXAiLzN7Bo1iqQNK9QTTK0v1MqQUfJXy4SfDQih7g1yHUDl80Ea1oinmQ==",
"dev": true
},
"eslint-plugin-no-jquery": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.2.1.tgz",

View file

@ -21,6 +21,7 @@
"@wdio/sync": "5.13.2",
"chromedriver": "73.0.0",
"eslint-config-wikimedia": "0.14.3",
"eslint-plugin-mediawiki": "0.1.0",
"grunt": "1.0.4",
"grunt-banana-checker": "0.7.0",
"grunt-contrib-copy": "1.0.0",

View file

@ -72,11 +72,13 @@
// Load license messages from the remote wiki if we don't have these messages locally
// (this means that we only load messages from the foreign wiki for custom config)
// These messages are documented where msgPromise resolves
if ( mw.message( 'upload-form-label-own-work-message-' + msgs ).exists() ) {
msgPromise = $.Deferred().resolve();
} else {
msgPromise = booklet.upload.apiPromise.then( function ( api ) {
return api.loadMessages( [
// These messages are documented where msgPromise resolves
'upload-form-label-own-work-message-' + msgs,
'upload-form-label-not-own-work-message-' + msgs,
'upload-form-label-not-own-work-local-' + msgs
@ -87,8 +89,15 @@
// Update license messages
return msgPromise.then( function () {
var $labels;
// The following messages are used here:
// * upload-form-label-own-work-message-generic-local
// * upload-form-label-own-work-message-generic-foreign
booklet.$ownWorkMessage.msg( 'upload-form-label-own-work-message-' + msgs );
// * upload-form-label-not-own-work-message-generic-local
// * upload-form-label-not-own-work-message-generic-foreign
booklet.$notOwnWorkMessage.msg( 'upload-form-label-not-own-work-message-' + msgs );
// * upload-form-label-not-own-work-local-generic-local
// * upload-form-label-not-own-work-local-generic-foreign
booklet.$notOwnWorkLocal.msg( 'upload-form-label-not-own-work-local-' + msgs );
$labels = $( [
@ -108,6 +117,7 @@
} );
} );
}, function ( errorMsg ) {
// eslint-disable-next-line mediawiki/msg-doc
booklet.getPage( 'upload' ).$element.msg( errorMsg );
return $.Deferred().resolve();
} )

View file

@ -193,6 +193,7 @@
);
},
function ( errorMsg ) {
// eslint-disable-next-line mediawiki/msg-doc
booklet.getPage( 'upload' ).$element.msg( errorMsg );
return $.Deferred().resolve();
}

View file

@ -163,12 +163,24 @@
if ( response.parse.displaytitle ) {
$displaytitle = $( $.parseHTML( response.parse.displaytitle ) );
// The following messages can be used here:
// * editconflict
// * editingcomment
// * editingsection
// * editing
// * creating
$( '#firstHeading' ).msg(
mw.config.get( 'wgEditMessage', 'editing' ),
$displaytitle
);
document.title = mw.msg(
'pagetitle',
// The following messages can be used here:
// * editconflict
// * editingcomment
// * editingsection
// * editing
// * creating
mw.msg(
mw.config.get( 'wgEditMessage', 'editing' ),
$displaytitle.text()
@ -235,11 +247,10 @@
$.when( parseRequest, diffRequest ).done( function ( parseResp ) {
var parse = parseResp && parseResp[ 0 ].parse,
isSubject = ( section === 'new' ),
summaryMsg = isSubject ? 'subject-preview' : 'summary-preview',
$summaryPreview = $editform.find( '.mw-summary-preview' ).empty();
if ( parse && parse.parsedsummary ) {
$summaryPreview.append(
mw.message( summaryMsg ).parse(),
mw.message( isSubject ? 'subject-preview' : 'summary-preview' ).parse(),
' ',
$( '<span>' ).addClass( 'comment' ).html(
// There is no equivalent to rawParams

View file

@ -70,9 +70,9 @@
if ( postEdit ) {
mw.hook( 'postEdit' ).fire( {
// The following messages can be used here:
// postedit-confirmation-saved
// postedit-confirmation-created
// postedit-confirmation-restored
// * postedit-confirmation-saved
// * postedit-confirmation-created
// * postedit-confirmation-restored
message: mw.msg(
'postedit-confirmation-' + postEdit,
mw.user

View file

@ -59,6 +59,7 @@
*/
loadMessagesIfMissing: function ( messages, options ) {
var missing = messages.filter( function ( msg ) {
// eslint-disable-next-line mediawiki/msg-doc
return !mw.message( msg ).exists();
} );

View file

@ -152,6 +152,9 @@
this.constructor.static.windowManager.openWindow(
this.constructor.static.dialog,
{
// The following messages are used here
// * feedback-dialog-title
// * config.dialogTitleMessageKey ...
title: mw.msg( this.dialogTitleMessageKey ),
foreignApi: this.foreignApi,
settings: {
@ -426,7 +429,11 @@
* @return {OO.ui.Error}
*/
mw.Feedback.Dialog.prototype.getErrorMessage = function () {
// Messages: feedback-error1, feedback-error2, feedback-error3, feedback-error4
// The following messages can be used here:
// * feedback-error1
// * feedback-error2
// * feedback-error3
// * feedback-error4
return new OO.ui.Error( mw.msg( 'feedback-' + this.status ) );
};

View file

@ -35,6 +35,7 @@
function loadMessage( $target, message ) {
if ( message ) {
$target.removeClass( 'empty' )
// eslint-disable-next-line mediawiki/msg-doc
.text( mw.message( message ).text() );
}
}

View file

@ -1419,6 +1419,7 @@ mw.Message.prototype.parser = function () {
mw.Message.prototype.parseDom = ( function () {
var $wrapper = $( '<div>' );
return function () {
// eslint-disable-next-line mediawiki/msg-doc
return $wrapper.msg( this.key, this.parameters ).contents().detach();
};
}() );

View file

@ -24,6 +24,7 @@
// Function suitable for passing to Array.prototype.map
// Can't use mw.msg directly because Array.prototype.map passes element index as second argument
function mwMsgMapper( key ) {
// eslint-disable-next-line mediawiki/msg-doc
return mw.msg( key );
}

View file

@ -39,7 +39,6 @@
throw new Error( 'Invalid action' );
}
// message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
msgKey = state === 'loading' ? action + 'ing' : action;
otherAction = action === 'watch' ? 'unwatch' : 'watch';
$li = $link.closest( 'li' );
@ -53,6 +52,11 @@
}
$link
// The following messages can be used here:
// * watch
// * watching
// * unwatch
// * unwatching
.text( mw.msg( msgKey ) )
.attr( 'title', mw.msg( 'tooltip-ca-' + action ) )
.updateTooltipAccessKeys()
@ -178,6 +182,11 @@
message = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
// The following messages can be used here:
// * addedwatchtext-talk
// * addedwatchtest
// * removedwatchtext-talk
// * removedwatchtext
mw.notify( mw.message( message, mwTitle.getPrefixedText() ).parseDom(), {
tag: 'watch-self'
} );

View file

@ -163,6 +163,10 @@ FilterItem.prototype.getStateMessage = function () {
if ( messageKey ) {
// Build message
// The following messages are used here:
// * rcfilters-state-message-subset
// * rcfilters-state-message-fullcoverage
// * conflict.message values...
return mw.msg(
messageKey,
mw.language.listToText( affectingItems ),

View file

@ -309,12 +309,15 @@ FiltersViewModel.prototype.initializeFilters = function ( filterGroups, views )
};
// Title is a msg-key
// eslint-disable-next-line mediawiki/msg-doc
data.title = data.title ? mw.msg( data.title ) : data.name;
// Filters are given to us with msg-keys, we need
// to translate those before we hand them off
for ( i = 0; i < data.filters.length; i++ ) {
// eslint-disable-next-line mediawiki/msg-doc
data.filters[ i ].label = data.filters[ i ].label ? mw.msg( data.filters[ i ].label ) : data.filters[ i ].name;
// eslint-disable-next-line mediawiki/msg-doc
data.filters[ i ].description = data.filters[ i ].description ? mw.msg( data.filters[ i ].description ) : '';
}
} );

View file

@ -152,15 +152,17 @@ ChangesLimitAndDateButtonWidget.prototype.updateButtonLabel = function () {
var message,
limit = this.limitGroupModel.findSelectedItems()[ 0 ],
label = limit && limit.getLabel(),
days = this.daysGroupModel.findSelectedItems()[ 0 ],
daysParamName = Number( days.getParamName() ) < 1 ?
'rcfilters-days-show-hours' :
'rcfilters-days-show-days';
days = this.daysGroupModel.findSelectedItems()[ 0 ];
// Update the label
if ( label && days ) {
message = mw.msg( 'rcfilters-limit-and-date-label', label,
mw.msg( daysParamName, days.getLabel() )
mw.msg(
Number( days.getParamName() ) < 1 ?
'rcfilters-days-show-hours' :
'rcfilters-days-show-days',
days.getLabel()
)
);
this.button.setLabel( message );
}

View file

@ -120,6 +120,8 @@ ChangesListWrapperWidget.prototype.onModelUpdate = function (
.text( mw.message( 'rcfilters-noresults-conflict' ).text() ),
$( '<div>' )
.addClass( 'mw-rcfilters-ui-changesListWrapperWidget-results-message' )
// TODO: Document possible messages
// eslint-disable-next-line mediawiki/msg-doc
.text( mw.message( conflictItem.getCurrentConflictResultMessage() ).text() )
);
} else {
@ -127,6 +129,12 @@ ChangesListWrapperWidget.prototype.onModelUpdate = function (
.append(
$( '<div>' )
.addClass( 'mw-rcfilters-ui-changesListWrapperWidget-results-noresult' )
// The following messages can be used here:
// * recentchanges-noresult
// * recentchanges-timeout
// * recentchanges-network
// * recentchanges-notargetpage
// * allpagesbadtitle
.text( mw.msg( this.getMsgKeyForNoResults( noResultsDetails ) ) )
);

View file

@ -39,6 +39,7 @@ var FilterMenuSectionOptionWidget = function MwRcfiltersUiFilterMenuSectionOptio
if ( whatsThisMessages.header ) {
$popupContent.append(
( new OO.ui.LabelWidget( {
// eslint-disable-next-line mediawiki/msg-doc
label: mw.msg( whatsThisMessages.header ),
classes: [ 'mw-rcfilters-ui-filterMenuSectionOptionWidget-whatsThisButton-popup-content-header' ]
} ) ).$element
@ -47,6 +48,7 @@ var FilterMenuSectionOptionWidget = function MwRcfiltersUiFilterMenuSectionOptio
if ( whatsThisMessages.body ) {
$popupContent.append(
( new OO.ui.LabelWidget( {
// eslint-disable-next-line mediawiki/msg-doc
label: mw.msg( whatsThisMessages.body ),
classes: [ 'mw-rcfilters-ui-filterMenuSectionOptionWidget-whatsThisButton-popup-content-body' ]
} ) ).$element
@ -58,6 +60,7 @@ var FilterMenuSectionOptionWidget = function MwRcfiltersUiFilterMenuSectionOptio
framed: false,
flags: [ 'progressive' ],
href: whatsThisMessages.url,
// eslint-disable-next-line mediawiki/msg-doc
label: mw.msg( whatsThisMessages.linkText ),
classes: [ 'mw-rcfilters-ui-filterMenuSectionOptionWidget-whatsThisButton-popup-content-link' ]
} ) ).$element

View file

@ -145,13 +145,13 @@ SaveFiltersPopupButtonWidget.prototype.onPopupReady = function () {
* @param {boolean} checked State of the checkbox
*/
SaveFiltersPopupButtonWidget.prototype.onSetAsDefaultChange = function ( checked ) {
var messageKey = checked ?
'rcfilters-savedqueries-apply-and-setdefault-label' :
'rcfilters-savedqueries-apply-label';
this.applyButton
.setIcon( checked ? 'pushPin' : null )
.setLabel( mw.msg( messageKey ) );
.setLabel( mw.msg(
checked ?
'rcfilters-savedqueries-apply-and-setdefault-label' :
'rcfilters-savedqueries-apply-label'
) );
};
/**

View file

@ -88,6 +88,7 @@ TagItemWidget.prototype.updateUiBasedOnState = function () {
if ( labelMsg ) {
this.setLabel( $( '<div>' ).append(
$( '<bdi>' ).html(
// eslint-disable-next-line mediawiki/msg-doc
mw.message( labelMsg, mw.html.escape( this.itemModel.getLabel() ) ).parse()
)
).contents() );

View file

@ -279,6 +279,11 @@
s /= 1024;
sizeMsgs = sizeMsgs.slice( 1 );
}
// The following messages are used here:
// * size-bytes
// * size-kilobytes
// * size-megabytes
// * size-gigabytes
return mw.msg( sizeMsgs[ 0 ], Math.round( s ) );
}

View file

@ -765,6 +765,7 @@
var message;
outerCalled = false;
innerCalled = false;
// eslint-disable-next-line mediawiki/msg-doc
message = mw.message( key );
message[ format ]();
assert.strictEqual( outerCalled, shouldCall, 'Outer function called for ' + key );
@ -1212,6 +1213,7 @@
for ( i = 0; i < cases.length; i++ ) {
mw.messages.set( cases[ i ].key, cases[ i ].msg );
assert.strictEqual(
// eslint-disable-next-line mediawiki/msg-doc
mw.message( cases[ i ].key, $( '<b>' ).text( 'x' ) ).parse(),
cases[ i ].expected,
cases[ i ].key