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

Related Objects

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

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.
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.

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 {baa7dadef8}; 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

T235502 creates an eslint plugin to ensure that message building code in JS is commented with at least 2 'msg:' items. As this is not a runtime analysis we can't know exactly what messages are being used, but ensuring some documentation should be an improvement.

I was thinking about this again today. I'd love to have something similar to the eslint plugin for PHP code. It wouldn't have to be perfect: for a start, just report every message key who doesn't appear when grepping the codebase, regardless of where it actually appears. This will likely have many false negatives, but I don't think it will have (many) false positive, as long as we require that every message be documented in a comment like in JS (and no need to enforce a specific syntax just yet). It could be a non-voting job and later be made voting. Better than nothing, I'd say.

I looked at it from a different perspective. I checked every "unused" i18n message in git history to find the last seen in code. This resulted in ~80 messages possibly being used explicitly up to some point. That commit helps figure out if the message became unused or became implicit (and where) so you can improve grep-ability easily.

messagelast seen
sitetitleee5d9224657b0104af33bf79228dabbe5dd9987a
sitesubtitlefb33b959dba0c971ea1f32319fcca71a0ffd333b
history_short16f1ee731909ae944b12ed03a7a6e5c39a73410c
mediawikipagefb33b959dba0c971ea1f32319fcca71a0ffd333b
templatepagefb33b959dba0c971ea1f32319fcca71a0ffd333b
viewhelppagefb33b959dba0c971ea1f32319fcca71a0ffd333b
youhavenewmessagesmultieb87d1d2bcb765389972c68c7f33c115af53b316
newtalkseparatoreb87d1d2bcb765389972c68c7f33c115af53b316
nstab-talkdf041d9f1e2b9dea6d99f11e94a59d8a632f2125
databaseerror-textcl00bee029718f3215396e984d04b9450bc3872503
databaseerror-query00bee029718f3215396e984d04b9450bc3872503
databaseerror-function00bee029718f3215396e984d04b9450bc3872503
databaseerror-error00bee029718f3215396e984d04b9450bc3872503
filecopyerror19818237553571378ee298a16f47cec06f82bade
filerenameerror19818237553571378ee298a16f47cec06f82bade
directorynotreadableerror752ad738e91ad1ba2766dc2095f7255bef80dcbe
title-invalid-interwiki9b1f8b4ca331e45c9718097af33f46416c7911f2
exception-nologin-text-manuald0439af89f6b254cea09b3773ab139f04f81a97d
logout-failed7171488c71e1f55b8451a99d636ff10ca419d081
password-change-forbidden854a462dc0aef59e1c26c057792b8a9214449af5
externaldberror854a462dc0aef59e1c26c057792b8a9214449af5
createacct-email-ph854a462dc0aef59e1c26c057792b8a9214449af5
createacct-another-email-ph854a462dc0aef59e1c26c057792b8a9214449af5
createacct-submit854a462dc0aef59e1c26c057792b8a9214449af5
createacct-another-submit854a462dc0aef59e1c26c057792b8a9214449af5
password-name-match7a17473dd10240664e9276bb1036ece30e625fff
passwordremindertitle854a462dc0aef59e1c26c057792b8a9214449af5
passwordremindertext854a462dc0aef59e1c26c057792b8a9214449af5
login-abort-genericcfb62c605f55553db1588bd1b81f8c2ae06e81b2
login-migrated-genericcfb62c605f55553db1588bd1b81f8c2ae06e81b2
pt-login-button854a462dc0aef59e1c26c057792b8a9214449af5
resetpass_announcecfb62c605f55553db1588bd1b81f8c2ae06e81b2
resetpass_text854a462dc0aef59e1c26c057792b8a9214449af5
resetpass_header854a462dc0aef59e1c26c057792b8a9214449af5
oldpassword854a462dc0aef59e1c26c057792b8a9214449af5
resetpass_submit854a462dc0aef59e1c26c057792b8a9214449af5
changepassword-success854a462dc0aef59e1c26c057792b8a9214449af5
changepassword-throttled854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-submit-loggedin854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-submit-cancel854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-wrong-oldpass854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-recycled854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-temp-password854a462dc0aef59e1c26c057792b8a9214449af5
resetpass-abort-generic854a462dc0aef59e1c26c057792b8a9214449af5
passwordreset-domain854a462dc0aef59e1c26c057792b8a9214449af5
changeemail-password854a462dc0aef59e1c26c057792b8a9214449af5
changeemail-throttled854a462dc0aef59e1c26c057792b8a9214449af5
sig-textcfa70ea6d003ae69d52aad67a543912449c71e00
editpage-cannot-use-custom-model495323c0635edbacd253cbe58d6e4e4e9bba4603
createaccount-hook-abortedcfb62c605f55553db1588bd1b81f8c2ae06e81b2
mergehistory-warning-redirect-not-createdfb58d390eaffe7d374d97ecb0a1a55871b9be16a
notextmatches8c23482db824c9a14d416729adef4fb27d16f181
search-relatedarticlea82024c637bfcc4f000cb2e947d97dcdcdb5f791
searchrelateda82024c637bfcc4f000cb2e947d97dcdcdb5f791
prefs-editing65b40721bd11827ceec07ae02c0c6050a2b59aa4
prefs-help-watchlist-token2dcf3eb793a0df81e4c1a11bdec236268df0d6e55
rc-enhanced-expand07aef8f753be6b0860a855eeecb7f0f1a66ce9d9
rc-enhanced-hide07aef8f753be6b0860a855eeecb7f0f1a66ce9d9
img-auth-public5252460f1dad0cb50a72d0e98b8c03b401ddea24
http-invalid-scheme928f7077319a089287046a00aa6ffb399fc11839
http-read-error928f7077319a089287046a00aa6ffb399fc11839
withoutinterwiki-legend944fc8152d46baa364177ba85e6a1fcee4bdac9c
movethispagefb33b959dba0c971ea1f32319fcca71a0ffd333b
cachedspecial-viewing-cached-tsab9ecc0268843fdaac350271b70b1c9b53437d35
cachedspecial-refresh-nowab9ecc0268843fdaac350271b70b1c9b53437d35
watchthispagefb33b959dba0c971ea1f32319fcca71a0ffd333b
unwatchthispagefb33b959dba0c971ea1f32319fcca71a0ffd333b
xffblockreason1eaf65d0a5f551216668a9e434ce323f0ba7dafe
bad-target-model495323c0635edbacd253cbe58d6e4e4e9bba4603
allmessages-filter-legendd0c31ac98888876453c75281c03fdf6db65c00e8
djvu_no_xml71107e750250bf120b4b186f63da47301abc81af
tooltip-p-lang05224f0aa381858229cd2c00f2eca96f057eea4b
tooltip-p-navigationdf041d9f1e2b9dea6d99f11e94a59d8a632f2125
tooltip-p-tbdf041d9f1e2b9dea6d99f11e94a59d8a632f2125
noimages1f49b66c6f89d56dee457196296a7869f0f7475d
watchlistedit-clear-done6aa89efb5a8a743840f106f0ac9782192b0cd6cd
watchlistedit-clear-removed6aa89efb5a8a743840f106f0ac9782192b0cd6cd
watchlisttools-clearcee296244772e6e6b3d820539a1155f9e4a1cf18
tag-filter-submita78a3f28a21f6e8e7886e0cf1c62b423503b5d28
api-error-badtokene8d7173e9f3215f06c65f7e329152066f6da92ce
api-error-emptypagee8d7173e9f3215f06c65f7e329152066f6da92ce
api-error-unknownerrore8d7173e9f3215f06c65f7e329152066f6da92ce
limitreport-ppgeneratednodese149a38d4f7b924e1928e5024eb56f6883c5cc5a
headline-anchor-title5205405385397782b5288b429f49c9d8c97ac6c6
authmanager-create-from-login9bb2875e2eb7e5deeff7b6647616eb279906b27b

