检测Rails中的浏览器语言

时间:2011-08-18 20:29:36

标签: ruby-on-rails ruby ruby-on-rails-3 locale

如何检测浏览器中设置的用户语言(在RoR中)?我将有一个用户可以随时使用的选择框,但我想默认使用他们的浏览器语言。

4 个答案:

答案 0 :(得分:16)

这是一个古老的问题,但我发现它是寻找答案的灵魂,唯一可用的答案是没有任何背景的链接。基于我随后的挖掘,这里的深度更深一些。

访问Accept-Language标题

查询相关控制器中的request对象:

request.env['HTTP_ACCEPT_LANGUAGE'] #returns nil or a string, e.g.:
# => "en-AU,en-US;q=0.7,en;q=0.3"

虽然这很容易。要在Rails中充分利用它,您需要解析该字符串(或者它可能是nil?)。

补充工具栏:Accept- * header

的入门读物

因此,查看上面的示例字符串,某些语言具有" q-values" - relative quality factor,介于0和1之间。较高的q值表示客户首选语言。缺少q值实际上是最高的q值 - 隐含的1.0(与上面的字符串中的en-AU一样)。虽然有点复杂 - 浏览器可能会向您发送带有q值的语言,例如0 - 我收集这意味着您应该拒绝这些语言。

解析Accept-Language标题

请注意,这里有一些不同但相似的方法,我们将这样的字符串解析为按其q值排序的语言列表。简单的Ruby:

# to_s to deal with possible nil value, since nil.to_s => ""
langs = request.env['HTTP_ACCEPT_LANGUAGE'].to_s.split(",").map do |lang| 
  l, q = lang.split(";q=")
  [l, (q || '1').to_f]
end
# => [["en-AU", 1.0], ["en-US", 0.7], ["en", 0.3]]

或者,如果你精通正则表达式,你可以达到与上面相同的效果,并且可能同时改进我的屠杀正则表达式:

rx = /([A-Za-z]{2}(?:-[A-Za-z]{2})?)(?:;q=(1|0?\.[0-9]{1,3}))?/
langs = request.env['HTTP_ACCEPT_LANGUAGE'].to_s.scan(rx).map do |lang, q|
  [lang, (q || '1').to_f]
end

无论哪种方式,您都可以根据需要跟进:

# return array of just languages, ordered by q-value
langs.sort_by(&:last).map(&:first).reverse
# => ["en-AU", "en-US", "en"]

我通过查看this gist开始我的解析,但最终相当显着地修改了它。正则表达式特别是抛弃了非常好的辅助语言环境,例如: en-AU成为enzh-TW成为zh。我试图通过对正则表达式的修改来纠正这个问题。

答案 1 :(得分:1)

这是一个经过良好测试的Ruby gem,它可以完全满足您的需求:https://github.com/ioquatix/http-accept

languages = HTTP::Accept::Language.parse("da, en-gb;q=0.8, en;q=0.7")

expect(languages[0].locale).to be == "da"
expect(languages[1].locale).to be == "en-gb"
expect(languages[2].locale).to be == "en"

它在各种输入上具有100%的测试覆盖率。它按照相关RFC指定的顺序返回语言。

此外,如果您尝试将用户语言与特定区域设置中可用的内容进行匹配,则可以执行以下操作:

available_localizations = HTTP::Accept::Languages::Locales.new(["en-nz", "en-us"])

# Given the languages that the user wants, and the localizations available, compute the set of desired localizations.
desired_localizations = available_localizations & languages

上述示例中的desired_localizations是available_localizations的子集,具体取决于用户首选项,可用的本地化和RFC建议。

答案 2 :(得分:1)

这是一个非常古老的问题,但我想提一下Rails指南中有关于如何检测用户浏览器语言的说明。

下面的代码基于Ruby on Rails指南中的解决方案并进行了改进:

  • 检查是否存在HTTP_ACCEPT_LANGUAGE - 有时机器人缺少此标头,您的错误报告工具会出现错误
  • 检查是您的应用程序支持的语言

代码:

  def set_locale_based_on_browser
    locale = extract_locale_from_accept_language_header

    I18n.locale =
      if locale_valid?(locale)
        locale
      else
        :en
      end
  end

  private

  def locale_valid?(locale)
    I18n.available_locales.map(&:to_s).include?(locale)
  end

  def extract_locale_from_accept_language_header
    accept_language = request.env['HTTP_ACCEPT_LANGUAGE']
    return unless accept_language

    accept_language.scan(/^[a-z]{2}/).first
  end
end

有关区域设置检测的更多信息,请参阅Ruby on Rails指南: https://guides.rubyonrails.org/i18n.html#choosing-an-implied-locale

答案 3 :(得分:1)

昨晚我做了这个小小的宝石:accept_language

它可以像这样集成在Rails应用中:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :best_locale_from_request!

  def best_locale_from_request!
    I18n.locale = best_locale_from_request
  end

  def best_locale_from_request
    return I18n.default_locale unless request.headers.key?('HTTP_ACCEPT_LANGUAGE')

    string = request.headers.fetch('HTTP_ACCEPT_LANGUAGE')
    locale = AcceptLanguage.intersection(string, I18n.default_locale, *I18n.available_locales)

    # If the server cannot serve any matching language,
    # it can theoretically send back a 406 (Not Acceptable) error code.
    # But, for a better user experience, this is rarely done and more
    # common way is to ignore the Accept-Language header in this case.
    return I18n.default_locale if locale.nil?

    locale
  end
end

我希望它能提供帮助。