i18n
Components
Ready-to-use React components for language selection and suggestion.
Import from @workspace/i18n/components.
LanguageSelect
A dropdown for selecting a language, built on the shared Select component from @workspace/ui.
import { LanguageSelect } from '@workspace/i18n/components';
import { getLanguageSelectorOptions } from '@workspace/i18n/validation';
const SUPPORTED = ['fr', 'en'] as const;
const languages = getLanguageSelectorOptions(SUPPORTED);
<LanguageSelect
languages={languages}
value={currentLang}
onValueChange={setLanguage}
/>Demo
Display Modes
The display prop controls what appears in the trigger button:
// Default — shows language name
<LanguageSelect display="label" ... />
// Globe icon only (label is sr-only for accessibility)
<LanguageSelect display="icon" ... />
// Both icon and language name
<LanguageSelect display="icon-and-label" ... />Props
| Prop | Type | Default | Description |
|---|---|---|---|
languages | readonly LanguageSelectorOption<T>[] | — | Available languages. Use getLanguageSelectorOptions() to generate |
value | T | — | Currently selected language code |
onValueChange | (value: T) => void | — | Callback when language changes |
display | "label" | "icon" | "icon-and-label" | "label" | What to display in the trigger |
placeholder | string | — | Placeholder text when no value |
disabled | boolean | — | Disables the select |
className | string | — | Additional class for the trigger |
id | string | — | ID for form labels |
aria-label | string | "Select language" | Accessible label |
LanguageBanner
A banner that suggests switching to the user's preferred language, detected via detectPreferredLanguage.
import { LanguageBanner } from '@workspace/i18n/components';
import { ALL_LANGUAGE_CODES } from '@workspace/i18n/constants';
// URL-based routing (apps/web)
<LanguageBanner
currentLanguage={locale}
supportedLanguages={ALL_LANGUAGE_CODES}
onSwitchLanguage={(lang) => router.push(`/${lang}${pathname.slice(3)}`)}
/>
// i18next-based (apps/app)
<LanguageBanner
currentLanguage={i18n.language as 'fr' | 'en'}
supportedLanguages={['fr', 'en']}
onSwitchLanguage={(lang) => i18n.changeLanguage(lang)}
/>Behavior
- Compares
currentLanguagewithdetectPreferredLanguage(supportedLanguages, currentLanguage) - If different, displays the banner suggesting the detected language
- Switch button — calls
onSwitchLanguage(detectedLanguage), label is shown in the suggested language - Stay button — saves preference to localStorage (won't show again), calls
onStayInCurrentLanguage - Dismiss (X) button — closes without saving (banner reappears on next visit)
Demo
Current: English — The banner suggests switching to French.
Custom Labels
<LanguageBanner
currentLanguage="en"
supportedLanguages={['fr', 'en']}
onSwitchLanguage={handleSwitch}
labels={{
message: (langName) => `We detected you prefer ${langName}`,
stayIn: (langName) => `Continue in ${langName}`,
}}
/>The "Switch" button always displays in the suggested language using built-in translations (not customizable via labels).
Props
Prop
Type