Реализация AI-перевода текста в реальном времени в мобильном приложении
Перевод «в реальном времени» означает разное в зависимости от сценария. Для чата — отправить сообщение и получить перевод за 200–400 мс, незаметно для пользователя. Для текстового поля с live-переводом — реагировать на каждые N символов без спама запросами. Для документа — батч с прогресс-баром. Каждый сценарий требует своей архитектуры.
Три варианта стека и когда что выбирать
Google Translate API (Cloud Translation v3) — стандарт для продакшена. Поддерживает 130+ языков, нейросетевой перевод (NMT), формальные/неформальные регистры для ряда языков. REST или gRPC. Для мобильного клиента — REST через обычный HTTPS, ключ прячем за собственный бэкенд.
DeepL API — лучшее качество для европейских языков, особенно немецкого, французского, польского. Для русского — сопоставимо с Google. Ограничение Free-плана 500К символов/месяц.
On-device: ML Kit Translate — Google ML Kit с локальными моделями. Модель ~15 МБ на языковую пару, скачивается один раз. Полный офлайн, нулевая задержка сети, но качество уступает cloud-версиям. Поддерживает 58 языков. Подходит для мессенджеров с оффлайн-требованиями.
Debounce и управление запросами
Главная ошибка live-перевода — отправлять запрос на каждое нажатие клавиши. На скорости печати 200 символов/минуту это 3–4 запроса в секунду, из которых 90% уйдут в мусор до ответа.
Правильный паттерн на iOS (Combine):
@Published var inputText: String = ""
inputText
.publisher
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
.removeDuplicates()
.filter { $0.count >= 3 }
.flatMap(maxPublishers: .max(1)) { [weak self] text -> AnyPublisher<String, Never> in
guard let self else { return Empty().eraseToAnyPublisher() }
return self.translationService.translate(text)
.replaceError(with: "")
.eraseToAnyPublisher()
}
.receive(on: RunLoop.main)
.assign(to: &$translatedText)
.flatMap(maxPublishers: .max(1)) — ключевой момент. Это switchMap: при новом вводе отменяет предыдущий незавершённый запрос. Без этого старые ответы могут прийти позже новых и перезаписать актуальный перевод.
На Android с Kotlin Flow:
val translatedText: StateFlow<String> = inputText
.debounce(500)
.filter { it.length >= 3 }
.distinctUntilChanged()
.flatMapLatest { text ->
flow { emit(translationRepo.translate(text)) }
.catch { emit("") }
}
.stateIn(viewModelScope, SharingStarted.Lazily, "")
flatMapLatest — эквивалент switchMap, отменяет предыдущий coroutine.
Интеграция с Google Cloud Translation v3
suspend fun translate(text: String, targetLang: String = "ru"): String {
val body = JSONObject().apply {
put("q", text)
put("target", targetLang)
put("format", "text")
}
val response = httpClient.post("https://translation.googleapis.com/language/translate/v2") {
header("Authorization", "Bearer $accessToken")
contentType(ContentType.Application.Json)
setBody(body.toString())
}
return response.body<TranslationResponse>().data.translations[0].translatedText
}
Для access_token в продакшене — сервисный аккаунт GCP, JWT-подпись на бэкенде. Мобильный клиент получает краткосрочный токен через собственный /api/translate-token эндпоинт. API-ключ GCP в APK/IPA — нет.
ML Kit для офлайн-сценариев
// iOS: Google ML Kit Translate
let options = TranslatorOptions(sourceLanguage: .english, targetLanguage: .russian)
let translator = Translator.translator(options: options)
translator.downloadModelIfNeeded { error in
guard error == nil else { return }
translator.translate("Hello world") { result, error in
print(result ?? "")
}
}
Модель скачивается один раз через Wi-Fi (желательно). Далее — работа без сети. Задержка на устройстве — 20–50 мс на фразу. Идеально для мессенджеров, субтитров в офлайн-видео, туристических приложений.
Кэширование переводов
Повторные запросы одного и того же текста — деньги в мусор. Кэш на уровне SQLite (Room/CoreData) с ключом sha256(source_text + target_lang). TTL 7 дней для обычного контента, без TTL для статичных строк UI.
На уровне HTTP — Cache-Control для GET-запросов. Google Translate поддерживает GET с параметрами, что позволяет кэшировать на уровне URLCache / OkHttp Cache.
Пример из практики
В приложении для медицинского туризма (iOS + Android) переводили описания клиник и отзывы пациентов (en→ru, ru→en, de→ru). Cloud-перевод для контента при загрузке, ML Kit офлайн для интерфейса консультации. Debounce 700 мс для поисковой строки. Кэш перевода в Room снизил количество API-запросов на 73% в течение первой недели после запуска.
Сроки
Базовая интеграция одного провайдера с debounce — 3–5 дней. Добавление ML Kit офлайн, кэша, автодетекции языка — ещё 4–6 дней. Обработка форматированного текста (HTML, Markdown) — отдельная задача.







