The widget ships with react-i18next wired up. The default bundle includes English; every other locale is loaded from a JSON file under src/locales/<locale>/translation.json. Browser language detection is on by default — visitors with navigator.language set to de-DE see the German bundle if it exists, English otherwise.
Out of the box
Pass language to the component to force a locale, or omit it to let the detector pick:
<AppmintChat
orgId="..."
configId="..."
appId="..."
language="de"
/>
The detector chain is configured in src/locales/i18n.ts — it tries localStorage first (i18nextLng key), then navigator.language, then falls back to en.
Adding a locale
Translations are static JSON. Add a new folder per locale:
src/locales/
├── en/
│ └── translation.json
├── de/
│ └── translation.json
└── fr/
└── translation.json # ← new
Copy en/translation.json to your new locale and translate the strings in place. Keys are flat:
{
"welcome.title": "Hi there 👋",
"welcome.subtitle": "How can we help?",
"chat.placeholder": "Type a message",
"chat.send": "Send",
"register.email": "Email",
"register.firstName": "First name",
...
}
Then register the locale in src/locales/i18n.ts:
import en from './en/translation.json';
import de from './de/translation.json';
import fr from './fr/translation.json';
i18n.init({
resources: {
en: { translation: en },
de: { translation: de },
fr: { translation: fr }, // ← new
},
fallbackLng: 'en',
...
});
Rebuild (yarn build:lib for the package, yarn build for the standalone) and the new locale is available.
Custom translations without forking
There is no i18n export from the bundle today — the widget loads its translations from its own bundled JSON files. For a one-off override (e.g., your brand uses "Concierge" instead of "Support"), the practical path is to fork chat-client and rebuild the bundle, or override copy via the chat-config record (welcome.title, welcome.subtitle, headerContent) which is server-driven and bypasses i18n.
If you self-host the bundle, you can also patch src/locales/<locale>/translation.json directly before running yarn build.
RTL languages
The Tailwind config includes tailwindcss-rtl, so RTL locales (Arabic, Hebrew) flip layout automatically when the document dir attribute is rtl:
<html lang="ar" dir="rtl">
Or set it programmatically before mounting:
document.documentElement.dir = ['ar', 'he', 'fa', 'ur'].includes(language) ? 'rtl' : 'ltr';
The default message bubble handles RTL correctly. Slot-based overrides aren't supported today (see Slots).
Date and time formatting
The widget renders timestamps via Intl.DateTimeFormat using the current locale. There is no slot today to swap formatting — if you need a different format than the locale's default, fork the bundle.
Test coverage
src/locales/__tests__/ contains snapshot tests for the English bundle. Add a snapshot for each new locale to catch missing keys early.
Common pitfalls
- A key shows as
welcome.titlein the UI — the locale file is missing that key. Check the key list inen/translation.jsonand add it to your locale. - The detector picks the wrong language —
navigator.languagereturnsen-US, noten. The widget normalises to the language code (first two letters), soen-USanden-GBboth map toen. - localStorage value is sticky — if a visitor switched languages before,
localStorage.i18nextLngholds the override. Clear it (or expose a language picker that callsi18n.changeLanguage(...)) to switch.