ActiveRecord validation messages not I18N-friendly and enforces grammar

ActiveRecord validation messages are currently not I18N-friendly. Specifically, I'm referring to the fact that it enforces a certain grammar, namely the beginning of the message must consist of the field name. For example:

validates_presence_of :name, :message => "can't be blank."

...generates the message "Name can't be blank". This grammar works in English, but may not work in other languages.

Furthermore, such messages are not always appropriate even in English. Suppose I want the message to be "Please enter your name". I can't. Right now I work around this by writing my own show_errors() view helper method (an alternative for error_messages_on()), which only shows the ':message' part of the errors (i.e. without the field part). In my models I then write:

  validates_presence_of :name, :message => "Please enter your name."

Or, suppose I'm making an application that's only in Dutch. I put this in my models:

  validates_presence_of :name, :message => "Voer a.u.b. uw naam in."

This mostly works, but some plugins, such as attachment_fu, add their own validation rules to models (such as 'validates_presence_of :size'). If I use my show_errors() method then I can get messages like this:

  * Please enter your name.   * can't be blank. <--- ouch! the field name is omitted

It's interesting to note Ruby-Gettext allows one to change the grammar by allowing one to set the position of the field name, like this:

  validates_presence_of :name, :message => "Please enter your %{fn}."

There had been discussions in the past on how to properly support I18N in Rails, and people can't seem to reach to a consensus. I would like to propose a solution which solves the grammar issue, thereby making validation messages at least a little more I18N-friendly. This solution is minimal and does not enforce any particular full-blown I18N solution on the developer, and should be backwards compatible. The proposal is as follows:

If the :message argument is given to a validates_whatever, then check whether the :message string contains "%{fn}". If so, then the full validation error message should be formatted using the :message string, like this:

  full_message = message.sub('%{fn}', human_attribute_name)

If no "%{fn}" is given, then it should check whether the following is true:

  message.chars.capitalize == message

If so, then the full validation error message should equal that of the message argument. This allows one to specify a full sentence in the :message parameter, without ActiveRecord::Errors automatically prepending the field name. If the message is not in a western language, e.g. Chinese, then the above expression should return true. Since in these languages it is always inappropriate to prepend the (English) database attribute name, using the verbatim message as given by the message argument would be the correct behavior.

If none of the above 2 conditions are true, then the full validation error message should be generated in the same way as the current way.

Examples:

  validates_presence_of :name # => "Name can't be blank"   validates_presence_of :name, :message => "must be given" # => "Name must be given."   validates_presence_of :name, :message => "Please enter your name." # => "Please enter your name."   validates_presence_of :name, :message => "请你写你的名字。" # => "请你写你的名 字。"

This mostly works, but some plugins, such as attachment_fu, add their own validation rules to models (such as 'validates_presence_of :size'). If I use my show_errors() method then I can get messages like this:

attachment_fu doesn't add validations for this reason. There's a shortcut method to add the default recommended validations, but you're of course free to specify them however you like.

There had been discussions in the past on how to properly support I18N in Rails, and people can't seem to reach to a consensus. I would like to propose a solution which solves the grammar issue, thereby making validation messages at least a little more I18N-friendly. This solution is minimal and does not enforce any particular full-blown I18N solution on the developer, and should be backwards compatible. The proposal is as follows:

If the :message argument is given to a validates_whatever, then check whether the :message string contains "%{fn}". If so, then the full validation error message should be formatted using the :message string, like this:

  full_message = message.sub('%{fn}', human_attribute_name)

If no "%{fn}" is given, then it should check whether the following is true:

  message.chars.capitalize == message

If so, then the full validation error message should equal that of the message argument. This allows one to specify a full sentence in the :message parameter, without ActiveRecord::Errors automatically prepending the field name. If the message is not in a western language, e.g. Chinese, then the above expression should return true. Since in these languages it is always inappropriate to prepend the (English) database attribute name, using the verbatim message as given by the message argument would be the correct behavior.

If none of the above 2 conditions are true, then the full validation error message should be generated in the same way as the current way.

Examples:

  validates_presence_of :name # => "Name can't be blank"   validates_presence_of :name, :message => "must be given" # => "Name must be given."   validates_presence_of :name, :message => "Please enter your name." # => "Please enter your name."   validates_presence_of :name, :message => "请你写你的名字。" # => "请你写你的名 字。"

Where would this be done at? ActiveRecord::Errors#full_message? The error_message_on/error_messages_for helpers?

One other idea is a global option like ActiveRecord::Errors.generate_full_messages.

I thought there was a new i18n rails plugin coming, what do they do?

Yes, full_message would be a pretty good place to do this. This way all code that uses ActiveRecord::Errors will benefit from these changes.

Would anyone object if I write a patch to implement this?

Don't the various i18n plugins handle this? I'm pretty sure it was one of the first issues tackled by them. Supporting %{fn} seems very gettext specific. However, there seem to be multiple ways to tackle this problem.

Yes. However: 1. It is pretty tedious if one has to install an I18N plugin just to solve the grammar problem. Suppose one is developing a website in Chinese, and only Chinese. Everything would work fine up until the point where validation messages are to be displayed. And then the developer has to install some third party I18N plugin, even though the website is only in one language. Or suppose that all I want is to display "Please enter a username" instead of "Username can't be blank". Why should I have to install an I18N plugin? This is pretty inconsistent with the general Ruby on Rails experience. Although installing an I18N plugin is not hard, it'll be yet-another-boring-and- mind-killing-step. 2. This is a problem that can be solved with minimal effort, and is fully backwards compatible. It would make upstream Rails a bit less sucky. I think having to install a plugin to workaround suckiness is a pretty bad thing. 3. The %{fn} proposal is based on the mechanism in Gettext, but it will in no way force a dependency upon Gettext. We *could* invent our own replacement token (e.g. ":attr"), but since Gettext already uses "% {fn}", why not use it? It's likely that some people already know it. They can directly apply their existing knowledge to upstream Rails, thus reducing the learning curve.

I'd also like to add these: - The grammar problem is not really an I18N problem. It even applies to plain English. - Solving the grammar problem with an I18N plugin is overkill. This is one very specific and simple problem. Using an I18N plugin would mean a significantly higher learning curve. - There are many I18N plugins, and they have very little common ground. This solution would give them at least some common ground and would remove some code duplication. The proposed solution shouldn't disadvantage any of the I18N plugin and should only bring benefits to all of them.

I'm in the same situation. This patch would be great for me.

I'm in the same situation. This patch would be great for me.

+1

+1