Add RuboCop rule Gettext/StaticIdentifier
What does this MR do and why?
This MR adds a new Gettext/StaticIdentifier to resolve #408337 (closed).
# bad
_("Hi #{name}")
_('Hi %{name}' % { name: 'Luki' })
_(format('Hi %{name}', name: 'Luki'))
# good
_('Hi %{name}') % { name: 'Luki' }
format(_('Hi %{name}', name: 'Luki'))
# also good
var = "Hi"
_(var)
_(some_method_call)
_(CONST)
With this MR the Cop/RubyInterpolationInTranslation is now no longer necessary as Gettext/StaticIdentifier also catches the interpolation case.
Offenses
The following offenses were caught by RuboCop.
Ruby (now TODOs)
Offenses:
app/graphql/types/project_type.rb:691:58: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
Gitlab::Utils::ErrorMessage.to_user_facing(_(format('You must %s before using Security features.', add_file_docs_link.html_safe)).html_safe)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/models/integrations/apple_app_store.rb:56:12: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
s_(format("To get started, see the <a href='%{url}' target='_blank'>integration documentation</a> for instructions on how to generate App Store Connect credentials, and how to use this integration.", url: Rails.application.routes.url_helpers.help_page_url('user/project/integrations/apple_app_store'))).html_safe
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/models/integrations/confluence.rb:36:11: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
'ConfluenceService|Your GitLab wiki is still available at %{wiki_link}. To re-enable the link to the GitLab wiki, disable this integration.' % ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/models/integrations/google_play.rb:47:12: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
s_(format("To generate a Google Play service account key and use this integration, see the <a href='%{url}' target='_blank'>integration documentation</a>.", url: Rails.application.routes.url_helpers.help_page_url('user/project/integrations/google_play'))).html_safe
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/import/fogbugz_service.rb:41:12: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
s_("Fogbugz|Fogbugz import failed due to an error: %{error}" % { error: e }),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/issuable_links/create_service.rb:116:9: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_('%{issuable}(s) already assigned' % { issuable: target_issuable_type.capitalize })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/issuable_links/create_service.rb:120:9: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_('No matching %{issuable} found. Make sure that you are adding a valid %{issuable} URL.' % { issuable: target_issuable_type })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/issues/set_crm_contacts_service.rb:152:56: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
ServiceResponse.error(payload: issue, message: _("You can only add up to %{max_contacts} contacts at one time" % { max_contacts: MAX_ADDITIONAL_CONTACTS }))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/projects/create_from_template_service.rb:39:44: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
project.errors.add(:template_name, _("'%{template_name}' is unknown or invalid" % { template_name: template_name }))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/security/ci_configuration/base_create_service.rb:23:21: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_(format('You must %s before using Security features.', docs_link.html_safe)).html_safe)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/users/banned_user_base_service.rb:27:15: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
error(_("You cannot %{action} %{state} users." % { action: action.to_s, state: user.state }), :forbidden)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/users/banned_user_base_service.rb:35:15: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
error(_("You are not allowed to %{action} a user" % { action: action.to_s }), :forbidden)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/work_items/widgets/hierarchy_service/base_service.rb:59:19: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
error(_("One or more arguments are invalid: %{args}." % { args: params.keys.to_sentence }))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/controllers/admin/licenses_controller.rb:25:20: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_('The license was successfully uploaded and will be active from %{starts_at}. You can see the details below.' % { starts_at: @license.starts_at })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/controllers/subscriptions/groups_controller.rb:20:22: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_('Welcome to GitLab, %{first_name}!' % { first_name: current_user.first_name })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/controllers/subscriptions/groups_controller.rb:22:22: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_('Subscription successfully applied to "%{group_name}"' % { group_name: @group.name })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/mailers/ee/emails/admin_notification.rb:19:26: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
@ban_scope = _('your group (%{group_name})' % { group_name: group.name })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/mailers/emails/namespace_storage_usage_mailer.rb:22:21: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
subject: s_("NamespaceStorage|Action required: Storage has been exceeded for %{namespace_name}" % { namespace_name: namespace.name })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/mailers/emails/namespace_storage_usage_mailer.rb:36:21: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
subject: s_("NamespaceStorage|You have used %{used_storage_percentage}%% of the storage quota for %{namespace_name}" % ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/models/ee/member.rb:193:9: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_("email '%{email}' is not a verified email." % { email: user.email })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/models/integrations/github.rb:52:10: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
s_("GithubIntegration|This requires mirroring your GitHub repository to this project. %{docs_link}" % { docs_link: docs_link }).html_safe
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/ee/projects/create_from_template_service.rb:42:32: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_("%{template_project_id} is unknown or invalid" % { template_project_id: template_project_id }))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/ee/projects/create_from_template_service.rb:44:48: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
project.errors.add(:template_name, _("'%{template_name}' is unknown or invalid" % { template_name: template_name }))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/security/security_orchestration_policies/policy_configuration_validation_service.rb:21:15: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
_("Policy management project does not have any policies in %{policy_path}" % { ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/timebox/rollup_report_service.rb:54:47: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
when :unsupported_type then _(format('%{timebox_type} does not support burnup charts', timebox_type: timebox_type))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/timebox/rollup_report_service.rb:55:47: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
when :missing_dates then _(format('%{timebox_type} must have a start and due date', timebox_type: timebox_type))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/timebox_report_service.rb:64:45: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
when :unsupported_type then _('%{timebox_type} does not support burnup charts' % { timebox_type: timebox_type })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/app/services/timebox_report_service.rb:65:45: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
when :missing_dates then _('%{timebox_type} must have a start and due date' % { timebox_type: timebox_type })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/spec/controllers/groups/security/policies_controller_spec.rb:156:45: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
expect(flash[:alert]).to eq(_("Policy management project does not have any policies in %{policy_path}" % { ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ee/spec/features/registrations/identity_verification_spec.rb:217:36: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method _(...).
expect(page).to have_content(_(format('Welcome to GitLab, %{first_name}!', first_name: user.first_name)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/gitlab/github_import/settings.rb:34:23: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
label: s_(format("GitHubImport|%{text}", text: data[:label])),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/gitlab/github_import/settings.rb:35:25: C: Gettext/StaticIdentifier: Ensure to pass static strings to translation method s_(...).
details: s_(format("GitHubImport|%{text}", text: data[:details]))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30579 files inspected, 32 offenses detected
HAML ✅
Fixed in !118369 (merged).
app/views/projects/runners/edit.html.haml:7 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `s_(...)`.
app/views/shared/integrations/prometheus/_metrics.html.haml:36 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `s_(...)`.
app/views/shared/runners/_runner_details.html.haml:5 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `s_(...)`.
ee/app/views/admin/geo/shared/_replication_nav.haml:5 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `_(...)`.
ee/app/views/admin/licenses/_license_status.html.haml:13 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `_(...)`.
ee/app/views/subscriptions/groups/edit.html.haml:12 [W] RuboCop: Gettext/StaticString: Ensure to pass static strings to translation method `_(...)`.
2058 files inspected, 6 lints detected
ERB ✅ (undetected by RuboCop nor HAML lint)
Fixed in !118369 (merged).
$ rg '_\([^)]+? % .+?\)' -t erb app/views/ ee/app/views/
app/views/devise/mailer/user_admin_approval.text.erb
5:<%= _('Your username is %{username}.' % { username: @resource.username }) %>
7:<%= _('Your sign-in page is %{url}.' % { url: Gitlab.config.gitlab.url }) %>
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Edited by Peter Leitzen