Gettext and Rails

I encountered some problems with gettext rails integration recently, so here they are with explanation and resolution (if I found it).

Localization of ActiveRecordHelper error messages :

I choose to change the error message as explained by the gettext tutorial :

in application.rb

ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title(Nn_("An error is occured", "%{num} errors are occured"))  
ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation(Nn_("The error is:", "The errors are:"))

this works great, the error message displayed by the helper is the good one… except it only appears in the base language (English in my case). And the text never appears in my .pot file. After some searches in the gettext files, I found the problem : the textdomain for the helper is ‘rails’, which is great because you got validations and errors localized by gettext team for free. But it means you can’t set your own message without modifying the rails.pot in gettext package, which is not a solution because you need to maintain your own gettext gem or apply modifications to all you servers installs, and no you can’t install gettext as a rails plugin as gettext gem compile a gettext.so|.dylib on install which depends of your system.

The solution ? I didn’t find any satisfying one, if you force the textdomain to your app’s one then you need to duplicate and update all the rails translations strings from gettext package.

Gettext#updatepo and models with observers :

One of gettext nifty feature is its ability to extract your model columns names for translation. But the implementation cause some trouble when you have declared some observers in your environment.rb.

To accomplish its tasks, gettext first load rails environment. Then each files are parsed by Gettext::ActiveRecordParser#parse where you have :

old_constants = Object.constants
begin
  eval(open(file).read, TOPLEVEL_BINDING)
rescue
  $stderr.puts _("Ignored '%{file}'. Solve dependencies first.") % {:file => file}
  $stderr.puts $! 
end
loaded_constants = Object.constants - old_constants

This code try to catch the constants the current file add to Object.constants and then parse them either as ruby or ActiveRecord subclass. But if you declared an observer in your environment.rb, the model and its observer is already in the Object.constants and are just ignored by the gettext active record parser.

Solution ? Comment the config.observers line in your environment.rb each time you need to run gettext:updatepo task, not really sexy but it works.

Fri, 30 Mar 2007 10:49 Posted in ,

  1. By Tim 02/04/2007 at 19h11


    Regarding your ActiveRecordHelper ‘solution’, how did you go about forcing the textdomain to the one you used in your app? I’m curious what change you made and where.

    Thanks

  2. By Jonathan Tron 03/04/2007 at 22h33


    Tim : I tried so many combination I didn’t remember exactly which one worked, but I think it was something like :

    
    module Helpers  #:nodoc:
      module ActiveRecordHelper #:nodoc: all
        module L10n
           include GetText
           bindtextdomain("MyDomain")  
        end
      end
    end
    
    

    in my application_helper.rb.

    After some time I decided I lost too much time on this problem and choosed a different approach. I defined a new error_message_for in my application_helper.rb :

    
    def error_messages_for(object_name, view_partial = nil)
        obj = instance_variable_get("@#{object_name}")
        count = obj.errors.size
        unless count == 0
          if view_partial.nil?
            if File.exist?("#{RAILS_ROOT}/app/views/shared/_error_messages.haml")
              render :partial => "shared/error_messages", :locals => {:object => obj, :count => count}
            end
          else        
            render :partial => view_partial, :locals => {:object => obj, :count => count}
          end
        else
          ""
        end
      end
    
    

    which looks for a given shared template, mine looks like this :

    
    %div
      #errorExplanation.errorExplanation
        %h2
          = n_(ActionView::Helpers::ActiveRecordHelper::L10n.error_message_title, count) % {:num => count, :record => object}
        %p
          = n_(ActionView::Helpers::ActiveRecordHelper::L10n.error_message_explanation, count)
        %ul
          - object.errors.full_messages.each do |message|
            %li= message
    
    

    This solution is more flexible and benefits from Gettext Rails localization messages.

  3. By Eric Northam 18/10/2007 at 00h48


    I made a quick monkey patch plugin to fix the observer parsing bug you ran into. You can install it via:

    ruby script/plugin install http://svn.northam.us/gettext_active_record_fix/trunk/gettext_active_record_fix

    Let me know if you run into any issues. It only tries to parse models that are defined at the top level of the file.

  4. By Jonathan Tron 18/10/2007 at 21h34


    Eric : Thanks, as far as I can tell it works beautifully !

  5. By Eric Northam 31/10/2007 at 18h07


    I contacted Masao about the bug and he proposed a more elegant and simple solution to my plugin. Just update your environment.rb with something similar to

    unless defined? GetText
     config.active_record.observers = :user_observer
    end
    

  6. By Jonathan Tron 05/11/2007 at 13h17


    Eric : this will completely disable observers whenever GetText is used. I’m not sure about the solution… did it meant to set a variable to know when you run the updatepo/makemo tasks ?

  7. By george 17/12/2007 at 17h53


    erics methods seems to work. did some debugging and GetText seems to be only defined when the task runs.

Comment Gettext and Rails


RSS