Platform docs
Auth, users, webhooks & more
Info:
This is beta documentation for Stream Video React Native SDK v2. For the latest stable version, see the latest version (v1).

Internationalization

The SDK includes translation services powered by i18next, supporting:

  • Language switching - Change languages dynamically
  • Custom translations - Add or override translations in any language
  • i18next API access - Full access to the underlying library

English ships with the SDK, written inline at every call site - there is no en.json and no translations export.

Note:

The i18n API changed in v2. translationsOverrides is now translations, fallbackLanguage is gone, and every key is a namespaced dotted identifier instead of an English sentence. See Migrating from v1 below.

Integration

Access translation services via the StreamVideo provider using useI18n consumer.

useI18n() returns:

  • t function - Takes a translation key plus its English copy, returns the translation for the active language
  • tDateTimeParser - A dayjs instance bound to the active language and timezone

Translation keys

Every string the SDK renders is a namespaced dotted key, written at its call site with the English copy alongside it:

t("ringingCall.incoming.title", "Incoming Call...");

The second argument is i18next's defaultValue, and it is why English needs no dictionary. A key missing from your dictionary renders that inline English, never the raw dotted key.

Plural keys pass count with one default per plural form:

t("lobby.footer.otherParticipants.text", {
  count: numberOfParticipants,
  defaultValue_one: "There is {{ count }} more person in the call.",
  defaultValue_other: "There are {{ count }} more people in the call.",
});

In a dictionary those are written per form - lobby.footer.otherParticipants.text_one, lobby.footer.otherParticipants.text_other, plus _zero / _two / _few / _many where a language needs them.

The React Native catalog is small - the SDK's UI components carry far less copy than the web SDK's. The complete list is the exported TranslationCatalog type, which your editor autocompletes; the SDK repository can also emit a flat translator-facing JSON with yarn i18n:export in packages/react-native-sdk.

Note:

Hermes ships a partial ICU, so Intl.PluralRules has no data for most locales. The SDK imports the intl-pluralrules polyfill from its own entry point, so importing @stream-io/video-react-native-sdk is normally enough. If you initialize i18n before that import runs, add import "intl-pluralrules"; as the first line of your entry file. Without it, every count selects the _other form; the SDK warns when the polyfill is missing.

Configuration

The i18n parameters StreamVideo accepts are:

type StreamVideoI18nProps = {
  i18nInstance?: Streami18n;
  language?: string;
  translations?: Record<string, LooseTranslationDictionary>;
};

Custom translations

Pass dictionaries keyed by language code to the translations prop.

They are registered over the SDK's built-in copy rather than replacing it. Keys you omit fall back to the English at the call site, so a partial dictionary is safe.

import type { LooseTranslationDictionary } from "@stream-io/video-react-native-sdk";

const translations: Record<string, LooseTranslationDictionary> = {
  de: {
    "ringingCall.incoming.title": "Eingehender Anruf...",
    "common.join.label": "Beitreten",
  },
};

const App = () => {
  return (
    <StreamVideo client={client} language="de" translations={translations}>
      {/*...*/}
    </StreamVideo>
  );
};

A dictionary may also carry your own application's keys alongside the SDK's - that is what LooseTranslationDictionary allows.

Note:

In v1 this prop was called translationsOverrides, and despite what that page said it replaced the SDK's dictionary instead of merging with it, so every untranslated key rendered as a raw key. That is fixed: translations registers over the SDK's defaults.

Typed dictionaries

Type Accepts Use it when
TranslationDictionary only the SDK's keys the dictionary holds SDK copy only - a typo or a removed key fails the build
LooseTranslationDictionary the SDK's keys plus any other string one dictionary carries both SDK and application copy
import type { TranslationDictionary } from "@stream-io/video-react-native-sdk";

export const de: TranslationDictionary = {
  "ringingCall.incoming.title": "Eingehender Anruf...",
};

TranslationKey (every key t() accepts) and TranslationCatalog (the key to English copy map) are exported too.

Adding a language

Dates and relative times are rendered by dayjs, which needs the language's locale file imported once in your app. Without it, dates render with the English locale and the SDK logs a warning.

import "intl-pluralrules";
import "dayjs/locale/nl";
import { Streami18n, StreamVideo } from "@stream-io/video-react-native-sdk";

