Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails locale not persistent

I am trying to develop a dual language app.

However, I found that the locale change is not persistent by the default i18n gem.

I have this index page and provided a link to change the default language.

<% if I18n.locale == I18n.default_locale %>
 <%= link_to "English", :locale=>'en'%>
<% else %>
 <%= link_to "Français", :locale=>'fr'%>
<%end%>

The problem is that when I login to another page, the locale falls back to the default language.

So, I just wonder how to keep the user language choice and using that locale persistently until the user logs out.

Thanks in advance.

like image 299
The questioner Avatar asked Aug 15 '14 15:08

The questioner


3 Answers

I recommend you to read this Rails guides about I18n to understand how I18n works in Rails. It's obvious that change of I18n.locale is temporary, and will be reset per session.

According to your requirement, you can store the selected locale in session. Here is the sample code:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_locale

  def set_locale
    I18n.locale = params[:locale] || session[:locale] || I18n.default_locale
    session[:locale] = I18n.locale
  end
end

Then you can clear the session[:locale] when user signed out.

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def sign_out
    # ...
    session.delete :locale
  end
end
like image 124
Blue Smith Avatar answered Nov 17 '22 00:11

Blue Smith


If you look at docs it says

You may be tempted to store the chosen locale in a session or a cookie, however do not do this. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you.

If you set your locals in domain name or url then it has several advantages

1. The locale is an obvious part of the URL.
2. People intuitively grasp in which language the content will be displayed.
3. It is very trivial to implement in Rails. 
4. Search engines seem to like that content in different languages lives at different, inter-linked domains.

Fix

a. You can pass locals in urls and then set it a filter:

In this case your url will look like www.example.com/books?locale=ja or www.example.com/ja/books then in application controller you can set your local in a filter

before_action :set_locale

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end 

To include an explicit option in every URL (e.g. link_to( books_url(locale: I18n.locale))) would be tedious and probably impossible so you can override rails default_url_options by something like this in application controller

# app/controllers/application_controller.rb
def default_url_options(options={})
  logger.debug "default_url_options is passed options: #{options.inspect}\n"
  { locale: I18n.locale }
end

This will allow every helper method dependent on url_for (e.g. helpers for named routes like root_path or root_url, resource routes like books_path or books_url, etc.) will now automatically include the locale in the query string, like this: http://www.example.com/?locale=ja.

but if you prefer to use your urls to look like www.example.com/en/books for that you'll need to override default_url_options method as well as you'll have to scope your routes like this:

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

b. Setting the Locale from the Client Supplied Information:

People may set Accept-Language in their browser or other clients (such as curl) and you can use utilize it like this in application controller:

def set_locale
  logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
  I18n.locale = extract_locale_from_accept_language_header
  logger.debug "* Locale set to '#{I18n.locale}'"
end

private
  def extract_locale_from_accept_language_header
    request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
  end
like image 31
Mandeep Avatar answered Nov 17 '22 01:11

Mandeep


Good practice is to set locale and add that locale to default_url_options a in ApplicationController :

before_filter :set_locale

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end 

def default_url_options(options={})
  { locale: I18n.locale }
end

Then add scope to route.rb:

scope "/:locale", locale: /en|fr/ do
  your routes goes here
end

And in your layouts/application.html.erb:

    <%= link_to "English", { locale: :en }%>

    <%= link_to "French", { locale: :fr } %>

Have a nice day.

like image 2
Ivica Lakatoš Avatar answered Nov 16 '22 23:11

Ivica Lakatoš