Realtime Translation - Flutter
Realtime translation allows you to convert spoken audio into translated text instantly during a live room session. This guide explains how to use the startTranslation(), stopTranslation(), and changeTranslationLanguage() methods of the Room class to manage translation in real time in your application.
You can define a participant's translation language when creating the room with createRoom(), or update it at any point during the session with the changeTranslationLanguage() method.
The translation language is always a language code, both in the translationLanguage parameter of createRoom() and in changeTranslationLanguage(). Only the codes listed in Supported Languages return translated text. If you pass an unsupported code, no translation output is received.
Integrating Realtime Translation Feature
-
Start Translation: You start realtime translation with the
startTranslation()method. ThetranslationStateChangedevent then reportsTRANSLATION_STARTING, followed byTRANSLATION_STARTEDonce translation is running. -
Translation Data: As translation progresses, the
translationTextevent delivers aTranslationTextobject holding the translated text, the participant who spoke it, the timestamp, and the language. -
Change Translation Language: A participant can switch to another language while translation is running with
changeTranslationLanguage(). ThetranslationLanguageChangedevent then reports the new language code. -
Stop Translation: When you stop translation with
stopTranslation(), thetranslationStateChangedevent reportsTRANSLATION_STOPPING, followed byTRANSLATION_STOPPED.
Step 1: Configure the translation languages
- You can set the participant's translation language and speaking language while creating the room with
createRoom().
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: participantName,
micEnabled: true,
camEnabled: true,
translationLanguage: "en",
speakingLanguage: "en",
);
-
translationLanguage: The language the participant receives translated text in. ThetranslationTextevent fires only when the text is in this language, so a participant who does not settranslationLanguagereceives no translated text until they callchangeTranslationLanguage(). -
speakingLanguage: Fixes the language the participant speaks in for the whole session. When it is not set, the participant'stranslationLanguagedoubles as their spoken language. Refer to Speaking Language and Translation Language for how the two interact.
Step 2: Start realtime translation
- Initiate the realtime translation using the
startTranslation()method.
// Starts realtime translation
try {
await room.startTranslation();
} on VideoSDKError catch (error) {
print("startTranslation failed: ${error.code} ${error.name}");
}
Step 3: Change the translation language
- Switch to another language while translation is running using the
changeTranslationLanguage()method. The method takes the language code to switch to.
// Changes the translation language to Spanish
try {
await room.changeTranslationLanguage("es");
} on VideoSDKError catch (error) {
print("changeTranslationLanguage failed: ${error.code} ${error.name}");
}
Step 4: Stop realtime translation
- Terminate the realtime translation using the
stopTranslation()method.
// Stops realtime translation
try {
await room.stopTranslation();
} on VideoSDKError catch (error) {
print("stopTranslation failed: ${error.code} ${error.name}");
}
startTranslation(), stopTranslation() and changeTranslationLanguage() return a Future that throws a VideoSDKError when the SDK refuses the request, so await them inside try/catch.
Call these methods only after the roomJoined event. On Android, a call made while the room reconnects reports 3065 on Events.error instead of throwing, so keep an Events.error handler too.
Step 5: Listen for the translation events
- Here, you configure the event listeners for the translation events.
import 'package:videosdk/videosdk.dart';
void registerTranslationEvents(Room room) {
// Listen for translation state changed event
room.on(Events.translationStateChanged, (TranslationState state) {
if (state == TranslationState.TRANSLATION_STARTING) {
print("Realtime Translation is starting");
} else if (state == TranslationState.TRANSLATION_STARTED) {
print("Realtime Translation is started");
} else if (state == TranslationState.TRANSLATION_STOPPING) {
print("Realtime Translation is stopping");
} else if (state == TranslationState.TRANSLATION_STOPPED) {
print("Realtime Translation is stopped");
}
});
// Listen for translation text event
room.on(Events.translationText, (TranslationText translationText) {
print(
"${translationText.participantName} (${translationText.language}): ${translationText.text} ${translationText.timestamp}");
});
// Listen for translation language changed event
room.on(Events.translationLanguageChanged,
(String? participantId, String language) {
print("$participantId changed their translation language to $language");
});
}
-
translationStateChangeddelivers aTranslationStatevalue, one ofTRANSLATION_STARTING,TRANSLATION_STARTED,TRANSLATION_STOPPING, orTRANSLATION_STOPPED. -
translationTextdelivers aTranslationTextobject holdingtext,participantId,participantName,participantLanguage(the language the speaker spoke in),language(the language the text was translated into),timestamp, and atype,TranslationType.FULLorTranslationType.PARTIAL. It fires only for text in the receiving participant's own translation language. -
translationLanguageChangeddelivers the id of the participant whose language changed and the new language code. It fires when any participant changes their translation language, including you.
Example
- The following code snippet allows you to start, stop, and change the language of realtime translation with just a click.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class TranslationView extends StatefulWidget {
final Room room;
const TranslationView({Key? key, required this.room}) : super(key: key);
@override
State<TranslationView> createState() => _TranslationViewState();
}
class _TranslationViewState extends State<TranslationView> {
bool isTranslationOn = false;
String currentLanguage = "en";
List<String> translationMessages = [];
@override
void initState() {
super.initState();
registerTranslationEvents();
}
@override
void dispose() {
// The room outlives this widget, so take the handlers off it.
widget.room.off(Events.translationStateChanged, onTranslationStateChanged);
widget.room.off(Events.translationText, onTranslationText);
super.dispose();
}
void registerTranslationEvents() {
// Listen for translation state changed event
widget.room.on(Events.translationStateChanged, onTranslationStateChanged);
// Listen for translation text event
widget.room.on(Events.translationText, onTranslationText);
}
void onTranslationStateChanged(TranslationState state) {
if (state == TranslationState.TRANSLATION_STARTED) {
setState(() => isTranslationOn = true);
} else if (state == TranslationState.TRANSLATION_STOPPING ||
state == TranslationState.TRANSLATION_STOPPED) {
setState(() => isTranslationOn = false);
}
}
void onTranslationText(TranslationText translationText) {
setState(() {
translationMessages.add(
"${translationText.participantName}: ${translationText.text}");
});
}
Future<void> startTranslation() async {
try {
await widget.room.startTranslation();
} catch (error) {
print("startTranslation failed: $error");
}
}
Future<void> stopTranslation() async {
try {
await widget.room.stopTranslation();
} catch (error) {
print("stopTranslation failed: $error");
}
}
Future<void> changeLanguage(String languageCode) async {
try {
await widget.room.changeTranslationLanguage(languageCode);
// Track your own language here. translationLanguageChanged
// reports every participant's change, with their participantId.
if (mounted) setState(() => currentLanguage = languageCode);
} catch (error) {
print("changeTranslationLanguage failed: $error");
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(isTranslationOn
? "Translation active ($currentLanguage)"
: "Translation inactive"),
Row(
children: [
ElevatedButton(
onPressed: isTranslationOn ? null : startTranslation,
child: const Text("Start Translation"),
),
ElevatedButton(
onPressed: isTranslationOn ? stopTranslation : null,
child: const Text("Stop Translation"),
),
],
),
Wrap(
children: [
for (final entry in {
"French": "fr",
"Spanish": "es",
"German": "de",
"Hindi": "hi",
}.entries)
ElevatedButton(
onPressed:
isTranslationOn ? () => changeLanguage(entry.value) : null,
child: Text(entry.key),
),
],
),
Expanded(
child: ListView.builder(
itemCount: translationMessages.length,
itemBuilder: (context, index) => Text(translationMessages[index]),
),
),
],
);
}
}
Speaking Language and Translation Language
The speakingLanguage parameter defines the language a participant speaks in, while translationLanguage defines the language they want to receive translated text in. The two examples below show how translation behaves with and without a speaking language.
Example 1: Without speakingLanguage
Both participants set only their translation language, so each participant's translation language doubles as their spoken language.
// Participant 1 (Spanish)
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: participantName,
translationLanguage: "es", // Spanish
);
// Participant 2 (German)
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: participantName,
translationLanguage: "de", // German
);
Participant 1 selected Spanish (es) as their translation language, so Spanish is also their spoken language. Participant 2 selected German (de), so German is also their spoken language.
| Speaker | Speaking Language (Input) | Receiver | Translated Output |
|---|---|---|---|
| Participant 1 | Spanish (es) | Participant 2 | German (de) |
| Participant 2 | German (de) | Participant 1 | Spanish (es) |
In this case each participant must speak in their own translation language, because no speaking language is defined.
Example 2: With speakingLanguage on one participant
Here only Participant 1 sets a speakingLanguage. That fixes the input language for their audio, while Participant 2's translation language still doubles as their spoken language.
// Participant 1 (speaks English, receives Spanish)
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: participantName,
translationLanguage: "es", // Spanish (output)
speakingLanguage: "en", // English (input)
);
// Participant 2 (no speakingLanguage, receives German)
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: participantName,
translationLanguage: "de", // German (output)
);
Participant 1 speaks in English (en) and receives translations in Spanish (es). Participant 2 has no speakingLanguage, so German (de) is both the language they speak and the language they receive translations in.
| Speaker | Speaking Language (Input) | Receiver | Translated Output |
|---|---|---|---|
| Participant 1 | English (en) | Participant 2 | German (de) |
| Participant 2 | German (de) | Participant 1 | Spanish (es) |
Participant 1 speaks English and Participant 2 receives it translated into German, while Participant 1 receives Participant 2's German speech translated into Spanish.
Key takeaways
- When
speakingLanguageis not set, the participant's translation language doubles as their speaking language. - When only one participant sets a
speakingLanguage, that participant's input language is fixed, while the others keep using their translation language as their input language. - Setting
speakingLanguagegives you better control and consistency, especially in multilingual rooms.
Supported Languages
These are the supported language codes:
- Multilingual (Spanish + English):
multi - Bulgarian:
bg - Catalan:
ca - Chinese (Mandarin, Simplified):
zh,zh-CN,zh-Hans - Chinese (Mandarin, Traditional):
zh-TW,zh-Hant - Chinese (Cantonese, Traditional):
zh-HK - Czech:
cs - Danish:
da,da-DK - Dutch:
nl - English:
en,en-US,en-AU,en-GB,en-NZ,en-IN - Estonian:
et - Finnish:
fi - Flemish:
nl-BE - French:
fr,fr-CA - German:
de - German (Switzerland):
de-CH - Greek:
el - Hindi:
hi - Hungarian:
hu - Indonesian:
id - Italian:
it - Japanese:
ja - Korean:
ko,ko-KR - Latvian:
lv - Lithuanian:
lt - Malay:
ms - Norwegian:
no - Polish:
pl - Portuguese:
pt,pt-BR,pt-PT - Romanian:
ro - Russian:
ru - Slovak:
sk - Spanish:
es,es-419 - Swedish:
sv,sv-SE - Thai:
th,th-TH - Turkish:
tr - Ukrainian:
uk - Vietnamese:
vi
API Reference
The API references for all the methods utilized in this guide are provided below.
Got a Question? Ask us on discord

