Localization
Built-in translation service powered by i18next for language switching and custom translations.
Best Practices
- Use
useI18n()hook to access thet()translation function. - Register your own copy via the
translationsprop onStreamVideo- it layers over the SDK's English, so a partial dictionary is safe. - Set language via
languageprop - a language code such asenorde. - Type your dictionaries as
TranslationDictionaryso a typo or a stale key is a compile error. - For advanced control, create your own
Streami18ninstance and pass it viai18nInstance.
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
The service is made available through the StreamVideo provider. That means that all the child components of this provider can access the translation context by using the useI18n context consumer.
The context carries the following properties:
- the translator function
t- takes a translation key and the English copy for it, and returns the translation for the active language. - the date/time parser
tDateTimeParser- a dayjs instance bound to the active language and timezone, used to render timestamps.
Translation keys
Every string the SDK renders is a namespaced dotted key, written at its call site together with the English copy:
t("participantList.muteAll.label", "Mute all");The second argument is i18next's defaultValue. It is what makes English the built-in language: the SDK ships no en.json and no translations export. If a key is missing from your dictionary - or you have registered no dictionary at all - the inline English renders, never the raw dotted key.
Plural keys pass count together with one default per plural form:
t("callFeedback.starRating.rateStars.ariaLabel", {
count: star,
defaultValue_one: "Rate {{ count }} star",
defaultValue_other: "Rate {{ count }} stars",
});In a dictionary those keys are written out per form - callFeedback.starRating.rateStars.ariaLabel_one, callFeedback.starRating.rateStars.ariaLabel_other, and _zero / _two / _few / _many for the languages that need them.
The complete key list is the TranslationCatalog type exported from the SDK, so your editor autocompletes it. The SDK repository can also emit a flat translator-facing JSON on demand with yarn i18n:export in packages/react-sdk.
Configuration
The i18n parameters StreamVideo accepts are:
type StreamVideoI18nProps = {
i18nInstance?: Streami18n;
language?: string;
translations?: Record<string, LooseTranslationDictionary>;
};In the following sections, we will look more into these individual configuration parameters.
Custom translations
To translate the SDK into another language, or to change some of the English copy, pass dictionaries keyed by language code to the translations prop.
The dictionaries are registered over the SDK's built-in copy rather than replacing it. Any key you leave out falls back to the English written at the call site, so a partial dictionary is safe and you do not have to translate everything before shipping a language.
import type { LooseTranslationDictionary } from "@stream-io/video-react-sdk";
const translations: Record<string, LooseTranslationDictionary> = {
de: {
"callControls.cancelCallButton.leaveCall.title": "Anruf verlassen",
"participantList.muteAll.label": "Alle stummschalten",
},
};
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.
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 key you did not translate rendered as a raw key. That is fixed: translations registers over the SDK's defaults.
Typed dictionaries
Two dictionary types are exported:
| 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 |
TranslationDictionary is the stricter of the two and the one to prefer for SDK-only files: a key that no longer exists is caught at compile time instead of silently never applying.
import type { TranslationDictionary } from "@stream-io/video-react-sdk";
export const de: TranslationDictionary = {
"callControls.cancelCallButton.leaveCall.title": "Anruf verlassen",
};TranslationKey (a union of 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 locale file for the language imported once in your application. Without it, dates render with the English locale and the SDK logs a warning.
import "dayjs/locale/nl";
import { Streami18n, StreamVideo } from "@stream-io/video-react-sdk";
const i18n = new Streami18n({ language: "nl" });
i18n.registerTranslation("nl", {
"callControls.cancelCallButton.leaveCall.title": "Gesprek verlaten",
});
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 ("Today at 14:30") must read natively needs a calendar config passed as the third argument to registerTranslation.
Provide your own instance of Streami18n
You may want to initialize the service somewhere else and pass the instance through the prop i18nInstance. If an instance is provided, StreamVideo adopts it and initializes it; the instance is not swapped out on later renders.
import { Streami18n, StreamVideo } from "@stream-io/video-react-sdk";
const i18n = new Streami18n({
language: "en",
translationsForLanguage: {
"participantList.muteAll.label": "Silence everyone",
},
});
const App = () => (
<StreamVideo client={client} i18nInstance={i18n}>
{/*...*/}
</StreamVideo>
);Streami18n also accepts timezone, formatters, disableDateTimeTranslations, DateTimeParser (to supply your own dayjs or moment module) and i18nextConfigOverrides, which is applied over the SDK's i18next init options and can reach settings the SDK does not surface.
registerTranslation(language, dictionary, dayjsLocaleConfig?) and setLanguage(language) may be called at any point, before or after initialization. setLanguage resolves with no value - the new t is published to i18n.state, which the provider subscribes to.
Language
You can set the current language for the translation service with the language prop. This should be a language code (for example en, de) - a key of translations, or a language you registered on your own Streami18n 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 copy written inline at every call site, so a key missing from the active language's dictionary already renders correct English. i18next's own fallbackLng is turned off for that reason.
If you deliberately want one language to fall back to another - a regional dictionary completed by its base language, say - turn it back on through the escape hatch:
const i18n = new Streami18n({
language: "de-AT",
i18nextConfigOverrides: { fallbackLng: "de" },
});Translation function
The function is passed a translation key and the English copy for it, 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
You can access the translation function in any child component of StreamVideo through the context consumer useI18n:
import { useI18n } from "@stream-io/video-react-sdk";
const CustomButton = () => {
const { t } = useI18n();
return (
<button>
{t("callControls.cancelCallButton.leaveCall.title", "Leave call")}
</button>
);
};useI18n() returns { t, tDateTimeParser }. It also works outside a provider, where the default translator renders each call site's inline English.
t() only accepts keys the SDK defines. If your key is only known at runtime - the value of a lookup table, for instance - wrap it in the exported asDynamicKey() helper, and supply the copy for it 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("Leave call") |
t("callControls.cancelCallButton.leaveCall.title", "Leave call") |
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.
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
As the translation service is based on i18next, we encourage you to consult the library's documentation in order to learn about:
- the use of the translation function
- how to dynamically insert text with interpolation documentation article
- how to format interpolated value i18next's formatting guide
- how to specify different plural forms with the pluralization guide