Page MenuHomePhabricator

Hunt and remove unused i18n messages in mediawiki core
Open, LowPublic

Description

Unfortunately there is no CI test to check if the message from i18n files has been removed in case a developer tries to remove usages of that message. I almost did it and then I checked for ones that might have been orphan. That's not good because it produces useless job of translating i18n messages that are not used at all.

The problem with hunting these messages is that lots of them are being used with dynamic string concatenation making the list full of false positives. I made P8562 that is list of unused i18n keys that don't have a dash character and still there's 150 messages there:

1sitetitle
2sitesubtitle
3viewdeleted_short
4imagepage
5mediawikipage
6templatepage
7viewhelppage
8categorypage
9viewtalkpage
10filecopyerror
11formerror
12myprivateinfoprotected
13yourname
14externaldberror
15createaccounterror
16nocookiesnew
17nocookieslogin
18passwordremindertitle
19passwordsent
20changepassword
21resetpass_text
22resetpass_header
23resetpass_submit
24resetpass_forbidden
25passwordreset
26resettokens
27bold_sample
28italic_sample
29link_sample
30link_tip
31extlink_sample
32extlink_tip
33headline_sample
34headline_tip
35nowiki_sample
36nowiki_tip
37image_sample
38image_tip
39media_sample
40media_tip
41sig_tip
42hr_tip
43usercsspreview
44userjsonpreview
45userjspreview
46sitecsspreview
47sitejsonpreview
48sitejspreview
49searchrelated
50searchresultshead
51rcshowhideminor
52rcshowhideanons
53rcshowhidemine
54rcshowhidecategorization
55uploadnologin
56upload_directory_missing
57upload_directory_read_only
58filepage.css
59mimesearch
60listredirects
61listduplicatedfiles
62unusedtemplates
63randomincategory
64randomredirect
65brokenredirects
66withoutinterwiki
67fewestrevisions
68lonelypages
69uncategorizedpages
70uncategorizedcategories
71uncategorizedimages
72uncategorizedtemplates
73unusedcategories
74unusedimages
75wantedcategories
76wantedpages
77wantedfiles
78wantedtemplates
79mostlinked
80mostlinkedcategories
81mostlinkedtemplates
82mostcategories
83mostimages
84mostinterwikis
85mostrevisions
86shortpages
87longpages
88deadendpages
89ancientpages
90movethispage
91apisandbox
92deletedcontributions
93listgrouprights
94listgrants
95trackingcategories
96mailnologintext
97usermaildisabledtext
98noemailtext
99watchthispage
100unwatchthispage
101wlshowlast
102wlshowhideminor
103wlshowhidebots
104wlshowhideliu
105wlshowhideanons
106wlshowhidepatr
107wlshowhidemine
108wlshowhidecategorization
109enotif_subject_deleted
110enotif_subject_created
111enotif_subject_moved
112enotif_subject_restored
113enotif_subject_changed
114enotif_body_intro_deleted
115enotif_body_intro_created
116enotif_body_intro_moved
117enotif_body_intro_restored
118enotif_body_intro_changed
119reverted
120changecontentmodel
121expiringblock
122djvu_no_xml
123common.css
124print.css
125noscript.css
126common.json
127common.js
128anonymous
129newimages
130imagelisttext
131confirmemail_body_set
132invalidateemail
133notificationemail_subject_changed
134notificationemail_subject_removed
135notificationemail_body_changed
136notificationemail_body_removed
137ascending_abbrev
138descending_abbrev
139table_pager_next
140table_pager_prev
141table_pager_first
142table_pager_last
143fileduplicatesearch
144comparepages
145suppressedarticle
146mediastatistics
147linkaccounts
148unlinkaccounts
149gotointerwiki
150passwordpolicies

(I really hope things like yourname is not used using dynamic string concatenation)

The ones that have dash are this list (700 cases):

