Skip to content

Quick start

@ardinsys/intl is a lightweight, type-safe internationalization toolkit for TypeScript that also offers first party integrations with some frontend frameworks. You can use it though in any framework, even in Razor pages.

Installation

sh
pnpm add @ardinsys/intl
sh
npm install @ardinsys/intl

Basic usage

Create the internalization instance first:

ts
import {
  createIntl,
  type LocalizationBundle,
  type LocalizationBundles,
} from "@ardinsys/intl";

// Create localizations
// Make sure you use "as const" to get strong type inference
const localization = {
  en: {
    messages: {
      greet: "Hello {placeholder}",
      nested: { title: "Home" },
      welcome: (p: { name: string }) => `Hello ${p.name}`,
    },
    numberFormats: {
      default: { style: "decimal" },
    },
    currencyFormats: {
      default: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      default: { dateStyle: "long" },
    },
  },
  hu: {
    messages: {
      greet: "Szia {placeholder}",
      nested: { title: "Főoldal" },
      welcome: (p: { name: string }) => `Szia ${p.name}`,
    },
    numberFormats: {
      default: { style: "decimal" },
    },
    currencyFormats: {
      default: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      default: { dateStyle: "short" },
    },
  },
} as const satisfies LocalizationBundles;

// Create the instance
const { t, d, n, c, locale, setLocale } = createIntl("en", localization);

// Translate messages
t("greet", { placeholder: "John" }); // "Hello John"
t("nested.title"); // "Home"
t("welcome", { name: "John" }); // "Hello John"

// Format numbers
n(12345.67); // "12,345.67"
n("default", 12345.67);

// Format currency numbers
c("default", 12345.67, "USD");

// Format dates
d(new Date("2025-09-12")); // e.g. "September 12, 2025"
d("default", new Date("2025-09-12"));

// Switch locale
setLocale("hu");
t("greet", { placeholder: "John" }); // "Szia John"

Note that nested objects are accessible using a dot notation like in t("nested.title").

n (number format), c (currency format) and d (date format) optionally can use 2 arguments instead of one. You can provide a key before the number/date to choose which formatter you want to use. It will try to use the default key by default and falls back to the first one if there is no default.

Using with global data

Sometimes you want to define global (seeded) translations and formats that are available across your whole app (e.g. common.ok, errors.notFound) and then merge them with local, feature-specific bundles.

WARNING

Proper type safety for keys can only be guaranteed if the format of the seeded data matches with the local data so the merge types can work properly.

Use createSeededIntl for that:

ts
import { createSeededIntl, type LocalizationBundle } from "@ardinsys/intl";

const seed = {
  en: {
    messages: {
      common: { ok: "OK", cancel: "Cancel" },
    },
    numberFormats: {
      compact: { notation: "compact" },
    },
    currencyFormats: {
      short: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      monthYear: { month: "long", year: "numeric" },
    },
  },
  hu: {
    messages: {
      common: { ok: "Rendben", cancel: "Mégse" },
    },
    numberFormats: {
      compact: { notation: "compact" },
    },
    currencyFormats: {
      short: { currencyDisplay: "symbol" },
    },
    dateFormats: {
      monthYear: { month: "long", year: "numeric" },
    },
  },
} as const satisfies Record<string, LocalizationBundle>;

// Pre-seed the global translations
const createIntl = createSeededIntl(seed);

// Create an instance with both global + local
const { t, n, d, c, setLocale } = createIntl("en", {
  en: {
    messages: { page: { title: "Dashboard" } },
  },
  hu: {
    messages: { page: { title: "Vezérlőpult" } },
  },
} as const);

// Global keys are always available
t("common.ok"); // "OK"
t("page.title"); // "Dashboard"

// Formats also fall back to seed
n("compact", 12345);
c("short", 12345, "USD");
d("monthYear", new Date("2025-09-12")); // "September 2025"

// Switching locale applies to both
setLocale("hu");
t("common.cancel"); // "Mégse"
t("page.title"); // "Vezérlőpult"

Integrations

To see how should you use first party integrations, visit Integrations.

Released under the Apache 2 License.