Running the code for this took a whole day 😅

Change 774005 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Drop limitreport-ppgeneratednodes message

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

Change 774006 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Drop djvu_no_xml i18n message

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

Change 774007 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Drop two unused Enhanced RC i18n messages

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

Change 774006 merged by jenkins-bot:

[mediawiki/core@master] Drop djvu_no_xml i18n message

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

Change 774007 merged by jenkins-bot:

[mediawiki/core@master] Drop two unused Enhanced RC i18n messages

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

Change 776296 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Drop unused directorynotreadableerror message

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

Change 776297 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Remove unsued youhavenewmessagesmulti and newtalkseparator messages

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

Change 776296 merged by jenkins-bot:

[mediawiki/core@master] Drop unused directorynotreadableerror message

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

Change 776297 merged by jenkins-bot:

[mediawiki/core@master] Remove unsued youhavenewmessagesmulti and newtalkseparator messages

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

Hi @Tgr There are 24 unused i18n messages found that were referenced last in 854a462dc0aef59e1c26c057792b8a9214449af5 (removing pre-auth manager code). See T224429#7809087. Can you take a look and remove the ones that you're sure not needed anymore?

Change 778677 had a related patch set uploaded (by Ladsgroup; author: Amir Sarabadani):

[mediawiki/core@master] Remove unused i18n message of removed CacheHelper classes

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

