> ## Documentation Index
> Fetch the complete documentation index at: https://developer.sanas.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Language Translation

> Translate speech from one language to another and receive both translated audio and transcript segments.

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](/Docs/Tutorials/Single-Stream) first — LT reuses the same activation, pipeline-readiness, and real-time feed mechanics.

<Warning>
  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.
</Warning>

## 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 |

```bash theme={null}
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 `TextFrame`s. 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.

```python theme={null}
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:

```python theme={null}
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.

```python theme={null}
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.

```python theme={null}
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)
```

<Note>
  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.
</Note>

## Step 5: Save the translated audio

```python theme={null}
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

<CardGroup cols={2}>
  <Card title="Processing a Single Stream" icon="waveform-lines" href="/Docs/Tutorials/Single-Stream">
    The core flow this benchmark scales up.
  </Card>

  <Card title="API Reference" icon="book" href="/API-Reference/Overview">
    Full SDK documentation for classes, enums, and callbacks.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Language Translation" icon="language" href="/Docs/Tutorials/Language-Translation">
    Translate speech between languages and receive audio plus transcripts.
  </Card>

  <Card title="Processing Multiple Streams" icon="layer-group" href="/Docs/Tutorials/Processing-Multiple-Streams">
    Run many concurrent processors on one SDK instance.
  </Card>
</CardGroup>

## References

<CardGroup cols={2}>
  <Card title="SDK" icon="server" href="/API-Reference/Reference/Core-Classes/RemoteSDK">
    SDK lifecycle methods including create\_sdk, activate\_api\_key, and create\_audio\_processor.
  </Card>

  <Card title="AudioProcessor" icon="waveform-lines" href="/API-Reference/Reference/Core-Classes/AudioProcessor">
    process\_frame method for audio processing.
  </Card>
</CardGroup>

***

## Need Help?

<CardGroup cols={2}>
  <Card title="Email Support" icon="envelope" href="mailto:support@sanas.ai">
    [support@sanas.ai](mailto:support@sanas.ai)

    Response time: 1 business day
  </Card>

  <Card title="Support Portal" icon="ticket" href="https://support.sanas.ai">
    Raise a [support ticket](https://help.sanas.ai/) for urgent issues
  </Card>
</CardGroup>
