Rails: Form Object not finding I18n on activemodel (namespacing issue) - ruby-on-rails

I have a form object (rails 3) and I am trying to get use config/locales/en.yml for the error messages.
My form object looks like this:
class Users::PasswordAndLoginUpdatingForm
include Virtus.model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attribute :requested_email_address, String
validates :requested_email_address, allow_blank: true, format: { with: User::VALID_EMAIL_REGEX }
In my form I have the usual
form_for #form_object ... do |f|
If I pry in and find out what the f.object_name is I get users_password_and_login_updating_form
Finally, my config/locales/en.yml looks like this:
en:
activemodel:
errors:
models:
users_password_and_login_updating_form:
attributes:
requested_email_address:
invalid: "bar"
The issue I'm having is, I cannot get it to use the internationalization.

Well I found out the answer by digging into the source code. The issue I was having that my form object was in a Users::PasswordAndLoginUpdatingForm namespace.
The trick is to use activemodel: as your namespace, but, if you have further namespaces in your models, they're separated with /s.
So, the correct yml looks like this:
en:
activemodel:
errors:
models:
users/password_and_login_updating_form:
attributes:
requested_email_address:
invalid: "foo"

Should it be activerecord: not activemodel: in your config/locales/en.yml file?

Related

ActiveRecord Validations : String added to error message in locales

I'm trying customize validation error messages using i18n.
Let's say I have an Address model that validates the presence of a zip_code.
My config/locales/activerecord.fr.yml looks like this
fr:
activerecord:
errors:
models:
address:
attributes:
civility:
blank: "Some message"
The issue if I fail the validation, the error message I'l have in #address.errors.full_messages will be:
"Zip code Some message"
Why does Zip code gets added to my error message ? And how can I avoid this behaviour ?
You can just add:
errors:
format: "%{message}"
This way you will just show error message with out attribute name.
Edit: this should be added to config/locales/fr.yml and not config/locales/activerecord.fr.yml (which also has errors:)
Found a gem which will solve your purpose
With the help of this gem, You just need to start the locale message with a caret and it shouldn't display the attribute name in the message.
A model defined as:
class Item < ApplicationRecord
validates :name, presence: true
end
with the following en.yml:
en:
activerecord:
errors:
models:
item:
attributes:
name:
blank: "^You can't create an item without a name."
item.errors.full_messages will display:
#You can't create an item without a name
instead of the usual
#Name You can't create an item without a name
You could simply use:
#address.errors.messages[:zip_code] # or #address.errors[:zip_code]
#=> ['Some message']

Localisation of attributes in Rails virtual models

I've created a virtual (non-persistent) model in Rails 3 (see below)
I now need to apply translations to the model but the standard translations locations don't seem to work. e.g.
en:
activerecord:
attributes:
media_upload:
title: "My Title"
I know I can apply this directly to the label with an optional string parameter eg. f.label :title, t('activerecord.attributes.media_upload') but that doesn't work for error messages resulting from validations. Similarly, I could add a key to the translations file for the label helper as suggested in Localise nested virtual attribute in Rails but this also fails to work for the validations.
helpers:
label:
media_upload:
title: "My Title"
Apart from redefining all of the relevant validation messages, is there any other way I can do localisation of attributes in non-persistent models??
A sample model is shown below,
class MediaUpload
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :media_file, :title
validates_presence_of :media_file
validates_presence_of :title
def initialize(attributes = {})
unless attributes.nil?
attributes.each do |name, value|
send("#{name}=", value)
end
end
end
def persisted?
false
end
end
You need write like this:
en:
activemodel:
attributes:
media_upload:
title: "My Title"
not activerecord replace it with activemodel
It seems like you are using simple_form gem for generation of forms.
So following i18n chapter from github your internationalization file should look like this
en:
simple_form:
labels:
media_upload:
media_file: My File
title: My Title
If you are using Rails 4 than there is an easier way to make ActiveModel Form Objects. You can just include ActiveModel::Model like so
class MediaUpload
include ActiveModel::Model
attr_accessor :media_file, :title
validates_presence_of :media_file
validates_presence_of :title
end

