Friday, August 24, 2007

Rails Plugins: Globalize

Imagine that you've built yourself a small application in Ruby on Rails. Here's the good news: there are all sorts of ways to simply add significant functionality to your Rails applications through plugins. Let me introduce you to a few.

I've been investigating the Globalize and Acts As Audited (acts_as_audited) and Acts As Versioned (acts_as_versioned) plugins for Ruby on Rails over the past few days. I've got a lot to share, so I'll break it up into a few posts.

Let's start by imagining that you've built yourself a really, really simple model:
You've cut a customer model class with a unique identifier and a name. Now, let's add to that.

Globalize
If your application needs to operate in a multilingual or global environment, you'll be pleased to discover that not only is Globalize a general-purpose internationalization and localization (i18n/l10n) mechanism for Ruby on Rails, but it can even translate your model classes.

If you're going to translate fields within your model, you should be aware that it has two storage models for the translated information. Historically, it did this by storing the translated text in a separate table, globalize_translations. I'll call this the external storage model.

Using our model from above, the name field corresponds to the name in the base language (whatever you decide that is; en-US is a common choice in this part of the world), and all other translations are stored in the globalize_translations table as follows:

When you load or save your model class within the context of a particular Locale, Globalize does its magic behind the scenes to make sure that the text is loaded from or saved to the appropriate spot -- base language text goes to the primary table, and text from any other language goes to the translated-text table.

As often seems to be true, this magic interferes with some of ActiveRecord's default mechanisms, and :select and :include clauses stop working as advertised. There are workarounds, apparently.

However, in recent versions of Globalize, you have another choice - you can store the translations directly in your model, as follows:

This interferes less with ActiveRecord, although it's better-suited to applications that need to work with a known number of languages up front, rather than an unknown and changing set of languages, as each language addition requires new columns for every translatable field. It has a few caveats of its own.