Клиенты часто приходят с задачей real-time перевода в мобильном приложении — для чата, текстового ввода или документов. Каждый сценарий требует своего подхода: задержка 200–400 мс для чата, debounce для live-ввода, батч с прогресс-баром для документов. За многолетний опыт мы реализовали более 30 проектов с переводом для медицинского туризма, e-commerce и мессенджеров. Гарантируем стабильную работу и соблюдение App Store Review Guidelines. Снижаем стоимость интеграции за счёт продуманной архитектуры. В этой статье разбираем стек, архитектуру и практические приёмы.
Как выбрать провайдера перевода?
Мы сертифицированные разработчики Google Cloud и имеем опыт интеграции всех трёх основных провайдеров. Сравнение в таблице:
| Провайдер | Языки | Качество | Офлайн | Цена |
|---|---|---|---|---|
| Google Cloud Translation v3 | 130+ | NMT, отлично | Нет | за символы |
| DeepL API | 31 (основные) | Лучше для европейских | Нет | за символы, есть бесплатный лимит 500K/мес |
| ML Kit Translate | 58 | Хорошо для офлайн | Да | бесплатно |
Google Cloud Translation — стандарт для продакшена. Поддерживает формальные/неформальные регистры, REST и gRPC. DeepL — выбор, когда критично качество перевода сложных текстов (немецкий, французский). ML Kit — основа для офлайн-сценариев: модель ~15 МБ на языковую пару, скачивается один раз.
Почему важен debounce?
Главная ошибка live-перевода — отправлять запрос на каждое нажатие клавиши. На скорости печати 200 символов/минуту это 3–4 запроса в секунду, из которых 90% уйдут в мусор до ответа. Правильный паттерн — debounce с отменой предыдущих незавершённых запросов.
На 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.
Без отмены при быстром вводе pipeline захлебнётся: каждый новый символ запускает перевод, но ответы приходят в произвольном порядке. Пользователь видит «мерцание» перевода. Использование flatMapLatest гарантирует, что в момент нового ввода предыдущий перевод отменяется, и в UI попадает только последний результат.
Интеграция с 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 Cloud Translation, поддерживаются GET-запросы с параметрами, что позволяет кэшировать на уровне URLCache / OkHttp Cache. Это существенно сокращает расходы на API.
| Сценарий | Без кэша | С кэшем |
|---|---|---|
| Live-перевод (100 запросов) | 100 API-вызовов | ~10–20 вызовов (90% попаданий) |
| Батч-перевод документов (1000 страниц) | 1000 вызовов | ~200 вызовов (80% попаданий) |
Пример из практики
В приложении для медицинского туризма (iOS + Android) переводили описания клиник и отзывы пациентов (en→ru, ru→en, de→ru). Cloud-перевод для контента при загрузке, ML Kit офлайн для интерфейса консультации. Debounce 700 мс для поисковой строки. Кэш перевода в Room снизил количество API-запросов на 73% в течение первой недели после запуска, а скорость отображения перевода выросла на 40%. Экономия бюджета на API составила значительную сумму.
Процесс работы
- Аналитика: изучаем сценарии использования (чат, live, батч), требования к задержке и языкам.
- Проектирование: выбираем провайдера, архитектуру (облачный/офлайн/гибрид), проектируем debounce и кэш.
- Реализация: интеграция через бэкенд, написание модуля перевода, настройка отмены запросов.
- Тестирование: проверяем задержки при разных нагрузках, качество перевода, работу в офлайн.
- Деплой: публикация в App Store / Google Play, мониторинг ошибок через Crashlytics.
Что входит в работу
- Исходный код iOS и/или Android с интеграцией выбранного провайдера.
- Документация по архитектуре, ключам доступа и эксплуатации.
- Настройка бэкенд-токенов и доступа к API.
- Обучение команды работе с решением.
- Гарантийная поддержка 1 месяц после сдачи.
Сроки и стоимость
Базовая интеграция одного провайдера с debounce и кэшем занимает от 5 до 7 дней. Добавление ML Kit офлайн, автоопределения языка и форматированного текста — от 4 до 6 дней. Точные сроки и стоимость рассчитываются индивидуально, с учётом вашего бюджета. Получите консультацию по выбору провайдера и архитектуры перевода. Свяжитесь с нами для оценки вашего проекта.







