Перейти к содержимому

Подробно

Локализация SaaS с первого дня: девять языков и арабский справа налево

Опубликовано · 3 мин чтения· Anton Shubin

Коротко

  • Приложение, админка, сайт и письма Seed работают на девяти языках, включая арабский. В каждом около 3 900 строк.
  • Тест валит сборку, если строки не хватает, она устарела или потеряла подстановку. Другой тест не пропускает отступ «слева» или «справа», который не развернётся в арабском.
  • Язык — настройка человека, хранится на сервере и не связан с форматом дат, часовым поясом и 12- или 24-часовыми часами.
  • Десятый язык — это одна строка в списке, по файлу на каталог и пара строк загрузки.

Как сделать SaaS сразу на нескольких языках, с арабским справа налево, и не переписывать потом полпроекта? Начать с первого экрана и поручить порядок тестам. Продукт на Seed с первого дня говорит на девяти языках: русском, английском, испанском, немецком, бразильском португальском, арабском, французском, японском и упрощённом китайском. Это касается приложения, админки, сайта и писем. В арабском весь интерфейс разворачивается справа налево.

Этот текст для основателей и разработчиков, которые знают, что «переведём потом» значит «не переведём никогда». Покажу, где живут строки, что ловят тесты и сколько на самом деле стоит RTL.

Почему не «потом»?

Потому что «потом» — это пройти каждый экран второй раз. Найти каждую вшитую строку, каждую дату, собранную руками, каждый margin-left, и всё это в коде, который к тому времени оброс фичами. Если переводить с самого начала, каждый новый экран переводится, пока его делают, и это минуты, а не отдельный проект, за который никто не хочет браться.

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

Где живут строки?

Каждое слово на экранах Seed лежит в каталоге: один TypeScript-файл на область и язык, i18n/app/ru.ts, i18n/site/ja.ts и так далее. Областей пять: приложение, админка, сайт, письма и сообщения об ошибках API. Источник — английский, в остальных файлах те же ключи.

Область Строк на язык
Приложение 1 503
Админка 1 487
Сайт 682
Коды ошибок 164
Письма 79

Около 3 900 строк на каждый язык, посчитано 2 ноября 2026 года. Компоненты из preact-components приносят свои каталоги сверху.

Приложение загружает только ваш язык, отдельным небольшим файлом: тот, кто читает по-русски, не скачивает японский.

Как перевод не отстаёт от оригинала?

За это отвечает тест i18n/i18n.test.ts. Он падает, если:

  • в каком-то языке не хватает строки или осталась лишняя, которой в английском уже нет;
  • английский текст поменялся после перевода (у каждого языка есть lock-файл, где записано, с какой версии английского сделан перевод);
  • перевод потерял или исказил подстановку вроде {count} или тег вокруг ссылки;
  • у множественного числа не хватает формы, которая нужна языку. В русском их четыре, в арабском шесть, и «5 файла» — это баг;
  • английскую строку больше не использует ни один кусок кода.

Так что ни кнопка с надписью Notes.create, ни немецкий экран, отставший от английского на неделю, в релиз не попадут.

Во что обходится письмо справа налево?

Дешевле, чем кажется, если вёрстка нигде не говорит «слева» и «справа». В стилях Seed стороны логические: «начало» и «конец». На странице слева направо начало слева, в арабском — справа. Разворачивает браузер, а не мы.

Тест превращает это из пожелания в правило: tests/spacing.test.ts проходит по всем приложениям и падает на любом классе отступа, который называет физическую сторону. Атрибут dir ставится по языку: на сайте — сервером, в приложении — в момент переключения.

Ещё две вещи, которые больно бьют, если о них забыть:

  • Имена внутри фраз. «Анна пригласила вас в группу Team» в арабском смешивает направления, и латинское имя может утащить знаки препинания не на ту сторону. Пользовательские значения обёрнуты в <bdi>, и их направление не влияет на соседний текст.
  • Даты, числа и время. Все даты на всех экранах идут через один форматтер. Отдельный тест, tests/no-direct-date-format.test.ts, падает на любом прямом вызове toLocaleString().

Язык, формат и часы — три разные настройки

Язык сохраняется на сервере вместе с профилем, поэтому одинаков на всех устройствах и в письмах. Формат дат и чисел, часовой пояс и 12- или 24-часовые часы настраиваются отдельно. Русскоязычный человек в Таиланде хочет русский интерфейс и время Бангкока. Англоязычный в Берлине — английские слова и 03.11.2026. Связать эти настройки в одну — верный способ показать неправильную дату половине пользователей.

На сайте язык виден в адресе (/ru/pricing, /ar/pricing). Никаких редиректов по браузеру или IP, и каждая страница сообщает поисковикам, какие у неё есть языковые версии.

Кто переводит?

ИИ-агенты, по письменному руководству i18n/TRANSLATING.md. В нём для каждого языка задан тон (по-русски на «вы», по-немецки на du, по-японски です/ます), глоссарий, чтобы одна вещь везде называлась одинаково, и главное правило: переводить ситуацию, а не слова. Второй агент переводит каждый новый текст обратно на английский и сверяет смысл, а русский я вычитываю сам. Это не бюро переводов, и делать вид я не буду. Но это намного лучше экрана, который человек не может прочитать.

Как добавить десятый язык

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

Попробуйте

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

Следующий пост — на почту

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

Все посты