Skip to content

Locale bundle format

The object you pass into the core createIntl (and all seeded helpers) is called a bundle. A bundle describes translations, number formats, and date/time formats for a specific locale.

Top-level shape

Each bundle is keyed by locale code ("en", "hu", "fr", …).

Each locale contains three optional sections:

  • messages - your translated text, with placeholders or custom formatter functions.
  • numberFormats - named number format options, passed to Intl.NumberFormat.
  • dateFormats - named date/time format options, passed to Intl.DateTimeFormat.
  • currencyFormats - named currency format options, passed to Intl.NumberFormat.

Messages

Plain strings

ts
const bundle = {
  en: {
    messages: {
      hello: "Hello world",
    },
  },
} as const;

Placeholders

Messages can include placeholders in curly braces. At runtime you must provide these placeholders.

ts
const bundle = {
  en: {
    messages: {
      greet: "Hello {name}, you have {count} new messages.",
    },
  },
} as const;

// usage
t("greet", { name: "John", count: 3 });
// -> "Hello John, you have 3 new messages."
  • Placeholders are matched by name ({name}).
  • Multiple placeholders can be used. (type checker can only infer up to 10 in a single string)
  • Ordering can vary between locales.

Function messages

For more control, messages can also be functions. This gives you typed parameters which you will need to pass down to the t function instead of the placeholders.

PARAMETERS

The function should always take in a single object parameter which is properly typed for inference.

ts
const bundle = {
  en: {
    messages: {
      items: (p: { count: number }) =>
        p.count === 1 ? "1 item" : `${p.count} items`,
    },
  },
} as const;

// usage
t("items", { count: 5 }); // -> "5 items"

This pattern is useful for pluralization or context-based formatting.

Nesting

For better organization you can nest objects and use the keys via the dot notation.

TYPES

Type inference can only go 10 objects deep.

ts
const bundle = {
  en: {
    messages: {
      hello: "Hello world",
      erros: {
        forbidden: "You can't do this. Error code: {code}",
      },
    },
  },
} as const;

// usage
t("errors.forbidden", { code: "0x12323" });

Number formats

Number formats are named presets for Intl.NumberFormat.

ts
const bundle = {
  en: {
    numberFormats: {
      default: { style: "decimal" },
      currency: { style: "currency", currency: "USD" },
      percent: { style: "percent", maximumFractionDigits: 2 },
    },
  },
} as const;

// usage
n("currency", 1234.5); // -> "$1,234.50"
n("percent", 0.125); // -> "12.5%"
n(1000); // -> 1,000 (uses default if not provided)
  • Keys are arbitrary names ("currency", "percent", "compact", etc).
  • The key named default will be used if not specified in the n function call
  • Values are standard Intl.NumberFormatOptions.

Currency formats

Currency formats are named presets for Intl.NumberFormat. They always have style: currency applied to them. The currency you pass into the function will be added to the Intl options object as well.

ts
const bundle = {
  en: {
    currencyFormats: {
      default: { currencyDisplay: "code" },
      symbol: { currencyDisplay: "symbol" },
    },
  },
} as const;

// usage
c("symbol", 1234.5, "USD"); // -> "$1,234.50"
c(1000, "USD"); // -> USD 1,000.00 (uses default if not provided)
  • Keys are arbitrary names ("currency", "percent", "compact", etc).
  • The key named default will be used if not specified in the n function call
  • Values are standard Intl.NumberFormatOptions.

Date/time formats

Date/time formats are named presets for Intl.DateTimeFormat.

ts
const bundle = {
  en: {
    dateFormats: {
      default: { dateStyle: "short", timeStyle: "short" },
      short: { dateStyle: "short" },
      long: { dateStyle: "full", timeStyle: "short" },
    },
  },
} as const;

// usage
d("short", new Date()); // -> "11/18/25"
d("long", new Date()); // -> "Tuesday, November 18, 2025 at 10:32 AM"
d(new Date())); // -> uses default if not provided
  • Keys are arbitrary names ("short", "long", "timeOnly", etc).
  • The key named default will be used if not specified in the d function call
  • Values are standard Intl.DateTimeFormatOptions.

Complete example

ts
const bundles = {
  en: {
    messages: {
      hello: "Hello {name}!",
      inbox: (p: { count: number }) =>
        p.count === 1 ? "1 message" : `${p.count} messages`,
    },
    numberFormats: {
      default: { style: "decimal" },
      percent: { style: "percent", maximumFractionDigits: 2 },
    },
    currencyFormats: {
      default: { currencyDisplay: "code" },
      symbol: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      short: { dateStyle: "short" },
    },
  },
  hu: {
    messages: {
      hello: "Szia {name}!",
      inbox: (p: { count: number }) =>
        p.count === 1 ? "1 üzenet" : `${p.count} üzenet`,
    },
    numberFormats: {
      default: { style: "decimal" },
      percent: { style: "percent", maximumFractionDigits: 2 },
    },
    currencyFormats: {
      default: { currencyDisplay: "code" },
      symbol: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      short: { dateStyle: "short" },
    },
  },
} as const;

Released under the Apache 2 License.