1tog-watchcreations
2tog-watchdefault
3tog-watchmoves
4tog-watchdeletion
5tog-watchuploads
6tog-watchrollback
7january-date
8february-date
9march-date
10april-date
11may-date
12june-date
13july-date
14august-date
15september-date
16october-date
17november-date
18december-date
19category-subcat-count-limited
20category-article-count
21category-article-count-limited
22category-file-count
23category-file-count-limited
24help-mediawiki
25policy-url
26feed-atom
27feed-rss
28nstab-user
29nstab-media
30nstab-image
31nstab-mediawiki
32nstab-category
33databaseerror-textcl
34databaseerror-query
35databaseerror-function
36databaseerror-error
37missingarticle-rev
38missingarticle-diff
39title-invalid-interwiki
40exception-nologin-text-manual
41password-change-forbidden
42userlogout-summary
43createacct-email-ph
44createacct-another-email-ph
45createacct-submit
46createacct-another-submit
47createacct-continue-submit
48createacct-another-continue-submit
49createacct-benefit-icon1
50createacct-benefit-head1
51createacct-benefit-body1
52createacct-benefit-icon2
53createacct-benefit-head2
54createacct-benefit-body2
55createacct-benefit-icon3
56createacct-benefit-head3
57createacct-benefit-body3
58login-abort-generic
59login-migrated-generic
60pt-login-continue-button
61changepassword-summary
62changepassword-success
63changepassword-throttled
64botpasswords-existing
65botpasswords-insert-failed
66botpasswords-update-failed
67resetpass-submit-loggedin
68resetpass-submit-cancel
69resetpass-wrong-oldpass
70resetpass-recycled
71resetpass-temp-password
72resetpass-abort-generic
73passwordreset-domain
74changeemail-summary
75changeemail-password
76changeemail-throttled
77resettokens-summary
78resettokens-text
79sig-text
80savearticle-start
81savechanges-start
82publishpage-start
83publishchanges-start
84addsection-preload
85addsection-editintro
86content-model-wikitext
87content-model-text
88content-model-javascript
89content-model-css
90content-model-json
91deprecated-self-close-category-desc
92duplicate-args-category-desc
93template-loop-category-desc
94node-count-exceeded-category-desc
95expansion-depth-exceeded-category-desc
96unstrip-depth-category
97unstrip-size-category
98undo-main-slot-only
99createaccount-hook-aborted
100mergehistory-fail
101search-summary
102search-relatedarticle
103preferences-summary
104prefs-skin
105prefs-labs
106prefs-user-pages
107prefs-personal
108prefs-rc
109prefs-watchlist
110prefs-editwatchlist
111prefs-editwatchlist-edit
112prefs-editwatchlist-raw
113prefs-editwatchlist-clear
114prefs-misc
115prefs-email
116prefs-rendering
117prefs-help-watchlist-token2
118prefs-searchoptions
119prefs-namespaces
120prefs-files
121prefs-custom-json
122prefs-info
123prefs-i18n
124prefs-dateformat
125prefs-timeoffset
126prefs-advancedediting
127prefs-developertools
128prefs-editor
129prefs-preview
130prefs-advancedrc
131prefs-advancedrendering
132prefs-advancedsearchoptions
133prefs-advancedwatchlist
134prefs-displayrc
135prefs-displaywatchlist
136prefs-changesrc
137prefs-changeswatchlist
138prefs-pageswatchlist
139prefs-tokenwatchlist
140prefs-diffs
141userrights-summary
142userrights-changeable-col
143userrights-unchangeable-col
144group-user
145group-autoconfirmed
146group-bot
147group-sysop
148group-interface-admin
149group-bureaucrat
150group-suppress
151group-user-member
152group-autoconfirmed-member
153group-sysop-member
154group-interface-admin-member
155group-suppress-member
156grouppage-user
157grouppage-bot
158grouppage-sysop
159grouppage-interface-admin
160grouppage-bureaucrat
161grouppage-suppress
162grant-group-page-interaction
163grant-group-file-interaction
164grant-group-watchlist-interaction
165grant-group-email
166grant-group-high-volume
167grant-group-customization
168grant-group-administration
169grant-group-private-information
170grant-group-other
171grant-blockusers
172grant-createaccount
173grant-createeditmovepage
174grant-delete
175grant-editinterface
176grant-editmycssjs
177grant-editmyoptions
178grant-editmywatchlist
179grant-editsiteconfig
180grant-editpage
181grant-editprotected
182grant-highvolume
183grant-oversight
184grant-patrol
185grant-privateinfo
186grant-protect
187grant-rollback
188grant-sendemail
189grant-uploadeditmovefile
190grant-uploadfile
191grant-basic
192grant-viewdeleted
193grant-viewmywatchlist
194grant-viewrestrictedlogs
195recentchanges-summary
196rcshowhideminor-show
197rcshowhideminor-hide
198rcshowhidebots-show
199rcshowhidebots-hide
200rcshowhideliu-show
201rcshowhideliu-hide
202rcshowhideanons-show
203rcshowhideanons-hide
204rcshowhidepatr-show
205rcshowhidepatr-hide
206rcshowhidemine-show
207rcshowhidemine-hide
208rcshowhidecategorization-show
209rcshowhidecategorization-hide
210rc-enhanced-expand
211rc-enhanced-hide
212recentchangeslinked-summary
213recentchanges-page-added-to-category-bundled
214recentchanges-page-removed-from-category-bundled
215upload-summary
216php-uploaddisabledtext
217upload-source
218upload-description
219upload-options
220upload-proto-error-text
221upload-file-error
222upload-file-error-text
223upload-misc-error
224upload-misc-error-text
225upload-too-many-redirects
226upload-http-error
227backend-fail-backup
228lockmanager-fail-db-bucket
229uploadstash-summary
230uploadstash-badtoken
231img-auth-notindir
232img-auth-isdir
233img-auth-streaming
234img-auth-public
235upload-curl-error6
236upload-curl-error6-text
237upload-curl-error28
238upload-curl-error28-text
239license-nopreview
240listfiles-summary
241listfiles-latestversion-yes
242listfiles-latestversion-no
243filehist-filesize
244mimesearch-summary
245unwatchedpages-summary
246listredirects-summary
247listduplicatedfiles-summary
248unusedtemplates-summary
249randompage-nopages
250randomincategory-legend
251randomredirect-nopages
252statistics-summary
253pageswithprop-summary
254doubleredirects-summary
255brokenredirects-summary
256withoutinterwiki-summary
257withoutinterwiki-legend
258fewestrevisions-summary
259lonelypages-summary
260uncategorizedpages-summary
261uncategorizedcategories-summary
262uncategorizedimages-summary
263uncategorizedtemplates-summary
264unusedcategories-summary
265unusedimages-summary
266wantedcategories-summary
267wantedpages-summary
268wantedfiles-summary
269wantedfiletext-cat-noforeign
270wantedfiletext-nocat-noforeign
271wantedtemplates-summary
272mostlinked-summary
273mostlinkedcategories-summary
274mostlinkedtemplates-summary
275mostcategories-summary
276mostimages-summary
277mostinterwikis-summary
278mostrevisions-summary
279prefixindex-summary
280shortpages-summary
281longpages-summary
282deadendpages-summary
283protectedpages-summary
284protectedtitles-summary
285listusers-summary
286newpages-summary
287ancientpages-summary
288apihelp-summary
289apisandbox-summary
290apisandbox-api-disabled
291booksources-summary
292magiclink-tracking-rfc-desc
293magiclink-tracking-pmid-desc
294magiclink-tracking-isbn-desc
295logeventslist-patrol-log
296logeventslist-tag-log
297checkbox-all
298checkbox-invert
299allpages-summary
300categories-summary
301deletedcontributions-summary
302linksearch-summary
303activeusers-summary
304listgrouprights-summary
305listgrouprights-helppage
306listgrouprights-addgroup
307listgrouprights-removegroup
308listgrouprights-addgroup-all
309listgrouprights-removegroup-all
310listgrouprights-addgroup-self
311listgrouprights-removegroup-self
312listgrouprights-addgroup-self-all
313listgrouprights-removegroup-self-all
314listgrants-summary
315trackingcategories-summary
316restricted-displaytitle-ignored-desc
317noindex-category-desc
318index-category-desc
319post-expand-template-inclusion-category-desc
320post-expand-template-argument-category-desc
321expensive-parserfunction-category-desc
322broken-file-category-desc
323hidden-category-category-desc
324emailuser-summary
325usermessage-summary
326usermessage-editor
327usermessage-template
328watchlist-summary
329log-name-create
330log-description-create
331sessionfailure-title
332changecontentmodel-legend
333log-name-contentmodel
334log-description-contentmodel
335protect-locked-blocked
336protect-locked-dblock
337protect-locked-access
338protect-level-autoconfirmed
339protect-level-sysop
340restriction-edit
341restriction-create
342restriction-upload
343undelete-summary
344contributions-summary
345whatlinkshere-summary
346unblock-summary
347block-actions
348block-expiry
349block-options
350block-reason
351block-target
352blocklist-summary
353block-log-flags-anononly
354block-log-flags-nocreate
355block-log-flags-noautoblock
356block-log-flags-noemail
357block-log-flags-nousertalk
358block-log-flags-angry-autoblock
359block-log-flags-hiddenname
360movepage-summary
361export-summary
362allmessages-filter-legend
363import-summary
364import-parse-failure
365accesskey-pt-userpage
366accesskey-pt-anonuserpage
367accesskey-pt-mytalk
368accesskey-pt-anontalk
369accesskey-pt-preferences
370accesskey-pt-watchlist
371accesskey-pt-mycontris
372accesskey-pt-anoncontribs
373accesskey-pt-login-private
374accesskey-pt-logout
375accesskey-ca-addsection
376accesskey-ca-protect
377accesskey-ca-unprotect
378accesskey-ca-delete
379accesskey-ca-undelete
380accesskey-ca-move
381accesskey-ca-watch
382accesskey-ca-unwatch
383accesskey-n-mainpage
384accesskey-feed-rss
385accesskey-feed-atom
386accesskey-t-contributions
387accesskey-t-emailuser
388accesskey-t-upload
389accesskey-ca-nstab-user
390accesskey-ca-nstab-media
391accesskey-ca-nstab-special
392accesskey-ca-nstab-project
393accesskey-ca-nstab-image
394accesskey-ca-nstab-mediawiki
395accesskey-ca-nstab-template
396accesskey-ca-nstab-help
397accesskey-ca-nstab-category
398accesskey-minoredit
399accesskey-save
400accesskey-publish
401accesskey-preview
402accesskey-diff
403accesskey-compareselectedversions
404accesskey-watch
405accesskey-summary
406accesskey-userrights-set
407accesskey-blockip-block
408accesskey-export
409accesskey-import
410tooltip-pt-userpage
411tooltip-pt-anonuserpage
412tooltip-pt-mytalk
413tooltip-pt-anontalk
414tooltip-pt-preferences
415tooltip-pt-mycontris
416tooltip-pt-anoncontribs
417tooltip-pt-login-private
418tooltip-pt-logout
419tooltip-ca-addsection
420tooltip-ca-protect
421tooltip-ca-unprotect
422tooltip-ca-delete
423tooltip-ca-undelete
424tooltip-ca-move
425tooltip-n-mainpage
426tooltip-feed-rss
427tooltip-feed-atom
428tooltip-t-contributions
429tooltip-t-emailuser
430tooltip-t-upload
431tooltip-ca-nstab-user
432tooltip-ca-nstab-media
433tooltip-ca-nstab-special
434tooltip-ca-nstab-project
435tooltip-ca-nstab-image
436tooltip-ca-nstab-mediawiki
437tooltip-ca-nstab-template
438tooltip-ca-nstab-help
439tooltip-ca-nstab-category
440tooltip-minoredit
441tooltip-save
442tooltip-publish
443tooltip-preview
444tooltip-compareselectedversions
445tooltip-recreate
446tooltip-summary
447group-autoconfirmed.css
448group-user.css
449group-bot.css
450group-sysop.css
451group-bureaucrat.css
452group-autoconfirmed.js
453group-user.js
454group-bot.js
455group-sysop.js
456group-bureaucrat.js
457pageinfo-header-basic
458pageinfo-header-edits
459pageinfo-header-restrictions
460pageinfo-header-properties
461pageinfo-robot-index
462pageinfo-robot-noindex
463pageinfo-category-info
464filedelete-current-unregistered
465filedelete-archive-read-only
466file-no-thumb-animation-gif
467newimages-summary
468video-dims
469monday-at
470tuesday-at
471wednesday-at
472thursday-at
473friday-at
474saturday-at
475sunday-at
476variantname-zh-hans
477variantname-zh-hant
478variantname-zh-cn
479variantname-zh-tw
480variantname-zh-hk
481variantname-zh-mo
482variantname-zh-sg
483variantname-zh-my
484variantname-gan-hans
485variantname-gan-hant
486variantname-gan
487variantname-sr-ec
488variantname-sr-el
489variantname-sr
490variantname-kk-kz
491variantname-kk-tr
492variantname-kk-cn
493variantname-kk-cyrl
494variantname-kk-latn
495variantname-kk-arab
496variantname-kk
497variantname-ku-arab
498variantname-ku-latn
499variantname-ku
500variantname-tg-cyrl
501variantname-tg-latn
502variantname-tg
503variantname-ike-cans
504variantname-ike-latn
505variantname-iu
506variantname-shi-tfng
507variantname-shi-latn
508variantname-shi
509variantname-uz
510variantname-uz-latn
511variantname-uz-cyrl
512variantname-crh
513variantname-crh-latn
514variantname-crh-cyrl
515size-terabytes
516size-petabytes
517size-exabytes
518size-zetabytes
519size-yottabytes
520size-pixel
521size-kilopixel
522size-megapixel
523size-gigapixel
524size-terapixel
525size-petapixel
526size-exapixel
527size-zetapixel
528size-yottapixel
529bitrate-bits
530bitrate-kilobits
531bitrate-megabits
532bitrate-gigabits
533bitrate-terabits
534bitrate-petabits
535bitrate-exabits
536bitrate-zetabits
537bitrate-yottabits
538editwatchlist-summary
539watchlistedit-clear-titles
540watchlistedit-clear-done
541watchlistedit-clear-removed
542watchlisttools-clear
543version-summary
544version-hooks
545version-license-not-found
546version-credits-not-found
547version-credits-summary
548version-db-mysql-url
549version-db-mariadb-url
550version-db-percona-url
551version-db-postgres-url
552version-db-oracle-url
553version-db-sqlite-url
554version-db-mssql-url
555redirect-text
556redirect-summary
557redirect-submit
558redirect-lookup
559redirect-value
560redirect-user
561redirect-page
562redirect-revision
563redirect-file
564redirect-logid
565redirect-not-exists
566redirect-not-numeric
567fileduplicatesearch-summary
568specialpages-summary
569specialpages-group-maintenance
570specialpages-group-other
571specialpages-group-login
572specialpages-group-changes
573specialpages-group-media
574specialpages-group-users
575specialpages-group-highuse
576specialpages-group-pages
577specialpages-group-pagetools
578specialpages-group-wiki
579specialpages-group-redirects
580specialpages-group-spam
581specialpages-group-developer
582tags-summary
583tag-filter-submit
584tag-mw-contentmodelchange
585tag-mw-contentmodelchange-description
586tag-mw-new-redirect
587tag-mw-new-redirect-description
588tag-mw-removed-redirect
589tag-mw-removed-redirect-description
590tag-mw-changed-redirect-target
591tag-mw-changed-redirect-target-description
592tag-mw-blank
593tag-mw-blank-description
594tag-mw-replace
595tag-mw-replace-description
596tag-mw-rollback
597tag-mw-rollback-description
598tag-mw-undo
599tag-mw-undo-description
600tags-activate-title
601tags-activate-question
602tags-activate-reason
603tags-activate-submit
604tags-deactivate-title
605tags-deactivate-question
606tags-deactivate-reason
607tags-deactivate-submit
608tags-edit-revision-selected
609tags-edit-logentry-selected
610tags-edit-revision-explanation
611tags-edit-logentry-explanation
612tags-edit-revision-legend
613tags-edit-logentry-legend
614tags-edit-revision-submit
615tags-edit-logentry-submit
616comparepages-summary
617diff-form-summary
618permanentlink-summary
619htmlform-no
620htmlform-date-placeholder
621htmlform-time-placeholder
622htmlform-datetime-placeholder
623htmlform-date-invalid
624htmlform-time-invalid
625htmlform-datetime-invalid
626htmlform-date-toolow
627htmlform-date-toohigh
628htmlform-time-toolow
629htmlform-time-toohigh
630htmlform-datetime-toolow
631htmlform-datetime-toohigh
632restore-count-revisions
633restore-count-files
634revdelete-content-hid
635revdelete-summary-hid
636revdelete-uname-hid
637revdelete-content-unhid
638revdelete-summary-unhid
639revdelete-uname-unhid
640log-name-managetags
641log-description-managetags
642log-name-tag
643log-description-tag
644patrol-log-auto
645api-error-badtoken
646api-error-emptypage
647api-error-unknownerror
648duration-seconds
649duration-minutes
650duration-hours
651duration-days
652duration-weeks
653duration-years
654duration-decades
655duration-centuries
656duration-millennia
657limitreport-cputime-value
658limitreport-walltime-value
659limitreport-ppvisitednodes-value
660limitreport-ppgeneratednodes-value
661limitreport-postexpandincludesize-value
662limitreport-templateargumentsize-value
663limitreport-expansiondepth-value
664limitreport-expensivefunctioncount-value
665limitreport-unstrip-depth-value
666limitreport-unstrip-size-value
667log-name-pagelang
668log-description-pagelang
669mediastatistics-summary
670mediastatistics-table-mimetype
671mediastatistics-table-extensions
672mediastatistics-table-count
673mediastatistics-table-totalbytes
674mediastatistics-header-unknown
675mediastatistics-header-bitmap
676mediastatistics-header-drawing
677mediastatistics-header-audio
678mediastatistics-header-video
679mediastatistics-header-multimedia
680mediastatistics-header-office
681mediastatistics-header-text
682mediastatistics-header-executable
683mediastatistics-header-archive
684mediastatistics-header-3d
685mediastatistics-header-total
686headline-anchor-title
687authmanager-create-from-login
688authform-nosession-login
689authform-nosession-signup
690authpage-cannot-login
691authpage-cannot-login-continue
692authpage-cannot-create
693authpage-cannot-create-continue
694authpage-cannot-link
695authpage-cannot-link-continue
696changecredentials-submit
697changecredentials-invalidsubpage
698changecredentials-success
699removecredentials-submit
700removecredentials-invalidsubpage
701removecredentials-success
702passwordpolicies-summary
703passwordpolicies-policy-minimalpasswordlength
704passwordpolicies-policy-minimumpasswordlengthtologin
705passwordpolicies-policy-passwordcannotmatchusername
706passwordpolicies-policy-passwordcannotmatchblacklist
707passwordpolicies-policy-maximalpasswordlength
708passwordpolicies-policy-passwordcannotbepopular
709passwordpolicies-policy-passwordnotinlargeblacklist
710passwordpolicies-policyflag-forcechange
711passwordpolicies-policyflag-suggestchangeonlogin

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald TranscriptMay 27 2019, 3:03 PM
Amire80 added a project: translatewiki.net.

