hero.tagline
Runtime i18n for Angular, built for everyone .
Signals-first and SSR-safe: per-key fallback chains, a tiny ICU-lite formatter, and hydration that never mutates the DOM before Angular is stable.
features.grid
What you get out of the box
Fallback chains
Configure fallbacks as an ordered list and lookups run active language, then each fallback in turn, then the default language. Missing keys reach onMissingKey after a single dev-mode warning.
ICU-lite formatting
A small, dependency-free formatter handles interpolation and plural blocks (one, other, and exact matches like =0). It covers common cases without a full ICU MessageFormat implementation.
Type-safe keys
Declare a catalog schema once via module augmentation and t(), the pipe, and the RxJS compat service all narrow to valid keys and required params. Without a declaration, t() still accepts a plain string.
TransferState SSR
provideRuntimeI18nSsr() seeds the same TransferState keys the client reads on boot, so the browser reuses the server-rendered catalog instead of fetching it again. No catalog fetch runs before Angular is stable.
Catalog caching modes
Choose none to keep only the active fallback chain in memory, memory to cache every catalog for the session, or storage to hydrate from localStorage and revalidate in the background. Storage access never runs on the server.
DevTools bridge
In dev mode, the service posts structured window.postMessage events for state, translations, and missing keys, so extensions and custom panels can observe language changes as they happen. Stripped out of production builds.
hero.code-showcase
Set it up once, translate anywhere
provideRuntimeI18n() wires the service, pipe, and locale data into your app config. Templates then read translations through the i18n pipe.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRuntimeI18n } from '@ngx-runtime-i18n/angular';
export const appConfig: ApplicationConfig = {
providers: [
provideRuntimeI18n(
{
defaultLang: 'en',
supported: ['en', 'hi', 'de'],
fallbacks: ['de'],
fetchCatalog: (lang, signal) =>
fetch(`/i18n/${lang}.json`, { signal }).then((r) => {
if (!r.ok) throw new Error(`Failed to load catalog: ${lang}`);
return r.json();
}),
onMissingKey: (key) => key,
},
{
localeLoaders: {
en: () => import('@angular/common/locales/global/en'),
hi: () => import('@angular/common/locales/global/hi'),
de: () => import('@angular/common/locales/global/de'),
},
options: {
autoDetect: true,
storageKey: '@ngx-runtime-i18n:lang',
cacheMode: 'storage',
cacheKeyPrefix: '@ngx-runtime-i18n:catalog:',
preferNavigatorBase: true,
},
}
),
],
};<!-- Template -->
<h1>{{ 'hello.user' | i18n:{ name: username } }}</h1>
<p>{{ 'cart.items' | i18n:{ count: items().length } }}</p>
<small>Fallback → {{ 'legacy.title' | i18n }}</small>packages.matrix
Six packages, one release train
Core and Angular ship the runtime. PrimeNG, Material, the CLI, and the ng add schematic are optional and versioned together.
compare.teaser
How this compares
See how fallback chains, caching, and SSR support compare against ngx-translate, transloco, and Angular's built-in i18n on the compare page.