Виджет поддержки должен читать язык страницы, а не браузера

Product

До этой недели чат-виджет Kookee понятия не имел, на каком он языке. Сайт мог передать locale, и виджет отдавал его дальше в API, но кнопки и «How can we help?» на Главной оставались английскими, если сайт не переводил каждую надпись сам, а часть текста, например сообщения об ошибках, перевести было вообще нельзя.

Уровнем ниже было ещё хуже. API сравнивал языковые теги как обычные строки, поэтому бразильский сайт, который передавал pt-BR в проект с центром помощи на pt, не находил совпадения и получал язык по умолчанию.

Виджет выглядел многоязычным, только если сайт делал половину работы и писал тег в точности правильно. Я переделал, как виджет выбирает язык и что потом с ним делает. Изменения вышли сегодня, и вот правила, на которых я остановился.

Главная виджета на русском: приветствие посетителя по имени, объявление, которое есть только на английском, и три ссылки

1. Сначала решает страница, потом браузер

Большинство виджетов читают navigator.language — язык браузера посетителя. Это очевидный сигнал и обычно неверный: он говорит о компьютере посетителя, а не о странице, которую тот читает. Немецкий разработчик на английском сайте с документацией получил бы немецкий виджет на английской странице, и этот виджет предлагал бы статьи, которые всё равно загрузил бы на английском.

Страница сама сообщает свой язык: <html lang>. Его выставляют большинство фреймворков и CMS, и это решение владельца сайта, а не догадка. Поэтому виджет читает его первым. Браузер получает право голоса, только если страница ничего не объявляет, и только для языка, на который виджет переведён. Если нет ни того ни другого, виджет берёт язык проекта по умолчанию.

Сайт по-прежнему может переопределить всё это через data-locale или locale в React. А когда переключатель языков на сайте меняет <html lang> без перезагрузки, виджет это замечает и переключается следом.

2. Языковой тег — это семейство, а не строка

pt-BR, pt, PT-br и pt_BR — это всё португальский, и виджет, который работает на чужих сайтах, должен принимать любой из этих вариантов. Вариант с подчёркиванием приходит с серверов, которые записывают локали в стиле POSIX. В некоторых тегах есть не только регион, например ca-ES-valencia.

Я обрабатываю это в двух местах. Виджет чистит тег перед отправкой: приводит его к каноническому виду, оставляет язык и регион и отбрасывает всё, что вообще не языковой тег, потому что запрос, который API отклонит, оставит посетителя со сломанным виджетом. Затем сервер сопоставляет тег с языками проекта: сначала ищет точное совпадение, потом базовый язык, потом несколько устаревших кодов, например nb и nn для норвежского и iw для иврита.

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

3. Ваш контент — на языке по умолчанию, текст виджета — на английском

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

Ваш контент (статьи центра помощи, коллекции, новости на Главной) без перевода показывается на языке проекта по умолчанию. Английская статья на французской странице — не идеал, но это лучше пустой вкладки «Помощь», и так API Kookee уже поступал с недостающим переводом.

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

Так что немецкая страница в проекте с немецкими статьями показывает немецкие статьи с английскими кнопками. Я долго сомневался и оставил так. Смешанный экран честно показывает, что есть. Прятать статью из-за того, что у виджета нет немецкого перевода кнопки «Back», было бы хуже.

Главная на скриншоте вверху показывает оба правила сразу. Текст виджета и приветствие, которое страница передала в коде, на русском, а объявление, написанное только по-английски, показывается на английском.

Пока я над этим работал, нашлись две старые ошибки в том же месте. Список мог показать одну статью дважды, по разу на каждом языке, а на языке без переведённых категорий вкладка «Помощь» целиком теряла коллекции. Обе исправлены: в списке теперь одна версия каждой статьи, а категории без перевода показываются на языке по умолчанию, как и всё остальное.

4. Отвечать на языке, на котором пишет посетитель

Всё, что выше, — догадки о посетителе. Его сообщение — не догадка. Если человек пишет по-испански на странице, которая объявлена английской, он хочет получить ответ на испанском.

Поэтому ассистент теперь отвечает на языке последнего сообщения посетителя и переходит на язык виджета, только когда по сообщению язык не понять, как в случае с «hi» или названием продукта. Статьи он по-прежнему ищет на любом языке, на котором они есть, и ссылается на них без перевода.

Чат виджета на испанском: вопрос про счета, поиски ассистента и его ответ на испанском со статьёй центра помощи на английском в качестве источника

5. Смена языка не должна стоить разговора

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

Статья, открытая в этот момент, закрывается, и посетитель возвращается на её вкладку. Она загружена на старом языке, а её адрес может отличаться от языка к языку, поэтому перезагружать её на месте ненадёжно. Шаг назад — небольшая цена, а разговор остаётся нетронутым.

Если переключатель языков на сайте не трогает <html lang>, вызовите вместо этого KookeeChat.setLocale('fr').

6. Переводы лежат в самом виджете

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

Поэтому все пять языков лежат в самом файле. Это стоило около 7 КБ в gzip: скрипт вырос со 149,0 до 156,2 КБ. Шестой язык — это один файл и релиз, а до тех пор выручает labels.

Чего я делать не стал

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

Переводы текстов в панели управления. Заголовок, приветствие, подсказки и ссылки на вкладке «Настроить» сохраняются на одном языке. Сайт на нескольких языках может оставить их пустыми, и тогда сработают переведённые тексты по умолчанию, или задать их для каждой страницы в коде, и тогда они перекроют сохранённые значения:

HTML
<script async src="https://kookee.dev/chat/latest.js"
  data-api-key="YOUR_API_KEY"
  data-locale="es"
  data-greeting="¿En qué podemos ayudarte?"></script>

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

Как подключить

Тег script подхватывает это сам, на сайте ничего менять не нужно. В React обновитесь до @kookee/react 1.12.0 и @kookee/sdk 1.12.0. Если ваша страница задаёт <html lang>, виджет уже следует ему.

Одно изменение в поведении: если сайт не передаёт виджету язык, теперь виджет берёт тот, что объявлен на странице. Чтобы виджет оставался английским, что бы ни говорила страница, задайте data-locale="en". На вкладке Установка страницы Виджет для сайта есть список «Язык», который пропишет это за вас.

Открытый список «Язык» на вкладке «Установка»: «Авто (страница или браузер)», English, Русский, Español, Français и Português

Все настройки описаны в статье Показывайте виджет на языке посетителей, а setLocale() — в статье Управляйте чат-виджетом из JavaScript. Готовый портал получил свои языки позже, об этом — в посте Многоязычный центр помощи без пустых страниц.