Logo

Complete developer guide and best practices to help you get started quickly.

Tutorial

Text translation

Walks through the full text translation flow: one-shot JSON, SSE streaming, and dictionary augmentation.

This tutorial centres on POST /api/v1/translations/text and walks through three scenarios: one-shot JSON translation, SSE streaming, and dictionary-augmented translation. Each scenario ships with a curl example and the gotchas to watch for.

Prepare your API key

Create a key in the developer console and pass it via the X-API-Key HTTP header. The key is displayed only once — keep it in environment variables rather than hard-coding it into source files or frontend bundles.

Store the key in an environment variable; the examples below all read $ABESTAR_API_KEY.

bash
export ABESTAR_API_KEY="sk_live_..."

Scenario 1: one-shot JSON translation

By default stream=false waits for the model to finish and returns the full result in one response, which suits short text and downstream processing. The input limit is 5000 characters; exceeding it returns 400 validation_error.

curl example in JSON mode.

bash
curl -X POST "https://abemono.abestar.com.tw/api/v1/translations/text" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "今天的會議改到下午三點。",
    "target_language": "en",
    "source_language": "auto"
  }'

The response includes the translated text, the auto-detected source language (null if you specified one), the cumulative quota usage percentage (usage.quota_percent.used, 0–100), and the number of forced replacements actually applied.

Successful response example (HTTP 200).

json
{
  "translated_text": "Today's meeting is rescheduled to 3 PM.",
  "detected_source_lang": "zh-TW",
  "usage": {
    "quota_percent": {
      "used": 12
    }
  },
  "forced_replacement_count": 0
}

Optional feature parameters

All parameters below are optional and can be combined freely with either JSON or SSE mode; omitted fields fall back to their defaults. The speech recognition endpoint accepts the same shared parameters — both chapters deliberately use identical table columns and ordering so you can compare them side by side.

The first seven rows are shared with the speech endpoint; the last row is translation-only.
ParameterTypeDefaultDescription
modelstring"PSSC-V1-251215"Translation model code. Use GET /translations/models to list what is currently available.
domainstring"general"Domain hint such as "legal", "medical", or "gaming", helping the model pick wording that fits the field.
context{ speech: string }[][]Conversation context or prior turns in chronological order, so pronouns and omitted subjects translate more accurately.
harm_content_filterbooleanfalseEnables harmful-content filtering. Blocked input returns 400 content_policy_violation.
punctuationbooleanfalseAuto-completes punctuation in the translation. Note the speech endpoint defaults the same parameter to true — the two differ.
number_format"spoken" | "arabic""spoken"Number transcription: spoken writes numbers out in words, arabic uses digits (1234).
date_format"natural" | "yyyy-MM-dd" | "yyyy/MM/dd" | "MM/dd/yyyy" | "dd-MM-yyyy""natural"Date presentation: natural follows the target language's convention; the rest are fixed templates.
source_languagestring"auto"Translation-only. Source language code, or leave as auto to let the model detect it.

A complete example combining the domain hint, conversation context, auto punctuation, number transcription, and date format.

bash
curl -X POST "https://abemono.abestar.com.tw/api/v1/translations/text" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "上一季營收成長了一成二,下次會議訂在二〇二六年三月五日。",
    "target_language": "en",
    "domain": "finance",
    "context": [{ "speech": "我們今天先討論第一季的營收。" }],
    "punctuation": true,
    "number_format": "arabic",
    "date_format": "yyyy-MM-dd",
    "harm_content_filter": true
  }'

Scenario 2: SSE streaming

Conversational UIs and long inputs work better with stream=true, which streams partial output as it arrives. The response is text/event-stream and events flow as head (source language) → chunk* (text fragments, zero or more) → tail (end marker) → meta (usage stats). On failure, an error event replaces remaining events and closes the stream.

curl example in SSE mode. -N disables curl buffering so events stream out immediately.

bash
curl -N -X POST "https://abemono.abestar.com.tw/api/v1/translations/text" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "今天的會議改到下午三點。",
    "target_language": "en",
    "stream": true
  }'

SSE parser example using fetch + ReadableStream + TextDecoder. Events are separated by a blank line.

javascript
const res = await fetch("https://abemono.abestar.com.tw/api/v1/translations/text", {
    method: "POST",
    headers: {
        "X-API-Key": process.env.ABESTAR_API_KEY,
        "Content-Type": "application/json",
        Accept: "text/event-stream",
    },
    body: JSON.stringify({
        text: "今天的會議改到下午三點。",
        target_language: "en",
        stream: true,
    }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let output = "";

while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });

    const events = buffer.split("\n\n");
    buffer = events.pop() ?? "";

    for (const evt of events) {
        const line = evt.split("\n").find((l) => l.startsWith("data: "));
        if (!line) continue;
        const payload = JSON.parse(line.slice(6));

        switch (payload.type) {
            case "head":
                console.log("source:", payload.data.source_lang);
                break;
            case "chunk":
                output += payload.data;
                break;
            case "tail":
                console.log("done:", output);
                break;
            case "meta":
                console.log("usage:", payload.data.usage);
                break;
            case "error":
                console.error(payload.data.code, payload.data.message);
                break;
        }
    }
}

SSE business errors still come back with HTTP 200

Once the streaming connection is established, HTTP status stays at 200. Model errors and timeouts are delivered as error events (with code, message, and request_id) instead of rewriting the status. Inspect the event payload to determine success or failure — don't rely on the HTTP status alone.

Scenario 3: with dictionaries

The translation endpoint accepts two dictionary kinds: vocabulary acts as soft guidance — matching terms are passed to the model as preferred wording — while forced_replacement performs string substitutions on the final output after translation. Both are referenced by UUID via vocabulary_dictionary_id / forced_replacement_dictionary_id.

Both dictionaries can be attached in JSON mode.

bash
curl -X POST "https://abemono.abestar.com.tw/api/v1/translations/text" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "請把這份合約交給法務組長確認。",
    "target_language": "en",
    "vocabulary_dictionary_id": "f3c1e9a2-9c2b-4f7a-9d3a-7e2b8a1c4d5e",
    "forced_replacement_dictionary_id": "1d2e3f4a-5b6c-7d8e-9f0a-1b2c3d4e5f6a"
  }'

SSE cannot be combined with forced_replacement

Post-processing substitutions need the full output before they can run, which conflicts with the chunk-by-chunk nature of streaming. Sending stream=true together with forced_replacement_dictionary_id returns 400 validation_error before the request reaches translation. For streaming scenarios, fall back to JSON mode or apply substitutions on the client side.

More on dictionaries

See “Dictionary operations” and “Dictionary concepts” for creation, batch creation, full CRUD, and guidance on choosing between the two dictionary kinds.