Skip to main content

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>
);
}