Minimalistic & type-safe i18n / internationalization library. Even this readme is longer than the library itself.
Type-safety of this system is achieved by using TypeScript to write your localization strings, rather than JSON. This leverages TypeScript's type system to catch localization mismatches & inconsistencies at compile-time, provides better IntelliSense, and supports more complex logic for interpolation.
mini18n is designed for fast prototyping and small projects. If your project has larger needs, such as pluralization, decimal or currency normalization, worded counting, or accommodating a dedicated team of localizers unfamiliar with code, this project will not satisfy you. I can, however, recommend i18next.
Anecdote: Stupid lil' me didn't know the proper difference between i18n and l10n, so I renamed & republished the library. It was formerly known to some as @kiruse/l10n. If you've come to this repository expecting l10n, that's why. It's the same project. :)
Install using npm:
npm i --save @kiruse/mini18n
or via yarn:
yarn add @kiruse/mini18n
Registry must be initialized & enriched with localization data. When initializing, it's advised to explicitly specify generic parameters:
import * as i18n from '@kiruse/mini18n';
import en from './lang/en';
import fr from './lang/fr';
// assume: typeof en ===
type Strings = {
foo: string;
bar: {
baz: string;
quux: string;
}
}
const strings = i18n.init<Strings>()
.addLocales({
en,
'en-US',
fr,
'fr-FR',
})
.strings;
console.log(strings().bar.baz);
console.log(strings('fr').bar.baz);
To further simplify, I generally assume one of my localizations is the canonical format:
// file: lang/en-us.ts
const strings = {
foo: 'the foo',
bar: {
baz: 'the baz',
quux: 'the quux',
},
};
export default strings;
// file: lang/fr-fr.ts
import enUS from './en-us';
const strings: typeof enUS = {
foo: 'le foo',
bar: {
baz: 'la baz',
quux: 'le quux',
},
};
export default strings;
With this approach, TypeScript IntelliSense will immediately complain if the contents of the French localization strings differ from those of the English localization either in type or by adding and/or removing properties.
It is common issue to wish to interpolate values into the localization
string. Other libraries use indexed or named placeholders indicated
directly within the string through, for example, {variable}
.
mini18n also supports this type-safely. This is accomplished
through template literals tagged by mini18n.interpolated
like such:
// file: lang/en-us.ts
import { interpolated } from '@kiruse/mini18n';
const strings = {
hello: interpolated`Hello, ${'firstName'} ${'lastName'}, age ${'age'}!`,
};
export default strings;
// file: foo.ts
import strings from './lang';
// assuming file: 'lang/index.ts' has been changed appropriately
console.log(strings.hello({
firstName: 'John',
lastName: 'Doe',
age: 42,
}));
// => Hello, John Doe, age 42!
The interpolated
function takes your string literal and produces
an object type for its sole argument based on the interpolated strings.
This has three implications:
- Placeholder names must be string literals - this also implies placeholders cannot be nested within objects,
- TypeScript IntelliSense will inform you of which placeholders exist, and
- TypeScript will complain if a placeholder is omitted or added on top.
- Default values for
interpolated
strings. - Lazy-loading localizations.
- More advanced features, like pluralization?