Good initiative. I occasionally remove unused messages it when I happen to find them, but I don't hunt them systematically.

Two comments:

  1. Whoever removes them from core itself, should also check that they don't appear anywhere in translatewiki configuration as optional or ignored, or in the "most important" messages list. The likeliest files where they can appear in the translatewiki repo are probably groups/MediaWiki/MediaWiki.yaml and groups/MediaWiki/wikimedia-mostused-2015.txt (there's also groups/MediaWiki/wikimedia-mostused-2011.txt, but it probably shouldn't be touched).
  2. There's a caveat: it's possible that some of these are not used in core, but are used in some skins or extensions. In this case they should probably moved from core to the relevant repos. It's probably worth checking at https://codesearch.wmflabs.org before removing.
Meno25 added a subscriber: Meno25.May 31 2019, 11:30 AM
Aklapper renamed this task from Hunt and remove unsed i18n messages in mediawiki core to Hunt and remove unused i18n messages in mediawiki core.Jun 30 2019, 9:34 PM

Change 532072 had a related patch set uploaded (by Ladsgroup; owner: Ladsgroup):
[mediawiki/core@master] Remove mediastatistics i18n message

https://gerrit.wikimedia.org/r/532072

We definitely should have a test that checks for this. Any existing cases can be whitelisted for the time being, but it could at least stop new cases from being inadvertently added.

