Определить язык браузера в Rails - PullRequest
18 голосов
/ 19 августа 2011

Как определить язык пользователя (в RoR), установленный в браузере? У меня будет поле выбора, которое пользователь может использовать в любое время, но я бы хотел по умолчанию использовать любой язык браузера.

Ответы [ 5 ]

21 голосов
/ 19 августа 2011

Вот решение :

2.6 Установка локали из предоставленной клиентом информации

В определенных случаях имеет смысл устанавливать языковой стандарт на основе предоставленной клиентом информации, т.е. не на URL. Эта информация может поступать, например, с предпочтительного языка пользователей (установленного в их браузере), может основываться на географическом местоположении пользователей, выведенном из их IP, или пользователи могут предоставить ее, просто выбрав локаль в интерфейсе приложения и сохранив ее. к их профилю. Этот подход больше подходит для веб-приложений или сервисов, а не для веб-сайтов - см. Вставку о сеансах , куки и архитектуре RESTful выше ...

Одним источником предоставленной клиентом информации будет HTTP-заголовок Accept-Language. Люди могут установить это в своем браузере или других клиентах (например, curl ) ...

Другим способом выбора локали из информации о клиенте будет использование базы данных для сопоставления IP-адреса клиента с регионом, например Страна GeoIP Lite ...

Вы также можете предоставить пользователям вашего приложения средства для настройки (и, возможно, переопределения) локали в интерфейсе вашего приложения, а также ...

16 голосов
/ 16 марта 2016

Это старый вопрос, но я наткнулся на него как на душу в поисках ответов, и единственным доступным ответом была ссылка без контекста. Так что здесь немного больше глубины, основанной на моем последующем копании.

Доступ к заголовку 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-

Итак, глядя на приведенный выше пример строки, некоторые языки имеют «q-значения» - коэффициент относительного качества , от 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"]

Я начал свой анализ с просмотра этой сущности , но в итоге изменил его довольно значительно. Регулярное выражение особенно отбрасывало совершенно хорошие вторичные локали, например, en-AU стало en, zh-TW стало zh. Я попытался исправить это с помощью моих модификаций этого регулярного выражения.

1 голос
/ 12 июня 2019

Прошлой ночью я сделал этот крошечный камень: 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

Надеюсь, это поможет.

1 голос
/ 10 июня 2018

Это действительно старый вопрос, но я хотел бы упомянуть, что в руководстве по 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

1 голос
/ 10 октября 2016

Вот хорошо протестированный 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

Желаемые_локализации в приведенном выше примере представляют собой подмножество доступных_ локализаций в соответствии с предпочтениями пользователя, доступными локализациями и рекомендациями RFC.

...