Localizing your Rails 2.2 Application
Ok, so rant over, let’s see how you do it:
1. Preparation
First thing you want to do is set what your default language will be. In environment.rb set the flag:
config.i18n.default_locale = :en
You’re going to see i18n a lot - it stands for Internationalization (I -> nationalization (18 characters) -> n)
In Rails 2.2 there’s a new directory for handling the different locales: /config/locales. In here you put YAML files pertaining to each locale you have translated. The naming convention of each of these files follows the locale name (e.g: en.yml, it.yml etc).
2. Translate your application into your default language
Next (oddly enough) you’ll need to translate your entire application into English (or whatever language your app is already). This means finding all the places where you have fragments of English and replacing it with something that looks like this:
t("snippet_name")
The t is shorthand for I18n.translate - a new method that handles translation. It takes as a parameter a string refering to a key in each of your locale yaml files. The yaml file can have nested keys, so, main.title finds the title key belonging to the main key in the yaml file. e.g.
main:
title: "Some title"
You can use string interpolation as well. So if you want to translate something that looks like this:
<%= "Welcome to the site #{user.name}!" %>
You instead use the t method and pass along the variable you want to use:
t("title", :name => user.name)
And refer to this variable within your locale yaml file with double braces like this:
title: "Welcome to the site !"
Ok, after you’ve translated your application into English (which is the most boring part of the process) you’re ready to translate it into other languages!
3. Translate your application into other languages
Your newly translated yaml file should hopefully look something like this (this is a copy of one from whatiwantforxmas.com:
en:
main_title: "What I want for Xmas"
countdown:
merry_xmas: "Merry Christmas!"
days_until_xmas: "Only until Xmas"
days: "days"
day: "day"
# snipped!
The next step is sending it to your translator to…erm…translate it! Here’s what the above looks like in Italian:
Put this into the locales/config folder with a name that corresponds to the correct locale, and you’re on your way to a translated site!
4. Add ActiveRecord, Currency, Date and Time translations
There’s more to translation then just the phrases and text on your pages. There’s also:
-
- time differences
- date differences
- currency differences
- all the text that comes with ActiveRecord validations
This is the easiest bit to implement, thanks to Sven Fuchs and a bunch of translators. Look at the code at http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale and you’ll find sample yaml files that contain all this stuff for tonnes of languages (including Australian, strewth!). Simply copy this code into your own locale files.
5. Provide a way to switch between different locales
So, now you’ll need to add the ability to switch locales.
The important magic variable that sets this is: I18n.locale. You want to set this to the locale string that your yaml file is named after. For instance:
I18n.locale = :en # English!
I18n.locale = :it # Italian!
For my application I added a before_filter in application.rb that sets the locale depending on:
* whether the user is logged in and has a locale
* whether the locale is stored in the session
* failing that, use the default_locale – although this step could be unnecessary, but makes me feel comfortable.
# in Application.rb
before_filter :set_locale
def set_locale
I18n.locale = (current_user.locale if current_user) || session[:locale] || I18n.default_locale
end
I also had a locales controller that allowed for an easy method to switch locales: I also included a locales controller for switching locality.
# locales_controller.rb
class LocalesController < ApplicationController
def show
if current_user # if I'm logged in
current_user.locale = params[:locale] # change my locality
current_user.save
end
session[:locale] = params[:locale]
redirect_to :back
end
end
# routes.rb
map.locales 'locales/:locale', :controller => 'locales', :action => 'show'
The beauty of this is that you can send links that represent different translations of your application:
www.yoursite.com/locales/it #=> Italian version of the site
www.yoursite.com/locales/en #=> English version of the site
6. Add tests to ensure your translation works
If a key is missing from a translation then rather irritatingly you’ll be greeted with something like this:
<span class="translation_missing">en, missing_key</span>
This looks pretty awful, so you’ll want to make sure that this doesn’t happen for each of your pages.
Here’s some sample functional test code for this:
['it', 'en', 'de'].each do |locale|
define_method "test_no_missing_translation_for_locale__#{locale}__" do
@request.session[:locale] = locale.to_s
get :index
assert_select "span.translation_missing", false
end
end
This is a bit of a painfully slow way to test this though. (If someone can come up with a better way of testing this let me know!)
Localizing Tips
Localizing your app can be a frustrating experience. Here’s some tips to ease the pain:
Repeat yourself
Resist the urge to DRY up your yaml snippets and have them used in several different locations. Different languages could translate your snippets differently depending on context.
Provide context for your translators
Your translators need to know a little bit about the context that the text represents. Write comments in your yaml file to make it easier for them.
Avoid the pluralize method
Right now, you can’t trust the pluralize helper method if you’re using localization. (i.e. it tried to say that the plural of giorno (day) is giornos (it’s actually giorni) Instead, provide keys in your translation yaml files asking for the plural and singular words, and write a method for dealing with the plural.
Adopt some sort of convention in your yaml files for links that are embedded in other text
For example, if you wanted to write
Sign up if you want to join yoursite.com
I would separate this out in the yaml file like this:
sign_up: " if you want to join yoursite.com"
sign_up_link: "Sign up" # this is the text used in the 'link' variable above
Just because the “Sign up” appears at the front of the sentence in English, doesn’t make it so in another language.
What’s this Yaml thing?
Ideally you’ll want to get your translators to translate the yaml file. Beware though that yaml is a strange file format to non-technical people. Let your translators know here do get a file editor that they can use (SciTE for example. Grab it at http://prdownloads.sourceforge.net/scintilla/Sc177.exe)
But is it REALLY right
After getting the translation, walk through the website with the translator to make sure it’s REALLY right. Sometimes things translate differently when the context changes. I also noticed that some special characters sometimes got mixed up, so this is a good opportunity to spot those.
Further reading
* As usual, Ryan Bates is on the scene with an excellent RailsCast: http://railscasts.com/episodes/138-i18n
* “Can’t read, won’t buy - Why Language Matters on Global Websites”: http://www.lionbridge.com/lionbridge/en-us/kc/globalization/cant_read_wont_buy.htm
* Locale examples (where you can find sample yaml files for tonnes of languages): http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale
http://advent2008.hackruby.com/past/2008/12/16/rails_22_internationalization.html