# Translation

Stream translates chat messages so that people who speak different languages can talk in the same channel. There are two ways to translate:

- **Automatic** (recommended): messages arrive already translated into the channel's languages.
- **On demand**: translate one message into any language with an API call.

## Automatic translation

When a message is sent in a channel with automatic translation on, Stream translates its text into the channel's target languages before the message is delivered. The translations are stored on the message in `message.i18n`, so every member receives them with the message itself.

Here is how it works:

1. A user sends a message in a channel with automatic translation enabled.

2. The sender's `language` is the source language. When it is not set, the language is detected from the text.

3. Stream translates the text into the channel's target languages and stores each translation in `message.i18n`.

4. The message is delivered to the channel members with its translations included.

>
> **Info:** Set a `language` on every user and add your users as channel members. Stream uses the members' languages to pick the default target language.
>

### Choose target languages

Set the channel's `auto_translation_language` to a comma-separated list of language codes, such as `"en,es,fr"`. Each message is translated into every listed language except the sender's own.

- A message is translated into at most 5 target languages. The list is a priority order: when there are more, the first-listed languages are kept.
- Spaces around codes are ignored, and empty or duplicate entries are dropped.
- Use codes from the [Available languages](#available-languages) table. Codes are not checked when the setting is saved, so a typo is accepted and then skipped at translation time.
- Delivery waits up to a few seconds for the translations. A target language that doesn't finish in time, or that fails at the translation provider, is missing from `message.i18n`, without an error.

A single code, such as `"en"`, is a list with one language.

### Default: the members' most common language

With no `auto_translation_language` set, each message is translated into one language: the most common `language` among the other channel members. The sender's own language never counts toward it.

Only a member language from the [Available languages](#available-languages) table can become the default target. Codes in `auto_translation_language` skip that check, which is why a code that is never picked as the default can still work when you list it.

This target is not only a fallback. With a list set, the members' most common language is still added when it is not already in the list and there is room under the 5-language cap.

Here is how the two combine:

| `auto_translation_language` | Sender | Other members speak | Translated into                                       |
| --------------------------- | ------ | ------------------- | ----------------------------------------------------- |
| (not set)                   | en     | mostly es           | es                                                    |
| `es,fr`                     | en     | mostly de           | es, fr, de                                            |
| `en,es`                     | en     | only es             | es                                                    |
| `en,es,fr,de,it`            | pt     | mostly ja           | en, es, fr, de, it (ja dropped by the 5-language cap) |

### Enable automatic translation

Automatic translation is not enabled by default. Turning it on or off is server-side only: a client token can't change `auto_translation_enabled`, or create a channel with it on. Use a server-side SDK or the CLI from your backend.

- **App-wide:** on or off. When it is on, every channel in the app is translated.
- **Per channel:** on or off, plus the channel's target languages in `auto_translation_language`. Besides your backend, any user allowed to update the channel can change the target languages with a full channel update. A partial update of either field needs server-side auth.

Translation runs when it is enabled for the app or for the channel. When it is enabled app-wide, setting `auto_translation_enabled: false` on a channel does not turn it off for that channel.

```js label="Node.js"
const channel = client.chat.channel("messaging", "melting-pot");

// enable auto-translation only for this channel
await channel.update({ data: { auto_translation_enabled: true } });

// translate messages into English, Spanish and French
await channel.update({
  data: {
    auto_translation_enabled: true,
    auto_translation_language: "en,es,fr",
  },
});

// auto translate messages for all channels
await client.updateApp({ auto_translation_enabled: true });
```

### Set user language

A user's `language` is the source language of the messages they send. When it is not set, Stream detects the language from the text. The members' languages also decide the [default target language](#default-the-members-most-common-language). Setting a language for every user is still recommended.

```js label="JavaScript"
// sets the user language
await client.connectUser({ id: "userId", language: "en" }, userToken);

// watch a channel
await client.channel("messaging", "melting-pot").watch();
```

```js label="JavaScript (stream-chat@9)"
// sets the user language
await client.connectUser({ id: "userId", language: "en" }, userToken);

// watch a channel
await client.channel("messaging", "melting-pot").watch();
```

>
> **Info:** A member's language is not automatically a target language. Without a list, only the members' most common language is a target. To translate into more languages, list them in `auto_translation_language` (see [Choose target languages](#choose-target-languages)).
>

### Reading translations

When a message is translated, the `i18n` object is added. The `i18n` includes the message text in all languages and the code of the original language.

The i18n object has one field for each language named using this convention `language-code_text`

Here is an example after translating a message from English into French and Italian.

```json label="JSON"
{
  "fr_text": "Bonjour, J'aimerais avoir plus d'informations sur votre produit.",
  "it_text": "Ciao, vorrei avere maggiori informazioni sul tuo prodotto.",
  "language": "en"
}
```

```js label="JavaScript"
// Show the reader's language, falling back to the original text
const text = message.i18n?.[`${client.user?.language}_text`] ?? message.text;
```

```js label="JavaScript (stream-chat@9)"
// Show the reader's language, falling back to the original text
const text = message.i18n?.[`${client.user?.language}_text`] ?? message.text;
```

## Translate a message on demand

Use this endpoint to translate an existing message into a language that is not one of the channel's target languages, for example when a user changes their language. The translation is added to the same `i18n` object as automatic translations.

The source language is inferred from the user language or detected automatically by analyzing its text. If possible, store the user language, see [Set user language](#set-user-language).

```js label="JavaScript"
await channel.sendMessage({
  message: {
    id: messageID,
    text: "Hello, I would like to have more information about your product.",
  },
});

// returns the message.text translated into French
const response = await client.translateMessage(
  { id: messageID },
  { language: "fr" },
);

// the translation will be added to the i18n object
console.log(response.message.i18n?.fr_text);
// "Bonjour, J'aimerais avoir plus d'informations sur votre produit.",
```

```js label="JavaScript (stream-chat@9)"
await channel.sendMessage({
  id: messageID,
  text: "Hello, I would like to have more information about your product.",
});

// returns the message.text translated into French
const response = await client.translateMessage(messageID, "fr");

// the translation will be added to the i18n object
console.log(response.message.i18n?.fr_text);
// "Bonjour, J'aimerais avoir plus d'informations sur votre produit.",
```

The endpoint returns the translated message, updates it and sends a **message.updated** event to all users on the channel.

>
> **Info:** Only the text field is translated, custom fields and attachments are not included.
>

## Poll translation

Polls attached to a message in an auto-translated channel are translated too. The poll name, description, option texts and free-form answers get an i18n object with the same shape as a message's `i18n`: one `<language-code>_text` field per language, plus `language` for the original language.

| Field                          | Translation                         |
| ------------------------------ | ----------------------------------- |
| `name`                         | `name_i18n`                         |
| `description`                  | `description_i18n`                  |
| `options[].text`               | `options[].text_i18n`               |
| `latest_answers[].answer_text` | `latest_answers[].answer_text_i18n` |

Here is how it works:

1. A poll is created. Creating a poll does not translate it.

2. The poll is sent in a message on a channel with automatic translation enabled. The poll is translated into the same languages as the message.

3. Later changes are translated into the languages the poll already has. This covers updating the poll name or description, adding or updating an option, and casting a vote with an `answer_text`.

```json label="JSON"
{
  "id": "poll_lunch",
  "name": "What's for lunch?",
  "name_i18n": {
    "language": "en",
    "en_text": "What's for lunch?",
    "es_text": "¿Qué hay de almuerzo?"
  },
  "options": [
    {
      "id": "opt_pizza",
      "text": "Pizza",
      "text_i18n": {
        "language": "en",
        "en_text": "Pizza",
        "es_text": "Pizza"
      }
    }
  ],
  "latest_answers": [
    {
      "id": "cbd67e2a-0ee2-48d2-8bde-dfb95392b6012",
      "answer_text": "Fish, please",
      "answer_text_i18n": {
        "language": "en",
        "en_text": "Fish, please",
        "es_text": "Pescado, por favor"
      }
    }
  ]
}
```

```js label="JavaScript"
const poll = message.poll;
const language = client.user?.language;

// Fall back to the original text when a field has no translation for the language
const name = poll?.name_i18n?.[`${language}_text`] ?? poll?.name;
const options = poll?.options.map(
  (option) => option.text_i18n?.[`${language}_text`] ?? option.text,
);
```

```js label="JavaScript (stream-chat@9)"
const poll = message.poll;
const language = client.user?.language;

// Fall back to the original text when a field has no translation for the language
const name = poll?.name_i18n?.[`${language}_text`] ?? poll?.name;
const options = poll?.options.map(
  (option) => option.text_i18n?.[`${language}_text`] ?? option.text,
);
```

Keep these points in mind:

- Translating changes to a poll (updates, new options and answers) requires automatic translation to be enabled for the app. When it is only enabled on the channel, the poll is translated once when it is sent. Later changes are not translated, and the old translations of a changed field are removed.

- When the same poll is sent to several channels, it is translated into the languages of each channel, up to 10 languages including the original.

- Poll translations are billed the same way as message translations (see [Caveats and limits](#caveats-and-limits)).

## Caveats and limits

- Each message is translated into at most 5 target languages. When the configured list plus the members' most common language comes to more than 5, the first-listed languages are kept.

- Translations are billed per character of the original text, for each language it is translated into. Adding target languages multiplies the cost. See [pricing](https://getstream.io/chat/pricing/) for the rate.

- A target language that doesn't finish within a few seconds, or that fails, is missing from `message.i18n`. Fall back to `message.text` when the reader's language is missing.

- Language codes are not validated when they are saved. An unrecognized code in `auto_translation_language` is skipped at translation time, without an error.

- Enabling automatic translation app-wide applies to every channel. A channel cannot opt out with `auto_translation_enabled: false`.

- Error messages are not translated.

- When a custom command rewrites the text, the new text is translated.

- A [before message send webhook](https://getstream.io/chat/docs/node/before-message-send-webhook/#translations) can keep, re-translate or replace a message's translations, see its Translations section.

- When a message is updated, translations are recomputed automatically for every language the message already has.

- Changing translation settings or a user's language has no effect on messages that are already translated.

## Available languages

Use these codes in `auto_translation_language` and in a user's `language`.

| Language name         | Language code |
| --------------------- | ------------- |
| Afrikaans             | af            |
| Albanian              | sq            |
| Amharic               | am            |
| Arabic                | ar            |
| Azerbaijani           | az            |
| Bengali               | bn            |
| Bosnian               | bs            |
| Bulgarian             | bg            |
| Chinese (Simplified)  | zh            |
| Chinese (Traditional) | zh-TW         |
| Croatian              | hr            |
| Czech                 | cs            |
| Danish                | da            |
| Dari                  | fa-AF         |
| Dutch                 | nl            |
| English               | en            |
| Estonian              | et            |
| Finnish               | fi            |
| French                | fr            |
| French (Canada)       | fr-CA         |
| Georgian              | ka            |
| German                | de            |
| Greek                 | el            |
| Haitian Creole        | ht            |
| Hausa                 | ha            |
| Hebrew                | he            |
| Hindi                 | hi            |
| Hungarian             | hu            |
| Indonesian            | id            |
| Italian               | it            |
| Japanese              | ja            |
| Korean                | ko            |
| Latvian               | lv            |
| Lithuanian            | lt            |
| Malay                 | ms            |
| Norwegian             | no            |
| Persian               | fa            |
| Pashto                | ps            |
| Polish                | pl            |
| Portuguese            | pt            |
| Romanian              | ro            |
| Russian               | ru            |
| Serbian               | sr            |
| Slovak                | sk            |
| Slovenian             | sl            |
| Somali                | so            |
| Spanish               | es            |
| Spanish (Mexico)      | es-MX         |
| Swahili               | sw            |
| Swedish               | sv            |
| Tagalog               | tl            |
| Tamil                 | ta            |
| Thai                  | th            |
| Turkish               | tr            |
| Ukrainian             | uk            |
| Urdu                  | ur            |
| Vietnamese            | vi            |

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/javascript/translation/](https://getstream.io/chat/docs/javascript/translation/).