Подробно
Локализация 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.