【问题标题】:Why locale setting in Rails acts as global (when using Thin)?为什么 Rails 中的语言环境设置是全局的(使用 Thin 时)?
【发布时间】:2012-03-05 00:04:51
【问题描述】:

我刚刚意识到在控制器中设置语言环境的推荐 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:请参阅我的答案以获得解释。

【问题讨论】:

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


    【解决方案1】:

    所以现在是最终答案。 TL;DR 仅当您使用线程 Web 服务器(如 Thin 和 Puma)时,设置区域设置才可作为全局设置。

    正如我提到的,I18n.locale=

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

    所以它应该是每个请求的,它在 Webrick 和 Unicorn 中以这种方式工作。

    但是,如果您使用 Thin 或 Puma 等线程 Web 服务器,则线程的寿命似乎更长,并且该值会保留以供将来的请求使用,直到明确更改为止。我从新的 Steve Klabnik 的 gem request_store 中了解到它:

    如果您需要全局状态,您可能已经使用了 Thread.current。

    <...>

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

    【讨论】:

    • 我遇到了完全相同的问题,但我使用的是独角兽!知道如何解决这个问题吗?谢谢
    • 我认为当您说“I18n.set_locale”时,您实际上是指“I18n.locale=”对吗? rubydoc.info/github/svenfuchs/i18n/I18n/Config:locale=
    • 您在非线程化的 Web 服务器中也会遇到同样的问题。如果您有不同用途的不同控制器(又名公共控制器和 api 控制器)并且其中一个没有正确设置语言环境,您将从上一个请求中获取语言环境。 x_X'
    【解决方案2】:

    上面推荐的代码不会全局设置语言环境,它是根据请求设置的。

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

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

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

    换句话说,如果您在一页上设置本地,让我们在家庭控制器中说德语并进入仪表板控制器,您将看到默认语言(英语)。因为变化不是全球性的。这就是代码放置在基本控制器中的原因。希望有道理。

    【讨论】:

    • I18n 是一个全局模块,I18n.locale= 改变了它的状态。我同意通过before_filter 为每个请求显式设置语言环境的推荐方法通常按预期工作。我仍然想知道 Rails 请求是否可以在不同的线程中同时运行 - 如果可以,我认为请求 1 设置 I18n.locale,然后请求 2 设置不同的 I18n.locale,然后请求 1 开始使用新的(不正确的)语言环境进行渲染.抱歉,如果我的回答有误。
    • @khustochka 如果同一个线程由于某种原因停止处理请求 1,然后开始处理请求 2 并更改语言环境,然后返回请求 1 并使用新的语言环境继续处理它,就会发生这种情况.作为每个线程的全局手段。
    猜你喜欢
    • 2017-11-01
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多