为什么 Rails 中的区域设置充当全局(使用 Thin 时)?
我刚刚意识到在控制器中设置区域设置的推荐 Rails 方法会
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
全局设置区域设置。上面的代码可以工作,但我想知道如果您必须显式键入它, default_locale
真的是默认的吗?
我期望的是每个请求都有一个区域设置(就像每个请求都有会话一样)并执行以下操作:
def set_locale
locale = params[:locale] if params[:locale]
end
并默认使用 I18n.default_locale
否则。这将理想地匹配路径中的可选区域设置:
# config/routes.rb
scope "(:locale)", :locale => /en|nl/ do
resources :books
end
现在,如果由于某种原因我在某些操作中跳过区域设置,它会使用上一个请求中设置的区域设置,该请求可能来自另一个用户!
是否存在潜在的竞争条件,因为一个请求可以更改全局 I18n.locale
而另一个请求(事先设置了另一个区域设置)正在渲染过程中?
更新:我现在从 I18n 文档中找到了一些详细信息:
伪全局设置当前语言环境,即在 Thread.current 哈希中 def locale=(区域设置)
现在我想了解是否每个请求都是一个单独的线程。
更新2:请参阅我的答案以获取解释。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
那么现在最终的答案。 TL;DR 仅当您使用线程 Web 服务器(例如 Thin 和 Puma)时,设置区域设置才充当全局设置。
正如我提到的,
I18n.locale=
因此它应该是针对每个请求的,并且在 Webrick 和 Unicorn 中以这种方式工作。
但是,如果您使用像 Thin 或 Puma 这样的线程 Web 服务器,线程的寿命似乎会更长,并且该值会保留以供将来的请求使用,直到它被显式更改为止。我从新的 Steve Klabnik 的 gem request_store 了解到它:
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:
上面推荐的代码不会全局设置区域设置,而是根据请求进行设置。
代码通常放置在 BaseController 中,因此在渲染每个页面之前会触发并设置它。不存在竞争条件,因为每个页面都会触发此代码,并且将在那里计算 I18n 区域设置。您可以将其扩展为查找用户区域设置、会话区域设置、请求参数、使用英语。
换句话说,如果您在一页上设置本地,假设在家庭控制器中设置为德语,然后进入仪表板控制器,您将看到默认语言(英语)。因为变化不是全球性的。这就是代码放置在基本控制器中的原因。希望这是有道理的。
Recommended code from above does not set locale globally it sets it by request.
Code is usually place in BaseController so before each page is render it is triggered and set. There is no race conditions since every page will trigger this code and I18n locale will be calculated there. You can expand this to let's say looks for users locale, than session locale, than request params, than uses English.
In other words if you set local on one page let's say in home controller to german and got to dashboard controller you will see default language (english). Since change is not global. That is why code is placed in base controller. Hope it makes sense.