npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sanas-ai/language-translation

v1.0.10

Published

Javascript client side library for Sanas Language Translation

Readme

Sanas Language Translation JS Client

A browser-based TypeScript client for real-time audio language translation, supporting both WebRTC and WebSocket transports.

Installation

npm install @sanas-ai/language-translation

Quick Start

import {
  SanasTranslationClient,
  TranslationState,
  WebRTCTransport,
  getMicrophoneTrack,
} from "@sanas-ai/language-translation";

// 1. Create a TranslationState to receive callbacks
const state = new TranslationState({
  onUtterance: (utterance, index) => {
    console.log("Transcription:", utterance.transcription.spokenText);
    console.log("Translation:", utterance.translation.spokenText);
  },
  onConnectionStateChange: (connectionState) => {
    console.log("Connection:", connectionState);
  },
  onError: (error) => {
    console.error("Error:", error);
  },
});

// 2. Create the client
const client = new SanasTranslationClient(state, {
  apiKey: "your-api-key",
  endpoint: "https://api.sanaslt.com",
});

// 3. Acquire a mic track and connect
const track = await getMicrophoneTrack();
const transport = new WebRTCTransport();
const { audio } = await client.connect({ transport, audioTrack: track });

// 4. Play translated audio
const audioElement = document.createElement("audio");
audioElement.srcObject = audio;
audioElement.autoplay = true;

// 5. Configure translation with Stream v3 language routes
await client.reset({
  languageRoutes: [{ langIn: "en-US", langOut: "es-ES" }],
});

// 6. When done
client.disconnect();
track.stop();

Architecture

The library is split into three main pieces:

  • TranslationState - Standalone state container that processes StreamMessages and fires callbacks. Consumers create one instance per participant (local + remote) to display both sides of a call.
  • SanasTranslationClient - Manages the connection lifecycle, wraps transport events into StreamMessages, and routes them to a TranslationState. Exposes an onMessage hook for relaying messages to other participants.
  • Transports (WebRTCTransport, WebSocketTransport) - Handle the network protocol. The consumer provides an audio track; the transport sends it to the server and delivers translated audio back.

Two-Party Call Pattern

// Device A
const myState = new TranslationState({
  onUtterance: (utt, idx) => renderLocal(utt, idx),
});
const theirState = new TranslationState({
  onUtterance: (utt, idx) => renderRemote(utt, idx),
});

const client = new SanasTranslationClient(myState, {
  apiKey: "...",
  endpoint: "...",
  onMessage: (msg) => relay.send(JSON.stringify(msg)), // send to Device B
});

relay.onMessage((data) => theirState.handleMessage(JSON.parse(data))); // receive from Device B

API

TranslationState

const state = new TranslationState(callbacks?);

Manages transcription, translation, connection state, and language detection state. All callbacks are optional.

Callbacks

| Callback | Signature | Description | | ------------------------- | ----------------------------------------------------------------- | --------------------------------------- | | onUtterance | (utterance: UtteranceDisplay, index: number) => void | Utterance created or updated | | onLanguages | (languages: IdentifiedLanguageDisplay[]) => void | Detected languages updated | | onConfigured | (requestId: string \| null) => void | Server confirmed a v3 configure request | | onLanguageRoute | (langIn: string, langOut: string, utteranceIdx: number) => void | Active v3 language route changed | | onOutputSpeechEnded | (utteranceIdx: number, outputTime: number) => void | Output speech ended | | onTranslationEnded | (utteranceIdx: number) => void | Translation ended | | onFlushed | (requestId: string \| null) => void | Server confirmed a v3 flush request | | onConnectionStateChange | (state: ConnectionState) => void | Connection state changed | | onError | (error: string) => void | Error occurred |

Methods

| Method | Returns | Description | | ------------------------------ | ------------------------ | --------------------------------------------------- | | handleMessage(msg) | void | Process a StreamMessage (from client or relay) | | waitForConfigured(requestId) | Promise<void> | Resolves when a matching configured message arrives | | waitForFlushed(requestId) | Promise<void> | Resolves when a matching flushed message arrives | | destroy() | void | Rejects all pending configure/flush promises | | getState() | TranslationClientState | Full snapshot of utterances and languages | | getUtteranceDisplay(index) | UtteranceDisplay | Display data for a single utterance |

Properties

| Property | Type | Description | | --------------------- | ----------------------------- | ------------------------ | | connectionState | ConnectionState | Current connection state | | identifiedLanguages | IdentifiedLanguageDisplay[] | Last detected languages |

SanasTranslationClient

const client = new SanasTranslationClient(translationState, options);

