Suppose I have a class with a handy validation like:
User < ActiveRecord::Base
validates :username, :format => {/regex/}, :message => :name_format
end
In this case, I can use i18n
to make the error message translatable, by including the following in my /config/locals/en.yml
:
en:
activerecord:
errors:
models:
user:
attributes:
username:
name_format: 'has the way-wrong format, bro!'
This is fine and generally really handy.
My question is: What happens when I have subclasses that inherit from User:
UserSubclassOne < User
# extra stuff
end
UserSubclassTwo < User
# extra stuff
end
...
UserSubclassEnn < User
# extra stuff
end
Now the problem is that Rails can't find the translation user_subclass_one.attributes.username.name_format
.
It complains:
translation missing:
en.activerecord.errors.models.user_subclass_one.attributes.username.name_format
I'd hope that Rails would look up the hierarchy of UserSubclassOne
to User
when searching for a string in en.yml
and then notice when it gets a 'hit', but (unless I've done something horribly wrong) apparently that doesn't happen.
An obvious solution is to duplicate the data in en.yml.en.errors.models
for user
, user_subclass_one
, user_subclass_two
, etc, but my Rails-sense tells me that this is deeply wrong.
Any ideas, folks?
User
is defined in a gem MyGem
that is included in a Rails engine MyEngine
that is included in the full-on Rails app MyApp
that defines UserSubclassOne
, ..., UserSubclassEnn
. I don't think this should matter though, since the validations are running in MyGem::User
, which is where the en.yml
file lives -- just wanted to let people know in case it does.
So it turns out that the problem was namespacing. Recall that MyApp
(which defines UserSubclassOne
) uses MyGem
(which defines User
). It turns out User
is actually in the namespace MyGem
(this is not necessarily always the case), so the full declaration line at the beginning of User
is not:
User < ActiveRecord::Base
but rather
MyGem::User < ActiveRecord::Base
.
When the i18n gem looks up the class hierarchy, it notices this namespace and searches for my_gem/user
, rather than simply user
, my_gem.user
, my_gem: user
, etc.
Thus I had to change my en.yml
file to:
/config/locals/en.yml
:
en:
activerecord:
errors:
models:
my_gem/user:
attributes:
username:
name_format: 'has the way-wrong format, bro!'
and bingo!
For that reason the Rails I18n API focuses on: As part of this solution, every static string in the Rails framework - e.g. Active Record validation messages, time and date formats - has been internationalized. Localization of a Rails application means defining translated values for these strings in desired languages.
The specified exception handler must be a method on the I18n module or a class with a call method: This would re-raise only the MissingTranslationData exception, passing all other input to the default exception handler.
Following the convention over configuration philosophy, Rails I18n provides reasonable default translation strings. When different translation strings are needed, they can be overridden. Rails adds all .rb and .yml files from the config/locales directory to the translations load path, automatically.
The i18n (internationalization) API is the standard way to support localization in Rails. The official guide has all the information you need, but it's also very long. This post is based on the notes I took when I was first learning how to set up i18n, and my goal here is to provide a more approachable walkthrough.
So it turns out that the problem was namespacing. Recall that MyApp
(which defines UserSubclassOne
) uses MyGem
(which defines User
). It turns out User
is actually in the namespace MyGem
(this is not necessarily always the case), so the full declaration line at the beginning of User
is not:
User < ActiveRecord::Base
but rather
MyGem::User < ActiveRecord::Base
.
When the i18n gem looks up the class hierarchy, it notices this namespace and searches for my_gem/user
, rather than simply user
, my_gem.user
, my_gem: user
, etc.
Thus I had to change my en.yml
file to:
/config/locals/en.yml
:
en:
activerecord:
errors:
models:
my_gem/user:
attributes:
username:
name_format: 'has the way-wrong format, bro!'
and bingo!
According to the Rails Guides for i18n regarding Error Message Scopes (5.1.1) for Active Record validation error messages, what you're attempting to do should work:
Consider a User model with a validation for the name attribute like this:
class User < ActiveRecord::Base validates :name, :presence => true end
<...snip...>
When your models are additionally using inheritance then the messages are looked up in the inheritance chain.
For example, you might have an Admin model inheriting from User:
class Admin < User validates :name, :presence => true end
Then Active Record will look for messages in this order:
activerecord.errors.models.admin.attributes.name.blank activerecord.errors.models.admin.blank activerecord.errors.models.user.attributes.name.blank activerecord.errors.models.user.blank activerecord.errors.messages.blank errors.attributes.name.blank errors.messages.blank
This way you can provide special translations for various error messages at different points in your models inheritance chain and in the attributes, models, or default scopes.
So, in your case, assuming your classes look something like this:
app/models/user.rb
User < ActiveRecord::Base
validates :username, :format => {/regex/}, :message => :name_format
end
app/models/user_subclass.rb
UserSubclass < User
validates :username, :format => {/regex/}, :message => :name_format
end
and your config/locales/en.yml looks something like:
en:
activerecord:
errors:
models:
user:
attributes:
username:
name_format: 'has the way-wrong format, bro!'
then the message searching for a validation on UserSubClass
should go:
activerecord.errors.models.user_sublcass.attributes.username.name_format # fail
activerecord.errors.models.user_sublcass.name_format # fail
activerecord.errors.models.user.attributes.username.name_format # success
activerecord.errors.models.user.name_format
# ...
Assuming that your model files and yaml files look similar to what's above, then the potential complication you mentioned may be the issue, but obviously I can't be certain.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With