ruby-i18n/i18n

[BUG] missing translation when i18n.translate is used inside model's validate method

Closed this issue · 3 comments

What I tried to do

Added an error custom message for model validate. Tried giving the yaml's string path, symbolized lookup, etc. The translation string is not loaded during initialization.

What I expected to happen

The string to be correctly found.

What actually happened

I get a translation missing exception. While rails server is initialized, the string cannot be found. Then the result is cached and the translation is missing whenever the model's exception is thrown. If I run I18n.t('activerecord.errors.custom.uniqueness.error')
in a rails console (example taken from the code block below) the translation is found correctly.
It seems that when i18n is used within a model's validate method, i18n has not yet loaded the strings correctly and cannot find any strings (?).

Versions of i18n, rails, and anything else you think is necessary

rails version: 6.1.6.1
rails-i18n: 6.0.0


Small code example:

class User < ApplicationRecord
  validates :email, uniqueness: { message: I18n.t('activerecord.errors.custom.uniqueness.error') }
end

Bonus points for providing an application or a small code example which reproduces the issue.

Thanks! ❤️

Unable to reproduce following the code example (though this looks like it would be an issue with rails, not with i18n - the error message that calls through to I18n is put together here).

You may want to change the approach slightly, due to how the locale may be handled for the message (though it sounds like you have taken a couple approaches to this, for context see rails/rails#40102 (comment)). Switching to a symbol should help guide where to add the key itself, eg:

class User < ApplicationRecord
  validates :email, uniqueness: { message: :custom_uniqueness_error }
end
> u2 = User.first.dup
> u2.valid?
=> false
> u2.errors.full_messages
=> ["User translation missing: en.activerecord.errors.models.user.attributes.email.custom_uniqueness_error"]

Actually the problem was solved using a Proc. The translation does not get evaluated during initialisation and thus when i18n has loaded all custom locale yamls, whenever the message needs to get evaluated it can access the translation.

class User < ApplicationRecord
  validates :email, uniqueness: { message:-> (object, data) {
     I18n.t('activerecord.errors.custom.uniqueness.error') 
  }
end

I did also try the symbol with scope approach beforehand with no luck. Don't know if it's actually an issue with I18n or rails. From my understanding during initialisation, I18n hasn't loaded all the custom locale yamls, only the defaults. Thus when the message gets evaluated during initialisation, it cannot find the path instructed. Don't know if this can be changed somehow. I did try eager loading the server but did not help.

Small edit for the comment mentioned:
Adding a different path than the one I want to use indeed works, as the symbol gets evaluated every time it is being used, which has its own performance drawbacks though and is not optimal for my usecase.

radar commented

Better way to do this would be to override the locale specific message for that field. I forget the exact syntax and I’m on my phone right now, but I think it’s something like:

active_record.models.user.errors.email.validation

the i18n guide for rails I believe includes examples for this use case