לקוחות מגיעים אלינו פעמים רבות עם משימה של תרגום בזמן אמת באפליקציה ניידת — לצ'אט, להזנת טקסט או למסמכים. כל תרחיש דורש גישה שונה: השהיה של 200–400 אלפיות השנייה לצ'אט, debounce להזנה חיה, ועיבוד אצווה עם פס התקדמות למסמכים. במשך שנים רבות של ניסיון, יישמנו למעלה מ-30 פרויקטים עם תרגום לתיירות רפואית, מסחר אלקטרוני ומסנג'רים. אנו מבטיחים ביצועים יציבים ועמידה בהנחיות 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 MB לזוג שפות, מורד פעם אחת.
למה Debounce חשוב?
הטעות העיקרית בתרגום חי היא שליחת בקשה על כל הקלדה. במהירות הקלדה של 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)@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) — זהו switchMap: הוא מבטל את הבקשה הקודמת על קלט חדש. ללא זה, תשובות ישנות יכולות לדרוס את התרגום הנוכחי.
באנדרואיד (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, "")flatMap(maxPublishers: .max(1)) — המקבילה של switchMap, מבטל את ה-coroutine הקודם.
ללא ביטול, במהלך הקלדה מהירה הצינור מוצף: כל תו חדש מפעיל תרגום, אבל התשובות מגיעות בסדר אקראי. המשתמש רואה תרגום "מהבהב". שימוש ב-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, "") מבטיח שעל קלט חדש התרגום הקודם מבוטל, ורק התוצאה העדכנית ביותר מגיעה לממשק המשתמש.
אינטגרציה עם 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
}עבור flatMapLatest בייצור — חשבון שירות של GCP, חתימת JWT בשרת. לקוח הנייד מקבל טוקן קצר מועד דרך נקודת קצה flatMapLatest משלו. אין מפתח 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) עם מפתח 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 } . TTL של 7 ימים לתוכן רגיל, ללא TTL למחרוזות UI סטטיות. ברמת HTTP, access_token לבקשות GET. לפי תיעוד Google Cloud Translation, בקשות GET עם פרמטרים נתמכות, מה שמאפשר שמירה במטמון ברמת /api/translate-token / OkHttp // 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 ?? "") } } . זה מפחית משמעותית עלויות API — שמירה במטמון משפרת יעילות עד פי 10 לעומת תרגום ללא מטמון.
| תרחיש | ללא מטמון | עם מטמון |
|---|---|---|
| תרגום חי (100 בקשות) | 100 קריאות API | ~10–20 קריאות (90% פגיעות) |
| תרגום מסמכים באצווה (1000 עמודים) | 1000 קריאות | ~200 קריאות (80% פגיעות) |
מקרה בוחן
בפרויקט עבור לקוח שלנו, אפליקציית תיירות רפואית (iOS + Android), תרגמנו תיאורי מרפאות וביקורות מטופלים (אנ→רו, רו→אנ, דה→רו). תרגום ענן לתוכן בעת טעינה, ML Kit לא מקוון לממשק הייעוץ. Debounce של 700 אלפיות השנייה לשורת החיפוש. מטמון תרגום ב-Room הפחית בקשות API ב-73% בשבוע הראשון לאחר ההשקה, ומהירות הצגת התרגום עלתה ב-40%. החיסכון בתקציב ה-API היה משמעותי — מעל $500 לחודש עבור הלקוח שלנו.
תהליך
- אנליטיקה: לימוד תרחישי שימוש (צ'אט, חי, אצווה), דרישות השהיה ושפה.
- עיצוב: בחירת ספק וארכיטקטורה (ענן/לא מקוון/היברידי), עיצוב debounce ומטמון.
- יישום: אינטגרציה דרך השרת, כתיבת מודול תרגום, הגדרת ביטול בקשות.
- בדיקות: בדיקת השהיות בעומסים שונים, איכות תרגום, התנהגות לא מקוונת.
- השקה: פרסום ל-App Store / Google Play, ניטור שגיאות דרך Crashlytics.
מה כלול
- קוד מקור ל-iOS ו/או אנדרואיד עם אינטגרציה של הספק הנבחר.
- תיעוד על ארכיטקטורה, מפתחות גישה ותפעול.
- הגדרת טוקנים בשרת וגישת API.
- הכשרת הצוות שלך לשימוש בפתרון.
- תמיכה באחריות למשך חודש לאחר המסירה.
לוחות זמנים ועלות
אינטגרציה בסיסית של ספק אחד עם debounce ומטמון אורכת 5 עד 7 ימים ועולה החל מ-$1,500. הוספת ML Kit לא מקוון, זיהוי שפה אוטומטי וטקסט מעוצב אורכת 4 עד 6 ימים. לוחות זמנים ועלות מדויקים מחושבים באופן אישי, תוך התחשבות בתקציב שלך. קבלו ייעוץ על בחירת ספק וארכיטקטורת תרגום. צרו קשר להערכת הפרויקט שלכם.







