# Message translation

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

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.

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-native/v10/ui/customizing/localization/).

## What users get

The user reads English, and Pierre writes "Bonjour a tous" in French. When the message has an English translation, the user sees "Hello everyone" in place of Pierre's text. The default UI marks nothing as translated and has no way to show the original; [Customizing](#customizing) adds both.

The translated text also shows in the channel list's last message, in the pinned messages list of channel details, and in what **Copy Message** copies.

## 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. `Chat` puts it in `TranslationContext` as `userLanguage`, which falls back to `"en"` and follows `user.updated` events for the connected user.
- **UI.** `MessageTextContainer` renders the text of `message.i18n.<userLanguage>_text` when the message has it, and `message.text` otherwise. A deleted message keeps its text. `ChannelPreview` and `PinnedMessageItem` do the same. In a component of your own, `useTranslatedMessage(message)` returns the message with that text, as [Message hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/message-hooks/#usetranslatedmessage) describes.
- **Navigation.** 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-native";

const apiKey = "YOUR_API_KEY";
const userId = "YOUR_USER_ID";
const userToken = "YOUR_USER_TOKEN";

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, the message shows it. To always show the original, render `message.text` yourself in a `MessageText` override, as below.

## Customizing

### A Translate action

Translate a message on request from the message menu. This action translates into the user's language, and the message shows the translation when `message.updated` arrives:

```tsx
import { useCallback } from "react";
import type {
  Channel as StreamChannel,
  TranslationLanguage,
} from "stream-chat";
import type { MessageActionsParams } from "stream-chat-react-native";
import {
  Channel,
  MessageComposer,
  MessageList,
  messageActions,
  useChatContext,
  useTranslationContext,
} from "stream-chat-react-native";

export const ChannelWithTranslate = ({
  channel,
}: {
  channel: StreamChannel;
}) => {
  const { client } = useChatContext();
  const { userLanguage } = useTranslationContext();

  const withTranslate = useCallback(
    (params: MessageActionsParams) => {
      const actions = messageActions(params);
      const { error, message } = params;
      const translated = !!message.i18n?.[`${userLanguage}_text`];

      // Hidden when there is no text, or a translation into the user's language exists already
      if (error || !message.text || translated) return actions;

      actions.push({
        action: () => {
          client
            .translateMessage(
              { id: message.id },
              { language: userLanguage as TranslationLanguage },
            )
            .catch((err) => console.warn(err));
        },
        actionType: "translate",
        title: "Translate",
        type: "standard",
      });
      return actions;
    },
    [client, userLanguage],
  );

  return (
    <Channel channel={channel} messageActions={withTranslate}>
      <MessageList />
      <MessageComposer />
    </Channel>
  );
};
```

[Message actions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/message-actions/#customizing) explains `messageActions`.

### Showing the original

`MessageText` is an optional override key. When it's set, `MessageTextContainer` renders it instead of the text, and passes it the original `message` and the `renderText` function that renders text with markdown and mentions. The translation is then up to the override. This one shows the translation with a toggle to the original:

```tsx
import { useState } from "react";
import { Pressable, Text, View } from "react-native";
import type { MessageTextProps } from "stream-chat-react-native";
import {
  useTranslationContext,
  WithComponents,
} from "stream-chat-react-native";

const MessageTextWithOriginal = (props: MessageTextProps) => {
  const { markdownStyles, message, renderText, theme } = props;
  const { userLanguage } = useTranslationContext();
  const [showOriginal, setShowOriginal] = useState(false);

  if (!message) return null;

  const translation =
    message.type === "deleted"
      ? undefined
      : message.i18n?.[`${userLanguage}_text`];
  const shownMessage =
    translation && !showOriginal ? { ...message, text: translation } : message;

  return (
    <View>
      {renderText({
        ...props,
        markdownStyles: {
          ...theme.theme.messageItemView.content.markdown,
          ...markdownStyles,
        },
        message: shownMessage,
        semantics: theme.theme.semantics,
      })}
      {translation && translation !== message.text ? (
        <Pressable onPress={() => setShowOriginal((value) => !value)}>
          <Text>{showOriginal ? "Show translation" : "Show original"}</Text>
        </Pressable>
      ) : null}
    </View>
  );
};

export const App = () => (
  <WithComponents overrides={{ MessageText: MessageTextWithOriginal }}>
    {/* the rest of the app */}
  </WithComponents>
);
```

The choice lives in the component's state, so it resets when the list unmounts the message, for example after scrolling far away. `message.i18n.language` holds the original language, for a label such as "Translated from French".

### Labels

The SDK's own texts have no translation indicator to translate. The language names, such as "French", come from the `language.*` translation keys, through `t()` from `useTranslationContext()`. See [Localization](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/localization/).

## 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-native/v10/ui/customizing/localization/): the UI language, `userLanguage` and `TranslationContext`.
- [ComponentsContext](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel/componentscontext/): the `MessageText` override key.
- [Message actions](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/message-actions/): how `messageActions` works.

---

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