Когда делать перевод на клиенте, когда на бэке или через отдельный сервис
Что проверяют
Умеете ли вы разделить два разных «перевода»: строки интерфейса, которые готовят переводчики, и пользовательский контент, который переводит машина. Следом спрашивают, где будет кэш и что случится, когда исходный текст поменяется.
Как отвечать
Решение зависит от типа текста и от того, кто его потребляет:
- клиент: кнопки, подписи и подсказки веб-приложения. Словари едут вместе с фронтендом, язык переключается без запроса к серверу. Бэкенд при этом отдаёт данные и коды ошибок, а не готовые фразы;
- бэкенд: всё, что сервер собирает сам. Письма, пуш-уведомления, PDF, сообщения валидации, страницы с серверным рендером для поисковиков. В Python это
gettextи каталоги.po/.mo, в Djangogettext_lazyиLocaleMiddleware, во Flask и FastAPI обычно Babel. Язык берут из профиля, а при его отсутствии изAccept-Language; - отдельный сервис: машинный перевод пользовательского контента (объявления, отзывы, переписка) или единая система переводов для веба, iOS и Android, чтобы тексты правились без релиза каждого клиента.
Для машинного перевода опишите конвейер. Задача уходит в очередь (Celery, RQ), результат ложится в таблицу с ключом «идентификатор текста, язык, хэш исходника», повторные показы идут из кэша. Так вы не платите за один текст дважды и не держите пользователя в ожидании внешнего API. Изменился исходник, хэш не совпал, перевод пересчитался.
Где спотыкаются
Вызывают внешний API перевода прямо из браузера. Ключ оказывается в открытом коде, а лимиты расходует кто угодно.
Пишут gettext вместо gettext_lazy в моделях и формах Django. Строка переводится один раз при импорте модуля, на языке по умолчанию, и дальше не меняется.
Собирают фразу склейкой: _("Найдено") + " " + str(n) + " " + _("вакансий"). Порядок слов и формы множественного числа в языках разные, поэтому нужны целые фразы с плейсхолдерами и ngettext.