Одна из предыдущих статей была посвящена I18n в Rails . Мы говорили о хранении и получении переводов, локализации приложения и других полезных вещей. Однако мы не обсуждали различные способы управления локализацией между запросами.
По умолчанию Rails собирается использовать языковой стандарт, заданный в I18n.default_locale
(который является :en
или любым другим значением, определенным вами в конфигурации), или значение, I18n.locale
если оно было явно определено. Конечно, если приложение поддерживает несколько языков, его пользователям нужен способ изменить локаль, и их выбор следует сохранить. Поэтому в этой статье мы рассмотрим следующие решения:
- Введите имя локали в качестве параметра GET (example.com?locale=en)
- Предоставьте это как часть доменного имени (en.example.com)
- Установите локаль на основе агента пользователя, отправленного браузером
- Установите локаль в зависимости от местоположения пользователя
Исходный код демонстрационного приложения доступен на GitHub .
Кстати, если вы только начинаете изучать Rails, вот большой список полезных ресурсов.
Подготовка приложения
В этой демонстрации я собираюсь использовать Rails 5.0.0.1, но описанные концепции применимы и к более старым версиям. Для начала создайте новое приложение без набора тестов по умолчанию:
$ rails new Localizer -T
Чтобы обеспечить поддержку дополнительных языков, включите гем rails-i18n в свой Gemfile :
Gemfile
[...]
gem 'rails-i18n'
[...]
Установить его
$ bundle install
В этой демонстрации я собираюсь оказать поддержку английскому и польскому языкам, но вы можете выбрать что-нибудь еще. Давайте явно определим поддерживаемые локали:
конфиг / application.rb
config.i18n.available_locales = [:en, :pl]
Также быстро настройте две маленькие страницы, управляемые PagesController
:
pages_controller.rb
class PagesController < ApplicationController
end
просмотров / страниц / index.html.erb
<h1><%= t('.title') %></h1>
<%= link_to t('pages.about.title'), about_path %>
* Вид / страницы / about.html.erb
<h1><%= t('.title') %></h1>
<%= link_to t('pages.index.title'), root_path %>
Не забывайте, что t
метод является псевдонимом для I18n.translate
и ищет перевод на
основе предоставленного ключа. Вот наши переводы:
конфиг / локали / en.yml
en:
pages:
index:
title: 'Welcome!'
about:
title: 'About us'
конфиг / локали / pl.yml
pl:
pages:
index:
title: 'Powitanie!'
about:
title: 'O nas'
Пока мы называем эти ключи, основываясь на именах контроллера и представления, внутри файла .html.erb мы можем просто сказать, t('.title')
опуская части pages.index
или pages.about
.
Установите маршруты:
конфиг / routes.rb
get '/about', to: 'pages#about', as: :about
root 'pages#index'
Наконец, предоставьте ссылки для изменения локали сайта (пока URL-адреса будут пустыми):
общий / _change_locale.html.erb
<ul>
<li><%= link_to 'English', '#' %></li>.
<li><%= link_to 'Polska', '#' %></li>
</ul>
Визуализируйте этот фрагмент внутри макета:
макеты / application.html.erb
<%= render 'shared/change_locale' %>
Ницца! Подготовка завершена, и мы можем перейти к основной части.
Настройка локали на основе доменного имени
Первое решение, которое мы обсудим, — установка языкового стандарта на основе имени домена первого уровня, например, example.com
будет отображаться английская версия сайта, тогда как example.pl
— польская версия. Это решение имеет ряд преимуществ, и, вероятно, наиболее важным является то, что пользователи могут легко понять, какой язык они собираются использовать. Тем не менее, если ваш сайт поддерживает множество локалей, покупка нескольких доменов может быть дорогостоящей.
Чтобы локально протестировать это решение, вам нужно немного настроить рабочую станцию, отредактировав файл hosts . Этот файл находится в каталоге etc (для Windows это будет % WINDIR% \ system32 \ drivers \ etc ). Отредактируйте его, добавив
127.0.0.1 localizer.com
127.0.0.1 localizer.pl
Теперь посетите localizer.com:3000
и localizer.pl:3000
должны перейти к нашему приложению Rails.
Очень распространенное место для установки локали — before_action
это ApplicationController
:
application_controller.rb
[...]
before_action :set_locale
private
def set_locale
I18n.locale = extract_locale || I18n.default_locale
end
[...]
Чтобы получить запрошенное имя хоста, используйте request.host
:
application_controller.rb
[...]
def extract_locale
parsed_locale = request.host.split('.').last
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
[...]
В этом методе мы удаляем последнюю часть имени домена ( com
и pl
т. Д.) И проверяем, поддерживается ли запрошенная локаль. Если да — верни, скажи иначе nil
.
Теперь настройте ссылки для изменения локали:
общий / _change_locale.html.erb
[...]
<li><%= link_to 'English', "http://localizer.com:3000" %></li>
<li><%= link_to 'Polska', "http://localizer.pl:3000" %></li>
[...]
Чтобы сделать его немного более удобным для пользователя, давайте добавим текущий путь к URL:
общий / _change_locale.html.erb
[...]
<li><%= link_to 'English', "http://localizer.com:3000#{request.env['PATH_INFO']}" %></li>
<li><%= link_to 'Polska', "http://localizer.pl:3000#{request.env['PATH_INFO']}" %></li>
[...]
Теперь вы можете проверить результат!
Использование субдомена
Конечно, вместо покупки нескольких доменов первого уровня, вы можете зарегистрировать субдомены в своей доменной зоне, например en.localizer.com
и pl.localizer.com
.
extract_locale
Метод должен быть изменен следующим образом:
application_controller.rb
[...]
def extract_locale
parsed_locale = request.subdomains.first
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
[...]
Конечно, ссылки будут выглядеть немного иначе:
общий / _change_locale.html.erb
<li><%= link_to 'English', "http://en.localizer.com:3000#{request.env['PATH_INFO']}" %></li>
<li><%= link_to 'Polska', "http://pl.localizer.com:3000#{request.env['PATH_INFO']}" %></li>
Установка локали на основе параметров HTTP GET
Например, другим очень распространенным подходом является использование параметров HTTP GET localhost:3000?locale=en
. Это потребует от нас изменить extract_locale
метод еще раз:
application_controller.rb
[...]
def extract_locale
parsed_locale = params[:locale]
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
[...]
Проблема, однако, заключается в необходимости сохранения выбранной локали между запросами. Конечно, вы можете говорить link_to root_url(locale: I18n.locale)
каждый раз, но это не лучшая идея. Вместо этого
вы можете положиться на default_url_options
метод, который устанавливает параметры по умолчанию для url_for
метода и другие методы, которые полагаются на него:
application_controller.rb
[...]
def default_url_options
{ locale: I18n.locale }
end
[...]
Это позволит вашим помощникам по маршруту автоматически включать ?locale
часть. Однако мне не очень
нравится этот подход, в основном из-за этого раздражающего параметра GET. Поэтому давайте обсудим еще одно решение.
Использование областей маршрутов
Как вы, наверное, помните, маршруты могут быть ограничены, и эту функцию можно использовать для простого сохранения названия локали:
конфиг / routes.rb
[...]
scope "(:locale)", locale: /en|pl/ do
get '/about', to: 'pages#about', as: :about
root 'pages#index'
end
[...]
Оборачивая :locale
круглыми скобками, мы делаем этот параметр GET необязательным. locale: /en|pl/
устанавливает регулярное выражение, проверяющее, что этот параметр может содержать только en
или pl
, следовательно, любая из этих ссылок верна:
http://localhost:3000/about
http://localhost:3000/en/about
http://localhost:3000/pl/about
Измените ссылки для переключения локали:
общий / _change_locale.html.erb
<li><%= link_to 'English', root_path(locale: :en) %></li>
<li><%= link_to 'Polska', root_path(locale: :pl) %></li>
На мой взгляд, это решение намного более аккуратно, чем передача локали через параметр ?locale
GET.
Определение локали на основе настроек пользователя
Если языковой стандарт не был установлен явно, вы вернетесь к значению по умолчанию, установленному в, I18n.default_locale
но мы можем изменить это поведение. Чтобы получить неявную локаль, вы можете использовать HTTP-заголовки или информацию о местонахождении посетителя, поэтому давайте посмотрим на эти два подхода в действии.
Использование заголовков HTTP
Существует специальный заголовок HTTP, Accept-Language
который браузеры устанавливают на основе языковых предпочтений на устройстве пользователя. Его содержимое обычно выглядит так en-US,en;q=0.5
, но нас интересуют только первые два символа, поэтому extract_locale
метод можно настроить следующим образом:
application_controller.rb
[...]
def extract_locale
parsed_locale = params[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/)[0]
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
[...]
Существует замечательная жемчужина http_accept_language, которая действует как промежуточное программное обеспечение Rack и помогает вам более надежно решить эту проблему.
Использование местоположения пользователя
Другой подход заключается в установке языкового стандарта по умолчанию на основе местоположения пользователя. Это решение обычно считается ненадежным и, как правило, не рекомендуется, но для полноты картины давайте также обсудим его.
Чтобы получить местоположение пользователя, давайте используем гем под названием геокодер, который можно использовать для множества различных задач и даже предоставляет хуки для ActiveRecord и Mongoid. Однако в этой демонстрации все будет намного проще. Прежде всего, добавьте этот новый драгоценный камень
Gemfile
[...]
gem 'geocoder'
[...]
и беги
$ bundle install
Теперь мы можем воспользоваться request.location.country_code
возможностью увидеть страну пользователя. Результирующая строка, однако, в заглавном случае, поэтому мы собираемся уменьшить ее. Вот соответствующий код:
application_controller.rb
[...]
def extract_locale
parsed_locale = if params[:locale]
params[:locale]
else
request.location.country_code ? request.location.country_code.downcase : nil
end
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
[...]
Единственная проблема здесь в том, что вы не сможете проверить это локально, так как request.location.country_code
всегда будет возвращать «RD». Тем не менее, вы можете развернуть свое приложение на Heroku (это займет буквально пару минут) и протестировать все, используя открытые прокси-серверы.
Хотелось бы еще раз напомнить вам, что установка языкового стандарта пользователя на основе его местоположения не считается рекомендуемой практикой, поскольку кто-то может, например, посетить другую страну во время деловой поездки.
PhraseApp и управление переводами
Конечно, введение механизма переключения и сохранения локали очень важно для любого многоязычного приложения, но это не имеет большого смысла, если у вас нет переводов. И PhraseApp здесь, чтобы сделать процесс управления переводами намного проще!
Вы можете попробовать PhraseApp бесплатно в течение 14 дней прямо сейчас. Он поддерживает огромный список различных языков и фреймворков от Rails до JavaScript и позволяет легко импортировать и экспортировать данные переводов. Что здорово, вы можете быстро понять, какие ключи перевода отсутствуют, потому что легко потерять след при работе со многими языками в больших приложениях. Поэтому я действительно призываю вас попробовать!
Заключение
В этом приложении мы рассмотрели различные способы переключения и сохранения данных локали между запросами. Мы видели, как локаль может передаваться как часть имени домена и как часть URL. Кроме того, мы говорили о выводе локали на основе заголовка HTTP и местоположения пользователя.
Надеюсь, эта статья была полезной и интересной для вас. Я благодарю вас за то, что вы остались со мной и счастливого кодирования!