@vocoder/react
v0.2.2
Published
React components for internationalization (i18n) with SSR support, ICU MessageFormat, and build-time translation generation
Downloads
303
Readme
@vocoder/react
React components and hooks for Vocoder internationalization. Provides the <T> component for translating JSX, the t() function for translating plain strings, and a provider that manages locale state with SSR hydration support.
Installation
npm install @vocoder/reactRequires React 18+.
Setup
Wrap your app with VocoderProvider:
import { VocoderProvider } from '@vocoder/react';
function App() {
return (
<VocoderProvider>
{/* your app */}
</VocoderProvider>
);
}The provider loads translations from virtual modules injected by @vocoder/unplugin at build time. If the unplugin is not installed, source text is rendered as-is.
Translating Strings
The <T> Component
Use <T> to mark JSX content for translation:
import { T } from '@vocoder/react';
// Simple text
<T>Hello, world!</T>
// Variable interpolation
<T name={user.name}>Hello, {name}!</T>
// ICU MessageFormat (pluralization)
<T msg="{count, plural, one {# item} other {# items}}" count={items.length} />
// Rich text with component placeholders
<T components={{ link: <a href="/help" /> }}>
Click <link>here</link> for help
</T>Props
| Prop | Type | Description |
|---|---|---|
| children | ReactNode | Source text (also used as the translation key) |
| msg | string | Alternative to children for ICU strings. Takes precedence over children. |
| id | string | Optional stable key for extraction/sync identity |
| context | string | Disambiguation context for identical source text |
| formality | 'formal' \| 'informal' \| 'auto' | Formality level hint for translators |
| components | Record<string, ReactElement> | Component placeholders for rich text |
| [key: string] | any | Variable values for interpolation |
The t() Function
Use t() for translations outside of JSX (utilities, services, constants):
import { t } from '@vocoder/react';
const greeting = t('Hello, world!');
const message = t('Hello, {name}!', { name: 'John' });
const items = t('{count, plural, one {# item} other {# items}}', { count: 5 });t() uses global state synced by VocoderProvider. Make sure the provider is mounted before calling it. Rich text with components is only supported in <T>, not in t().
The useVocoder Hook
Access locale state and translation utilities in components:
import { useVocoder } from '@vocoder/react';
function MyComponent() {
const {
locale, // Current locale code (e.g., 'es')
setLocale, // Switch locale: await setLocale('fr')
availableLocales, // Array of available locale codes
locales, // Locale metadata (nativeName, dir)
isReady, // True when translations are loaded
t, // Context-bound translate function
hasTranslation, // Check if a translation exists
getDisplayName, // Get translated locale name
} = useVocoder();
return (
<select
value={locale}
onChange={(e) => setLocale(e.target.value)}
>
{availableLocales.map((code) => (
<option key={code} value={code}>
{getDisplayName(code)}
</option>
))}
</select>
);
}Locale Selector
A pre-built locale switcher is available as a separate entry point (to avoid bundling Radix UI unless needed):
import { LocaleSelector } from '@vocoder/react/locale-selector';
// Floating selector with position control
<LocaleSelector position="bottom-right" />
// Custom styling
<LocaleSelector
position="top-right"
background="#1a1a1a"
color="#ffffff"
iconSize={20}
sortBy="native"
/>Requires @radix-ui/react-dropdown-menu and lucide-react as optional peer dependencies:
npm install @radix-ui/react-dropdown-menu lucide-reactProps
| Prop | Type | Default | Description |
|---|---|---|---|
| position | 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' | -- | Screen position |
| background | string | -- | Background color |
| color | string | -- | Text color |
| className | string | -- | Additional CSS class |
| iconSize | number | -- | Globe icon size in pixels |
| locales | LocalesMap | -- | Override locale metadata |
| sortBy | 'source' \| 'native' \| 'translated' | 'source' | Sort order for dropdown items |
Server-Side Rendering
VocoderProvider supports SSR with hydration. Pass cookies from the request to enable server-side locale detection:
// Next.js App Router
import { cookies } from 'next/headers';
export default function RootLayout({ children }) {
return (
<VocoderProvider cookies={cookies().toString()}>
{children}
</VocoderProvider>
);
}The provider injects a <script type="application/json"> tag with the hydration snapshot so the client can render the correct locale on first paint without a flash of the wrong language.
Locale Persistence
The user's locale preference is persisted across sessions:
- Client:
localStorageand avocoder_localecookie - Server: Reads the
vocoder_localecookie from the request headers
SPA Setup (Vite / Client-Only)
For client-only apps, call initializeVocoder() before the first render to avoid a flash of untranslated content:
import { initializeVocoder, VocoderProvider } from '@vocoder/react';
import { App } from './App';
async function bootstrap() {
await initializeVocoder();
ReactDOM.createRoot(document.getElementById('root')!).render(
<VocoderProvider>
<App />
</VocoderProvider>,
);
}
bootstrap();Background Refresh
When @vocoder/unplugin is installed, the build plugin injects metadata into the bundle. After the initial render, the provider checks the Vocoder API for translations newer than the build timestamp. If found, it updates the in-memory translations and re-renders.
This means:
- Initial page load uses translations baked in at build time (fast)
- New translations published after the build appear without redeployment (fresh)
How Translations Are Loaded
Translations are delivered as virtual modules by @vocoder/unplugin:
virtual:vocoder/manifest-- project config and per-locale dynamic import loadersvirtual:vocoder/translations/{locale}-- translation map for each locale
Each locale is a separate chunk that the bundler code-splits automatically. Only the active locale is loaded; others are fetched on demand when the user switches languages.
License
MIT
