A Rack-Middleware to switch I18n.locale
Ruby JavaScript
Switch branches/tags
Nothing to show
Pull request Compare This branch is 8 commits ahead of koenigc:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



This rack-application is used to switch the I18n.locale (in a rails application as middleware).
I developed this app during my 10 Minutes on Rails talks at Sales-Lentz::DevTeam.


Basic functionality

  • Create a new Rack::Request object an extract the params and the session.
  • Instead of using a query string like ?locale=de it is possible to set the locale inside the path.
    For example http://www.example.com/de, http://www.example.com/de/posts/123/comments.
    Using this method should be prefered.
  • store the locale value in session["locale"] if present
  • Set the I18n.locale to whatever is stored inside session["locale"] or, if not present, a defined default locale
  • Removing the locale part from ENV[REQUEST_URI | REQUEST_PATH | PATH_INFO], if there is something like a locale part,
    so that the including App doesn’t see the locale

Available locales and default locale

The next step to make this app more modular was the configuration from the rails app which include this as a middleware.
You can set an array with available locales and you can define a default locale.

The locale get processed as followed:

  • if a locale is given
    • if it matched a available locale it will be stored in the session, otherwise the default locale is used.
  • if no locale is given
    • if a locale is stored in the session, it will be used.
    • if no locale is stored, and the first HTTP_ACCEPT_LANGUAGE matched a available locale it will be used.
  • in any other case the default locale will be used.

The code for extraction the HTTP_ACCEPT_LANGUAGE is taken from the Rack-Contrib Project


The following rubygem’s should be present

  • i18n
  • rack
  • rack-test
  • rspec
  • sinatra
bundle install


copy rack_i18n_locale_switcher.rb to RAILS_ROOT/lib/rack/

insert this in config/environment.rb inside the Rails::Initializer.run do |config| block:

require File.join(File.dirname(__FILE__), '../lib/rack/', 'rack_i18n_locale_switcher')
config.middleware.use Rack::I18nLocaleSwitcher, :available_locales => [:your, :available, :locales], :default_locale => :en

Define the available locales inside the array. The locales should be set as symbols. Also define your preferred default locale.

Changing language proposal

Since the language handling is processed in a Rack- Application the routing in Rails has no knowledge about the locale part inside the path.
So you can’t use the url generation to generate href’s for changing the language.

I created a little helper example to generate these links. The idea is to use the current controller and action and put the new locale in front of it:

"/#{locale}" + url_for( :controller => controller_name(), :action => action_name() )

For two of the RESTful actions (create and update) an exception handler was implemented. If you want to change the language in one of this actions,
the new or edit action get called instead. You can extend this helper if you have other actions which should not be called with GET.

def link_to_locale(*args)
  options = args.extract_options!
  locale = args.first
  name = args.second
  case action_name()
  when "create" ; action = "new"
  when "update" ; action = "edit"
  else  action = action_name()
  url = "/#{locale}" + url_for(:controller => controller_name(), :action => action)
  link_to name, url, options

This helper is integrated in the example application. Inside a view you can call it like this:

- [:en, :fr, :de].each do |locale| 
  = link_to_locale locale, I18n.t("language_#{locale}")

If you have an enhancement or a better solution don’t hesitate to contact me.


To run the test’s just execute

rake spec

I used a FakeApp to simulate this App as a middleware.This is done with the help of RackBuilder

Rack::Builder.app do
  use Rack::I18nLocaleSwitcher
  run Rack::Test::FakeApp.new

You can run the FakeApp directly:

cd spec/fixtures

The FakeApp respond to the following requests:

  • get ‘/’
  • get ‘/home’
  • get ‘/imprint’
  • get ‘/locale’
  • get ‘/test’


The next features that could be implement: