Active Record, Migration, and Translation

Hi,

I think the columns and table in migration should be able to have an optional "display_name" set manually. Something like:

create_table :people, {display_name => "Personne"} do |t|
    t.column :first_name :string, :display_name => "Prénom".
end

Let me explain my point of view:

Rails is a framework made to write programs in English. You see it when you are confronted to the pluralization rules, or the _confirmation fields.
I think this is okay, because programming Rails without speaking English is a pain anyway: most of the docs and tutorial available on the web are in English, and all functions have english name.

But where I think Rails goes a little bit too far, is that it is also meant to be a lot more easy using it to write application with english output, for English-speaking user. And if programmers are writing for non-English user, it's a lot more pain for them. I think this is not fair. World is not just english-speaking, and Rails is a world-wide open software.

And I think it would not be difficult for Rails to make it easy programming a non-english output.

For the translations of the error messages, I found that localisation_simplified is a very nice and very lightweight plugin that should be integrated in Rails by default. This would spare programmers the pain to check all plugins availables at http://wiki.rubyonrails.org/rails/pages/InternationalizationComparison
when they only want one language output, but non-english.

But this plugin does not offer a solution to translate the database fields, so generated scafold is all in english, and I have to correct all the rhtml files manually
Moreover, I'm still displaying error messages like:
"First name est un champs obligatoire."
So I have to write all error messages manually, repeating myself a lot.
Using gettext or internationalisation plugin to solve this problem makes me feel like taking a hammer to kill a bee: I just want one language output.

Scaffolding is such a good part of rails, that it should also be fully enjoyable for programmers wanting a non-english-output!

To solve this, my first thought was to write my database in my target language (French). But then I was confronted to the pluralization problem, and the "_confirmation" things. Moreover, all functions of the framework are in English. So when I'm writing something like "utilisateur.create", I find it ugly, and I feel like neither English nor French programmer would understand what I meant when I wrote this. As I said, Rails is meant to be programmed in full-english.

So if I write my database in English, and want the user to see it in french, then I have to put the translation somewhere. And if I change or create or delete a field in my database, I also have to change, create or delete the translation. (and I don't want to repeat myself).

This is why I think the best place for translation is in migration: this is where we are defining what will be the name of the fields in the database, so it's the best place to define the name we want it to be displayed in the app.

The perfect thing would be just one more option, available for every kind of fields:

create_table :people, {display_name => "Personne"} do |t|
    t.column :first_name :string, :display_name => "Prénom".
end

If default scaffold behaviour is able to understand this, we're done. This feature would also be very useful to people with ugly existing database that they're just not allowed to touch.

And this would also help for globalisation: I would then be able to write:
t.column :first_name :string, display_name => translate("First Name").

And if you don't give a display name, you've just got the default behaviour with the "humanize" thing.

I know this is not very compatible with the "sexy migration" of rails 2.0, but I think there is a lot of people for who this feature would be more useful than the "sexy migration" one.

Hope I conviced someone able to do this!

Sebastien.

on a related note, I'd like to be able to display a more friendly
filed name than the table column in the form validation error message.
i.e., I want to say

validates_format_of :column_name
  :with => /^[a-z0-9 ]*$/i,
  :message => 'friendly name should be keywords separated by spaces.
Keywords should be letters and numbers only'

but I end up with the column_name stuck in front of the message. is
there a workaround (renaming the column isn't an option.)

To change the error messages, you can use override the fuction
"errormessage" as shown below. Place a file containing the new code in
the "lib" directory.
Then require this file in your config/environment.rb
(ie, if your file is named "my_custem_error_message.rb', put require
'my_custom_error_message_file' in config/environment.rb)
Here I give you the code used by the "localisation_simplified" plugin,
you can change it to fill your special needs.

module ActionView
  module Helpers

     #Modify ActiveRecord to use error message headers (text from lang-file)
     module ActiveRecordHelper
       alias_method :old_error_messages_for, :error_messages_for

       def error_messages_for(*params)
         options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
         objects = params.collect {|object_name|
instance_variable_get("@#{object_name}") }.compact
         count = objects.inject(0) {|sum, object| sum +
object.errors.count }
         unless count.zero?
           html = {}
           [:id, :class].each do |key|
             if options.include?(key)
               value = options[key]
               html[key] = value unless value.blank?
             else
               html[key] = 'errorExplanation'
             end
           end
           messages = ActiveRecord:: Errors.default_error_messages
           if options[:action]
             header_message = pluralize(count,
messages[:error_forbid])+' '+options[:action]
           else
           header_message = format( messages[:error_header],
             pluralize(count, messages[:error_translation]),
             (options[:object_name] ||
                 params.first).to_s.gsub("_", " "))
           end
           error_messages = objects.map {|object|
object.errors.full_messages.map {|msg|content_tag(:li, msg) } }
           content_tag(:div,
             content_tag(options[:header_tag] || :h2, header_message) <<
               content_tag(:p, messages[:error_subheader]) <<
               content_tag(:ul, error_messages),
             html
           )
         else
           ''
         end
       end
     end
   end
rubynuby a écrit :

on a related note, I'd like to be able to display a more friendly
filed name than the table column in the form validation error message.
i.e., I want to say

validates_format_of :column_name
  :with => /^[a-z0-9 ]*$/i,
  :message => 'friendly name should be keywords separated by spaces.
Keywords should be letters and numbers only'

Well looking in validations.rb, it looks like full_error_messages uses
calls human_attribute_name. By default this just does things like
convert first_name to First name, but you could imagine something like

class Person < AR::Base
   def self.human_attribute_name(attr_name)
     case attr_name
       when 'first_name' then 'Prénom'
...
     end
   end
end

Frederick Cheung a écrit :

Frederick Cheung a écrit :

on a related note, I'd like to be able to display a more friendly
filed name than the table column in the form validation error
message.
i.e., I want to say

validates_format_of :column_name
  :with => /^[a-z0-9 ]*$/i,
  :message => 'friendly name should be keywords separated by spaces.
Keywords should be letters and numbers only'

Well looking in validations.rb, it looks like full_error_messages
uses
calls human_attribute_name. By default this just does things like
convert first_name to First name, but you could imagine something
like

class Person < AR::Base
  def self.human_attribute_name(attr_name)
    case attr_name
      when 'first_name' then 'Prénom'
...
    end
  end
end

This is what I'm doing, but I wrote my message to say there should
be a
better way: I have to write two times the attribute name: one for
column
definition, one for translation definition. So if it changes I have to
correct it two times also.

I don't think it can be in the migration (how's the model going to
know at runtime?), but overwriting human_name is a chore.
If you could write

class Person < AR::Base
   display :first_name, :as => 'Prénom'
end

Then it would be less tedious.

Fred