const i18n = new Streami18n({ language: "nl" });
i18n.registerTranslation("nl", {
  "common.join.label": "Deelnemen",
  "participantView.screenShare.stop.label": "Scherm delen stoppen",
});

const App = () => (
  <StreamVideo client={client} i18nInstance={i18n}>
    {/*...*/}
  </StreamVideo>
);

No dayjs locale file defines the calendar strings - those belong to the calendar plugin - so a language whose relative dates must read natively needs a calendar config passed as the third argument to registerTranslation.

Provide your own instance of Streami18n

Pass an instance via the i18nInstance prop. StreamVideo adopts and initializes it; it is not swapped out on later renders.

import { Streami18n, StreamVideo } from "@stream-io/video-react-native-sdk";

const i18n = new Streami18n({
  language: "en",
  translationsForLanguage: {
    "ringingCall.incoming.title": "Someone is calling...",
  },
});

const App = () => (
  <StreamVideo client={client} i18nInstance={i18n}>
    {/*...*/}
  </StreamVideo>
);

Streami18n also accepts timezone, formatters, disableDateTimeTranslations, DateTimeParser (your own dayjs or moment module) and i18nextConfigOverrides, applied over the SDK's i18next init options.

registerTranslation(language, dictionary, dayjsLocaleConfig?) and setLanguage(language) may be called before or after initialization. setLanguage resolves with no value - the new t is published to i18n.state, which the provider subscribes to.

Language

Set the current language with the language prop using language codes (en, de, etc.) matching a translations key or a language registered on your own instance.

const App = () => {
  /*  a hook that keeps track of the current language in your app  */
  const { language, setLanguage } = useLanguage();

  return (
    <StreamVideo
      client={client}
      language={language}
      translations={translations}
    >
      {/*...*/}
    </StreamVideo>
  );
};

Changing language switches languages in place. A language with no registered dictionary is not an error - the SDK's English copy renders.

Fallback behaviour

There is no fallbackLanguage option. The fallback is the English written inline at every call site, so a key missing from the active dictionary already renders correct English, and i18next's fallbackLng is turned off.

To deliberately fall back from one language to another - a regional dictionary completed by its base language - turn it back on:

const i18n = new Streami18n({
  language: "de-AT",
  i18nextConfigOverrides: { fallbackLng: "de" },
});

Translation function

The t function takes a key and its English copy, and returns the translation for the active language. If the key is not in the active dictionary, the English copy is returned.

Accessing the translation function

Access via useI18n in any child of StreamVideo:

import { Text } from "react-native";
import { useI18n } from "@stream-io/video-react-native-sdk";

const CustomLabel = () => {
  const { t } = useI18n();

  return <Text>{t("common.join.label", "Join")}</Text>;
};

useI18n() returns { t, tDateTimeParser }, and works outside a provider too, where the default translator renders each call site's inline English.

Note:

t() only accepts keys the SDK defines. For a key known only at runtime, wrap it in the exported asDynamicKey() helper and supply its copy through your dictionaries. Prefer a switch over literal t() calls where you can - it keeps the copy statically visible and translatable.

Migrating from v1

v1 v2
translationsOverrides translations - registers over the defaults instead of replacing them
fallbackLanguage removed - use i18nextConfigOverrides: { fallbackLng }
StreamI18n Streami18n (different class, different options)
StreamI18nProvider removed - use StreamVideo
TranslationsMap Record<string, LooseTranslationDictionary>
TranslationLanguage, TranslatorFunction removed
the translations export / en.json removed - English is inline at each call site
useI18n().i18n removed - useI18n() returns { t, tDateTimeParser }
t("Join") t("common.join.label", "Join")

Every key changed. The old English-sentence key to new namespaced key mapping is published as ai-docs/i18n-2.0-key-map.json in the SDK repository; see also the v1 to v2 migration guide.

Warning:

An override under an old key fails silently: it never matches, so the SDK's English renders with no error. Type your dictionaries as TranslationDictionary to turn that into a compile error.

Final recommendations

Consult the i18next documentation for:

Add Chat to my app: getstream.io/SKILL.md

The fastest way to build with Stream. Start a new project or improve an existing one. Full CLI and documentation integration out of the box.


Ask your agent:

/stream Build me a Social App with Feeds and Moderation.
/stream Any livestream calls running?
/stream Video React Native v2: <Your Question>