API reference

Every export of @api18n/react, what it does, and how to use it.

The SDK has three entry points:

import { createApi18n, useTranslations } from '@api18n/react';       // client
import { setRequestLocale, getTranslations } from '@api18n/react/server';
import { detectLocale } from '@api18n/react/next';                   // optional

The default @api18n/react import is what 90% of users need. /server is opt-in for Next.js Server Components. /next is opt-in for Next.js middleware.

Client API (@api18n/react)

createApi18n(options)

Initialises the singleton. Call once, in your init file.

function createApi18n(options: CreateApi18nOptions): Api18nInstance;

interface CreateApi18nOptions {
  resources: Resources;            // { [locale]: NestedMessages }
  defaultLocale: string;
  fallbackLocale?: string;         // defaults to defaultLocale
}

interface Api18nInstance {
  getLocale(): string;
  setLocale(locale: string): void;
  /** @internal */ _store: Api18nStore;
}

Throws if resources is empty or defaultLocale isn't present in resources.

useTranslations(namespace?)

Returns the t() function. Subscribed via useSyncExternalStore — only re-renders the component when its locale's flat map changes.

function useTranslations(namespace?: string): TFunction;

// With Messages augmentation:
type TFunction = <K extends TranslationKey>(
  key: K,
  ...args: HasArgs<ArgsFor<K>> extends true ? [ArgsFor<K>] : []
) => RenderResult<K>;

// Without augmentation (permissive fallback):
type TFunction = (key: string, args?: Args) => string | ReactNode[];

The namespace argument is a prefix: useTranslations('checkout') makes t('confirm') look up 'checkout.confirm'.

useLocale()

function useLocale(): string;

Returns the active locale. Re-renders the calling component when locale changes.

setLocale(locale)

function setLocale(locale: string): void;

Plain function — callable from event handlers, route guards, even outside React. Notifies every component subscribed via useLocale or useTranslations.

getLocale()

function getLocale(): string;

One-shot read for non-React code (e.g., a fetch wrapper that wants the current locale).

useFormatter()

Locale-scoped wrappers for the standard Intl APIs.

function useFormatter(): Formatter;

interface Formatter {
  number(value: number, options?: Intl.NumberFormatOptions): string;
  dateTime(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
  relativeTime(
    value: number,
    unit: Intl.RelativeTimeFormatUnit,
    options?: Intl.RelativeTimeFormatOptions,
  ): string;
  list(items: Iterable<string>, options?: Intl.ListFormatOptions): string;
}

Useful when you need to format a value that isn't keyed in your translation files — e.g., a transaction amount or a relative timestamp.

const f = useFormatter();
return <span>{f.number(1234.5, { style: 'currency', currency: 'USD' })}</span>;
// → "$1,234.50" in en, "US$ 1.234,50" in pt-BR

Server API (@api18n/react/server)

setRequestLocale(locale)

Pins the locale for the current React cache() slot. Call this once in your root layout (or in middleware) before any Server Component reads translations.

import { setRequestLocale } from '@api18n/react/server';

export default async function Layout({ children, params }) {
  const { locale } = await params;
  setRequestLocale(locale);
  return <html lang={locale}>{children}</html>;
}

Behind the scenes, this uses React's cache() so the per-request locale doesn't leak between concurrent requests during streaming SSR.

getTranslations(namespace?)

The Server Component equivalent of useTranslations. Async because it resolves the per-request locale via cache().

async function getTranslations(namespace?: string): Promise<ServerTFunction>;
import { getTranslations } from '@api18n/react/server';

export default async function Page() {
  const t = await getTranslations();
  return <h1>{t('welcome', { name: 'Eduardo' })}</h1>;
}

Strings rendered by getTranslations arrive as plain HTML — zero client JavaScript per string.

Next.js helper (@api18n/react/next)

detectLocale(url, headers, cookies, options)

Framework-agnostic locale detection for Next.js middleware.

function detectLocale(
  url: URL,
  headers: Headers,
  cookies: { get(name: string): { value: string } | undefined },
  options: {
    locales: readonly string[];
    defaultLocale: string;
    cookieName?: string;            // default: NEXT_LOCALE
  },
): {
  locale: string;
  basePath: string;
  hasLocalePrefix: boolean;
};

Detection order:

  1. URL prefix/en/..., /pt-BR/... if the first segment matches a locale.
  2. CookieNEXT_LOCALE (or your cookieName).
  3. Accept-Language — best-effort match, including base-language fallback.
  4. defaultLocale as last resort.

Example wiring:

// middleware.ts
import { NextResponse } from 'next/server';
import { detectLocale } from '@api18n/react/next';

export function middleware(request: Request) {
  const url = new URL(request.url);
  const { locale, basePath, hasLocalePrefix } = detectLocale(
    url,
    request.headers,
    request.cookies,
    {
      locales: ['en', 'pt-BR', 'de'],
      defaultLocale: 'en',
    },
  );

  if (!hasLocalePrefix) {
    return NextResponse.redirect(new URL(`/${locale}${basePath}`, request.url));
  }
  return NextResponse.next();
}

Type exports

import type {
  Api18nInstance,
  ArgsFor,
  CreateApi18nOptions,
  Formatter,
  Messages,                // augmentable interface
  NestedMessages,
  Resources,
  TFunction,
  TranslationKey,
} from '@api18n/react';

Augmenting Messages

The CLI's messages.d.ts looks like:

import '@api18n/react';

declare module '@api18n/react' {
  interface Messages {
    'button.cancel': { __raw: 'Cancel' };
    hello: { __raw: 'Hello {name}'; name: string | number };
    'cart.items': {
      __raw: '{count, plural, one {# item} other {# items}}';
      count: number;
    };
  }
}

Once the interface is augmented, TFunction switches from the permissive fallback to the strict typed signature. Add messages.d.ts to tsconfig.json's include array (or let TS auto-discover it) and your editor will autocomplete keys and arg names.