Language Translation - Sanas Developer Hub
Language Translation (LT)
Language Translation (LT) runs on the remote-inference pipeline. Unlike audio processing, it is selected by the presence of an lt_config, not by a model key — so AudioAttributes.model_name is left empty. Translated audio comes back from process_frame; transcripts arrive on the lt_config callback. This tutorial follows the language_translation_example.py example. If you haven’t yet, read Processing a Single Stream first — LT reuses the same activation, pipeline-readiness, and real-time feed mechanics.
LT has a large round-trip latency (~3–4 s). You must keep pushing silence for a window that exceeds that latency after the input ends, or the translated tail will be lost.
Prerequisites
| Variable | Required | Default | Purpose |
|---|---|---|---|
SANAS_API_KEY |
Yes | — | Licence key |
SANAS_STORAGE_DIR |
No | ./storage |
SDK data + logs |
SANAS_LANG_IN |
No | en-US |
Source language code |
SANAS_LANG_OUT |
No | es-ES |
Target language code |
SANAS_CONVERSATION_ID |
No | conversation-id-1 |
Links two-party sessions |
SANAS_LT_DRAIN_SECONDS |
No | 5.0 |
Silence drain window; must exceed LT latency |
export SANAS_API_KEY="your-key"
python language_translation_example.py --input test_input.wav --output translated.wav
Step 1: Handle transcript callbacks
Transcripts arrive as TextFrames. Each frame is either a TRANSLATION or a TRANSCRIPTION, carries an utterance index and language code, and contains complete and partial segments. Segments may include redacted text.
import sanas
def print_transcript(tf):
label = "Translation" if tf.type_ == sanas.TranscriptType.TRANSLATION else "Transcription"
td = tf.transcript_data_
lang = f" lang={{td.language_code_}}" if td.language_code_ else ""
print(f"\n[{{label}}] utterance {{td.utterance_idx}}{{lang}}")
for kind, segs in (("complete", td.complete), ("partial", td.partial)):
for s in segs:
redacted = f' (redacted: "{{s.redacted_text}}")' if s.redacted_text else ""
print(f' [{{kind}}] "{{s.text}}"{{redacted}} [{{s.start}}-{{s.end}} p={{s.probability:.2f}}]')
Step 2: Activate and load audio
Identical to single-stream processing:
from helpers import PipelineWaiter, feed_and_drain, load_samples
from wav_utils import save_wav
sdk = sanas.create_sdk(sanas.InitParams(storage_dir=storage_dir))
res = sdk.activate_api_key(api_key)
if not res.success:
sys.exit(f"Activation failed: {{res.message}}")
print(f"[activation] OK (SDK {{sdk.version}})")
samples, sample_rate, channels = load_samples(args.input)
Step 3: Configure translation via lt_config
Leave model_name empty and attach a LanguageTranslationConfig. Its presence is what puts the pipeline into translation mode.
waiter = PipelineWaiter(on_state=lambda s: print(f"[pipeline] {{s}}"))
attrs = sanas.ProcessorAttributes(
audio_attributes=sanas.AudioAttributes(
sampling_rate=sample_rate,
channels=channels,
model_name="", # LT is selected by lt_config, not a model key
audio_pipeline_state_notify=waiter.callback,
),
lt_config=sanas.LanguageTranslationConfig(
language_in=lang_in,
language_out=lang_out,
conversation_id=conversation_id, # optional; links two-party sessions
callback=print_transcript,
),
)
Step 4: Feed with an extended drain window
Because translation lags several seconds behind the input, pass drain_seconds to feed_and_drain. This makes the helper pump silence for a fixed, real-time-paced window (instead of stopping early on empty frames), giving the remote wall-clock time to return the tail.
drain_seconds = float(os.environ.get("SANAS_LT_DRAIN_SECONDS", "5.0"))
with sdk.create_audio_processor(attrs) as proc:
print("[pipeline] waiting for RUNNING ...")
waiter.wait_until_running(timeout=30.0)
result = feed_and_drain(proc, samples, sample_rate, channels, drain_seconds=drain_seconds)
Set SANAS_LT_DRAIN_SECONDS comfortably above the observed round-trip latency. The default of 5 s covers the typical ~3–4 s; increase it if your target language or network is slower.
Step 5: Save the translated audio
combined = result["output"]
if combined:
save_wav(args.output, combined, sample_rate, channels)
print(f"\n[output] {{len(combined) // 4}} samples -> {{args.output}}")
else:
print("\n[output] no translated audio received.")
How it differs from audio processing
| Audio processing | Language Translation | |
|---|---|---|
| Selected by | model_name (e.g. VI_G_SE) |
presence of lt_config |
model_name |
a model key | empty string |
| Latency | ~50–100 ms | ~3–4 s |
| Tail drain | stop on empty frames (default) | fixed drain_seconds window |
| Extra output | processed audio only | audio and transcript callbacks |
Next Steps
[**Processing a Single Stream**
The core flow this benchmark scales up.](https://developer.sanas.ai/Docs/Tutorials/Single-Stream)
[**API Reference**
Full SDK documentation for classes, enums, and callbacks.](https://developer.sanas.ai/API-Reference/Overview)
[**Language Translation**
Translate speech between languages and receive audio plus transcripts.](https://developer.sanas.ai/Docs/Tutorials/Language-Translation)
[**Processing Multiple Streams**
Run many concurrent processors on one SDK instance.](https://developer.sanas.ai/Docs/Tutorials/Processing-Multiple-Streams)
References
[**SDK**
SDK lifecycle methods including create_sdk, activate_api_key, and create_audio_processor.](https://developer.sanas.ai/API-Reference/Reference/Core-Classes/RemoteSDK)
[**AudioProcessor**
process_frame method for audio processing.](https://developer.sanas.ai/API-Reference/Reference/Core-Classes/AudioProcessor)