I18n
Wrap your favorite i18n library (here I'm using react-intl) in a convenient
global $t
method.
Provider
/src/providers/I18n.tsx
import React, { PropsWithChildren, useCallback, useRef } from "react";
import { IntlConfig, IntlProvider, useIntl } from 'react-intl';
type I18nArgs = { id: string; defaultMessage: string; values?: Record<string, unknown> };
function MessageProvider(props: PropsWithChildren<unkown>) {
const { formatMessage } = useIntl();
const { current: globalI18n } = useRef(
({ id, defaultMessage, values }: I18nArgs) => {
return formatMessage(
{ id, defaultMessage },
{
...values,
// handle text formatting here
b: (str) => (<strong>{str}</strong>) as unknown as string,
i: (str) => (<em>{str}</em>) as unknown as string,
}
);
}
);
(window ?? global).$t = globalI18n;
return <>{children}</>;
}
export function I18nProvider({ children, locale, onError }: PropsWithChildren<Pick<IntlConfig, 'locale' | 'onError'>>) {
const navigatorLocale = navigator.language;
const handleError: IntlConfig['onError'] = useCallback(() => {
// Default error management
}, []);
return (
<IntlProvider locale={locale ?? navigatorLocale} onError={onError ?? handleError}>
<MessageProvider children={children}/>
</IntlProvider>
);
}
/src/declaration.d.ts
declare function $t(message: { id: string; defaultMessage: string; values?: Record<string, unknown> }): string;
Usage
/src/GenericComponent.tsx
import React from 'react';
export function GenericComponent() {
return (
<main>
<h1>{$t({ id: 'generic.title', defaultMessage: 'Lorem Ipsum' })}</h1>
<p>
{$t({
id: 'generic.content',
defaultMessage: 'Today is <b>{date}</b>',
values: { date: (new Date()).toLocaleDateString() }
})}
</p>
</main>
);
}
/src/App.tsx
import React from 'react';
import { I18nProvider } from './providers/I18n';
import { GenericComponent } from './GenericComponent';
export function App() {
return (
<I18nProvider>
<GenericComponent/>
</I18nProvider>
);
}
Caveat
When testing your components, you will need to add the provider or write your own
$t
implementation in your test setup.As
$t
won't be defined when compiling the code, it only works at runtime. The following code will result in an error:
/src/**/GenericComponent.tsx
import React from 'react';
const titleMessage = $t({ id: 'generic.title', defaultMessage: 'Lorem Ipsum' });
export function GenericComponent() {
return (
<main>
<h1>{titleMessage}</h1>
<p>
{$t({
id: 'generic.content',
defaultMessage: 'Today is <b>{date}</b>',
values: { date: (new Date()).toLocaleDateString() }
})}
</p>
</main>
);
}