Friday, April 5, 2013

Using Rails’ New I18n Support in Real Life: Part the First


December 11th, 2008 By: Daniel

Well, there are plenty of nice introductions and demos to Rails’ slick new I18n features out there but I haven’t seen much on using it on a real decent-sized app. So I’ll share some of my thoughts on the subject.
Now a real application has more text that just hello_world and a couple paragraphs, so the question is how to organize it all in a way that makes sense. My first thought was to use controllers and actions, but that doesn’t work because sometimes the action doesn’t match the page you are rendering (for example when a create fails validation you probably render the new page even though params[:action] is still ‘create’). So instead I set my namespaces up to mimic my directory/filename structure as much as possible. And in order to make that a lot shorter and cleaner, I wrote a helper method (and put it in application_helper.rb) to help me out:
UPDATE: Rails 2.3 added “lazy” lookup so instead of the ugly __FILE__ stuff you can just prepend a dot. e.g. “.title” I don’t think it woks in controllers though.
  # Translate from file (I18n namespace set to file path)
  def tf(file, key, options = {})
    t("#{file.gsub(%r|#{RAILS_ROOT}/app/\w*/|, '').sub(/\.(.*)$/, '').gsub('/', '.')}.#{key}", options)
  end
So now any page that starts with a title looks like this:
  <h1><%= tf __FILE__, "title" %></h1>
And I can also use it in my controllers (as long as I include ApplicationHelper):
  def create
    @agent = Agent.new(params[:agent])
    if @agent.save
      flash[:notice] = tf(__FILE__, 'create_s')
      redirect_to documents_url
    else
      flash[:notice] = tf(__FILE__, 'create_f')
      render :action => 'new'
    end
  end
And my config/locales/en-US.yml might look like this:
en-US:
  agent_interface:
    agents_controller:
      create_s: "Agent was successfully created."
      create_f: "Error creating agent."
    agents:
      new:
        title: "Create a new Agent"
      show:
        title: "Showing Agent"
      _form:
        legend: "Agent Information"
  layouts:
    agent_interface:
      agent_interface:
        login: "Log In"
        logout: "Log Out"
I like this method because not only does it work for the simple cases shown above but it also works great with layouts and partials and helpers.
I think it also helps to set some conventions for yourself. At first I started naming things with numbers (p1, p2, etc) but then I realized that makes views very cryptic to understand. So instead I tried to pick translation keys that explained the text a little better. And then tried to reuse those names when applicable. For instance, quite a few pages have a title, intro, legend, submit, etc. For flash messages I used the action name with an _s appended for successes and _f for failures. In general I tried to be as consistent as possible.
In future posts I’ll talk about how I handled images, some I18n customizations and how I checked for translation coverage.
P.S. I think it’s pretty ugly to have all those redundant __FILE__s every time I call the helper, but I couldn’t find a good way to get the filename of the file where the method was first called. I tried playing with the stack trace from caller but it got confused on partials and layouts, and I didn’t have the time to find a better way.

No comments:

Post a Comment