Given that sometimes we dynamically create message name strings, one way to do this would be to have PHPUnit tests generate a list of all the messages that are used over the course of the test. We could then aggregate all the names of messages used in all the tests and flag all message keys that were never used. I'm not sure how to architect this exactly, but it shouldn't be terribly difficult. (Of course, anyone who adds a new message will then have to add a new test that uses the message, but they should be doing that anyway!)

We definitely should have a test that checks for this. Any existing cases can be whitelisted for the time being, but it could at least stop new cases from being inadvertently added.

Yes, that'd be great.

Given that sometimes we dynamically create message name strings, one way to do this would be to have PHPUnit tests generate a list of all the messages that are used over the course of the test.

The idea would be good, but:

(Of course, anyone who adds a new message will then have to add a new test that uses the message, but they should be doing that anyway!)

I bet there are tons of messages not used in any test.

Another proposal is to have a smarter alternative to a lame grep that can collect all of the strings passed to wfMessage and the like. However, there's a trade-off between speed (e.g. simple grep like in the task description) and precision (e.g. static analysis, which could also read variable values etc.).
We could also use more precise regexps, without having to rely on static analysis. Of course, this would still fail for legoed/dynamic messages, but IMHO that's good. Adding at least a comment with the possible keys is not only something that devs should be doing anyway, but that we can also enforce brutally, without asking too much (like writing a test, which could end up being a bad test).

