Как стать автором
Обновить
0

Настраиваем NGINX для мультиязычных сайтов

Время на прочтение4 мин
Количество просмотров27K

Уже давно считается хорошим тоном отдавать контент сайта на языке, предпочитаемом пользователем. Некоторые сервера определяют язык по месту нахождения пользователя с помощью модулей геолокации, остальные берут настройки браузера. Языковые предпочтения пользователя часто сохраняются в cookie, и затем используются при повторном визите.

Какой метод определения языка пользователя подходит лучше – вопрос достаточно спорный. Мой личный ранг значимости языковой информации (в порядке убывания): cookie, настройки браузера, регион.

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

Распространенные варианты кодирования языковой информации о ресурсе следующие:
  • каждая языковая версия на отдельном субдомене, например en.example.com, ru.example.com
  • язык ресурса указывается в префиксе URI, например example.com/en/, example.com/ru
  • язык ресурса указывается в GET параметре, например example.com?lang=en, example.com?lang=ru

Первый вариант наиболее радикальный, каждая языковая версия сайта рассматривается как отдельный ресурс. Могут возникнуть сложности с SSL сертификатом, необходимо заранее предусмотреть все возможные варианты в SAN DNS Host Name, или заказать сертификат с маской, например *.example.com.

Второй вариант наиболее практичный, выбор языка входит в URI, значит, не будет проблем с индексацией и копированием ссылки.

Третий вариант выглядит менее привычно, требует дополнительной логики при добавлении остальных GET параметров и может смутить пользователя при копировании ссылки. Не самый лучший вариант для публичных ссылок.
image
Я расскажу о реализации второго варианта на базе сервера NGINX. При минимальных изменениях можно применить описанные настройки и для первого варианта.

Настройка состоит из нескольких этапов.

Сначала проверяется языковая настройка браузера. Если у пользователя установлены cookie, то это значение переписывает настройку браузера. Итоговое значение передается в переменную $lang.

На первом этапе надо настроить back-end на получение GET параметра с информацией о языке. То есть реализуем третий вариант из списка, но внутри нашей системы. Для примера будем рассматривать GET параметр locale=<код локали>, используем двухбуквенный код ISO 639-1.

Надо удостовериться, что при переходе на ссылку типа http://<адрес_back-end_сервера>?locale=ru мы получает ответ на русском языке.
После этого можно настраивать NGINX на фронтенде.

Второй этап – получение языковых настроек от пользователя. Подразумевается, что при посещении сервер устанавливает cookie в браузере клиента с предпочтительным языком. Cookie называется $lang.

В конфигурации сайта пишем

map $http_accept_language $browser_lang {
        default en;
        ~ru ru;
}
map $cookie_lang $lang {
    default $browser_lang;
    ~en en;
    ~ru ru;
}

Сначала надо выделить запрос типа /NN/* в отдельный локейшен. Применяем регулярные выражения с выделением переменных.

location ~ '^/(?<lang_code>[\D-]{2})/(?<rest_uri>.*)'

Сохраняем двухсимвольный код в переменную $lang_code, все остальное в переменную $rest_uri

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

if ($lang_code ~* (uk|be)) {
                                return 301 http://$host/ru/$rest_uri$is_args$args;
                                }

Если код неизвестен, то используется английский вариант сайта.

if ($lang_code !~* (en|ru)) {
                                return 301 http://$host/en/$rest_uri$is_args$args;
                                }

Для if-конструкций порядок расположения имеет значение. Поэтому сначала надо ставить блок на проверку соответствия, и только в конце – на проверку несоответствия.

Далее надо очистить пользовательскую ссылку от возможного использования параметра locale в GET запросе. Неизвестно, как поведет себя back-end, если на него послать дублирующие аргументы, типа ?locale=en&locale=ru. Поэтому если пользователь пришел со ссылкой example.com/en/?locale=ru, то locale=ru на back-end лучше не посылать.

if ($args ~ (.*)locale=[^&]*(.*)) {
          set $args $1$2;
}

Убираем повторяющиеся амперсанды

 if ($args ~ (.*)&&+(.*)) {
         set $args $1&$2;
}

Убираем амперсанд в начале

if ($args ~ ^&(.*)) {
          set $args $1;
}

Убираем амперсанд в конце

if ($args ~ (.*)&$) {
          set $args $1;
}

Все, осталось только передать необходимые параметры на back-end. У меня в примере все идет на группу серверов, прописанных как back-end в разделе конфигурации upstream.

proxy_pass      http://back-end/$rest_uri?locale=$lang_code&$args;

Итоговая конфигурация выглядит примерно так
## get locale
map $http_accept_language $browser_lang {
        default en;
        ~ru ru;
}
map $cookie_lang $lang {
    default $browser_lang;
    ~en en;
    ~ru ru;
}

upstream back-end {
        ip_hash;
        server 172.21.71.15:8080; # vm-deb-osl-scala-1
        server 172.21.71.16:8080; # vm-deb-osl-scala-2
        server 172.21.71.17:8080; # vm-deb-osl-scala-3
        server 172.21.71.18:8080; # vm-deb-osl-scala-4
        keepalive 32;
}
server {
        listen  109.233.59.100:80;
        server_name  ruvpn.net;

location / {
# Redirect to locale
        return 301 http://$host/$lang$uri$is_args$args;
     }

# Handle URL with locale
location ~ '^/(?<lang_code>[\w-]{2})/(?<rest_uri>.*)' {
# Redirect to Russian for some CIS countries
                 if ($lang_code ~* (uk|be|kk)) {
                                return 301 http://$host/ru/$rest_uri$is_args$args;
                }
# Redirect to English for unknown languages
                if ($lang_code !~* (en|ru)) {
                                return 301 http://$host/en/$rest_uri$is_args$args;
                  }
	
                        if ($args ~ (.*)locale=[^&]*(.*)) {
                                set $args $1$2;
                                }
# Cleanup any repeated & introduced
                        if ($args ~ (.*)&&+(.*)) {
                                set $args $1&$2;
                                }
# Cleanup leading &
                        if ($args ~ ^&(.*)) {
                                set $args $1;
                                }
# Cleanup ending &
                        if ($args ~ (.*)&$) {
                                set $args $1;
                                }

   proxy_pass      http://back-end/$rest_uri?locale=$lang_code&$args;
   include         /etc/nginx/proxy.conf;
}

Можно проверить, как это работает на реальном сайте. Как вы уже заметили из примера конфигурации, по такой схеме настроен ресурс http://ruvpn.net. Все запросы типа ruvpn.net/ru/product/details/4 будут отображать страницу на русском, в то время как запрос ruvpn.net/sv/product/details/4 будет переадресован на ruvpn.net/en/product/details/4, так как шведского варианта сайта не существует. При переходе на корневую ссылку ruvpn.net, произойдет автоматическая переадресация на ruvpn.net/ru или ruvpn.net/en, в зависимости от ваших языковых настроек.
Единственным недостатком описанного метода является то, что нельзя использовать ссылки с двумя символами в начале URI для чего-то отличного от выбора языка. Но это вопрос архитектуры сайта и легко решается при проектировании.
Теги:
Хабы:
Всего голосов 55: ↑50 и ↓5+45
Комментарии22

Публикации

Информация

Сайт
ruvpn.net
Дата регистрации
Дата основания
Численность
2–10 человек
Местоположение
Норвегия

Истории