Conversation Translation
Build a two-person, turn-based translation experience by starting a short simultaneous-interpretation session for the active speaker and swapping source and target languages for the other participant.
This is a product workflow demonstrated by ConversationTranslationDemoController; it does not introduce a separate ConversationTranslationAPI.
One conversation turn
Each speaker turn owns one interpretation session. Reverse the language direction for the partner's turn and never run both turns concurrently.
Prerequisites
- Meet all prerequisites from Simultaneous Interpretation.
- Use different hyphenated source and target language identifiers.
- Track the active speaker, current session, start/stop state, and rendered turn messages in the host app.
- Prevent both speakers from starting sessions simultaneously.
Implement with AI Assistance
Implement this workflow with AI
Use the official Implement AIBuds Conversation Translation skill to adapt this workflow to your app.
Read and follow https://docs-aibuds.github.io/skills/implement-aibuds-conversation-translation. Use it to implement Implement AIBuds Conversation Translation in this iOS project and verify the result.API Reference
Conversation Translation uses startSimultaneousInterpretation, stopSimultaneousInterpretation, and SimultaneousInterpretationConfig.
See Simultaneous Interpretation for the complete public declarations and callback contract.
Usage Examples
Start a Speaker Turn
The Demo maps “Me” to myLanguage → partnerLanguage and “Partner” to the reverse direction. The following helpers accept the resolved direction, making that product state explicit.
- Swift
- Objective-C
func startConversationTurn(source: String, target: String) {
guard currentSession == nil, source != target else { return }
let config = SimultaneousInterpretationConfig.default
config.sourceLanguage = source
config.targetLanguage = target
config.usesInternalAudioRecording = true
config.preferSpeakerOutput = true
config.enableVoicePlayback = true
AIBudsAISDK.startSimultaneousInterpretation(
config,
onStartSuccess: { session in
currentSession = session
setTurnActive(true)
},
onStartFailure: { error in
currentSession = nil
setTurnActive(false)
show(error)
},
onStopByInterruption: { error in
currentSession = nil
setTurnActive(false)
if let error { show(error) }
},
onException: { error in
showRecoverable(error)
},
streamResultHandler: { _, response, error in
if let error {
show(error)
return
}
guard let response else { return }
if response.isSourceTextDefinite { renderSource(response) }
if response.isTargetTextDefinite { renderTarget(response) }
},
onEvent: { event in
handle(event)
},
onFinish: { report in
currentSession = nil
setTurnActive(false)
save(report)
}
)
}- (void)startConversationTurnFrom:(NSString *)source to:(NSString *)target {
if (self.currentSession != nil || [source isEqualToString:target])
return;
AIBudsSimultaneousInterpretationConfig *config =
[AIBudsSimultaneousInterpretationConfig defaultConfig];
config.sourceLanguage = source;
config.targetLanguage = target;
config.usesInternalAudioRecording = YES;
config.preferSpeakerOutput = YES;
config.enableVoicePlayback = YES;
[AIBudsAISDK startSimultaneousInterpretationWithConfig:config
onStartSuccess:^(id<AIBudsSimultaneousInterpretationSessionConvertible> session) {
self.currentSession = session;
[self setTurnActive:YES];
}
onStartFailure:^(NSError *error) {
self.currentSession = nil;
[self setTurnActive:NO];
[self showError:error];
}
onStopByInterruption:^(NSError *error) {
self.currentSession = nil;
[self setTurnActive:NO];
if (error != nil)
[self showError:error];
}
onException:^(NSError *error) {
[self showRecoverableError:error];
}
streamResultHandler:^(
BOOL isFinal, AIBudsSimultaneousInterpretationDataModel *response, NSError *error) {
if (error != nil) {
[self showError:error];
return;
}
if (response.isSourceTextDefinite)
[self renderSource:response];
if (response.isTargetTextDefinite)
[self renderTarget:response];
}
onEvent:^(AIBudsSimultaneousInterpretationEventModel *event) {
[self handleEvent:event];
}
onFinish:^(AIBudsSimultaneousInterpretationReportModel *report) {
self.currentSession = nil;
[self setTurnActive:NO];
[self saveReport:report];
}];
}Stop the Active Turn
When currentSession.isRecordingInternally is false, stop device-side AI recording before stopping the service so the final audio is delivered in order.
- Swift
- Objective-C
func stopConversationTurn() {
guard let session = currentSession else { return }
if !session.isRecordingInternally,
let recordingDevice = device as? DeviceAudioRecordingAPI
{
recordingDevice.stopAIAudioRecording(.onSite) { _, _ in
AIBudsAISDK.stopSimultaneousInterpretation()
}
} else {
AIBudsAISDK.stopSimultaneousInterpretation()
}
}- (void)stopConversationTurn {
if (self.currentSession == nil)
return;
if (!self.currentSession.isRecordingInternally) {
id<AIBudsDeviceAudioRecordingAPI> recordingDevice =
(id<AIBudsDeviceAudioRecordingAPI>)self.device;
if ([recordingDevice conformsToProtocol:@protocol(AIBudsDeviceAudioRecordingAPI)]) {
[recordingDevice
stopAIAudioRecordingWithScene:AIBudsRecordingSceneOnSite
completion:^(__unused BOOL success, __unused NSError *error) {
[AIBudsAISDK stopSimultaneousInterpretation];
}];
return;
}
}
[AIBudsAISDK stopSimultaneousInterpretation];
}Error Handling
Handle startup failure, interruption, recoverable exception, stream error, device-recording failure, and final completion separately. If a stop is requested while device recording is still starting, defer the service stop until the device start callback has completed; the Demo tracks this race explicitly.
Notes
- A conversation can contain many turns, but only one interpretation session should be active at a time.
- Swap languages only while no turn is active.
- Use source and target sequence fields to order definite segments instead of blindly appending every callback.
- When external device recording is used, register the retained interpretation session with the application's PCM forwarding path before starting device audio so the first packet is not lost.