Rails - Customize model validation error messages but without a database

I have a model that validates a form but it does not inherit from ActiveRecord....it does not have a database
I am validating certain fields in the model but i want to have custom validation messages.....in the past I've went into the
en.yml
en:
activerecord:
models:
mymodel: "CHANGE TO SOMETHING ELSE"
and could easily change it to whatever i want but it is not working for this model....i think its because its not inheriting from ActiveRecord.....
how can i customize my validation errors?
try including translations like this:
include ActiveModel::Translation
EDIT:
if your model had an email attribute and the blank validation didnt pass you can format you locale like this
locale:
en:
activemodel:
attributes:
mymodel:
email: "foo"
errors:
models:
mymodel:
blank: "bar"
here are other errors you can add a locale
https://github.com/rails/rails/blob/3-2-stable/activemodel/lib/active_model/locale/en.yml
You want to use ActiveModel support for validations, as added in Rails 3.
class MyModel
include ActiveModel::Validations
attr_accessor :name
validates :name, presence: true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
end
Please see these references:
http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
The first link even includes use of an i18n file for errors, as you referenced.

Inheriting Rails i18n validation error messages in the subclass

What I understand
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.
What I want to know:
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?
Potential Complication:
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.
Ultimate problem/solution:
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!
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.

Trouble on using the i18n gem with "resources of resources"

I am using Ruby on Rails 3.1 and I would like to know how to correctly handle internationalization related to "resources of resources". That is, ...
... in my config/routes.rb file I have:
resources :users do
resource :account
end
... in my app/models/users/account.rb file I have:
class Users::Account < ActiveRecord::Base
validates :firstname,
:presence => true
...
end
... in my config/locales/models/user/account/en.yml file I have:
en:
activerecord:
errors:
messages:
presence: "custom presence message - english"
... in my config/locales/models/user/account/it.yml file I have:
it:
activerecord:
errors:
messages:
presence: "custom presence message - italian"
The above code doesn't display in front end the "custom presence message" (it still displays the default RoR presence message: can not be blank). Furthermore if in my app/models/users/account.rb file I use:
class Users::Account < ActiveRecord::Base
validates :firstname,
:presence => { :message => t(:presence) } # Here I try to use the i18n helper method
...
end
I get the following error:
NoMethodError (undefined method `t' for #<Class:0x000001075bbc80>)
Why I get the NoMethodError?
Is the problem related to how I am organizing in directories my locale files? At this time (as stated in the official RoR guide) my file system is:
config/locales/defaults/en.yml
config/locales/defaults/it.yml
config/locales/models/user/en.yml
config/locales/models/user/it.yml
config/locales/models/user/account/en.yml
config/locales/models/user/account/it.yml
In few words, I would like to display my "custom presence message" only on validating "resources of resources" kind of Users::Account. How can I do that?
I also tried to state the following code in the config/locales/models/user/account/en.yml file
en:
activerecord:
errors:
models:
user:
account:
attributes:
firstname:
blank: "custom presence message - english"
but it doesn't work. Anyway the following works but I need different translations for different attributes (as I tried to state in the previous code example):
en:
activerecord:
errors:
messages:
blank: "custom presence message - english"
ok, due to this: https://github.com/rails/rails/issues/1402 last comment nested model look-up is removed
so try maybe something like
activerecord:
errors:
models:
users:
account:
attributes:
first_name:
blank: "You should fill up first name field to complete that"
and change inside :message hash to
I18n.t(:"activerecord.errors.models.users.account.attributes.first_name.blank")
and try avoid nested models ;-)
update:
after some debuging this will work:
activerecord:
errors:
models:
"users/account":
attributes:
first_name:
blank: "You should fill up first name field to complete that"

Resources