为什么在Rails中将语言环境设置用作全局语言(使用Thin时)?

问题描述:

我刚刚意识到建议的Rails设置控制器语言环境的方法

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

全局设置语言环境.上面的代码有效,但是我想知道default_locale是否真的是默认值,如果您必须显式地键入它?

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

,默认情况下使用I18n.default_locale.理想情况下,这将匹配路径中的可选语言环境:

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!

难道没有潜在的竞争条件,因为一个请求可以更改全局I18n.locale,而另一个请求(已预先设置了另一个语言环境)处于渲染过程中?

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?

更新:我现在从I18n文档中发现了一些细节:

UPDATE: Some details I found for now, from the I18n documentstion:

伪全局设置当前语言环境,即在Thread.current哈希中 def locale =(locale)

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.

更新2:参见我的答案以获取解释.

UPDATE 2: See my answer for explanation.

现在是最终答案. TL; DR :仅当您使用Thin和Puma等线程化Web服务器时,设置区域设置才能用作全局设置.

So now the final answer. TL;DR Setting locale acts as global only when you use threaded web servers, like Thin and Puma.

正如我提到的,I18n.locale=

伪全局设置当前语言环境,即在Thread.current哈希中

Sets the current locale pseudo-globally, i.e. in the Thread.current hash

因此,它应该是按请求的,并且在Webrick和Unicorn中以这种方式工作.

So it is supposed to be per-request, and it works this way in Webrick and Unicorn.

但是,如果您使用Thin或Puma之类的线程Web服务器,则该线程的寿命似乎更长,并且该值将保留以供将来使用,直到对其进行显式更改为止.我从新的史蒂夫·克拉伯尼克(Steve Klabnik)的宝石 request_store :

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:

如果您需要全局状态,则可能已经达到Thread.current.

If you need global state, you've probably reached for Thread.current.

< ...>

<...>

所以人们正在使用那些精巧的线程Web服务器,例如Thin或Puma.但是,如果您使用Thread.current,并且使用其中一台服务器,请当心!值的保留时间可能比您预期的长,这可能会导致错误.

So people are using those fancy threaded web servers, like Thin or Puma. But if you use Thread.current, and you use one of those servers, watch out! Values can stick around longer than you'd expect, and this can cause bugs.