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