A simple, type-safe i18n library for TypeScript. Define your messages in JSON, get fully-typed TypeScript functions out. No runtime parsing, no stringly-typed keys.
→ Full documentation at li18n.thelukez.com
Important
This is a beta project. As of now, it should work but don't be surprised if you encounter bugs or rough edges. Feedback and contributions are very welcome!
- Fully typed — every message key becomes a typed TypeScript function; variables and conditionals are reflected in the signature
- Conditionals & pluralization — boolean, string, and number conditionals with case-based dispatch
- Async-safe locale scoping — built-in
AsyncLocalStoragesupport viawithLocale()for per-request isolation
bun add -D @the-lukez/li18n
# or: npm install -D @the-lukez/li18n// li18n.config.json
{
"$schema": "./node_modules/@the-lukez/li18n/li18n.schema.json",
"locales": ["en", "de"],
"defaultLocale": "en",
"messagesDir": "./messages",
"outputDir": "./src/i18n"
}// messages/en.json
{
"greeting": "Hello {name}!",
"itemCount": [
{
"var": { "num": "count" },
"cases": {
"=== 1": "1 item",
"else": "{count} items"
}
}
]
}li18n buildimport { m, withLocale } from "./src/i18n";
const handler = withLocale(
async (interaction) => {
interaction.reply(m.greeting({ name: interaction.user.username }));
},
(interaction) => interaction.locale,
);Or pass the locale directly to any message function:
m.greeting({ name: "Alice" }, "de"); // → "Hallo Alice!"
m.itemCount({ count: 1 }, "de"); // → "1 Element"
m.itemCount({ count: 5 }, "de"); // → "5 Elemente"See the docs for the full message format, CLI reference, and runtime API.
MIT