为什么Rails中的语言环境设置充当全局(使用Thin时)?

时间:2012-02-13 15:23:06

标签: ruby-on-rails ruby-on-rails-3 localization internationalization

我刚刚意识到推荐的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 =(locale)

现在我想了解每个请求是否都是一个单独的线程。


更新2:请参阅我的回答进行解释。

2 个答案:

答案 0 :(得分:13)

所以现在最后的答案。 TL; DR 只有在使用线程Web服务器(如Thin和Puma)时,设置区域设置才会充当全局。

正如我所提到的,I18n.locale=

  

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

所以它应该是按照请求,并且它在Webrick和Unicorn中以这种方式工作。

但是如果你使用像Thin或Puma这样的线程Web服务器,似乎线程的寿命更长,并且为将来的请求保留该值,直到它被显式更改。我从中学到的是来自新的Steve Klabnik的宝石 request_store

  

如果你需要全局状态,你可能已经达到了Thread.current。

     

< ...>

     

所以人们正在使用那些花哨的线程Web服务器,比如Thin或Puma。但是如果您使用Thread.current,并且您使用其中一个服务器,请注意!值可能会比您预期的更长时间,这可能会导致错误。

答案 1 :(得分:2)

上面的推荐代码不会全局设置区域设置,而是按请求设置它。

before_filter :set_locale

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

代码通常放在BaseController中,因此在每个页面呈现之前,它会被触发并设置。没有竞争条件,因为每个页面都会触发此代码,并且将在那里计算I18n语言环境。您可以将此扩展为比使用params更多地查找用户区域设置而不是会话区域设置,而不是使用英语。

def set_locale
  I18n.locale = @user.locale || session[:locale] || params[:locale] || :en
end

换句话说,如果你在一个页面上设置本地,让我们在家庭控制器中说德语,并得到仪表板控制器,你会看到默认语言(英语)。因为改变不是全球性的。这就是代码放在基本控制器中的原因。希望它有意义。