-
-
Notifications
You must be signed in to change notification settings - Fork 411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to prevent calls outside of I18n context by setting locale to false
#471
Conversation
I like the plan. I believe those kind of mistakes are easy to do and having protection around it in the framework. |
Activerecord already does validation messages without having to specify the message like this. See section 4.5 here: https://guides.rubyonrails.org/i18n.html |
This is not about Active Record. It is about any code that calls Things like:
|
Define these as class methods instead so they’re evaluated on call rather than on compilation. I am loathe to further complicate or add features to the i18n codebase. |
Yes, there is way to do this properly. The point here is not to allow something that was impossible before, but to eagerly catch programmers mistakes. |
This is not a code to allow strange usage of I18n. It is a feature to direct people with less experience on how Ruby and I18n work to the right direction. This came out from one real application where people called I understand and agree with the hesitation to add new feature to this codebase, but I believe this one will help many experienced and non-experienced developers to catch real problems earlier. @radar can you please reconsider in this optic? |
@rafaelfranca I can definitely see the advantages of that. I think setting One last thing: I do wonder if the Maybe we could make that exception's default error message clearer? Something like "I18n.locale is set to false, but must be configured to be a locale symbol. <list available locales here?>". I am not sure entirely. What do you think would be useful error message to display to a junior developer? I think "No locale specified" is a good start, but we could probably do a better "Developer UX" here. |
This was the image we used in our custom backend:
The context on our implementation is different so I don't think this message fully apply to this patch. When you do
We can complete the message with explanation on how to solve some of those cases and how to opt-out the protection in case you really want to use translated text when loading the code. |
I indeed didn't port the exception message as is, because it makes some assuptions about the default locale etc, that might not hold in raw I'll try to come up with something better though. |
c6dafe3
to
226342b
Compare
I just updated the exception name and error message, let me know how it looks. |
@@ -204,7 +207,9 @@ def translate!(key, options = EMPTY_HASH) | |||
alias :t! :translate! | |||
|
|||
# Returns true if a translation exists for a given key, otherwise returns false. | |||
def exists?(key, locale = config.locale) | |||
def exists?(key, _locale = nil, locale: _locale) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also added this. While crafting the error message I figured that exists?
was inconsistent with the others, as it would take a locale as second param instead of a named one.
Having all methods accepting it as named param allows for a more insightful error message.
lib/i18n/exceptions.rb
Outdated
class Disabled < ArgumentError | ||
def initialize(method) | ||
super(<<~MESSAGE) | ||
I18n.#{method} is currently disabled, likely because your application is still in it's loading phase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I18n.#{method} is currently disabled, likely because your application is still in it's loading phase. | |
I18n.#{method} is currently disabled, likely because your application is still in its loading phase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed. Thanks!
226342b
to
5eeaad7
Compare
Error message looks great. Thanks for taking the time to update that. I will merge this now, and do a new release tomorrow AM (Eastern Australian time) |
Related ruby-i18n#501, ruby-i18n#500, and ruby-i18n#471. In Ruby 3.0, the behavior of `I18n.t("key", { value: "foo" })` has silently changed to `{ value: "foo" }` is no longer regarded as `**options` but regarded as noop slpat arguments `*`. https://github.com/ruby-i18n/i18n/blob/4709391dceab9096d5988576f93935843023a6ef/lib/i18n.rb#L195 So allowing (discarding) noop arguments will be more harmful in Ruby 3.0. This removes the allowing noop arguments, it makes `{ value: "foo" }` will raise ArgumentError instead of silently ignored the options.
Related #501, #500, and #471. In Ruby 3.0, the behavior of `I18n.t("key", { value: "foo" })` has silently changed to `{ value: "foo" }` is no longer regarded as `**options` but regarded as noop slpat arguments `*`. https://github.com/ruby-i18n/i18n/blob/4709391dceab9096d5988576f93935843023a6ef/lib/i18n.rb#L195 So allowing (discarding) noop arguments will be more harmful in Ruby 3.0. This removes the allowing noop arguments, it makes `{ value: "foo" }` will raise ArgumentError instead of silently ignored the options.
Context
We recently discovered a common error pattern in our application.
I18n.t
and similar API would get called during the boot phase, and the result stored in constant or similar class level variables. e.g.:Of course this is a simple programming mistake, but I believe it could be prevented with a relatively simple change.
Solution
By allowing frameworks like Rails to set
I18n.locale = false
early in the boot phase, and whenever they load code, it could catch this type of mistake early.That safety is totally opt-in, so it wouldn't break existing usage.
Also if for some reason you need to access a translation during those phases, you can explicitly specify the locale you want:
Alternative solution
For now we've implemented a similar feature by simply setting a fake I18n backend. However since the locale selection is done inside
I18n
itself, the backend can't know wether the locale was passed explicitly or not, so it's hard to opt-out of that safety in the few case you need it.cc @rafaelfranca
@radar what do you think?