I just realized that the recommended Rails way to set locale in your controller
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
sets the locale globally. The code above works, but I wonder is default_locale really default if you have to type it explicitly?
What I’d expect is to have a locale per request (like we have session per request) and doing something like:
def set_locale
locale = params[:locale] if params[:locale]
end
And having I18n.default_locale used by default otherwise. This would match ideally the optional locale in path:
# config/routes.rb
scope "(:locale)", :locale => /en|nl/ do
resources :books
end
For now if for some reason I skip locale setting in some action it uses the locale set in the previous request which could be from another user!
And isn’t there a potential race condition as one request can change global I18n.locale while another request (having set another locale beforehande) is in the middle of rendering?
UPDATE: Some details I found for now, from the I18n documentstion:
Sets the current locale pseudo-globally, i.e. in the Thread.current hash
def locale=(locale)
Now I want to understand if every request is a separate thread.
UPDATE 2: See my answer for explanation.
So now the final answer. TL;DR Setting locale acts as global only when you use threaded web servers, like Thin and Puma.
As I mentioned,
I18n.locale=So it is supposed to be per-request, and it works this way in Webrick and Unicorn.
But if you use threaded web server like Thin or Puma, seems that the thread lives longer, and the value is preserved for future requests, until it is changed explicitly. Where I learned it is from the new Steve Klabnik’s gem request_store: