ts-intl
Chapters:Overview & Installation

Overview & Installation

ts-intl is a zero-dependency, framework-agnostic, strictly type-safe internationalization (i18n) library for modern TypeScript environments.

It provides an API design inspired by next-intl, without requiring Next.js or binding to a specific UI framework.

Design Principles

  • Zero Dependencies: Zero runtime dependencies and no code generation build steps.
  • Framework Agnostic: Supports Astro, React, Vue, Svelte, Node.js, Bun, Cloudflare Workers, and browser environments.
  • Strict Type Safety: Complete compile-time type inference for translation keys, interpolated variables, namespaces, and dictionary structure alignment.

Installation

Install @aaakul/ts-intl using a package manager:

# pnpm
pnpm add @aaakul/ts-intl

# npm
npm install @aaakul/ts-intl

# yarn
yarn add @aaakul/ts-intl

# bun
bun add @aaakul/ts-intl

Quick Start

1. Define Message Dictionaries

Create static dictionaries using as const to guarantee strict type inference:

// messages/en-US.ts
export default {
  common: {
    title: "System Dashboard",
    greeting: "Hello, {name: string}!",
    items: {
      one: "1 item in total",
      other: "{count} items in total",
    },
  },
} as const;
// messages/zh-Hans.ts
export default {
  common: {
    title: "系统仪表盘",
    greeting: "你好,{name: string}!",
    items: {
      one: "共 1 项数据",
      other: "共 {count} 项数据",
    },
  },
} as const;

2. Initialize i18n Instance

// i18n.ts
import { createI18n } from "@aaakul/ts-intl";
import enUS from "./messages/en-US";
import zhHans from "./messages/zh-Hans";

export const {
  getTranslations,
  getFormatter,
  isSupportedLanguage,
  languages,
  defaultLanguage,
} = createI18n({
  defaultLanguage: "en-US",
  messages: {
    "en-US": enUS,
    "zh-Hans": zhHans,
  },
});

export type Language = (typeof languages)[number];

3. Translate with Full Type Safety

import { getTranslations } from "./i18n";

const t = getTranslations("en-US", "common");

// 1. Static text
const title = t("title");
// Type: (key: "title") => string
// Output: "System Dashboard"

// 2. Interpolation with typed parameters
const greeting = t("greeting", { name: "Developer" });
// Type: (key: "greeting", params: { name: string }) => string
// Output: "Hello, Developer!"

// 3. Plural handling
const items = t("items", { count: 5 });
// Type: (key: "items", params: { count: number }) => string
// Output: "5 items in total"

Dictionary Formats

Using TypeScript files with as const enables parameter type inference ({name: string}, {count: number}) and compile-time key validation:

export default {
  auth: {
    login: "Sign In",
    welcome: "Welcome back, {username: string}!",
  },
} as const;

JSON Dictionaries

createI18n supports JSON dictionaries without extra build plugins:

import enUS from "./messages/en-US.json";
import zhHans from "./messages/zh-Hans.json";

export const { getTranslations } = createI18n({
  defaultLanguage: "en-US",
  messages: { "en-US": enUS, "zh-Hans": zhHans },
});

Note: JSON dictionaries provide strong key and namespace autocomplete. Because JSON lacks as const, template parameter types default to relaxed inference.