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:
-
A user sends a message in a channel with automatic translation enabled.
-
The sender's
languageis the source language. When it is not set, the language is detected from the text. -
Stream translates the text into the channel's target languages and stores each translation in
message.i18n. -
The message is delivered to the channel members with its translations included.
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 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 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.
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. Setting a language for every user is still recommended.
// sets the user language
await client.connectUser({ id: "userId", language: "en" }, userToken);
// watch a channel
await client.channel("messaging", "melting-pot").watch();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).
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.
{
"fr_text": "Bonjour, J'aimerais avoir plus d'informations sur votre produit.",
"it_text": "Ciao, vorrei avere maggiori informazioni sul tuo prodotto.",
"language": "en"
}// 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.
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.",The endpoint returns the translated message, updates it and sends a message.updated event to all users on the channel.
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:
-
A poll is created. Creating a poll does not translate it.
-
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.
-
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.
{
"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"
}
}
]
}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
-
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 for the rate.
-
A target language that doesn't finish within a few seconds, or that fails, is missing from
message.i18n. Fall back tomessage.textwhen the reader's language is missing. -
Language codes are not validated when they are saved. An unrecognized code in
auto_translation_languageis 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 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 |