Neil Middleton

Internationalisation with Rails

Over the past few weeks I’ve been spending time working on applications which require internationalisation.

It’s something I’ve done before, but not to the level that the requirements for this current project requested. Therefore I’ve had to do a certain amount of figuring out and prototyping to figure out some of the edge cases, but I think now I have a relatively good pattern that can be repeated further down the line on other projects.

First of all though, what is Internationalisation (or I18n)? Well, firstly it’s part of two larger topics, that and Localisation (L11n). So what’s the difference? Well to quote wikipedia:

Localisation

A localized system has been adapted or converted for use in a particular locale (other than the one it was originally developed for), including the language of the user interface (UI), input, and display, and features such as time/date display and currency.

Internationalisation

An internationalized system is equipped for use in a range of “locales” (or by users of multiple languages), by allowing the co-existence of several languages and character sets for input, display, and UI.

From these two definitions it’s easy to see that we have a variety of topics that need worrying about other than the language of the words on the page. These include:

  • How dates are formatted
  • Which direction the text is written in (whilst English is left to right, there are several right to left, and even top to bottom languages)
  • How numbers are formatted, including currency and so on

…and the list goes on.

Translations

So, how as a Rails developer do we tackle this? Well, firstly we’re treated by the fact that Rails 3 has a very capable and mature I18n API which is pleasant to use.

For instance, in simple terms we can introduce other languages into a page, but setting a locale specific version of a particular string:

/config/locales/en.yml


en:
  hello: "Hello world"

/config/locales/es.yml


en:
  hello: "Hola, mundo"

Now, depending on the value of I18n.locale, Rails will allow us to pull the appropriate string with the simple translate helper:


I18n.locale = :en
I18n.t(:hello)
=> "Hello world"

I18n.locale = :es
I18n.t(:hello)
=> "Hola, mundo"

Awesome right?

Dates and numbers

So, what about dates, and prices etc? Well, luckily that’s pretty simple too, as I18n is baked right down into Rails. For instance, all active record errors, date displays and the like are checked for translations prior to render. For instance, let’s consider dates. The way that we show dates over in the UK is different to, say, the US. Where we like to do 23rd March 2012, the US likes to do March 23, 2012…

So, by default, Rails will use the US version, as that’s it’s spiritual home, which means we need to provide details to Rails of how to render dates over here in the UK:

/config/locales/en_GB.yml


en-GB:
  date:
    formats:
      default: ! '%d-%m-%Y'
      long: ! '%d %B, %Y'
      short: ! '%d %b'

Now, that we have this config, we’ll now get the correct dates in the UK. (Note the en_GB locale, you can have more than one country which uses a language ;) ).

So, we can now localise all of our dates and numbers, but that’s a fair amount of effort to do – there’s hundreds… Well, luckily that’s already been largely done for us via the Rails-I18n gem. This gem provides pre-baked locale files for all of the popular locales, and loads more besides. They contain translations and formats for dates, numbers, times, activerecord errors and more. Very useful stuff. Simply dropping this into your application will automatically provide you with a massive amount of localisation out of the box.

Data

So, we’ve now got our pages localised and we can flip between them happily serving all sorts of nationalities, but unfortunately our English data is still showing through. We’ve got a database full of products which are all in English being shown to a French audience – not so good to them.

Well, the solution I’ve found works well here is the Globalize3 gem. This gem allows you to add translations for a given ActiveRecord model in a nice simple matter. What’s more you can treat them as nested attributes for simple editing and updating:

/app/controllers/products_controller.rb


def edit
    @product = Product.find_by_id(params[:id])
    Locale.all.each do |locale|
      @product.translations.build locale => locale.code
    end
end

/app/models/product.rb


has_many :translations
accepts_nested_attributes_for :translations

/app/views/products/edit.erb


<%= form_for @product do |f| %>
  <%= f.text_field :name %>
  <%= f.fields_for :translations do |t| %>
      <% if t.object.locale != :en %>
           <%= f.text_field :name %>
      <% end -%>
  <% end -%>
<% end -%>

Now, there’s a few things going on here. Globalize3 is taking the translations for a model as optional, this is because it will fall back to the default locale when a translation is not available (hence the exclusion of :en in this particular example). Secondly, notice that as these are nested attributes you can stick them in the same form as the main data itself making it nice and simple to manage.

When showing data Globalize3 will respect the value of I18n.locale and render the appropriate translation for a record if available. This means that if you have translations, your front end will show them with no intervention from yourself, which is always a bonus.

So – now we have a full set of I18n complete. We can localise the dates, numbers, errors and so on that emanate from Rails, whilst also being able to provide translated data on a needs basis, and all without too much pain and suffering.

For more detailed information on the Rails I18n API see the Rails Guide. For more information on Globalize3, see the Github Page

See more posts

Comments: 3

Samir
commented on

Thanks Neil, but, what about those languages that starts from Right To Left ? is there a recommended approach to handle them beside languages that starts from Left to Right ?

I know that CSS is the answer, but, I always prefer to hear from experts on the subject before I dive!

Neil Middleton
commented on

Yess, CSS is the answer here. This blog post is more aimed at the backend side of things rather than how to present the language itself. Bear in mind RTL languages aren't the only problem as the direction of the text quite often will also affect the design itself and how it must be built.

Robin
commented on

You can either load a completely separate CSS file once you’ve done your back-end localisation. Alternatively, HTML has the dir attribute that you can tag styles onto. For example:

p { color: red; }

html[dir=rtl] p { color: blue; }

Add a comment

Note: comments are moderated before publication.

Most Popular

Kyan.com colophon

Robin Whittleton

Now that our new site is live, I can finally talk about development decisions we made. The site last had a makeover in mid-2008 so what we can do has moved on quite considerably, and we’ve tried to take advantage of that where possible.

"DO NOT EAT" THROW AWAY

Steven Wake

I have the driest draw here at Kyan towers. You see, I am the proud owner of a Silica Gel collection. There is just something about them which compels me to not throw away the little fellas.

Web Meet Guildford, join us for a drink

Paul Sturgess

It’s been over a year now since we moved to Guildford and we’re really feeling settled in our new home on the High Street. We’ve got our artwork on the walls, an arcade machine setup and we’ve even hosted a live gig. However, one thing we haven’t done yet is meet our fellow web …

Now residing at 171 High Street, Guildford

Peter Roome

NEW ADDRESS: Kyanmedia, Guildford, 171 High Street, Guildford, Surrey, GU1 3AJ Yes thats right, we have made the big move, a week earlier than the scheduled 24th July. The impromptu decision was made mid morning Friday (17th July, 2009) after discovering, Smithbrook was…