← Home

Upcoming Globalize feature: an alternative way of storing model translations

As the title of Saimons blog post mentions this feature deals with a different implementation of model translations.

With Globalize you'd say, for example:

def Page <ActiveRecord::Base
  translates :title
end

Now, traditionally, your database schema would - regardless of how many languages you'd have to deal with in your application - include a pages table with one column named title, right? This column would hold the base_language translations of titles. All other translations would go into a separate table named globalize_translations - a central storage table for all translations of all your "translates-ed" attributes.

For example, the page title value "I18n the easy way" would be stored directly in the pages table (assuming that English is the base_language of our app). The German translation "I18n leicht gemacht" of this title would go into the globalize_translations table however.

This approach has some obvious advantages like that the translation of your application is entirely separated from the rest of your application/data. But there are some serious drawbacks also, most importantly being less flexible due to the necessity to JOIN in the globalize_translations table. For example, Globalize hasn't been able to support the standard :include and :selectbehaviour of ActiveRecord (which in turn breaks, ahem, ... disables other ActiveRecord default stuff like has_many :through).

Enter Saimons Alternative Model Translations ...

Saimons approach turns things around from a traditional Globalize perspective. His alternative implementation of ActiveRecord::Base#translates:

  • requires you to store your translations directly in the model tables on the one hand but
  • allows you to leverage all the ActiveRecord::Base::find functionality you're used to without any restrictions on the other hand (such as not being able to use :include and :select in the traditional approach)

As Saimon told me you'll be able to switch this behaviour on by putting Globalize::DbTranslate.keep_translations_in_model = true in your environment.rb. But you'll also be able to overwrite this per model, in class scope: self.keep_translations_in_model = true.

To store the translation data directly in the model table you have to have a column per supported language per translated attribute. That is, for the title attribute above, we'd have three db columns when there are three languages supported:

class CreatePages <ActiveRecord::Migration
  def self.up
    create_table :pages do |t|
      t.column :title, :string
      t.column :title_de, :string
      t.column :title_fr, :string
    end
  end
end

It's been noted that this might get a bit cumbersome as soon as you've more than very few attributes to translate and very few languages to support. (On the other hand, one might come up with some automation support based on ActiveRecord::Migrations to manage this kind of stuff.) Also, what if you want to add new languages in an entirely dynamic way, e.g. through a web interface? Well, of course, as always ... peoples mileages vary and there's no silverbullet to whatever job you're trying to get done.

What Saimons approach is buying you is the removal of a beforehand necessary JOIN. Depending on your situation - that's probably quite a lot.

Also, as a side-effect of this all the translations will be pre-loaded with your models, so switching the locale won't result in an additional database hit.

PS: To be honest, I'm writing this after only having read Saimons article, the code in the Globalize SVN etc. I haven't played around with this myself yet.

I'd love to see your comments though!