Simple Localization in Rails 2.2
July 21st, 2008 by TrevorI've been staying on the sidelines when it comes to localization in Rails for a while now, but I couldn't help getting excited about the upcoming native support in Rails 2.2. So, with some guidance from the Rails i18n team, I decided to give things a try.
I've been extremely pleased with the results so far, but I'm all ears if anyone would like to offer suggestions on how to better achieve basic localization for a Rails app. Here's where I'm at so far in a kind of how-to format. This is all plugin-free, using only what's available in core. I expect that plugins will be coming out to add features and functionality, but you can accomplish quite a bit without any extras.
You can try to follow along, or just get the gist be reading through the steps. As noted in the comments, this is just a proof of concept, is not secure, and shouldn't be used in production as-is.
1. Make a new Rails app and freeze edge:
~ $ rails i18n ~ $ cd i18n ~ $ rake rails:freeze:edge
2. Make a couple of translation stores (files) in lib/locale directory:
# lib/locale/en-US.rb { 'en-US' => { :hello_world => "Hello World", :hello_flash => "Hello Flash" }} # lib/locale/pirate.rb { 'pirate' => { :hello_world => "Ahoy World", :hello_flash => "Ahoy Flash" }}
3. Set I18n.locale with a before_filter:
# app/controllers/application.rb class ApplicationController < ActionController::Base before_filter :set_locale def set_locale locale = params[:locale] || 'en-US' I18n.locale = locale I18n.load_translations "#{RAILS_ROOT}/lib/locale/#{locale}.rb" end end
4. Make a controller and route to test things out, using symbols from your translation for user messages:
# config/routes.rb ActionController::Routing::Routes.draw do |map| map.root :controller => 'home', :action => 'index' end # app/controllers/home_controller.rb class HomeController < ApplicationController def index flash[:notice] = :hello_flash end end
5. Create a view using symbols for user messages and use the "t" helper to translate:
# app/views/home/index.html.erb <h1><%=t :hello_world %></h1> <%=t flash[:notice] %> <%= link_to 'en-US', root_path(:locale => 'en-US') %> or <%= link_to 'pirate', root_path(:locale => 'pirate') %>
6. Fire up the old script/server and check it out:
~ $ script/server


I think that about covers it. Of course, this is a very simple example, but it should cover the basics well enough to get started. Please let me know if you have any ideas about how to simplify/improve this, and thanks again to the Rails i18n team for all of their work - everything looks great so far!
Update: You can use YAML to store translations now. Also, the I18n.populate and I18n.store_translations are no longer necessary (or available).
# lib/locale/pirate.yml pirate: hello_world: Ahoy World hello_flash: Ahoy Flash # app/controllers/application.rb I18n.load_translations "#{RAILS_ROOT}/lib/locale/pirate.yml"

Thanks for posting this, step by step instructions is exactly what we need.
One thing though, isn’t it dangerous to toss params directly into a “require” statement? This gives them access to run pretty much any ruby file on your system.
I believe it also allows them to fill up your memory if they keep requiring files at slightly different relative paths since Ruby thinks it’s a different file. Yeah you could make sure “..” isn’t in the string, but this still seems like a bad idea.
I would prefer to require the translation files you need to support outside of the request entirely (maybe on startup). Not sure how that would work exactly.
Good point. I’ll add a note in the post to make clear that this is just a proof of concept and shouldn’t actually be used in production.
I was thinking of using validates_inclusion_of to check that the language chosen was valid, and storing that as an value in the users table (as you would with a time zone).
Allowing a user selected language would mean you’d need to do something like this for each request, though, I believe. I’m also not sure if the languages should just be put into a single file and loaded all at once. I’m thinking that people won’t change the language much, though, so having separate files probably makes sense.
Anyway, we’re making progress at least :)
Loading the translations could be done in an initializer. Then you could have an app/locales directory and in the initialized simply require all the files in that directory. But it would be nice to be able to lazy load the translation files.
Wouldn’t be better if the translations were loaded in a initializer, and then selected in the before filter? (We would need a method to know which languages were loaded too).
Thanks for writing this tutorial: it focused the right points, it was what we need now!
Thanks for the writeup. I had read Sven’s post but was unclear about how it was to be used. This helps a lot.
A possible way to get around the require/params problem: Store the possible language options in an array or hash at startup. Match the user params against the array, if it exists in the array, require. Otherwise, decline.
Should set_locale not take request.accept_language into account? There’s no point in serving en-US by default if the user agent specifies that it does not want to see english in the first place.
John, this is just a simple example meant to illustrate the basics. Of course there is plenty of room for improvement. If you’re really interested in localization, you should check out the Rails i18n Google Group: http://groups.google.com/group/rails-i18n
There is no “t” helper and you have to make your own because the authors wanted to avoid the very fruitless flamewar discussing which one of those one-letter (or two-) method names is better: t() _() or [].
[...] Simple Localization in Rails 2.2 - A look at the coming feature. (via RubyFlow) [...]
_@Ryan:_ It’s actually fairly easy to strip the locale loading from request cycle, see here: http://github.com/karmi/rails_i18n_demo_app/tree/master/config/initializers/locales.rb . Rails apparently loads the hash structure (available in `I18n.backend.send(:class_variable_get, :@@translations)` for inspection.)
Then you can just set the locale in the controller filter: http://github.com/karmi/rails_i18n_demo_app/tree/master/app/controllers/application.rb#L13 (and even perform some check like `available_locales.include? user_submitted_locale`
Thanks Trevor for this great write-up!
Karel
Hello,
I’m designing a web application that needs to be translated in English and Finnish. I am using the approach presented in this post to do the localization. However, when I try to display form error messages with I get a “can’t convert Array to String” error if I use any other language than English (default en-US).
I’ve tried to also create fi.rb -files in active_record, active_support and action_view -folders but this seems to not be enough. What should I do?
Juho, I’m not sure. Check with the Rails i18n team in their Google Group:
http://groups.google.com/group/rails-i18n
Juho, I had a similar problem. It seems to be some kind of bug, and I can’t even figure out what it is well enough to file a report. However, if you provide rails with translations of all the activerecord error messages for your target locale, the error goes away.
The list of ‘railties’ available for translation can be found here:
http://rails-i18n.org/wiki/pages/translations-available-in-rails
From there, just make a file with an I18n.store_translations call and all the messages you need to translate, and require it (either with a before_filter or in an initializer). This seems a little convoluted right now, I think the ideal would be to make a plugin that grabs a YAML translation file and makes the store_translations call based on that.
While the possibilites of the new I18n as far as multilingual applications go are very impressive, I think the first thing we need to do is develop a simple framework for people who just want to localize rails to one non-english language, using the new API. That would allow us to get all of the “translating rails messages” bit out of the way and allow people to focus on whatever specific translation their app requires.
Louis, thanks! That solved my problem.
Thanks for writing
[...] ??? Rails-2.1.1??????????? - Hello, world! - s21g Riding Rails: Rails 2.1.1: Lots of bug fixes ???????Ruby on Rails?????????Rails 2.2???????? | ???????? | ????????? Simple Localization in Rails 2.2 - almost effortless [...]