human_attribute_name, titleize, form label helper and such

1,283 views
Skip to first unread message

Sven Fuchs

unread,
Aug 26, 2009, 8:22:06 AM8/26/09
to rails-i18n
I'm still hesitant on the form label ticket [1], because I feel I just
do not understand how Rails should work regarding titleization of
Strings in helpers.

When I was working on the Validations patch a few days ago I noticed
that having human_attribute_name uppercasing the attribute name might
not be a good idea at all. Consider something like this:

full_message_format = "This {{model}}'s {{attribute}} {{message}}."

Validations traditionally use human_name for the model class name and
human_attribute_name for the attribute name. Both internally use
Inflections#humanize which capitalizes the first word of the given
string.

So, we'd get a result like "This Squirrel's Nose is broken." ... wtf.
Obviously this is broken even for English.

Where does the assumption come from that "humanizing" a String
involves "capitalizing its first word"?

I could understand if something like #sentencize would do that, but
the humanized result of "squirrel_id" would be just "squirrel", no?

Any opinions?

[1] https://rails.lighthouseapp.com/projects/8994/tickets/745-form-label-should-use-i18n


José Valim

unread,
Aug 26, 2009, 11:09:21 AM8/26/09
to rails-i18n
Sven,

I understand the issue, but I don't think it relates to label. I think
label *should* use the humanize version as it's today and we should
think in a different solution for error messages. Maybe a
configuration value that lowercase the attribute? Or a
{{downcased_attribute}} interpolation value?
> [1]https://rails.lighthouseapp.com/projects/8994/tickets/745-form-label-...

Sven Fuchs

unread,
Aug 26, 2009, 12:38:35 PM8/26/09
to rails...@googlegroups.com
Doesn't it relate to labels when there are cultures where people do
not always/automatically capitalize the first letter of their labels?

Iain Hecker

unread,
Aug 26, 2009, 1:04:50 PM8/26/09
to rails...@googlegroups.com
Hi,

I agree with José.

Humanize is the default for attribute names and that has always been that way.

<%= f.label :rodent_id %> has always returned "Rodent" and is what most people want for labels.

Downcasing isn't as straightforward in every language either. It might be handy to use a proc for that:
{ :en => { :downcase_rule => lambda { |value| value.downcase } } } for instance.

But even besides that, what if an attribute really starts with a capital, like acronyms and other special names. A simple "This {{downcased_model}}'s {{downcased_attribute}} {{message}}" might not even be sufficient.

So you might need to implement a certain fallback principle like the messages are. It first looks up the attribute specific message, than a model specific message, and if all that fails, it returns the global method. Similarly, a attribute name might undergo such a process. Return the attribute name for full_message_format, if that fails, return the short_message_format, and then the general attribute name, and then the humanized version.

attribute_name = I18n.t("squirrel.full_message_attributes.nose", :default => [:"squirrel.short_message_attributes.nose", :"squirrel.attributes.nose", "nose".humanize], :scope => xxx)
model_name = I18n.t("squirrel.full_message_name", :default => [:"squirrel.short_message_name", :"squirrel.name", Squirrel.name.humanize], :scope => xxx)

Or something like that. I'm forgetting STI in this example, but you get the point.

String#sentencize is not possible to implement. It requires a knowledge of what is being said to do properly.

And a bit off topic: Humanizing a model name might not be the right solution. "FooBar".humanize #=> "Foobar", I think it would need to be: "FooBar".underscore.humanize #=> "Foo bar".

Iain

Iain Hecker

unread,
Aug 26, 2009, 3:27:41 PM8/26/09
to rails...@googlegroups.com
English does, if you don't want to do that, don't capitalize your translations. Don't change translated values of course, but nobody was suggesting that. Only if you don't translate them, it assumes it needs to be humanized.

Sven Fuchs

unread,
Aug 26, 2009, 4:01:31 PM8/26/09
to rails...@googlegroups.com
Ok, that sounds reasonable :)

Thanks, guys

Lawrence Pit

unread,
Aug 27, 2009, 1:20:54 AM8/27/09
to rails...@googlegroups.com

I think the rule is: human_attribute_name, human_name and humanize should not be used anywhere in rails internals, specifically not by AR validation methods, except to serve as a default value. Method humanize should be considered a helper method for the English language only.


> Maybe a configuration value that lowercase the attribute? Or a
> {{downcased_attribute}} interpolation value?
>>
>> full_message_format = "This {{model}}'s {{attribute}} {{message}}."
>>

How about:

full_message_format = "This %{model}'s %{attribute} %{message}."

vs

full_message_format = "%{Model} %{attribute} hat %{message}."

I.e., the case of the interpolation variable is used as an indication of whether or not to capitalize.



Cheers,
Lawrence

Sven Fuchs

unread,
Aug 27, 2009, 3:02:48 AM8/27/09
to rails...@googlegroups.com
On 27.08.2009, at 07:20, Lawrence Pit wrote:
> I think the rule is: human_attribute_name, human_name and humanize
> should not be used anywhere in rails internals, specifically not by
> AR validation methods, except to serve as a default value. Method
> humanize should be considered a helper method for the English
> language only.

Perfect!

Can we have that tattoo'ed on every Rails dev's forehead? ;) Ok, at
least I'll copy and keep it for some kind of best-practice quotes
collection. Thanks :)

> > Maybe a configuration value that lowercase the attribute? Or a
> > {{downcased_attribute}} interpolation value?
> >>
> >> full_message_format = "This {{model}}'s {{attribute}} {{message}}."
> >>
>
> How about:
>
> full_message_format = "This %{model}'s %{attribute} %{message}."
>
> vs
>
> full_message_format = "%{Model} %{attribute} hat %{message}."
>
> I.e., the case of the interpolation variable is used as an
> indication of whether or not to capitalize.

