Add real-time translation by calling Google Cloud Translation API v3 (translate.googleapis.com/v3/projects/{projectId}:translateText) from a Cloud Function with the target language and source text. Display a language selector DropDown, trigger translation on button tap or language change, and cache translations in a Firestore map field {en: 'Hello', es: 'Hola'} to avoid re-translating identical content.
Translate user-generated content and dynamic text in real time within your FlutterFlow app
FlutterFlow's built-in multi-language support handles pre-translated static UI strings — but it cannot translate user-generated content like chat messages, product reviews, or support tickets that you do not know in advance. This tutorial covers real-time dynamic translation: calling Google Cloud Translation API from a Cloud Function, building a language picker, displaying original and translated text, and caching translations in Firestore to keep costs manageable. You will also learn when on-device translation (no internet required) is the better choice.
Prerequisites
- A FlutterFlow project with Firebase connected and Cloud Functions enabled (Blaze plan)
- Google Cloud Translation API enabled in your Google Cloud Console (APIs & Services → Enable APIs → Cloud Translation API)
- A Google Cloud API key or service account with Translation API access (Cloud Console → APIs & Services → Credentials)
- A use case with user-generated or dynamic text content to translate (chat messages, product descriptions, reviews)
Step-by-step guide
Create the Cloud Function that calls Google Cloud Translation API v3
Create the Cloud Function that calls Google Cloud Translation API v3
In your Firebase Cloud Functions, create a function named translateText. It receives a POST request from FlutterFlow with: sourceText (the text to translate), targetLanguage (a BCP-47 language code like 'es', 'fr', 'ja', 'zh'), and optionally sourceLanguage (if not provided, the API auto-detects). The function calls the Translation API v3 endpoint using your service account credentials. Using Cloud Functions keeps your API key server-side — never put a Translation API key in FlutterFlow's API Manager headers where it can be extracted from the compiled app. The endpoint is https://translation.googleapis.com/v3/projects/{projectId}:translateText with a POST body containing the text and target language. Return the translated text in the response.
1// functions/index.js — Google Cloud Translation proxy2// Install: cd functions && npm install axios3const functions = require('firebase-functions');4const admin = require('firebase-admin');5const axios = require('axios');67admin.initializeApp();89exports.translateText = functions.https.onRequest(async (req, res) => {10 res.set('Access-Control-Allow-Origin', '*');11 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }1213 const { sourceText, targetLanguage, sourceLanguage, contentId } = req.body;1415 if (!sourceText || !targetLanguage) {16 res.status(400).json({ error: 'sourceText and targetLanguage required' });17 return;18 }1920 // Check Firestore translation cache first21 const db = admin.firestore();22 if (contentId) {23 const cacheDoc = await db24 .collection('translation_cache')25 .doc(`${contentId}_${targetLanguage}`)26 .get();27 if (cacheDoc.exists) {28 res.json({ translatedText: cacheDoc.data().text, fromCache: true });29 return;30 }31 }3233 // Call Google Cloud Translation API v334 const projectId = process.env.GCLOUD_PROJECT;35 const apiKey = functions.config().translation.api_key;3637 try {38 const response = await axios.post(39 `https://translation.googleapis.com/v3/projects/${projectId}:translateText`,40 {41 contents: [sourceText],42 targetLanguageCode: targetLanguage,43 sourceLanguageCode: sourceLanguage || null,44 mimeType: 'text/plain',45 },46 { params: { key: apiKey } }47 );4849 const translatedText =50 response.data.translations[0].translatedText;51 const detectedLanguage =52 response.data.translations[0].detectedLanguageCode;5354 // Cache the translation in Firestore55 if (contentId) {56 await db57 .collection('translation_cache')58 .doc(`${contentId}_${targetLanguage}`)59 .set({60 text: translatedText,61 sourceLanguage: detectedLanguage || sourceLanguage,62 targetLanguage,63 contentId,64 cachedAt: admin.firestore.FieldValue.serverTimestamp(),65 });66 }6768 res.json({ translatedText, detectedLanguage });69 } catch (err) {70 console.error('Translation API error:', err.response?.data || err.message);71 res.status(500).json({ error: 'Translation failed', details: err.message });72 }73});7475// Set config: firebase functions:config:set translation.api_key='YOUR_KEY'76// Deploy: firebase deploy --only functionsExpected result: Cloud Function is deployed. A test POST to translateText with {sourceText: 'Hello', targetLanguage: 'es'} returns {translatedText: 'Hola'}.
Configure the FlutterFlow API call and build the language selector
Configure the FlutterFlow API call and build the language selector
In FlutterFlow, go to API Manager → Add API Group → name it TranslationService. Set the Base URL to your Cloud Function URL. Add an API Call named translate with Method: POST and request body: {"sourceText": "[sourceText]", "targetLanguage": "[targetLanguage]", "contentId": "[contentId]"}. The [variableName] syntax creates dynamic variables you bind to widget values. Click Test and verify the response. For the language selector, add a DropDown widget to your page. Populate it with the most common target languages as static options — label/value pairs: English/en, Spanish/es, French/fr, German/de, Japanese/ja, Portuguese/pt, Chinese Simplified/zh-CN, Arabic/ar, Hindi/hi, Russian/ru. Bind the DropDown's initial value to a Page State variable named selectedLanguage (initial value: your app's default language, e.g., 'en'). On DropDown change, update the selectedLanguage Page State variable to the newly selected language code.
Expected result: API Manager shows the TranslationService group with a working translate API call. A DropDown on the page shows language options and updates Page State on selection.
Add translate button and display original and translated text
Add translate button and display original and translated text
Create a UI layout for a translatable content item — for example, a chat message bubble or a product review. Add a Column widget containing: a Text widget (originalText) displaying the source content from your Backend Query, a Divider, a Text widget (translatedText) initially hidden, a Row with a translate icon button and a language label. Create two Page State variables: translatedText (String, initial: ''), showTranslation (Boolean, initial: false). The translatedText Text widget uses Conditional Value for visibility: show if showTranslation == true. Bind its text to the translatedText Page State variable. On the translate icon button, add an Action Flow: (1) Update Page State showTranslation = true, (2) call translate API with sourceText = the content text, targetLanguage = selectedLanguage Page State, contentId = the document ID, (3) Update Page State translatedText = API response translatedText field. Add a small 'Show original' toggle below the translated text that sets showTranslation = false. This gives users control — translation is on-demand, not automatic.
Expected result: Tapping the translate icon calls the API and displays the translated text below the original. Tapping 'Show original' hides the translation.
Cache translations in Firestore to control costs
Cache translations in Firestore to control costs
Google Cloud Translation API charges $20 per million characters. A chat app with 1,000 messages of 100 characters each translated to 5 languages = 500,000 characters = $10 every time your full message history is translated. Cache translations to eliminate this cost for repeated translations. The Cloud Function already implements a translation_cache Firestore collection (from Step 1). For content stored in Firestore (blog posts, product descriptions), add a translations map field to the document: {en: 'original text', es: 'texto traducido', fr: 'texte traduit'}. When the user requests a translation: check if the translations map already has the target language → if yes, display cached translation → if no, call the Cloud Function, then update the document's translations map. In FlutterFlow: modify the translate Action Flow: first check the Backend Query result's translations map for the selected language key. Use a Custom Function translateFromCache(Map translations, String lang) that returns the cached translation if it exists, or an empty String if not. If the result is empty, call the API and update the translations map using Firestore Update Document Action.
Expected result: Second and subsequent translation requests for the same content and language return instantly from Firestore cache with no API call, reducing Translation API costs by 80-95%.
Add on-device offline translation with google_mlkit_translation
Add on-device offline translation with google_mlkit_translation
For use cases where users might be offline (traveling, poor connectivity) or where privacy matters (translating sensitive content without sending to Google servers), use on-device translation via the google_mlkit_translation Flutter package. In FlutterFlow: Custom Code → Pubspec Dependencies → add google_mlkit_translation with the current version. Create a Custom Action named translateOnDevice with parameters: sourceText (String), targetLanguageCode (String). The action downloads the language model if not already downloaded (one-time download per language, about 20-30MB), then translates locally without an internet connection. Add a Toggle widget to your translation UI: 'Use offline translation'. Bind a Page State useOfflineTranslation: Boolean to the toggle. In the translate Action Flow, use a Conditional Action: if useOfflineTranslation == true → call translateOnDevice Custom Action, else → call the TranslationService API Call. Language model downloads happen on first use — show a CircularProgressIndicator with text 'Downloading language model...' during the first offline translation for a new language.
1// Custom Action: translateOnDevice2// Add to Pubspec: google_mlkit_translation: ^0.9.03// Parameters:4// sourceText (String)5// targetLanguageCode (String) e.g. 'es', 'fr', 'de'6// Returns: String (translated text)78import 'package:google_mlkit_translation/google_mlkit_translation.dart';910Future<String> translateOnDevice(11 String sourceText,12 String targetLanguageCode,13) async {14 // Map BCP-47 code to MLKit TranslateLanguage15 final languageMap = {16 'es': TranslateLanguage.spanish,17 'fr': TranslateLanguage.french,18 'de': TranslateLanguage.german,19 'ja': TranslateLanguage.japanese,20 'zh': TranslateLanguage.chinese,21 'pt': TranslateLanguage.portuguese,22 'ar': TranslateLanguage.arabic,23 'hi': TranslateLanguage.hindi,24 'ru': TranslateLanguage.russian,25 'ko': TranslateLanguage.korean,26 };2728 final targetLanguage =29 languageMap[targetLanguageCode] ?? TranslateLanguage.spanish;3031 // Download model if needed (first time per language)32 final modelManager = OnDeviceTranslatorModelManager();33 final isDownloaded = await modelManager.isModelDownloaded(34 targetLanguage.bcpCode,35 );36 if (!isDownloaded) {37 await modelManager.downloadModel(38 targetLanguage.bcpCode,39 isWifiRequired: false,40 );41 }4243 final translator = OnDeviceTranslator(44 sourceLanguage: TranslateLanguage.english,45 targetLanguage: targetLanguage,46 );4748 final result = await translator.translateText(sourceText);49 translator.close();50 return result;51}Expected result: Toggle between cloud and on-device translation. On-device translation works without an internet connection after the first download and produces results in under 200ms.
Complete working example
1Cloud Function: translateText (HTTPS onRequest)2================================================3Input: sourceText, targetLanguage, contentId (optional)4Output: translatedText, detectedLanguage, fromCache56Flow:7 1. Check Firestore translation_cache/{contentId}_{lang}8 2. If cached → return cached text immediately9 3. Call Translation API v3:10 POST https://translation.googleapis.com/v3/projects/{id}:translateText11 Body: { contents: [text], targetLanguageCode: lang }12 Auth: API key via query param (server-side only)13 4. Cache result in Firestore14 5. Return translatedText1516Firestore Schema17=================18Message / review / post document:19 content: String (original text)20 authorId: String21 createdAt: Timestamp22 translations: Map23 en: 'Original English text'24 es: 'Texto traducido en español'25 fr: 'Texte traduit en français'2627translation_cache/{contentId}_{lang}28 text: String29 sourceLanguage: String30 targetLanguage: String31 cachedAt: Timestamp3233FlutterFlow UI Layout34======================35Column (Backend Query: messages)36 ├── Text (content — original)37 ├── Row (translate controls)38 │ ├── DropDown (language selector → Page State: selectedLanguage)39 │ └── IconButton (translate icon)40 │ Action Flow:41 │ 1. Update Page State isTranslating = true42 │ 2. Check translations[selectedLanguage] (Custom Fn)43 │ 3. If empty → Call translateText API44 │ → Update Page State translatedText = response45 │ → Update Firestore translations map46 │ 4. Update Page State isTranslating = false47 └── ConditionalColumn (visible when showTranslation)48 ├── Text (translatedText Page State)49 └── TextButton ('Show original' → showTranslation = false)5051Page State Variables52=====================53selectedLanguage: String initial: 'en'54translatedText: String initial: ''55showTranslation: Boolean initial: false56isTranslating: Boolean initial: false57useOfflineTranslation: Boolean initial: falseCommon mistakes
Why it's a problem: Translating every text widget on every page load automatically without caching, resulting in hundreds of API calls per user session
How to avoid: Only translate on explicit user request (tap a translate button or change the language selector). Cache all translations in the Firestore document's translations map field after the first translation — subsequent reads of the same content in the same language come from Firestore with zero API cost.
Why it's a problem: Putting the Google Cloud Translation API key directly in FlutterFlow's API Manager headers to call the Translation API from the client
How to avoid: All Translation API calls must go through a Cloud Function. The Cloud Function uses an API key stored in Firebase Functions config (server-side, never in the client app). Use firebase functions:config:set translation.api_key='YOUR_KEY' to store it securely.
Why it's a problem: Assuming the Translation API auto-detects the source language correctly for short texts like single words or product titles
How to avoid: Store the source language when content is created: when a user submits a form, detect their device locale (Global Properties → Device Info → locale) and store it as the sourceLanguage field on the document. Pass this stored sourceLanguage value to the Translation API rather than relying on auto-detection for content creation.
Best practices
- Always route Translation API calls through a Cloud Function — never put the API key in FlutterFlow's client-side API Manager headers
- Cache every translation in Firestore immediately after it is generated — use a Map field on the content document with language codes as keys
- Make translation on-demand (user taps a button) rather than automatic on page load — this gives users control and prevents surprise API costs
- Store the original content language on each document (sourceLanguage field) to avoid language auto-detection errors on short texts
- Use ML Kit on-device translation for use cases requiring offline access or privacy-sensitive content — downloaded language models are free to use unlimited times
- Show the detected source language to users so they can correct it if the auto-detection is wrong — a wrong source language produces poor translations
- Set up Google Cloud budget alerts at $10, $50, and $100 for the Translation API — it is easy to accidentally over-use if caching is not working correctly
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow chat app and want to add real-time translation for messages. Write a Firebase Cloud Function in Node.js that accepts sourceText, targetLanguage, and contentId, checks a Firestore translation_cache collection first, and if not cached calls the Google Cloud Translation API v3 endpoint (https://translation.googleapis.com/v3/projects/{projectId}:translateText) and caches the result. Also write a Dart Custom Action using the google_mlkit_translation package for on-device offline translation with model download management.
Add a translate button below each message in the chat ListView. On tap, call the TranslationService API with the message content and the selected language from the DropDown at the top of the page. Display the translated text in a secondary Text widget below the original message, and show a CircularProgressIndicator while the translation is loading.
Frequently asked questions
How much does Google Cloud Translation API cost?
Cloud Translation Basic (v2) and Advanced (v3) both charge $20 per million characters after the first 500,000 characters per month (which are free). For a typical chat app with 1,000 users sending 10 messages/day of 50 characters each: 1,000 × 10 × 50 = 500,000 characters/day. If all messages are translated once, that is the free tier daily. With caching, only new unique messages are translated, dramatically reducing usage. Set up a Google Cloud budget alert at $10 to catch unexpected usage early.
What languages does Google Cloud Translation API support?
Over 100 languages. Most common: es (Spanish), fr (French), de (German), it (Italian), pt (Portuguese), ru (Russian), ja (Japanese), ko (Korean), zh-CN (Chinese Simplified), zh-TW (Chinese Traditional), ar (Arabic), hi (Hindi), nl (Dutch), tr (Turkish), pl (Polish), sv (Swedish). Get the full list by calling GET https://translation.googleapis.com/v3/projects/{projectId}/supportedLanguages in your Cloud Function and caching the result in Firestore.
Can I translate rich text with HTML formatting?
Yes — set mimeType: 'text/html' in the Translation API request body instead of 'text/plain'. The API translates the text content while preserving HTML tags (bold, italic, links). The translated text returned will have the HTML tags intact. In FlutterFlow, use a flutter_html Custom Widget to render HTML text with formatting in your app. For plain text chat messages, always use 'text/plain' — sending plain text as HTML can corrupt special characters.
How do I handle translation in a real-time chat app where messages arrive via Firestore listener?
Use a Firestore Cloud Function trigger instead of client-side translation. Create a Cloud Function triggered by new document creation in your messages collection: functions.firestore.document('chats/{chatId}/messages/{messageId}').onCreate(). This function auto-translates the message to all active languages in that chat room and stores them in the message's translations map before any client reads it. The FlutterFlow real-time listener then picks up the message WITH translations already populated — no client-side translation call needed.
Does FlutterFlow's built-in multi-language support work for this?
No — FlutterFlow's built-in localization (Settings → Localizations → Add Language) handles static UI strings you define in advance, like button labels, navigation titles, and fixed messages. It does not handle dynamic content from your database that users write at runtime. For user-generated content (chat messages, reviews, posts), you must use the Translation API approach described in this tutorial.
Can RapidDev add multilingual support to my existing FlutterFlow app?
Yes. A full multilingual implementation includes static UI localization via FlutterFlow's built-in system, dynamic content translation via Cloud Translation API with Firestore caching, user language preference stored in the user's Firestore profile, on-device fallback for offline users, and auto-detection of the user's preferred language on first launch. RapidDev can implement the full stack across any FlutterFlow app.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation