01 · Problem
What is it for?
Hard-coded UI strings make language switching and missing translations difficult to test.
02 · Mental model
How should you think about it?
Separate catalog storage, current language selection and text formatting.
Resolves language catalogs and interpolates literal placeholder values. No plural-message syntax is implemented. Use Intl for dates, numbers and plural selection.
03 · Control flow
What happens in which order?
- Register catalogs by locale.
- Choose a supported locale.
- Look up an own message key.
- Replace known placeholders and preserve missing ones.
04 · Building blocks
The important contracts
TranslationManagerSelect a supported catalog locale.Translator.translate(key, variables?)Resolve a key and substitute literal values.05 · Build it yourself
Implement a small core
Start with this small independent implementation, then use the steps below to make its contract explicit.
- Register catalogs by locale.
- Choose a supported locale.
- Look up an own message key.
- Replace known placeholders and preserve missing ones.
function createI18n<C extends Record<string, Record<string, string>>>(
catalog: C,
initial: keyof C,
fallback: keyof C,
) {
let locale = initial;
return {
setLocale(next: keyof C) { locale = next; },
t(key: string, vars: Record<string, string> = {}) {
const text = catalog[locale][key] ?? catalog[fallback][key] ?? "[" + key + "]";
return text.replace(/\{([^}]+)\}/g, (_, name) => vars[name] ?? "{" + name + "}");
},
};
}06 · Verify
What should you test?
- Switch between English and German.
- Test missing keys and unknown locales.
- Interpolate repeated placeholders and values containing dollar signs.
Try it yourself
Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.
Switch between English and German.
Compare your approach
Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.
07 · Limits
Trade-offs and failure modes
- The package name remains i811n for compatibility.
- Plural rules and rich messages require an explicit extension.
- Catalog completeness belongs in build validation.
No plural-message syntax is implemented. Use Intl for dates, numbers and plural selection.
08 · Keep exploring
Source code and related ideas
yarn tsx packages/i811n/examples/basic.ts