Wicked. We'd then need to pass both :Model and :model as well
as :Attribute and :attribute, right?

I've been thinking about something like "%{model:humanize} %
{attribute} hat %{message}." ... but maybe this is going a bit over
board.

Mateo Murphy

unread,
Aug 27, 2009, 1:05:53 PM8/27/09
to rails...@googlegroups.com

On 27-Aug-09, at 3:02 AM, Sven Fuchs wrote:

>
> On 27.08.2009, at 07:20, Lawrence Pit wrote:
>> I think the rule is: human_attribute_name, human_name and humanize
>> should not be used anywhere in rails internals, specifically not by
>> AR validation methods, except to serve as a default value. Method
>> humanize should be considered a helper method for the English
>> language only.
>
> Perfect!
>
> Can we have that tattoo'ed on every Rails dev's forehead? ;) Ok, at
> least I'll copy and keep it for some kind of best-practice quotes
> collection. Thanks :)

While I do agree with this on one hard, on the other, wouldn't it make
sense to be able configure inflections per locale?


>
>>> Maybe a configuration value that lowercase the attribute? Or a
>>> {{downcased_attribute}} interpolation value?
>>>>
>>>> full_message_format = "This {{model}}'s {{attribute}} {{message}}."
>>>>
>>
>> How about:
>>
>> full_message_format = "This %{model}'s %{attribute} %{message}."
>>
>> vs
>>
>> full_message_format = "%{Model} %{attribute} hat %{message}."
>>
>> I.e., the case of the interpolation variable is used as an
>> indication of whether or not to capitalize.
>
> Wicked. We'd then need to pass both :Model and :model as well
> as :Attribute and :attribute, right?
>
> I've been thinking about something like "%{model:humanize} %
> {attribute} hat %{message}." ... but maybe this is going a bit over
> board.

That's quite similar to liquid[1], and starts approaching a full on
templating engine. I'm not necessarily opposed to that idea, but I'm
not sure it's the best approach to the problem. capitalizing the
interpolation key is definitely clever, but may be confusing/

Although it would be more verbose, providing a hierarchy of
full_message translations would be a foolproof way of dealing with
this issue, and I think that should be encouraged over trying to make
the code too clever.


[1]
http://www.liquidmarkup.org/

Lawrence Pit

unread,
Aug 27, 2009, 8:24:15 PM8/27/09
to rails...@googlegroups.com

> Although it would be more verbose, providing a hierarchy of
> full_message translations would be a foolproof way of dealing with
> this issue, and I think that should be encouraged over trying to make
> the code too clever.
>

Yeah, I actually agree with this. :-)


Cheers,
Lawrence

José Valim

unread,
Sep 4, 2009, 8:39:50 AM9/4/09
to rails-i18n
Inflection per locale is a good suggestion, but it does not solve the
problem. I might have the label "Name" (upcased) but in my error
message it should be "User's name is invalid" (downcased).

Another suggestion would be a label lookup:

views:
labels:
user:
name: "Your name:"

Where human_attribute_name would be a fallback if it's not found in
labels. This way would at least decouple actionview and activerecord
(i.e. it would work with datamapper and other non object forms). If we
do that, we should also consider to add lazy defaults:

I18n.t("views.labels.#{model.class.name}.#{attribute}") do
model.class.human_attribute_name(attribute)
end

We would call "model.class.human_attribute_name" just if
"views.labels.model.attribute" can't be found. A straightforward gain
if we compare it with:

I18n.t("views.labels.#{model.class.name}.#{attribute}", :default =>
model.class.human_attribute_name(attribute))

Wdyt?

Regards!

Mateo Murphy

unread,
Sep 4, 2009, 12:29:43 PM9/4/09
to rails...@googlegroups.com

On 4-Sep-09, at 8:39 AM, José Valim wrote:

>
> Inflection per locale is a good suggestion, but it does not solve the
> problem. I might have the label "Name" (upcased) but in my error
> message it should be "User's name is invalid" (downcased).

This is exactly where being able to specify a full message for each
error would come in handy. IMO, the main purpose of the generic
full_message_format was to provide a quick and easy way to remove the
attribute name concatenation while preserving backwards compatibility.

IMO inflection per locale is something that would be for app
developers, not necessarily for use by the internals

>
> Another suggestion would be a label lookup:
>
> views:
> labels:
> user:
> name: "Your name:"
>
> Where human_attribute_name would be a fallback if it's not found in
> labels. This way would at least decouple actionview and activerecord
> (i.e. it would work with datamapper and other non object forms). If we
> do that, we should also consider to add lazy defaults:
>
> I18n.t("views.labels.#{model.class.name}.#{attribute}") do
> model.class.human_attribute_name(attribute)
> end
>
> We would call "model.class.human_attribute_name" just if
> "views.labels.model.attribute" can't be found. A straightforward gain
> if we compare it with:
>
> I18n.t("views.labels.#{model.class.name}.#{attribute}", :default =>
> model.class.human_attribute_name(attribute))
>

Agreed. In fact, I'd suggest that every single part of rails that
currently uses inflections should do so only as a fallback, after
having called I18n lookup first.

> Wdyt?
>
> Regards!

José Valim

unread,
Sep 8, 2009, 5:32:34 AM9/8/09
to rails-i18n
Sven,

What is your point of view? Can I make a patch? :)
Reply all
Reply to author
Forward
0 new messages