Почему настройка локали в 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: См. мой ответ для объяснения.
Ответы
Ответ 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
Другими словами, если вы установите локальный на одной странице, скажите в домашнем контроллере на немецкий язык и подключитесь к контроллеру панели управления, вы увидите язык по умолчанию (английский). Поскольку изменение не является глобальным. Вот почему код помещается в базовый контроллер. Надеюсь, что это имеет смысл.