I think best would be to require annotating all dynamic message uses (near the place where they are used) with a specially formatted comment. We may need to support patterns like logentry-*-* unless we are okay having these comments somewhere else than near the wfMessage call.

Nemo_bis triaged this task as Low priority.Mon, Aug 26, 2:51 PM
Anomie added a subscriber: Anomie.Tue, Aug 27, 7:31 PM

I took a quick look at the ones containing "api".

apisandbox
apisandbox-summary

Used by SpecialApiSandbox via SpecialPage::getDescription() and SpecialPage::outputHeader().

apihelp-summary

Same for SpecialApiHelp. Although in this case I don't know offhand of a way to actually cause it to be used.

BTW, your grep seems to have missed the corresponding "apihelp" message.

apisandbox-api-disabled

Legitimate, missed for removal in rMW225b462a50b5: Drop deprecated EnableAPI and EnableWriteAPI settings. Looks like it was being removed in PS5 but the removal got lost in a rebase in PS7.

api-error-badtoken
api-error-emptypage
api-error-unknownerror

Added originally in rMWd683e249756e: Remove API error message duplication from Translate and UploadWizard into core., which seems self-explanatory. Looks like Translate still uses all three. api-error-badtoken seems to have been removed from UploadWizard in rEUWIbaa7dadef8b3: Use parsed error messages straight from API; if the other two were ever used, I can't find it.

