# Notifications text

Use this page to change the text of a notification, whether a built-in one or one your app publishes. The text is translated with the rest of the UI, so it follows the user's language. Where notifications appear and how they look is on [Displaying notifications](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/). What screen readers do with them is on [Notifications accessibility](https://getstream.io/chat/docs/sdk/react-native/features/notifications/accessibility/).

## Translating notifications

The `Notification` component chooses the text of each notification as it renders it, by the notification's `type`:

1. A type `stream-chat` publishes, such as `api:attachment:upload:failed`, renders a translation key through `t()`. The SDK maps every type `stream-chat` defines.
2. A few types the SDK publishes itself, `api:poll:end:success`, `api:poll:end:failed`, `browser:audio:playback:error` and `channel:jumpToFirstUnread:failed`, are mapped the same way.
3. Any other type renders `notification.message` as it is.

The SDK's components translate most of their notifications before publishing them: "Message pinned" is `t("message.pinned.text", "Message pinned")`. So there are three ways to change the text:

- **A notification from an SDK component:** override the key it was published with. When a message action fails with an error that has a message, the notification shows that message instead.
- **A notification from `stream-chat`, or one of the four SDK types above:** override the key its type is mapped to, listed in [Keys by type](#keys-by-type).
- **Text that depends on the notification:** render your own `Notification`.

```ts
import { Streami18n } from "stream-chat-react-native";

export const streami18n = new Streami18n({
  translationsForLanguage: {
    // Published by the Pin action, translated at the call site.
    "message.pinned.text": "Pinned",
    // Published by stream-chat: an upload that failed.
    "notifications.attachmentUploadFailed.error":
      "That file could not be uploaded",
    "notifications.attachmentUploadFailed.withReason.error":
      "That file could not be uploaded: {{reason}}",
  },
});
```

Pass the instance to `Chat` and `OverlayProvider` as `i18nInstance`. [Localization](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/localization/) covers dictionaries, other languages and reading the catalog of keys from your editor.

### Keys by type

A type `stream-chat` publishes, with the key it renders:

| Type                                       | Key                                                                                                      | English                                                |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `validation:attachment:file:missing`       | `notifications.attachmentFileMissing.error`                                                              | File is required for upload attachment                 |
| `validation:attachment:id:missing`         | `notifications.attachmentIdMissing.error`                                                                | Local upload attachment missing local id               |
| `validation:attachment:upload:blocked`     | `notifications.attachmentUploadBlocked.error`                                                            | Attachment upload blocked due to `{{reason}}`          |
| `api:attachment:upload:failed`             | `notifications.attachmentUploadFailed.error`, or `notifications.attachmentUploadFailed.withReason.error` | Error uploading attachment                             |
| `validation:attachment:upload:in-progress` | `notifications.attachmentUploadInProgress.error`                                                         | Wait until all attachments have uploaded               |
| `validation:command:disabled`              | `notifications.commandUnavailable.error`, `.whileEditing.error` or `.whileReplying.error`                | Command not available                                  |
| `validation:command:not-ready`             | `notifications.commandNotReady.error`                                                                    | Command not ready to be sent                           |
| `api:location:create:failed`               | `notifications.locationShareFailed.error`                                                                | Failed to share location                               |
| `api:message:jump:failed`                  | `notifications.messageJumpFailed.error`                                                                  | Failed to jump to the message                          |
| `api:message:jumpToLatest:failed`          | `notifications.messageJumpToLatestFailed.error`                                                          | Failed to jump to the latest message                   |
| `api:message:send:failed`                  | `common.sendMessageFailed.error`                                                                         | Send message request failed                            |
| `api:message:update:failed`                | `common.editMessageFailed.error`                                                                         | Edit message request failed                            |
| `validation:poll:castVote:limit`           | `notifications.voteLimitReached.error`                                                                   | Reached the vote limit. Remove an existing vote first. |
| `api:poll:create:failed`                   | `notifications.pollCreateFailed.error`, or `notifications.pollCreateFailed.withReason.error`             | Failed to create the poll                              |

Four types the SDK publishes:

| Type                               | Key                                                                                                                          | English                                    |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| `api:poll:end:success`             | `notifications.pollEnded.text`                                                                                               | Poll ended                                 |
| `api:poll:end:failed`              | `poll.endVote.error`, or `notifications.pollEndFailed.withReason.error`                                                      | Failed to end the poll                     |
| `browser:audio:playback:error`     | the message the player published: `audioPlayer.playbackFailed.error`, `.formatUnsupported.error` or `.seekUnsupported.error` | Failed to play the recording               |
| `channel:jumpToFirstUnread:failed` | `channel.jumpToFirstUnreadFailed.error`                                                                                      | Failed to jump to the first unread message |

The keys with `withReason` are used when the notification carries a string in `metadata.reason`, which they put in `{{reason}}` in lowercase. An upload that was blocked fills `{{reason}}` from three keys of its own: `notifications.attachmentUploadBlocked.reason.sizeLimit.text` for a `size_limit` reason, `.unknownError.text` for a reason that isn't a string, and `.unsupportedFileType.text` for any other. A disabled command uses `.whileEditing.error` for the reason `editing`, and `.whileReplying.error` for `quoted_message`. When `browser:audio:playback:error` carries no message, it renders `notifications.recordingPlaybackFailed.error`, "Error reproducing the recording".

### Text that depends on the notification

The SDK has no registry of translators. To change the text beyond what the keys allow, replace the `Notification` component and pass the default one a notification with the text you want. This one names the author of a pinned message:

```tsx
import type { PropsWithChildren } from "react";
import {
  asDynamicKey,
  Notification,
  useTranslationContext,
  WithComponents,
} from "stream-chat-react-native";
import type {
  ComponentOverrides,
  NotificationProps,
} from "stream-chat-react-native";

const PIN_SUCCESS = "api:message:pin:success";

const PinNotification = (props: NotificationProps) => {
  const { t } = useTranslationContext();
  const { notification } = props;
  if (notification.type !== PIN_SUCCESS) return <Notification {...props} />;

  const message = notification.origin.context?.message as
    { user?: { name?: string } } | undefined;
  const text = t(
    asDynamicKey("myApp.notification.messagePinned"),
    "Pinned a message from {{author}}",
    { author: message?.user?.name ?? "someone" },
  );

  return (
    <Notification
      {...props}
      notification={{ ...notification, message: text }}
    />
  );
};

const overrides: ComponentOverrides = { Notification: PinNotification };

export const WithPinNotification = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);
```

This works for the types the SDK doesn't map. A type from the tables above renders its key instead of the message the notification carries, except `browser:audio:playback:error`, which renders its message when it has one. A replacement `Notification` has to render the text itself for those types. Declare `overrides` at module scope: `WithComponents` reads it once.

## Text of notifications you publish

A notification of your own renders its `message` as you publish it, so translate it first. Keys of your own take part in the dictionaries like any other, and `t` reads them through `asDynamicKey`, as [Localization](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/localization/#your-own-keys-and-loosetranslationdictionary) shows:

```tsx
import { Pressable, Text } from "react-native";
import {
  asDynamicKey,
  useNotificationApi,
  useTranslationContext,
} from "stream-chat-react-native";

export const FavoriteButton = ({
  onFavorite,
}: {
  onFavorite: () => Promise<void>;
}) => {
  const { addNotification } = useNotificationApi();
  const { t } = useTranslationContext();

  const favorite = async () => {
    try {
      await onFavorite();
      addNotification({
        message: t(
          asDynamicKey("myApp.favorite.success"),
          "Added to favorites",
        ),
        options: { severity: "success", type: "app:channel:favorite:success" },
        origin: { emitter: "FavoriteButton" },
      });
    } catch (error) {
      addNotification({
        message: t(
          asDynamicKey("myApp.favorite.failed"),
          "Could not add to favorites",
        ),
        options: {
          originalError: error instanceof Error ? error : undefined,
          severity: "error",
          type: "app:channel:favorite:failed",
        },
        origin: { emitter: "FavoriteButton" },
      });
    }
  };

  return (
    <Pressable accessibilityRole="button" onPress={favorite}>
      <Text>Favorite</Text>
    </Pressable>
  );
};
```

Use a type of your own, in the `domain:entity:operation:result` form. A type from the tables above renders its mapped key instead of the message, except `browser:audio:playback:error`, which renders its message when it has one. Publishing, severity and durations are on [Publishing notifications](https://getstream.io/chat/docs/sdk/react-native/state/publishing-notifications/), and routing a notification to another part of the screen is on [Routing notifications](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/#routing-notifications).

## Text the SDK's components don't translate

The SDK's components publish notifications of their own, such as when a message action succeeds or a channel can't be left. [Notifications](https://getstream.io/chat/docs/sdk/react-native/reference/notifications/) lists every type, with its emitter and when it's published, and [What the SDK publishes](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/#what-the-sdk-publishes) lists them by component.

Most messages are translated with `t()` before they're published. A failed message action whose error carries a message is the exception: the notification shows that message, which is the error's own text and isn't translated.

Uploads, sending, jumps in the message paginator, commands and poll votes are published by `stream-chat`: see [Publishing notifications](https://getstream.io/chat/docs/sdk/react-native/state/publishing-notifications/).

## Used in

- [Displaying notifications](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/)
- [Localization](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/localization/)

---

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