This project is archived and is in readonly mode.
Flexible formatting for AR validation error messages
Reported by mtyaka | January 2nd, 2009 @ 06:36 AM | in 2.3.4
Some months ago a patch that adds an option to change the default separator between attribute's name and message strings was committed (see #1294).
I'd like to propose a more flexible solution - instead of only
making the separator
configurable, use interpolation
to construct the messages. Inside a locale file:
en:
activerecord:
errors:
format:
# Used in full_messages, consequently affects the output of error_messages_for helper
# The values :model, :attribute and :message are available for interpolation
full_message: "{{attribute}} {{message}}"
# The values :model, :attribute and :value are always available for interpolation
# The value :count is available when applicable. Can be used for pluralization.
messages:
inclusion: "is not included in the list"
...
This can be useful for languages where nouns have an attribute
of "gender" and the form of the error message depends on that
gender. While I was trying to come up with Slovenian translations
for default AR error messages, I found there is no way I can make
them "gender-neutral" without making them seem extremely awkward.
Now I can declare the full_message
format like
this:
sl:
activerecord:
errors:
format:
full_message: Polje "{{attribute}}" {{message}}
"Polje" means "Field" in Slovenian. Now that I know the gender of the noun (polje) that the message is referring to, it is easier to write default error messages.
Another, more flexible way to make translated error messages
sound more natural would be to omit the attribute
from
the full_message
format string and instead include it
in each of the error messages explicitly:
sl:
activerecord:
errors:
format:
full_message: "{{message}}"
messages:
blank: Polje "{{attribute}}" ne sme biti prazno.
not_a_number: Vsebina polja "{{attribute}}" mora biti število
...
Of course this doesn't make the translations perfect and in a real production application you would probably want to declare custom error messages for each of your models (which the new I18n makes very easy). But this patch at least makes it possible to translate the default error messages in a way that the translations are grammatically correct and not completely useless.
The patch includes code changes and updated tests. I also added
the format.full_message
line to the English locale
file together with some comments. Existing translations will
continue to work normally, as "{{attribute}}
{{message}}"
is the default format.
Any ideas or other implementation suggestions are welcomed.
Comments and changes to this ticket
-
mtyaka January 2nd, 2009 @ 06:43 AM
I'm sorry, there was a typo in the diff file. Please use this one instead.
-
Yaroslav Markin January 2nd, 2009 @ 08:56 AM
- Tag changed from activerecord, error, errors, error_messages_for, i18n, l10n to activerecord, error, errors, error_messages_for, i18n, l10n, patch
Kinda +1 on this one, but this does not also solve the problem with validation messages that don't need formatting, for instance,
You must agree with terms of service
of whatever. This kind of problem was usually solved using a simple plugin/hack involving
"^message"
format for validations -- so, when full_messages sees"^"
as the first character of validation message, it does not add attribute name to that message.Maybe we can have some kind of formatting like you did available for every attribute, so that I can easily say that "ToS agreement" does not need attribute name prefix?
At the moment, this is solved using that 2-year old (I think) hack.
I just thought that this problem may be somehow related to your patch.
-
mtyaka January 2nd, 2009 @ 09:20 AM
@Yaroslav: You can do that if you omit the {{attribute}} variable from the full_message format, as per my second example. Of course then you have to add it to all of the other error messages where you do want the attribute to appear. If you want the attribute's name to appear in the
inclusion
andexclusion
error message, but do not want it in theaccepted
error message, you'd put something like this in the resource file:en: activerecord: errors: format: full_message: "{{message}}" messages: inclusion: "{{attribute}} is not included in the list" exclusion: "{{attribute}} is reserved" accepted: "You must agree with terms of service"
-
Pratik March 8th, 2009 @ 02:26 PM
- State changed from new to hold
Putting on "hold" until the patch is ready to be pushed.
-
José Valim March 8th, 2009 @ 04:24 PM
I've run into this problem myself (in portuguese) and I like your solution but maybe we could make it a little bit more flexible instead of applying the behavior to the whole application.
What if it searches in different places?
activerecord: errors: full_message: "{{attribute}} {{message}}" models: account: full_message: "{{message}}"
It would first search in activerecord.errors.models.account.full_message and then in activerecord.errors.full_message.
Another approach would be to keep the separator option, add an attribute option under format and a only_message option for each model:
activerecord: errors: format: separator: " " attribute: 'Field "{{attribute}}"' models: account: only_message: [ :eula ] eula: You must agree with terms of service
This would generate:
Field "name" can't be blank
For all fields except for eula, which would generate:
You must agree with terms of service
The first approach (proposed on your patch) requires a lot of work if we need to change just one field, while the second is more flexible.
-
Sven Fuchs July 8th, 2009 @ 11:25 PM
For the record, it seems we're approaching a solution here: http://groups.google.com/group/rails-i18n/browse_thread/thread/2791...
More related discussions:
-
Dmitry Polushkin July 29th, 2009 @ 12:37 PM
- Tag changed from activerecord, error, errors, error_messages_for, i18n, l10n, patch to activerecord, error, errors, error_messages, error_messages_for, i18n, l10n, patch
Any news on this ticket?
-
Sven Fuchs August 5th, 2009 @ 07:17 PM
- State changed from hold to open
- Milestone changed from 2.x to 2.3.4
It seems we're finally getting somewhere with this. Could please everybody review:
http://groups.google.com/group/rails-i18n/browse_thread/thread/2791...
-
Jarl Friis August 7th, 2009 @ 07:09 AM
I am curious:
Could someone please enlight me: Does the fix to this ticket make it possible to create a custom error message, that will write "Country code 'US' is not valid, it must be one these: DK, SE, NO, FI" opposed to the default "Contry code is not in the list" when using a validation like this:validates_inclusion_of :country_code, :in => ['DK', 'SE', 'NO', 'FI']
?I would really like to see that being possible to let the valid options being part of the error message along the the actual value. So if that is not the case, I will probably create a new (feature request) ticket.
Jarl
-
Rodrigo Rosenfeld Rosas August 13th, 2009 @ 07:06 PM
I still think that another approach would be more elegant and less hacky:
http://rails.uservoice.com/pages/10012-rails/suggestions/204356-ful...
How about just:
validates_inclusion_of :country_code, :in => ['DK', 'SE', 'NO', 'FI'], :full_message => 'Country code "{{value}}" is not valid. It must be one of: {{in_list}}'
or
validates_inclusion_of :country_code, :in => (valid_options = ['DK', 'SE', 'NO', 'FI']), :full_message => "Country code '{{value}}' is not valid. It must be one of: #{valid_options.join(', ')}"
or, in other context:
validates_acceptance_of :eula, :full_message => 'You must agree with terms of service.'
I would like to try to implement this but I don't know how to start. I didn't find any unit test for errors.full_messages. And now there is ActiveModel and ActiveRecord and I don't know where should I work in.
-
Jarl Friis August 14th, 2009 @ 08:56 AM
Of course I ment that my examples of error messages should be generated by some user custom supplied generic message like you, Rodrigo, explain it. Whether that be be through
:message
, or some new:full_message
.The current problem is that substitution variables such as
{{in_list}}
or#{valid_options}
are not available today to create a generic message that can produce the message in my exmples.So my question was does this bug take care of that (and introduce variables such as
{{in_list}}
to be available to custom user messages)?Jarl
-
Rodrigo Rosenfeld Rosas August 14th, 2009 @ 01:24 PM
Jarl, I didn't understand very well your last message. My message wasn't intended to be a response to your message. I just used your example for showing the idea. The problem is that the other suggestions presented in this topic treat this subject as an internationalization issue, when I think it is not only applicable to internationalized sites.
The solution I present would be both more elegant and more powerful/flexible.
Another thing that you might not have understood. In this topic it has been discussed the possibility to do expansions such as in {{in_list}} on the i18n files. If this would happen to i18n files it could also be availabe with :full_message parameter. Actually, if I am not wrong, string interpolation with this syntax is already available to I18n module, I think the implementation would be something like:
full_messages=[]
errors.each do |field, message|
full_messages << (message.full_message? ? '' : I18n.translate(field) + SEPARATOR) +I18n.translate(message.value, message.interpolation_parameters)
end
On the other side, #{valid_options} is indeed available since always, and that is why I wrote that example. It is just variable substitution into "double-quoted #{string}".
-
Rodrigo Rosenfeld Rosas August 14th, 2009 @ 01:29 PM
Just reformatting the code, since it became confustion without proper formatting:
full_messages=[] errors.each do |field, message| full_messages << (message.full_message? ? '' : I18n.translate(field) + SEPARATOR) + I18n.translate(message.value, message.interpolation_parameters) end @@
-
Rodrigo Rosenfeld Rosas August 14th, 2009 @ 01:33 PM
I don't know exactly why, but I do not feel very confortable with this lighthouse system. Sometimes I miss the simplicity (yet powerfulness) of Redmine, and the ability to preview the messages before sending it... And, of course, the fact that it is written using the Rails framework ;)
-
Sven Fuchs August 26th, 2009 @ 11:14 AM
So, we've done a lot of work on figuring out what's a good solution to this in 2.3.x (i.e. maintaining backwards compatibility and not breaking tons of tests in userland)
See:
- http://github.com/svenfuchs/rails/commit/68082af76c3d56d7afa1bf3e37...
- http://github.com/svenfuchs/rails/blob/78e5885f8ac6ac02943a9cee4fce...
I'm not quite sure, yet, about the backwards compatibility approach with the extracted #generate_message method. José, can you review whether this actually work with Paperclip and Shoulda?
I've also pushed an alternative solution (closer to Mateo's original one) that keeps #generate_message on the Errors collection:
-
Rodrigo Rosenfeld Rosas August 26th, 2009 @ 03:18 PM
I've just noted a small typo in the documentation:
"activerecord.errrors.messages" (the extra 'r')I guess you've copied it from somewhere since I remember I've already submitted a patch do docrails aiming exactly this typo.
I've take a glance at the commits, but I'm not sure it it will be possible to have different format for full_messages to different contexts, in a per model/validation manner. Could you exemplify how to use this use case with your approach?
validates_presence_of :name
validates_accpetance_of :eula, :full_message => 'You must agree with our contract terms.'To generate the errors:
"Name must be present. You must agree with our contract terms."
Thank you anyway for addressing this problem.
-
Rodrigo Rosenfeld Rosas August 26th, 2009 @ 03:31 PM
The lack of preview in LightHouse is really a problem. Trying to format the example with pre:
validates_presence_of :name validates_accpetance_of :eula, :full_message => 'You must agree with our contract terms.'
To generate the errors:
"Name must be present. You must agree with our contract terms." -
Sven Fuchs August 26th, 2009 @ 05:15 PM
José recommended I'd already submit the patch for this change. So I'll do just that :)
Rodrigo, please be aware that we're right now talking about 2.3.x changes. This means they need to be backwards compatible with the current API. We'll look into a 3.x solution afterwards.
That said I believe that with this patch you can do the following:
validates_accpetance_of :eula, :message => :eula
And then provide a translation for :message (if you need it anywhere) and :full_message => 'You must agree with our contract terms.'
You can then join account.errors.full_messages and that should give you the expected result. If the key :eula clashes with other models you could pick any other key that fits your needs better: validates_accpetance_of :eula, :message => :"users.eula"
-
Sven Fuchs August 26th, 2009 @ 05:48 PM
- Assigned user changed from Sven Fuchs to Jeremy Kemper
Jarl,
I've created a new ticket for your issue as it is not related to the error message formatting.
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets...
-
Rodrigo Rosenfeld Rosas August 26th, 2009 @ 06:00 PM
Sven, what I meaned is if I'll be able to set multiple 'full_message' translations, one for each model/validation. I mean, one attribute can have more than one validator in the same model. They should have independent 'full_message' translations. Is that the case?
-
Sven Fuchs August 26th, 2009 @ 06:49 PM
Rodrigo, yes, you can always pass whatever message key you want to the validates_* macros. This key can then be used to use individual full_message formats, too:
validates_presence_of :password, :message => :"user.password.must_not_be_blank" validates_format_of :password, :message => :"user.password.must_have_valid_format"
You can then specify an individual full_message format for each of these. Does that answer the question? Otherwise we might want to further discuss this on the rails-i18n mailinglist - maybe a more extensive example/usecase would be helpful then, too :)
-
Rodrigo Rosenfeld Rosas August 26th, 2009 @ 07:08 PM
Thanks, Sven, now I understood what is happening, with your last examples. :)
-
Sven Fuchs August 29th, 2009 @ 04:47 PM
Ok, now here's a rebased version of the patch. It applies to 2-3-stable and passes all tests. It's not perfect, but probably never will in 2.3.x because we need to maintain BC. We'll focus on the "perfect" solution for Rails 3.x next.
Jeremy, could you please apply it? Please ping me for any questions.
José will make sure our compat workaround for such libraries as shoulda and (what was the other one?) works.
-
Repository August 29th, 2009 @ 06:02 PM
(from [13fb26b714dec0874303f51cc125ff62f65a2729]) Fix ActiveRecord Error message I18n:
- allow messages and full_messages to be lazily translated at any time
- allow locales to be swapped and still obtain correctly localized messages
- allow localized global and error-type specific full_message formats
- extract an Error class
[#1687 state:open]
Signed-off-by: Jeremy Kemper jeremy@bitsweat.net
http://github.com/rails/rails/commit/13fb26b714dec0874303f51cc125ff... -
José Valim August 30th, 2009 @ 12:05 AM
Attaching a patch that allow the scope to be changed in ActiveRecord::Error.generate_full_message as well. The deprecation stuff works like a charm.
-
Sven Fuchs August 30th, 2009 @ 01:03 PM
- State changed from open to committed
I've discussed this with José through private email and we agreed not to apply this functionality to Rails 2.3.x for now. I'll therefor close this ticket.
For any add-ons to the now included functionality it might be appropriate to open a new ticket (there's been a lot discussion here that now probably is obsolete).
For the record, this was my explanation to José:
we've actually left that out intentionally for now because we didn't want to introduce too many new features that will be deprecated or changed in Rails 3 anyway.
Do you think this is a must-have or nice-to-have thing? Or, put differently, how often do you think people want to
- have different short messages and full messages AND
- overwrite the default full_message format for a message on a per-model basis?
We expected this to be rather an edge case.
Please note that this is still possible with the current implementation. You can
class User < ActiveRecord::Base validates_presence_of :name, :message => 'user.name.blank' end
and then have both the message itself and the full_message format defined for the (sub) key :'user.name.blank'
-
José Valim August 30th, 2009 @ 10:31 PM
- State changed from committed to open
Sven, what about the patch attached? It just allows to change the scope for generate_full_message as well. Can we apply it?
-
Sven Fuchs August 30th, 2009 @ 11:02 PM
Oh, I probably misunderstood your mail then. Yeah, I don't see a problem with that patch. Umm, are we supposed open a new ticket for it?
-
José Valim August 31st, 2009 @ 01:39 PM
A new patch with tests attached. Also refactored the default options to a common place.
-
Rodrigo Rosenfeld Rosas August 31st, 2009 @ 01:43 PM
Sven, I really don't think it is important how often someone needs this feature. Once she needs, she should be able to do it. What should I do if for any rare circumstances I can't write the error message like I would it to show? Write my own full_messages implementation?
But in Brazil, at least, I don't think this is a corner case. First, people usually start the phrase with an article in Brazil. In a lot of validation messages, it makes more sense that the attribute is shown in the middle or in the end of the sentence. In other cases, the attribute should not even be in the phrase. I think I would more often use :full_message than :message when writing validations.
You say it is possible to do with current implementation but I tried very hard to find how to do it in Rails API and Rails guides and was not able to find any hint about that.
At the moment, I'm trying to convince the company where I work in to replace Java with Ruby on Rails. I'm preparing a presentation and it will be really hard to explain to them that it is currently not possible to write custom full messages for Rails validations or that it is too complicated and it is not even documented in API nor in the guides.
Having a :full_message option would be good enough and I don't think it breaks backward compatibility. It just adds an option to validation methods.
This is really a serious problem in Rails since the first version and I'm really surprised people didn't address this issue yet.
I have addressed this issue in the past with the humanize_attributes plugin, that allowed be to set a blank attribute name so that :message would have the behaviour of a :full_message parameter.
But you have to agree that this is not a clean solution.
-
José Valim August 31st, 2009 @ 04:26 PM
Rodrigo,
Sven meant that the feature is not adequate for Rails 2.3.x, since it's a maintenance branch. More improvements must happen in master branch.
Besides, the amount of people using a feature in the framework is extremely important. If some feature is not being used frequently, it probably belongs to a plugin instead of the framework. Otherwise, those who are maintaining the framework have to deal with code rarely used (while they could put this effort somewhere else).
We do not have a lot of people working on the I18n part of the framework, if you want to be sure that :full_message will be on Rails 3, we will be glad to have your help!
And feel free to join us on the I18n group: http://groups.google.com/group/rails-i18n/
-
Sven Fuchs August 31st, 2009 @ 05:44 PM
Hi Rodrigo,
I agree with everything that José said.
Please keep in mind that we're talking about a bugfix release and what we're introducing already is hardly just a "bugfix". We'll try to implement the "perfect" solution for Rails 3 next. If there ever will be a 2.4 release we'll backport this solution. But we're a bit constrained regarding 2.3.x.
That said, you really can do this with the given solution that now is in the 2-3-stable branch. It is not yet documented in the guides and probably not in the API docs as it's just a few days old :) Please look at the tests and the source code docs.
From what you are saying you do not seem to actually need both messages and full_messages in parallel. So, even without this patch you already could specify custom messages on a very detailled level (per base model, each inherited model, attribute on the base model, attribute on each inherited model etc). If you do not really need both messages and full_messages in parallel then you are free to use the existing behaviour (messages) effectively for full_messages. It works, I know quite some people doing just that.
If, on the other hand, you do need both messages and full_messages then you can now do that with this patch applied in 2.3.x. You can specify custom message keys per validation. Thus, you can always also specify custom full_messages. I've tried to explain that before (see above). If that wasn't clear enough, please have a look at the code docs and tests.
The question how often people are using this and whether we're seeing it as an edge case matters a lot for how much convenience stuff we wanna build in. Convenience support (in this case) makes lookups slower and more complex. We also need to work around some parts of what we already have had in Rails 2.3 and care about BC.
All that said, I second José's invitation. If you care about all this enough, please join the rails-i18n mailinglist and help us improve this stuff for Rails 3 (so it might get backported for 2.4 in case that ever appears on the horizon).
Thanks
-
Rodrigo Rosenfeld Rosas August 31st, 2009 @ 08:07 PM
I'm afraid I was not clear in my last message. What I meant for often, is that :full_message would be rarely used inside a project, but that projects will often need to specify a :full_message for at least one validation in the hole project. I meant that often inside a project should not matter, since I always needed this behaviour in all of my projects. Even if just once per project. I believe this will be truth for most non-english projects. How can we guess if this will be often needed or not?
Another thing I am concerned about is that it seems that there is a common understanding that this topic is related to internationalization where I think these are two different subjects. Internationalization is important, but using it as a work-around for a not related problem is not the correct solution. I would expect to find how to have control over the messages in the validations documentation and I would not relate it to I18n, so I could never found how to do it...
DHH did not care for a long time about internationalization it it made it very hard in the past to write internationalized application since all plugins were forced to write monkey patches, since Rails didn't make it easier to add internationalization support. Thank to Sven, these days are gone and it is much simpler to write i18n plugins nowadays without being forced to write monkey patches that are broken each Rails release.
I think that :full_message is in the same level. It is too basic for the framework, in my opinion, that it should not be delegated to external plugins. And I don't think it is hard to implement and maintain. Here is what I wrote some messages ago:
"I would like to try to implement this but I don't know how to start. I didn't find any unit test for errors.full_messages. And now there is ActiveModel and ActiveRecord and I don't know where should I work in."
Now I see that a new test was written ('validations_i18n_test.rb'). But I would like to have further direction of what approach should I take. I am still available to add this feature as soon as possible, but I know that I need some help about Rails internals before I can do it. I still don't know if I should address ActiveModel or ActiveRecord, for instance. I just think this feature is not directly related to i18n and the patch should address directly ActiveRecord (or ActiveModel?). But I still miss some test cases for full_messages that is not related to i18n.
For helping understanding that this is a basic requirement to ActiveRecord::Validations, take this case, where a company decided that the following messages should be given to the users in case of errors:
validates_presence_of :login, :message => 'You must specify a user name.'
validates_length_of :login, :in => 4..6, :allow_nil => true, :full_message => 'Your user name should contain from 4 to 6 characters.'
validates_format_of :login, :with => /[a-z]*/, :allow_nil => true, :full_message => 'Only down case letter are allowed for user names.'Please, show me the complete code that would allow me to do that currently with Rails. And, do you really think these requirements are related to internationalization?
Please, don't get me wrong. It is very hard to write text on Internet, since we have no way of expressing ourselves. It might give the false impression that we are angry or hostiles, while that is not the case. I respect both of you (Sven and José Valim) for the wonderful jobs you guys are doing with i18n and Thor integration to generators and have already talked to both of you by e-mail before (sending translation patches to Rails i18n github project and some documentation patches to Rails generators - I know I still didn't send you the new patch correction we talked about Valim, but I did not forget about it).
I am just trying to help, and I'll be able to help more once I get my company to accept Rails for developing new applications. Right now, I cannot concentrate myself in Rails since I need to develop in Java. Having :full_messages parameter would certainly make easier for me to convince them. It would be really hard to say them that this feature is not supported in Rails right now. They'll get the impression that Rails is not mature enough, since this is a common requirement for Brazilian applications. I can't convince them that Rails has support for i18n if it doesn't give me control about the entire error message.
Another thing I do not agree is that we should wait until Rails 3. Rails 3 is still a work-in-progress and lots of things will change. It won't be stable to release any time soon. On the other way, adding :full_message to validations should not be seen as a major feature. I would consider it a bug as well not having any way for having full control about the validation messages. It would not break any existent code, since the behavior would not change for validations that doesn't use the :full_messages parameter. I really think that this is a major problem in Rails that should be addressed soon, instead of waiting for the next major Rails release. I am available to help writing this feature, but I need some direction on which approach to take.
I tried to generate the guides both in trunk and 2-3-stable and I am getting errors. But I didn't find any references to full_message format in API nor in i18n guide. Could you point me where this format is documented? I just found documentation about the separator... Please have in mind that setting the key to a empty string makes the application code illegible and hard to maintain when working in a team.
Best Regards,
Rodrigo.
-
Rodrigo Rosenfeld Rosas September 2nd, 2009 @ 12:29 AM
I've just realized this mistake in the last message:
validates_presence_of :login, :message => 'You must specify a user name.'
Of course, I meant:
validates_presence_of :login, :full_message => 'You must specify a user name.'
-
Repository September 8th, 2009 @ 04:39 PM
- State changed from open to resolved
(from [e1b109633cb03ad84b88e8d1f9d3fcaef08619b1]) Allow scope to be changed for ActiveRecord::Errors#generate_full_message and change deprecation message [#1687 state:resolved]
Signed-off-by: Joshua Peek josh@joshpeek.com
http://github.com/rails/rails/commit/e1b109633cb03ad84b88e8d1f9d3fc... -
Ruy Asan November 5th, 2009 @ 12:17 AM
+1 to Rodrigo's rant - this is not an i18n issue, it's a just a general pita that's bugged me since probably the first day I tried rails. ActiveRecord::Validations can generate the most maddening, unhelpful error messages ever - it exposes internal model bits that are 100% irrelevant to the user (ever had errors like "Resource is invalid" shown to users? good times eh?).
The current validations.rb in 2-3-stable makes it quite a bit easier to add a :full_message option to the validation macros...
The code has alread diverged a fair bit from the active model implementation - backporting shouldn't actually be affected that much.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 3182 error_messages_for | doesn't work properly since 2.3.4 You need to define the full message format in your other ...
- 1687 Flexible formatting for AR validation error messages [#1687 state:open]
- 1687 Flexible formatting for AR validation error messages (from [e1b109633cb03ad84b88e8d1f9d3fcaef08619b1]) Allow s...
- 3167 Flexible formatting for AR validations should be on master https://rails.lighthouseapp.com/projects/8994/tickets/16...
- 3182 error_messages_for | doesn't work properly since 2.3.4 I had the same problem. https://rails.lighthouseapp.com/...