| Option | Type | Description | | ------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | apiKey | string? | API key (use this or accessToken) | | accessToken | string? | OAuth token (use this or apiKey) | | endpoint | string | Server URL (e.g. https://api.sanaslt.com) | | onMessage | (message: StreamMessage) => void | Fires for every message; use this for relay | | onAudioData | (samples: Int16Array, sampleRate: number) => void | Fires with raw output audio (Int16 PCM) as received from the server. Works with both WebRTC and WebSocket transports. |

client.connect(options): Promise<ConnectResult>

Connects to the translation server through the given transport.

| Option | Type | Required | Description | | ------------------ | ------------------ | -------- | ------------------------------------------------------------ | | transport | Transport | Yes | WebRTCTransport or WebSocketTransport | | audioTrack | MediaStreamTrack | Yes | Audio track to send (from mic, file, etc.) | | conversationId | string \| null? | | Conversation ID to join; pass null to generate one locally | | sessionName | string? | | Session name for this participant | | inputSampleRate | SampleRate? | | Input sample rate in Hz (default: 16000) | | outputSampleRate | SampleRate? | | Output sample rate in Hz (default: 16000) | | realtimePlayback | boolean? | | Request realtime playback from the v3 service |

Returns { audio: MediaStream } - the translated audio stream.

client.drainAudio(): Promise<void>

Waits for all pending audio playback and scheduled speech delimiters to complete. Call this before disconnect() on server-initiated disconnects to avoid cutting off in-flight audio.

// On server disconnect, drain before cleanup
state.onConnectionStateChange = async (connectionState) => {
  if (connectionState === "disconnected") {
    await client.drainAudio();
    client.disconnect();
  }
};

client.disconnect()

Closes the connection, destroys the translation state's pending promises, and cleans up audio resources. The consumer is responsible for stopping the audio track.

client.reset(options): Promise<void>

Configures the translation session. Sends a Stream v3 configure message and resolves when the server returns a matching configured message.

| Option | Type | Description | | ---------------- | --------------------------------------- | ------------------------------------------------------- | | languageRoutes | { langIn: string; langOut: string }[] | Source and target language routes | | voiceId | string? | Voice ID for translated audio | | glossary | { terms: Record<string, string> }[]? | Stream v3 glossary entries ("*" or lang code -> term) | | features | string[]? | Optional v3 feature flags | | requestId | string? | Optional caller-supplied correlation ID |

client.flush(): Promise<void>

Sends a Stream v3 flush message and resolves when the server returns a matching flushed message.

SanasTranslationClient.fetchLanguages(credentials, options?): Promise<Language[]>

Static method. Fetches available languages without needing a connection.

const languages = await SanasTranslationClient.fetchLanguages({
  apiKey: "your-api-key",
  endpoint: "https://api.sanaslt.com",
});

StreamMessage

A Zod-validated union representing client-side transport state plus Stream v3 server messages. Server messages are app-facing directly, without legacy LTMessage wrappers.

| Type | Shape | Description | | ------------------------------- | ----------------------------------------------- | ------------------------------ | | transport | { type: "transport", state: ConnectionState } | Connection state change | | configured | { type: "configured", request_id? } | Configure request acknowledged | | transcription / translation | v3 text word payloads | Utterance text updates | | output_text_boundary | v3 boundary payload with output_time | Output audio/text progress | | language_route | v3 language route payload | Active route update | | flushed | { type: "flushed", request_id? } | Flush request acknowledged | | error | v3 error payload | Server or client error |

Breaking v3 Message Changes

The JS client now exposes Stream v3 messages directly:

| Legacy | Stream v3 | | ------------------ | ---------------------- | | ready | configured | | speech_delimiter | output_text_boundary | | speech_languages | language_route | | languages | identified_languages |

float32ToInt16(float32: Float32Array): Int16Array

Converts Float32 PCM samples (range -1..1) to Int16 PCM. Useful when working with raw audio data from onAudioData or Web Audio API pipelines.

getMicrophoneTrack(options?): Promise<MediaStreamTrack>

Helper to acquire a microphone audio track. The caller owns the returned track and must call track.stop() when done.

const track = await getMicrophoneTrack({ sampleRate: 16000 });

| Option | Type | Description | | ------------- | ------------------------ | --------------------------------------- | | sampleRate | number? | Desired sample rate (default: 16000) | | constraints | MediaTrackConstraints? | Custom constraints (overrides defaults) |

Transports

Both transports implement the Transport interface. Choose one when connecting:

import {
  WebRTCTransport,
  WebSocketTransport,
} from "@sanas-ai/language-translation";

const transport = new WebRTCTransport(); // or new WebSocketTransport()
await client.connect({ transport, audioTrack: track });

// Mute/unmute
transport.setAudioEnabled(false);

// Session ID (available after connect)
console.log(transport.sessionId);

// Wait for pending audio before cleanup (useful on server disconnect)
await transport.drainAudio();

Test Client

A browser-based test client is included in examples/index.html for interactively testing the library against a live server.

To run it:

npm run build
npx serve .

Then open http://localhost:3000/examples/ in your browser. Enter your API key, configure source/target languages, and click Connect to start a live translation session. The example demonstrates the two-state pattern with separate local and remote utterance displays, audio file input (upload a WAV/MP3 instead of using the microphone), and output recording to WAV via the onAudioData callback.

Development

npm install       # Install dependencies
npm run build     # Type-check and build to dist/
npm test          # Run tests