Change 778677 merged by jenkins-bot:

[mediawiki/core@master] Remove unused i18n message of removed CacheHelper classes

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

Hi @Tgr There are 24 unused i18n messages found that were referenced last in 854a462dc0aef59e1c26c057792b8a9214449af5 (removing pre-auth manager code). See T224429#7809087. Can you take a look and remove the ones that you're sure not needed anymore?

Something seems wrong with the script (git log --grep instead of git log -S?), those messages aren't removed in that patch.

It was git log -S, Are you saying it should be git log --grep? I will double check it in the weekend.

@Tgr Are you sure they are not deleted in that patch? for example. createacct-submit was in includes/templates/Usercreate.php and the whole file got deleted in that patch (without re-adding):

amir@amir-ThinkPad-P1-Gen-3:~/core$ git diff 854a462dc0aef59e1c26c057792b8a9214449af5^ 854a462dc0aef59e1c26c057792b8a9214449af5 | grep -i createacct-submit
-					$this->getMsg( $this->data['loggedin'] ? 'createacct-another-submit' : 'createacct-submit' ),

Unless you made it dynamic loading.

createacct-submit is used on https://en.wikipedia.org/wiki/Special:CreateAccount?uselang=qqx right now. The message key comes from here: https://gerrit.wikimedia.org/g/mediawiki/core/+/7ab4f666be6dd31ed552fc4575c7b99571407857/includes/specialpage/LoginSignupSpecialPage.php#869 and it's indeed constructed dynamically. It's probably similar for many other messages used on login/signup pages.

Change 774005 merged by jenkins-bot:

[mediawiki/core@master] Drop limitreport-ppgeneratednodes message

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

Change 805788 had a related patch set uploaded (by Esanders; author: Esanders):

[mediawiki/core@master] LoginSignupSpecialPage: Document used message keys

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

Change 805788 merged by jenkins-bot:

[mediawiki/core@master] LoginSignupSpecialPage: Document used message keys

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

Change 805878 had a related patch set uploaded (by Esanders; author: Esanders):

[mediawiki/core@master] SkinTemplate: Document more nstab- message keys

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

Change 805878 merged by jenkins-bot:

[mediawiki/core@master] SkinTemplate: Document more nstab- message keys

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

I'm working on a phan plugin to detect dyamic message usage in PHP, see subtask. There are a few details that need to be hashed out in T338091#8900888. Suggestions are welcome :)

I wrote a script to search the current codebase for existing messages, either in the code or documentation:

https://gerrit.wikimedia.org/r/c/VisualEditor/VisualEditor/+/971262

It can be run on any extension with a bit of tweaking. It would be nice to make it generic and configurable, then it can be part of CI.

Also for missing messages, checking if they ever appeared in the git history, as done by above, would be useful.

Change 532072 abandoned by Ladsgroup:

[mediawiki/core@master] Remove mediastatistics i18n message

Reason:

The i18n is still needed

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

Change 1005616 had a related patch set uploaded (by Umherirrender; author: Umherirrender):

[mediawiki/core@master] Remove unused message filehist-filesize

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

Change 1005616 merged by jenkins-bot:

[mediawiki/core@master] Remove unused message filehist-filesize

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