i18n-typed
v0.2.0
Published
[](https://www.npmjs.com/package/i18n-typed) [](https://opensource.org/licenses/MIT)
Maintainers
Readme
i18n-typed
Type-safe i18n string interpolation for TypeScript. Automatically infers parameter types from your translation templates at compile time — no codegen, no runtime overhead.
Installation
npm i i18n-typedOr using bun:
bun add i18n-typedFeatures
- Zero-dependency — no runtime dependencies, just TypeScript.
- Full type inference — parameter names and types are inferred directly from the template string.
- Simple placeholders —
{name}acceptsstring | number. - ICU-style plurals —
{count, plural, zero {...} one {...} other {...}}requiresnumber. - Tiny footprint — a single function, under 1 KB minified.
Usage
Simple Placeholders
import { toInferredTypedFn } from 'i18n-typed';
const greet = toInferredTypedFn('Hello, {name}! You have {count} new messages.');
// TypeScript infers: (params: { name: string | number; count: string | number }) => string
greet({ name: 'Alice', count: 5 });
// => "Hello, Alice! You have 5 new messages."ICU-Style Plurals
const itemsLabel = toInferredTypedFn(
'{count, plural, zero {No items} one {1 item} other {# items}}'
);
// TypeScript infers: (params: { count: number }) => string
itemsLabel({ count: 0 }); // => "No items"
itemsLabel({ count: 1 }); // => "1 item"
itemsLabel({ count: 42 }); // => "42 items"Combining Both
const summary = toInferredTypedFn(
'{user} has {count, plural, zero {no followers} one {1 follower} other {# followers}}'
);
// TypeScript infers: (params: { user: string | number; count: number }) => string
summary({ user: 'Alice', count: 0 });
// => "Alice has no followers"
summary({ user: 'Bob', count: 1280 });
// => "Bob has 1280 followers"Using with Translation Objects
A common pattern is to compile all your translation strings at once:
import { toInferredTypedFn } from 'i18n-typed';
const en = {
greeting: toInferredTypedFn('Welcome back, {name}!'),
itemCount: toInferredTypedFn(
'You have {count, plural, zero {no items} one {1 item} other {# items}} in your cart'
),
transfer: toInferredTypedFn('{sender} sent {amount} to {recipient}'),
} as const;
// All parameters are fully typed
en.greeting({ name: 'Alice' });
en.itemCount({ count: 3 });
en.transfer({ sender: 'Alice', amount: '$50', recipient: 'Bob' });API
toInferredTypedFn<T>(template: T)
Creates a type-safe interpolation function from a template string.
Parameters:
template— A string containing{placeholder}and/or{key, plural, zero {...} one {...} other {...}}patterns.
Returns: (params: InferredParams<T>) => string
createProxyWithFallback<T>(target: T, fallback: T)
Creates a Proxy that gracefully falls back to another object if a property is missing in the target. This is useful for providing default translations or handling missing keys.
import { createProxyWithFallback } from 'i18n-typed';
const en = { hello: 'Hello' };
const es = { hello: 'Hola', bye: 'Adiós' };
const i18n = createProxyWithFallback(en, es);
console.log(i18n.hello); // "Hello" (from en)
console.log(i18n.bye); // "Adiós" (from es fallback)InferredParams<S>
A utility type that extracts the parameter types from a template string. You can use this independently for type-level operations:
import type { InferredParams } from 'i18n-typed';
type Params = InferredParams<'Hello, {name}! You have {count} items.'>;
// => { name: string | number; count: string | number }Combining Proxies and Interpolation
By combining createProxyWithFallback and toInferredTypedFn, you can build a complete, type-safe i18n solution with zero runtime dependencies:
const en = {
items: toInferredTypedFn('{count, plural, zero {No items} one {1 item} other {# items}}')
};
const es = {
items: toInferredTypedFn('{count, plural, zero {Sin items} one {1 item} other {# items}}')
};
// Language switching with a fallback
const t = createProxyWithFallback(en, es);
console.log(t.items({ count: 5 })); // "5 items"License
MIT
