# Message translation

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

Message translation shows each message in the reader's language. The Stream API does the translating; the SDK picks the translation that matches the connected user and lets them switch back to the original.

This is about the text of messages. The language of the SDK's own labels and buttons is a separate setting, see [Localization](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/localization/).

## What users get

The user reads English, and Pierre writes "Bonjour à tous" in French. The user sees "Hello everyone", with "Translated from French · View original" above it. "View original" shows Pierre's text and changes the line to "Original · View translation". The choice is per message and lasts until the message list unmounts; the channel's list and a thread's list each keep their own.

The translated text also shows in channel list previews, thread list previews and quoted messages.

## How it works across the layers

- **State.** A translated message carries `message.i18n`: the original language under `language`, and one `<code>_text` field per translation. Pierre's message could have `{ language: "fr", en_text: "Hello everyone", de_text: "Hallo zusammen" }`. The server fills it in two ways:
- **Automatic translation**, when it's on for the app or the channel: the server translates each new message for the channel's members as it arrives.
- **`client.translateMessage`**, on request: it translates one message into one language, stores the translation on the message and sends `message.updated` to the channel. The message list then updates like for any edit.
- **The user's language** is `client.user.language`, set when the user connects. The UI reads it as `userLanguage` from `useTranslationContext()`, which falls back to `"en"` and follows `user.updated` events for the connected user.
- **UI.** `MessageText` shows the translation for `userLanguage`, such as `message.i18n.en_text` for the user, and `message.text` when there is none. `MessageTranslationIndicator` renders the "Translated from…" line when the translation differs from the original. Each message list keeps the per-message choice between original and translation, which reaches each message through `useMessageContext()` as `translationView` and `setTranslationView`.
- **Layout.** Not involved.

Only the user's language is shown. A message translated into Italian with `client.translateMessage()` shows nothing different to the user until it also has `en_text`.

Translating a message on request looks like this:

```ts
const { message: translated } = await client.translateMessage(
  { id: message.id },
  { language: "en" },
);

translated.i18n?.en_text; // "Hello everyone"
```

The default message menu has no Translate action. [Customizing](#customizing) adds one.

## Turning it on or off

Translation is a server feature, and the UI follows the data:

- **Automatic translation** is off by default. Turn it on for the app, or per channel with `auto_translation_enabled` and `auto_translation_language`, as [Translation](https://getstream.io/chat/docs/javascript/translation/) in the JavaScript client docs describes.
- **The user's language** decides which translation shows. Set it when connecting:

```tsx
import { useCreateChatClient } from "stream-chat-react";

export const useFrenchReaderClient = () =>
  useCreateChatClient({
    apiKey,
    tokenOrProvider: userToken,
    userData: { id: userId, language: "fr" },
  });
```

The client has no switch for it. When `message.i18n` holds a translation in the user's language, `MessageText` shows it, and the user can switch each message back. To always show the original, render `message.text` yourself in a custom message UI, see [Message list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/message-list-ui/).

## Customizing

### A Translate action

Translate a message on request from the message menu. This dropdown action translates into the user's language; `MessageText` picks up the translation when `message.updated` arrives:

```tsx
import type { Channel as ChannelType, TranslationLanguage } from "stream-chat";
import {
  Channel,
  ContextMenuButton,
  MessageActions,
  WithComponents,
  defaultMessageActionSet,
  useChatContext,
  useContextMenuContext,
  useMessageContext,
  useTranslationContext,
  type ContextMenuItemProps,
  type MessageActionSetItem,
} from "stream-chat-react";

const TranslateAction = (props: ContextMenuItemProps) => {
  const { client } = useChatContext();
  const { message } = useMessageContext();
  const { userLanguage } = useTranslationContext();
  const { closeMenu } = useContextMenuContext();

  // Hidden when there is no text, or a translation into the user's language exists already
  if (!message.text || message.i18n?.[`${userLanguage}_text`]) return null;

  return (
    <ContextMenuButton
      {...props}
      onClick={() => {
        closeMenu();
        void client.translateMessage(
          { id: message.id },
          { language: userLanguage as TranslationLanguage },
        );
      }}
    >
      Translate
    </ContextMenuButton>
  );
};

const messageActionSet: MessageActionSetItem[] = [
  ...defaultMessageActionSet,
  { Component: TranslateAction, placement: "dropdown", type: "translate" },
];

const CustomMessageActions = () => (
  <MessageActions messageActionSet={messageActionSet} />
);

export const ChannelWithTranslate = ({ channel }: { channel: ChannelType }) => (
  <WithComponents overrides={{ MessageActions: CustomMessageActions }}>
    <Channel channel={channel}>{/* MessageList, MessageComposer */}</Channel>
  </WithComponents>
);
```

[Actions](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/actions/) explains action sets.

### The translation indicator

Replace `MessageTranslationIndicator` through `WithComponents`. Read the choice from `useMessageContext()` and change it with `setTranslationView`:

```tsx
import type { Channel as ChannelType } from "stream-chat";
import {
  Channel,
  WithComponents,
  getTranslatedMessageText,
  useMessageContext,
  useTranslationContext,
} from "stream-chat-react";

const CustomTranslationIndicator = () => {
  const { message, setTranslationView, translationView } = useMessageContext();
  const { userLanguage } = useTranslationContext();
  const translation = getTranslatedMessageText({
    language: userLanguage,
    message,
  });

  if (!translation || translation === message.text || !setTranslationView)
    return null;

  const showingOriginal = translationView === "original";

  return (
    <button
      className="custom-translation-toggle"
      onClick={() =>
        setTranslationView(showingOriginal ? "translated" : "original")
      }
    >
      {showingOriginal ? "Show translation" : "Show original"}
    </button>
  );
};

export const ChannelWithCustomIndicator = ({
  channel,
}: {
  channel: ChannelType;
}) => (
  <WithComponents
    overrides={{ MessageTranslationIndicator: CustomTranslationIndicator }}
  >
    <Channel channel={channel}>{/* MessageList, MessageComposer */}</Channel>
  </WithComponents>
);
```

To hide the indicator, register a component that renders nothing. Messages still show their translation, but users can no longer switch to the original.

### Labels

The indicator's texts come from the `message.translationIndicator.*` translation keys, and the language names, such as "French", from the `language.*` keys. Translate or override them as [Localization](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/localization/) describes.

## Reference

- [Translation](https://getstream.io/chat/docs/javascript/translation/): automatic translation, `client.translateMessage()`, the `i18n` field and the supported languages.
- [Localization](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/localization/): the UI language, `userLanguage` and `TranslationContext`.
- [ComponentContext](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/componentcontext/): the `MessageTranslationIndicator` override key.
- [MessageContext](https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/messagecontext/): `translationView` and `setTranslationView`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/features/messaging/message-translation/](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/message-translation/).