I think best would be to require annotating all dynamic message uses (near the place where they are used) with a specially formatted comment. We may need to support patterns like logentry-*-* unless we are okay having these comments somewhere else than near the wfMessage call.

The unwritten convention is VE code is something along the lines of:

// The following messages are used here:
// * visualeditor-mweditmodewt-popup-body
// * visualeditor-mweditmodeve-popup-body
$content = $( '<p>' ).text( mw.msg( prefix + '-popup-body' ) );

With a well defined format, we could probably write an eslint rule for this.

Change 532818 had a related patch set uploaded (by Jforrester; owner: Jforrester):
[mediawiki/core@master] Follow-up 225b462: Remove 'apisandbox-api-disabled' i18n too

https://gerrit.wikimedia.org/r/532818

Change 532818 merged by jenkins-bot:
[mediawiki/core@master] Follow-up 225b462: Remove 'apisandbox-api-disabled' i18n too

https://gerrit.wikimedia.org/r/532818

With a well defined format, we could probably write an eslint rule for this.

Is the format you showed simple enough and well defined? I'd imagine it would need a code that find a comment block that matches /the following message.*:/i and then take the stuff after the colon, explode on whitespace and strip lonely * and - to allow multiline lists, and strip trailing , to allow inline lists.

https://codesearch.wmflabs.org/search/?q=(%2F%2F%7C%5C*).%2Bthe%20following%20message&i=fosho&files=(js%7Cphp)%24&repos= confirms this pattern is frequently used and likely without false positives.

