← Home

Five minutes instant overview - Get on Rails with Globalize! Part 1 of 8

This article is part of my series "Get on Rails with Globalize". At the end of this article you'll find a summary of what will be discussed in the next article of this series.

Start your engines, please!

You'll need to have a working Rails app up and running to check things out. If you start a fresh Rails app from scratch make sure that your conf/database.yml knows about your database, valid credentials etc. and everything else is nice.

Now, make your way to your Rails app directory and install the plugin from the Globalize Subversion repository. (Take care to choose the right version. As of writing there's a branch for Rails 1.1 while trunk is not compatible with 1.1.)

script/plugin install svn://svn.globalize-rails.org/globalize/

And if you're keen to keep your stuff straight you'll probably want to rename the directory:

mv vendor/plugins/for-1.1 vendor/plugins/globalize

Now just do:

rake globalize:setup

... and you're already done with the setup!

(If you're curious, check your database! You'll find three new globalize_* tables with 239 countries, 186 languages and 3420 pre-translated strings ... including several languages I'd bet you've never heard of like Avar, Manx or Wolof!)

Configuring Globalize

That's it. You're globalized now. Welcome to the club :-)

There's nothing more to do than include the library and set the base_language for your Globalize instance.

In environment.rb this should do the job:

Rails::Initializer.run do |config|
  # ...
include Globalize # put this here
Locale.set_base_language('en-US') # and here :-)

Don't forget to restart your server after changing the environment.rb file in case it's already running.

You're done.

The meaning of the base_language is that this language is what Globalize treats as the "primary" language - which means that data stored in your model tables is stored in the primary language whereas translations are stored in the globalize_translations table.

Joshua Harvey explains it like this:

[In the database] "... base language entries are stored in the parent table itself, as opposed to the globalize_translations table. So if you have a table called 'products', and your base language is English, the English product name would be stored in products.name, whereas the German translation would be stored in globalize_translations."

And thus: "It's important to never change the base language once you've started populating the database." So choose your base_language carefully.

Dates, Time, Numbers: a question of form

Already impatient to check out some working stuff? I were, too ...

There's a nice example in the Globalize wiki using Rails Unit tests and fixtures. For now we'll check things out on the console. So, fire up your console (i.e., within your Rails app directory, do: script/console).

The api documentation for Time.localize() says that this method "acts the same as strftime, but returns a localized version of the formatted date/time string".

Ok, let's check that out:

>> Time.now.localize("%d. %B %Y")

which should give you:

=> "21. June 2006"

You're probably not overly surprised about this. ;-) But in fact this means that Globalize is already at work here. The string "June" comes from the Globalize database tables and represents the English full name of, well, the month June as requested by %B.

So let's try to change the current language setting to something else, say: Spanish. I have no idea if this format would actually be used in Spain but you get the point:

>> Locale.set("es-ES")
>> Time.now.localize("%d. %B %Y")
=> "21. Junio 2006"

(Beware that the pre-translated data that Globalize is coming with is not complete. For example when you try the same example with the German locale de-DE than instead of the expected "21. Juni 2006" you'd actually get "21. June 2006" because the German translation is missing so far.)

You can format Integers, Floats, Dates, Times with localize. Just like Time.localize, Date.localize mimics strftime's behaviour.

When you call localize on an Integer it will return "the integer in String form, according to the rules of the currently active locale" - and the same applies to Floats:

>> Locale.set("de-DE")
>> 123456.localize # format an Integer
=> 123.456
>> 123.456.localize # format a Float
=> 123,456

Also, you can use the alias loc instead of localize for convenience.

ViewTranslations: Time for .t

What's the most basic purpose of I18n tools? It's the ability to translate arbitrary text, isn't it? Text that belongs to the application tier itself and most likely won't change that often once the application is out of the door: view templates, mail templates, error messages, flash notices, ... hardcoded stuff mostly.

Globalize adds a new method translate to Rubys String and Symbol classes that will do all the heavy lifting for this type of translation (which is called "ViewTranslation" internally). Also, there's an alias t so that your templates don't get too cluttered.

Let's see that in action ...

>> Locale.set("de-DE")
>> "Welcome".t
=> "Welcome"

What the heck? We'd expect to see the result "Willkommen" here, wouldn't we? This doesn't seem to work, right? Well, actually, it does.

We need to provide Globalize with the translation of "Welcome" first of course (and we'll do so in short - so stay tuned). You'll most probably develop your Rails application in one (base) language and add your translations afterwards.

Besides that this perfectly demonstrates Globalize's behaviour when it encounters a string that it doesn't know yet:

  1. Instead of throwing an error it will simply return the original string and
  2. it will remember the string for you by dumping it into the database (so you can look it up later).

If you're curious go and look up the globalize_translations table. You'll find that there's a new record for the key "Welcome" - waiting for translation. Globalize remembers strings that are used in your application by dumping them in the database:

>> Translation.find(:first, :order => "id DESC")
=> #<Globalize::ViewTranslation:0x275c410 @attributes={ ...
   "tr_key"=>"Welcome", "text"=>nil,
   "type"=>"ViewTranslation", ...}>

(In case you expected a method _() like that's common in classic gettext libraries: yes, you can use that one, too. Globalize adds a (although deprecated) method _() to Rubys Object class so you can call it "globally" and in turn it will call Locale.translate then.)

How to add ViewTranslations

Ok, let's teach Globalize how to welcome people in German. That's as simple as:

Locale.set_translation('Welcome', 'Willkommen')

Or alternatively:

lang = Language.pick('de-DE')
Locale.set_translation('Welcome', lang, 'Willkommen')

So finally Globalize will come up with:

>> Locale.set("de-DE")
>> "Welcome".t
=> "Willkommen"

Of course in your templates, you'd use something like this:

<%= "Welcome".t -%>

Now, that's waaay cool ... really. Think about it for a moment: Globalize lets you append .t to any string which will transparently lookup and find available translations. In case there's no translation available it will simply return the original string (which seems to be the default behaviour most t10n/i18n tools show). As soon you've added a translation it will be used.

ModelTranslations? Just add hot water ...

Now, so far that's basically about date formats and static strings like those in your templates. What about your models, you wonder?

Globalize comes with the capability to transparently translate any attribute of your ActiveRecords Models ... all you have to do is add a translates directive to your model class like this:

def Page < ActiveRecord::Base
  translates :title

As soon as there actually is a translation for the title attribute in the database it will be used - otherwise, like above, the base language's value will be used by default. (Also, an empty record will be saved to the database so that you can check back for strings that need to be translated).

Now how do you tell Globalize about a model translation? Simply by saving the model. Like this:

page = Page.create!(:title => 'Welcome to Globalize!')

page.reload # we'd get a Globalize::WrongLanguageError here w/o this
page.title = 'Willkommen bei Globalize!'

Ok, cool. Let's sum things up.

You'll need two fingersnips to get your Rails app globalized: install the Globalize plugin and use it :-). Using it is as simple as 1-2-3, too:

  1. There's .t for ViewTranslations (arbitrary, static text),
  2. there's the translates directive for ModelTranslations (ActiveRecord) and
  3. there's .loc to localize your date, time and number formats.

And of course there's more ...

So much for the basics.

Of course there's much more to discuss. In Part 2 of the series "Get on Rails with Globalize" we'll talk about Some common questions on getting started like:

  • How to setup your application to use Unicode
  • How to select and persist the current user's locale
  • How to translate Rails ActiveRecord messages
  • How to localize entire templates