Skip to content

Translations

The translation dictionary is a TranslationsList = { [localeCode: string]: Keywords }, where Keywords is a recursive map of strings or nested keyword groups.

Shape

type TranslationsList = { [localeCode: string]: Keywords };
type Keywords = { [key: string]: string | Keywords };
const translations: TranslationsList = {
en: {
home: "Home",
ui: {
sidebar: "Sidebar",
menu: "Menu",
},
},
ar: {
home: "الرئيسية",
ui: {
sidebar: "الشريط الجانبي",
},
},
};

Reads use dot-notation: trans("ui.sidebar") reads translations[currentLocale].ui.sidebar.

extend(localeCode, keywords)

The idiomatic registration call. Multiple calls for the same locale merge — keep one file per locale and re-extend per feature:

src/locales/en.ts
import { extend } from "@mongez/localization";
extend("en", { home: "Home" });
extend("en", { contact: "Contact Us" });
extend("en", {
ui: { sidebar: "Sidebar" },
});

The merge is recursive (provided by @mongez/reinforcementsmerge), so nested groups accumulate too.

groupedTranslations(groupKey?, dict)

Declares translations keyword-first (every keyword carries its locale map), the inverse of extend:

import { groupedTranslations } from "@mongez/localization";
groupedTranslations({
home: { en: "Home", ar: "الرئيسية" },
contact: { en: "Contact Us", ar: "اتصل بنا" },
});

Optionally namespace a flat dictionary under a prefix:

groupedTranslations("store", {
orders: { en: "Orders", ar: "الطلبات" },
products: { en: "Products", ar: "المنتجات" },
});
// Reads as: trans("store.orders"), trans("store.products")

Nested groups work too — the locale map is whichever leaf object has only string leaves:

groupedTranslations({
general: {
home: { en: "Home", ar: "الرئيسية" },
},
});
// trans("general.home")

Internally, groupedTranslations flattens the input, splits each path on ., treats the last segment as the locale code, and writes the leaf into translationsList[localeCode][keyword]. So groupedTranslations and extend write into the same underlying map — you can mix them freely.

setTranslationsList / getTranslationsList / getKeywordsListOf

setTranslationsList(list: TranslationsList): void // full replace
getTranslationsList(): TranslationsList // live reference
getKeywordsListOf(locale: string): Keywords | null

setTranslationsList replaces the entire map — useful when you load translations from a single JSON file at boot:

setTranslationsList(await fetch("/api/i18n").then(r => r.json()));

Alternatively, set translations via the configuration object:

setLocalizationConfigurations({
translations: { en: { home: "Home" }, ar: { home: "الرئيسية" } },
});

setLocalizationConfigurations({ translations }) calls setTranslationsList internally — same effect.

Pattern: one file per locale

src/locales/en.ts
import { extend } from "@mongez/localization";
extend("en", { home: "Home", contact: "Contact" });
// src/locales/ar.ts
import { extend } from "@mongez/localization";
extend("ar", { home: "الرئيسية", contact: "اتصل" });
// src/config/localization.ts
import "src/locales/en";
import "src/locales/ar";
import { setLocalizationConfigurations } from "@mongez/localization";
setLocalizationConfigurations({
defaultLocaleCode: "en",
fallback: "en",
});

Pattern: one file per feature (keyword-first)

src/features/cart/locales.ts
import { groupedTranslations } from "@mongez/localization";
groupedTranslations("cart", {
empty: { en: "Your cart is empty", ar: "سلتك فارغة" },
checkout: { en: "Checkout", ar: "الدفع" },
});
// somewhere in the cart feature
trans("cart.empty");
trans("cart.checkout");

The downside is that lookups via the dynamic string (trans("cart.empty")) don’t get keyword-scope type checking. If you want that, see translating.md for transObject.