Change 532965 had a related patch set uploaded (by Bartosz Dziewoński; owner: Bartosz Dziewoński):
[mediawiki/core@master] Remove unused localisation messages for old edit toolbar

https://gerrit.wikimedia.org/r/532965

Change 532969 had a related patch set uploaded (by Bartosz Dziewoński; owner: Bartosz Dziewoński):
[mediawiki/core@master] Remove unused localisation messages 'ascending_abbrev', 'descending_abbrev'

https://gerrit.wikimedia.org/r/532969

Change 532971 had a related patch set uploaded (by Bartosz Dziewoński; owner: Bartosz Dziewoński):
[mediawiki/core@master] Remove unused localisation message 'wlshowlast'

https://gerrit.wikimedia.org/r/532971

I looked briefly at a few areas I'm familiar with.

Your list has some false positives:

  • These messages are used in includes/pager/TablePager.php and documented in code comments there:
    • table_pager_next
    • table_pager_prev
    • table_pager_first
    • table_pager_last
  • These messages are used in includes/specialpage/ChangesListSpecialPage.php and documented in code comments there:
    • wlshowhideminor
    • wlshowhidebots
    • wlshowhideliu
    • wlshowhideanons
    • wlshowhidepatr
    • wlshowhidemine
    • wlshowhidecategorization

