Introducing Typed-i18n, a Type-Safe Internationalization for TypeScript Apps

Explore @qzlcorp/typed-i18n — a zero-dependency, TypeScript-first i18n library with module-based organization, compile-time type safety, and scalable code-splitting.

December 1, 2025By qz-l team

Typed-i18n: Type-Safe Internationalization for TypeScript Apps

We’re excited to introduce @qzlcorp/typed-i18n — a zero-dependency, TypeScript-first i18n library designed for modern apps that need scalable, type-safe, and modular translations.

Inspired by modern i18n libraries, typed-i18n ensures that all your translation keys are validated at compile-time, catching errors early and keeping your app robust.

Demo

Typed-i18n type-safety demo

Watch how TypeScript catches translation key errors at compile time, ensuring type-safe i18n throughout your app.

Live Demo

🚀 View React Demo - Interactive demo showcasing dynamic module loading, locale switching, and type-safe translations.


Features

  • Module-based architecture – Organize translations by feature/page for better code-splitting.
  • Compile-time type safety – All translation keys are validated at build time.
  • Shape validation – TypeScript ensures all locale files match the same structure.
  • Modern API – Simple t('key') syntax with namespace support.
  • Mutable locale – Change language dynamically with setLocale().
  • Fallback support – Automatic fallback to default locale for missing translations.
  • Zero runtime dependencies – Lightweight and performant.

Installation

npm install @qzlcorp/typed-i18n

Quick Start

1. Create translation files

// locales/common/en.json
{
  "hello": "Hello",
  "goodbye": "Goodbye"
}

// locales/common/fr.json
{
  "hello": "Bonjour",
  "goodbye": "Au revoir"
}

2. Define modules and create i18n instance

import { defineModule, createI18n } from '@qzlcorp/typed-i18n';
import commonEn from './locales/common/en.json';
import commonFr from './locales/common/fr.json';

// ⚠️ Provide explicit reference type for compile-time validation
const common = defineModule('common')<typeof commonEn>({
  en: commonEn,
  fr: commonFr
});

const i18n = createI18n({
  locale: 'en',
  fallbackLocale: 'en',
  modules: { common }
});

3. Use translations

i18n.t('common.hello'); // "Hello"

// Change locale dynamically
i18n.setLocale('fr');
i18n.t('common.hello'); // "Bonjour"

// Interpolation
i18n.t('common.greeting', { name: 'John' }); // Supports {{name}} in JSON

Advanced Usage

Multiple Modules (Code-Splitting)

const dashboard = defineModule('dashboard')<typeof dashboardEn>({
  en: dashboardEn,
  fr: dashboardFr
});

const i18n2 = i18n.addModule(dashboard); // Returns new typed instance
i18n2.t('dashboard.title'); // ✅ Fully typed

Dynamic Module Loading (React)

const dashboardModule = defineModule('dashboard')<typeof dashboardEn>({
  en: await import('./locales/dashboard/en.json'),
  fr: await import('./locales/dashboard/fr.json')
});

const i18n3 = i18n.addModule(dashboardModule);

⚠️ Important: Always use the returned instance from addModule to preserve type safety. Original instance will still work at runtime but loses typing for new modules.


Nested Keys

{
  "dashboard": {
    "stats": {
      "clicks": "{{count}} clicks"
    }
  }
}
i18n.t('common.dashboard.stats.clicks', { count: 5 }); // "5 clicks"

Get Available Locales

i18n.getLocales(); // ['en', 'fr']

Fallback Locale

const i18n = createI18n({
  locale: 'de', // German not available
  fallbackLocale: 'en',
  modules: { common }
});
i18n.t('common.hello'); // Falls back to "Hello"

API Reference

defineModule(namespace)<Ref>(locales)

  • Creates a typed translation module.
  • ⚠️ Must provide explicit reference type for type-safe cross-locale validation.

createI18n(options)

  • locale: current locale
  • fallbackLocale: optional fallback
  • modules: translation modules

Returns an instance with:

  • t(key, params?): translate
  • setLocale(locale): change locale
  • getLocale(): get current locale
  • addModule(module): add module dynamically, returns new typed instance
  • getLocales(): get all available locales

TypeScript Tips

Always provide explicit reference type when defining modules to enable compile-time validation:

const common = defineModule('common')<typeof enJson>({
  en: enJson,
  fr: frJson
});

Without it, TypeScript may infer a loose union type and mismatched locale structures could pass unnoticed.


React Integration

Use @qzlcorp/typed-i18n-react for first-class React support:

<I18nProvider i18n={i18n}>
  <App />
</I18nProvider>

const { t } = useTranslation<I18nModules>();
t('common.hello'); // ✅ compile-time checked

Support & License

Typed-i18n is your modern, type-safe, and scalable solution for managing translations in TypeScript projects.


Links

Related Posts

qz-l.com Now Supports English, French, and Chinese! 🌎

Explore qz-l.com in multiple languages — English, French, and Chinese — powered by our open-source typed-i18n library for type-safe translations.

@qzl/typed-i18n — Our Open-Source Typed i18n Library is Gaining Traction

QZ-L.com is proud to announce our open-source typed internationalization library is being adopted by developers worldwide. Thanks for your support!

Introducing Safety Link Preview - Know Before You Click

Preview your links safely with our new Safety Link feature. See link content and risks before visiting. Safety first!

Introducing Typed-i18n, a Type-Safe Internationalization for TypeScript Apps | qz-l