Подтвердить что ты не робот

Почему настройка локали в 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 = (locale)

Теперь я хочу понять, является ли каждый запрос отдельным потоком.


ОБНОВЛЕНИЕ 2: См. мой ответ для объяснения.

4b9b3361

Ответ 1

Итак, теперь окончательный ответ. TL; DR Настройка локали действует как глобальная только при использовании потоковых веб-серверов, таких как Thin и Puma.

Как я уже говорил, I18n.locale=

Устанавливает текущую локаль псевдо-глобальную, т.е. в хеше Thread.current

Поэтому он должен быть запрошен, и он работает таким образом в Webrick и Unicorn.

Но если вы используете поточный веб-сервер, такой как Thin или Puma, кажется, что поток живет дольше, и значение сохраняется для будущих запросов, пока оно не будет изменено явно. Где я узнал, что это новый камень Стива Клабника request_store:

Если вам нужно глобальное состояние, вы, вероятно, достигли Thread.current.

<... >

Таким образом, люди используют эти причудливые поточные веб-серверы, такие как Thin или Puma. Но если вы используете Thread.current, и используете один из этих серверов, следите! Значения могут придерживаться дольше, чем вы ожидали, и это может вызвать ошибки.

Ответ 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

Другими словами, если вы установите локальный на одной странице, скажите в домашнем контроллере на немецкий язык и подключитесь к контроллеру панели управления, вы увидите язык по умолчанию (английский). Поскольку изменение не является глобальным. Вот почему код помещается в базовый контроллер. Надеюсь, что это имеет смысл.