With a well defined format, we could probably write an eslint rule for this.

Is the format you showed simple enough and well defined? I'd imagine it would need a code that find a comment block that matches /the following message.*:/i and then take the stuff after the colon, explode on whitespace and strip lonely * and - to allow multiline lists, and strip trailing , to allow inline lists.
https://codesearch.wmflabs.org/search/?q=(%2F%2F%7C%5C*).%2Bthe%20following%20message&i=fosho&files=(js%7Cphp)%24&repos= confirms this pattern is frequently used and likely without false positives.

Perhaps, although we may want to make it more obviously structured, by using a magic word before the list (e.g. // messages-keys-used) or in it (e.g. // * MSG:visualeditor-mweditmodewt-popup-body). This would make it more obvious the devs that the format should not be changed, and would make it a bit easier to parse.

Change 532969 merged by jenkins-bot:
[mediawiki/core@master] Remove unused localisation messages 'ascending_abbrev', 'descending_abbrev'

https://gerrit.wikimedia.org/r/532969

Change 532971 merged by jenkins-bot:
[mediawiki/core@master] Remove unused localisation message 'wlshowlast'

https://gerrit.wikimedia.org/r/532971

Change 532965 merged by jenkins-bot:
[mediawiki/core@master] Remove unused localisation messages for old edit toolbar

https://gerrit.wikimedia.org/r/532965