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)