Ruby on Rails I18n - Internationalization Set locale through requests


Example

In most cases, you may want to set I18n locale. One might want to set the locale for the current session, the current user, or based on a URL parameter This is easily achievable by implementing a before_action in one of your controllers, or in ApplicationController to have it in all of your controllers.

class ApplicationController < ActionController::Base
  before_action :set_locale

  protected

  def set_locale
    # Remove inappropriate/unnecessary ones
    I18n.locale = params[:locale] ||    # Request parameter
      session[:locale] ||               # Current session
      (current_user.preferred_locale if user_signed_in?) ||  # Model saved configuration
      extract_locale_from_accept_language_header ||          # Language header - browser config
      I18n.default_locale               # Set in your config files, english by super-default
  end

  # Extract language from request header
  def extract_locale_from_accept_language_header
    if request.env['HTTP_ACCEPT_LANGUAGE']
      lg = request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first.to_sym
      lg.in?([:en, YOUR_AVAILABLE_LANGUAGES]) ? lg : nil
    end
  end

URL-based

The locale param could come from an URL like this

http://yourapplication.com/products?locale=en

Or

http://yourapplication.com/en/products

To achieve the latter, you need to edit your routes, adding a scope:

# config/routes.rb
scope "(:locale)", locale: /en|fr/ do
  resources :products
end

By doing this, visiting http://yourapplication.com/en/products will set your locale to :en. Instead, visiting http://yourapplication.com/fr/products will set it to :fr. Furthermore, you won't get a routing error when missing the :locale param, as visiting http://yourapplication.com/products will load the default I18n locale.

Session-based or persistence-based

This assumes the user can click on a button/language flag to change the language. The action can route to a controller that sets the session to the current language (and eventually persist the changes to a database if the user is connected)

class SetLanguageController < ApplicationController
  skip_before_filter :authenticate_user!
  after_action :set_preferred_locale

  # Generic version to handle a large list of languages
  def change_locale
    I18n.locale = sanitize_language_param
    set_session_and_redirect
  end

You have to define sanitize_language_param with your list of available languages, and eventually handle errors in case the language doesn't exist.

If you have very few languages, it may be worth defining them like this instead:

def fr
  I18n.locale = :fr
  set_session_and_redirect
end

def en
  I18n.locale = :en
  set_session_and_redirect
end

private

  def set_session_and_redirect
    session[:locale] = I18n.locale
    redirect_to :back
  end

  def set_preferred_locale
    if user_signed_in?
      current_user.preferred_locale = I18n.locale.to_s
      current_user.save if current_user.changed?
    end
  end
end

Note: don't forget to add some routes to your change_language actions

Default Locale

Remember that you need to set your application default locale. You can do it by either setting it in config/application.rb:

config.i18n.default_locale = :de

or by creating an initializer in the config/initializers folder:

# config/initializers/locale.rb
I18n.default_locale = :it