Getting Started
li18n compiles your JSON locale files into TypeScript modules. Each message key becomes a typed function. No runtime parsing, no stringly-typed keys.
import { m } from "./src/i18n";
m.greeting({ name: "Alice" }); // "Hello Alice!" (active locale)
m.greeting({ name: "Alice" }, "de"); // "Hallo Alice!" (locale override)
m.userStatus({ isOnline: true }); // "Online"
m.itemCount({ count: 3 }); // "3 items"Installation
bun add -D @the-lukez/li18nnpm install -D @the-lukez/li18npnpm add -D @the-lukez/li18nSetup
1. Create a config file
Add li18n.config.json to your project root:
{
"$schema": "./node_modules/@the-lukez/li18n/li18n.schema.json",
"locales": ["en", "de"],
"defaultLocale": "en",
"messagesDir": "./messages",
"outputDir": "./src/i18n"
}The $schema field enables IDE autocomplete and inline validation.
2. Create your locale files
Add one JSON file per locale in messagesDir:
messages/
├── en.json
└── de.jsonen.json:
{
"greeting": "Hello {name}!",
"farewell": "Goodbye!"
}de.json:
{
"greeting": "Hallo {name}!",
"farewell": "Auf Wiedersehen!"
}3. Build
li18n buildThis generates typed TypeScript files in outputDir.
4. Import and use
IMPORTANT
li18n is not designed for webservers with per-request state - while it may work, it is not supported officially - please make open issues on GitHub if you got it working.
If you want localization for express, hono or similar, use another library. Instead, li18n is ideal for functional programming styles - like Discord bots - where you can pass the locale explicitly or use withLocale() to scope it.
import { m, withLocale } from "./src/i18n";
const handler = withLocale(
async (req: Request) => m.greeting({ name: "Alice" }),
(req) => req.headers.get("Accept-Language")?.split(",")[0],
);Or pass the locale directly to any message function:
m.greeting({ name: "Alice" }, "de"); // "Hallo Alice!"Troubleshooting
"No overload matches this call"
This error usually occurs if you pass variables with the wrong type, or forget to pass required variables.
For example, if you have a message with count variable, which is a string in the message file:
{
"itemCount": "{count} items"
}Then you must pass count as a string:
m.itemCount({ count: "3" }); // ✅
m.itemCount({ count: 3 }); // ❌ No overload matches this callcount to be a number, you can use the typed variable syntax in your message file:{
"itemCount": "{count:number} items"
}Next steps
- Learn about the full Message Format - conditionals and pluralization
- See all available CLI commands
- Explore the Runtime API for locale management