API

React API

Package

@foo-i18n/react

The React integration of the translation function.

This module includes both @foo-i18n/t and @foo-i18n/base for conveniense.

Declarations

Provider

type TranslationProviderProps<M extends NamespaceMessages> = {
  locale: Locale;
  messages: M;
  plural?: PluralRule;
  children: React.ReactNode;
};

function TranslationProvider<M extends NamespaceMessages>({
  locale,
  messages,
  plural = defaultPlural,
  children,
}: TranslationProviderProps<M>): JSX:Element;

See translate API for NamespaceMessages, and plurals API for PluralRule declarations.

Hooks

type UseTranslation<M extends NamespaceMessages> = <
  NS extends StringKeys<M>,
  S extends StringKeys<M[NS]>,
>(
  ns: NS
) => {
  readonly locale: Locale;
  readonly plural: PluralRule;
  readonly t: TranslateMessage<S>;
};

type UseLocale = () => Locale;

const useTranslation: UseTranslation<NamespaceMessages>;

const useLocale: UseLocale;

Usage

Building blocks

See fundamentals for more information about messages.

import type {
  Locale,
  ExpandedMessages,
  UseTranslation,
} from '@foo-i18n/react/types';

// 1. Declare messages dictionary shape from JSON
export type AppMessages = ExpandedMessages<{
  people: typeof import('../messages/en/people.json');
}>;

// 2. Define allowed application locales
export type AppLocale = Extract<Locale, 'en' | 'fr'>;

// 3. Typed translation hook
export type UseAppTranslation = UseTranslation<AppMessages>;
//import { en, fr } from "@foo-i18n/plurals";
import en from '@foo-i18n/plurals/en';
import fr from '@foo-i18n/plurals/en';
import type { AppLocale } from './types';
import type { PluralRule } from '@foo-i18n/react/types';

// 4. export only required plural rules
export const appPlurals: Record<AppLocale, PluralRule> = { en, fr };
'use server';
import type { AppLocale, AppMessages } from './types';

// This function uses type inference so that it's return
// value can be statically inspected.
export const loadMessages = async (locale: AppLocale) =>
  ({
    people: (await import(`../messages/${locale}/people.json`))
      .default as AppMessages['people'],
  }) satisfies AppMessages;
'use client';
import { TranslationProvider } from '@foo-i18n/react';
import { appPlurals } from '@/i18n/plurals';
import type { AppLocale, AppMessages } from '@/i18n/types';

export type AppTranslationProviderProps = {
  locale: AppLocale;
  messages: AppMessages;
  children: React.ReactNode;
};

const AppTranslationProvider = ({
  locale,
  messages,
  children,
}: AppTranslationProviderProps) => {
  // get the plural rules for the specified (allowed) locale
  const plural = appPlurals[locale];

  return (
    <TranslationProvider locale={locale} messages={messages} plural={plural}>
      {children}
    </TranslationProvider>
  );
};

export default AppTranslationProvider;
import { useTranslation } from '@foo-i18n/react';
import type { UseAppTranslation } from '@/i18n/types';

export const useAppTranslation = useTranslation as UseAppTranslation;

Putting it all together

The following example uses Next.js App Router, however the principle is quite straightforward from here.

import { loadMessages } from '@/i18n/messages';
import AppTranslationProvider from '@/components/TranslationProvider';
import PageContent from '@/components/pages/PageContent';
import type { AppLocale } from '@/i18n/types';

// React SSR component
export default async function Page() {
  // get locale from route, or middleware, etc.
  const locale: AppLocale = 'en';
  const messages = await loadMessages(locale);

  return (
    <AppTranslationProvider locale={locale} messages={messages}>
      <PageContent />
    </AppTranslationProvider>
  );
}
'use client';

// import typed hook
import { useAppTranslation } from '@/i18n/useAppTranslation';

const PageContent = () => {
  const { locale, t, plural } = useAppTranslation('people');
  const count = 123;
  const plurality = plural.ordinal(count);

  return (
    <>
      <div>Current locale: {locale}</div>
      <div>{t('There are {count} people', { count, plurality })}</div>
    </>
  );
};

export default PageContent;

For components only needing the current locale value:

'use client';

// import typed hook
import { useLocale } from '@foo-i18n/react/useLocale';

const MyComponent = () => {
  const locale = useLocale();

  return <div>Current locale: {locale}</div>;
};

export default PageContent;