How to change locale via URL?
My bilingual Rails 4 application has LocalesController
something like this:
class LocalesController < ApplicationController
def change_locale
if params[:set_locale]
session[:locale] = params[:set_locale]
url_hash = Rails.application.routes.recognize_path URI(request.referer).path
url_hash[:locale] = params[:set_locale]
redirect_to url_hash
end
end
end
The user can change their language through this form:
def locale_switcher
form_tag url_for(:controller => 'locales', :action => 'change_locale'), :method => 'get', :id => 'locale_switcher' do
select_tag 'set_locale', options_for_select(LANGUAGES, I18n.locale.to_s)
end
It works.
However, right now, the user is unable to change the language via the URL.
eg. if the user is on the page www.app.com/en/projects
and then manually changes the url to www.app.com/fr/projects
, they should see the french version of the page, but nothing happens instead.
This may not matter a lot in many Rails applications, but it does matter in my case.
How can this be fixed?
Thanks for any help.
source to share
This is how I did it in one of the Rails 4 applications:
in config / routes.rb:
Rails.application.routes.draw do
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
# rest of your routes here
# for example:
resources :projects
end
end
make sure that in config / environment / production.rb this line is uncommented:
config.i18n.fallbacks = true
If you want to install default_locale
other than :en
, then in config / application.rb uncomment this line:
config.i18n.default_locale = :de # and then :de will be used as default locale
Now, the last part of your setup, add this method to ApplicationController
:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :set_locale
private
def set_locale
I18n.locale = params[:locale] || session[:locale] || I18n.default_locale
session[:locale] = I18n.locale
end
def default_url_options(options={})
logger.debug "default_url_options is passed options: #{options.inspect}\n"
{ locale: I18n.locale }
end
end
Now to your application, you can access: http://localhost:3000/en/projects
, http://localhost:3000/fr/projects
or http://localhost:3000/projects
. The latter http://localhost:3000/projects
will be used :en
as the default locale (unless you make this change in application.rb).
source to share
It might be better to set the locale to routes.rb
like this:
# config/routes.rb
scope "(:locale)", locale: /en|nl/ do
resources :books
end
More details here http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-url-params
UPD. If you also keep the locale for the session, you also need to update it on every request. You can set it in a filter as suggested in another answer. But I prefer to use fewer filters:
def locale_for_request
locale = params[:locale]
if locale && I18n.locale_available?(locale)
session[:locale] = locale
else
session[:locale] || I18n.default_locale
end
end
# then use it in the around filter: I18n.with_locale(locale_for_request)
source to share
If you want this behavior, you will have to compare the URL against the session of each request. One way to do this is as follows:
before_filter :check_locale
def check_locale
if session[:locale] != params[:locale] #I'm assuming this exists in your routes.rb
params[:set_locale] = params[:locale] #Generally bad to assign things to params but it short for the example
change_locale
end
end
source to share