lexora
v1.5.2
Published
A lightweight TypeScript helper for managing multi-language strings with optional grammatical support.
Maintainers
Readme
Lexora 🌍
What is Lexora
Lexora is a lightweight, pipeline-based translation and formatting library for TypeScript. It is designed for applications that need more than simple key-value translations, but still want to stay small, explicit and easy to reason about.
Lexora supports:
- language based string resources
- nested placeholders
- translation metadata
- value pipelines
- plural/form selection
- grammatical articles
- number, date, time, currency and list formatting
- watchable reactive strings
- composable grammar-aware pipelines
Installation
npm install lexoraUsage
Basic
import { LexoraContext } from "lexora";
const ctx = LexoraContext.createWithDefaults();
ctx.loadMultipleStringResourceTranslations({
house: {
en: "house",
de: "Haus",
},
greeting: {
en: "Hello {{house}}",
de: "Hallo {{house}}",
},
});
ctx.language = "de";
ctx.get("greeting");
// "Hallo Haus"String Resources
A string resource can be either a plain string:
house: {
en: "house",
de: "Haus",
}or a tuple with metadata:
house: {
en: "house",
de: ["Haus", { gender: "neuter" }],
}Metadata is useful for language-specific pipeline functions, for example German articles.
Templates
Templates can reference other resources with {{key}}.
ctx.translate("Hello {{name}}", {
name: "Luca",
});Output:
Hello LucaTemplates can also be stored as resources:
ctx.loadMultipleStringResourceTranslations({
welcome: {
en: "Welcome {{name}}",
de: "Willkommen {{name}}",
},
});Pipelines
Pipelines are written with ->.
ctx.translate("{{house->upper}}");Example:
HOUSEPipelines can be chained:
ctx.translate("{{house->prefix('My ')->capitalize}}");Output:
My houseBuilt-in Pipelines
Lexora includes common default pipelines:
upper
lower
trim
capitalize
prefix
suffix
number
currency
date
time
boolean
list
form
switchLanguage packs can add additional pipelines, such as articles.
Plural and Forms
Lexora supports forms for grammatical variants like singular and plural.
point: {
en: [{ _: "point", other: "points" }],
de: [{ _: "Punkt", other: "Punkte" }, { gender: "masculine" }],
}_ is the default form. Use the form pipeline to select the correct form:
ctx.translate("{{count}} {{point->form(count)}}", {
count: 1,
});Output:
1 pointctx.translate("{{count}} {{point->form(count)}}", {
count: 5,
});Output:
5 pointsYou can also select a form explicitly:
ctx.translate("{{point->form(:other)}}");German Articles
With metadata and language packs, Lexora can apply grammatical articles.
house: {
de: ["Haus", { gender: "neuter" }],
}ctx.language = "de";
ctx.translate("{{house->article(nominative)}}");Output:
das HausForms also work together with articles:
ctx.translate("{{point->form(count)->article(nominative)}}", {
count: 5,
});Output:
die PunkteSwitch Pipeline
Use switch for semantic choices, for example gender-based labels.
ctx.loadMultipleStringResourceTranslations({
maleUser: {
en: "user",
de: "Benutzer",
},
femaleUser: {
en: "user",
de: "Benutzerin",
},
});ctx.translate(
"{{gender->switch('male:{{maleUser}}','female:{{femaleUser}}')}}",
{
gender: "female",
}
);Output: Output:
BenutzerinSwitch results can contain nested placeholders and pipelines:
ctx.translate(
"{{gender->switch('male:{{maleUser->upper}}','female:{{femaleUser->upper}}')}}",
{
gender: "female",
}
);Output:
BENUTZERINFormatting
Numbers:
ctx.translate("{{value->number}}", {
value: 1234.56,
});Dates:
ctx.translate("{{value->date(long)}}", {
value: new Date(),
});Currency:
ctx.translate("{{value->currency(USD)}}", {
value: 1234.56,
});Lists:
ctx.translate("{{items->list}}", {
items: ["apple", "banana", "cherry"],
});Call Context
Values passed in the call context override resources.
ctx.translate("{{house}}", {
house: {
en: "villa",
de: "Villa",
},
});Context values can also include metadata:
ctx.translate("{{house->article(nominative)}}", {
house: ["Villa", { gender: "feminine" }],
});Output:
die VillaWatchable Strings
Lexora can create reactive strings that update when the language changes.
import { LexoraContext } from "lexora";
const ctx = LexoraContext.createWithDefaults();
ctx.loadMultipleStringResourceTranslations({
greeting: {
en: "Hello {{user}}",
de: "Hallo {{user}}",
},
});
ctx.language = "en";
const watch = ctx.translateWatch("{{greeting}}", {
user: "Luca",
});
console.log(watch.value);
// -> Hello Luca
watch.on("update", (value) => {
console.log("Updated:", value);
});
ctx.language = "de";
// console:
// Updated: Hallo Luca
console.log(watch.value);
// -> Hallo LucaStrict and Lenient Mode
Lexora can either throw errors or gracefully fall back.
const ctx = LexoraContext.createWithDefaults({
ignoreMissingKeys: false,
skipFailedPipelineFunctions: false,
ignoreMissingPipelineFunctions: false,
});Lenient mode:
const ctx = LexoraContext.createWithDefaults({
ignoreMissingKeys: true,
defaultValueForMissingKeys: "?",
skipFailedPipelineFunctions: true,
ignoreMissingPipelineFunctions: true,
});