Skip to content

Recipes

Idiomatic compositions across @mongez/localization features.

One file per locale (the simplest layout)

src/locales/en.ts
import { extend } from "@mongez/localization";
extend("en", {
home: "Home",
contact: "Contact Us",
ui: {
sidebar: "Sidebar",
menu: "Menu",
},
});
src/locales/ar.ts
import { extend } from "@mongez/localization";
extend("ar", {
home: "الرئيسية",
contact: "اتصل بنا",
ui: {
sidebar: "الشريط الجانبي",
},
});
src/config/localization.ts
import "src/locales/en";
import "src/locales/ar";
import { setLocalizationConfigurations } from "@mongez/localization";
setLocalizationConfigurations({
defaultLocaleCode: "en",
fallback: "en",
});
src/index.ts
import "src/config/localization";
// rest of the app

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: "الدفع" },
total: { en: "Total: :amount", ar: "الإجمالي: :amount" },
});

Side-effect import to register them at boot:

src/features/cart/index.ts
import "./locales";
export * from "./components";

Typed feature dictionary with transObject

src/features/auth/translations.ts
import { transObject } from "@mongez/localization";
export const t = transObject({
signIn: { en: "Sign In", ar: "تسجيل الدخول" },
signOut: { en: "Sign Out", ar: "تسجيل الخروج" },
welcomeBack: { en: "Welcome :name", ar: "مرحبا :name" },
});
src/features/auth/UserMenu.tsx
import { t } from "./translations";
function UserMenu({ user }: { user: User }) {
return (
<>
<h2>{t.p("welcomeBack", { name: user.name })}</h2>
<button>{t.signOut}</button>
</>
);
}

t.signOut is typed as string. t.p("welcomeBack", …) typechecks against the keys you declared.

Lazy-loading locales

import { extend, setCurrentLocaleCode } from "@mongez/localization";
const loaders: Record<string, () => Promise<Record<string, string>>> = {
ar: () => import("./locales/ar.json").then(m => m.default),
fr: () => import("./locales/fr.json").then(m => m.default),
};
export async function switchLocale(code: string) {
if (loaders[code]) {
const dict = await loaders[code]();
extend(code, dict);
}
setCurrentLocaleCode(code);
}

Only the locales the user actually visits are downloaded. Subsequent switches to the same locale skip the import (the bundler caches the module).

Sync the URL with the locale

import {
getCurrentLocaleCode,
localizationEvents,
setCurrentLocaleCode,
} from "@mongez/localization";
// On boot: read from URL.
const url = new URL(window.location.href);
const fromQuery = url.searchParams.get("lang");
if (fromQuery) setCurrentLocaleCode(fromQuery);
// On switch: write back to URL.
localizationEvents.onChange("localeCode", (next) => {
const url = new URL(window.location.href);
url.searchParams.set("lang", next);
window.history.replaceState({}, "", url);
});

Driving React re-renders without the React adapter

src/hooks/useCurrentLocale.ts
import { useEffect, useState } from "react";
import { getCurrentLocaleCode, localizationEvents } from "@mongez/localization";
export function useCurrentLocale() {
const [locale, setLocale] = useState(getCurrentLocaleCode);
useEffect(() => {
const sub = localizationEvents.onChange("localeCode", (next, prev) => {
if (next !== prev) setLocale(next);
});
return () => sub.unsubscribe();
}, []);
return locale;
}
function Header() {
useCurrentLocale(); // subscribe; ignore the return
return <h1>{trans("home")}</h1>;
}

Any component that calls useCurrentLocale() will re-evaluate its trans(...) calls on locale switches.

React with JSX placeholders

import { setLocalizationConfigurations } from "@mongez/localization";
import { jsxConverter } from "@mongez/react-localization";
setLocalizationConfigurations({
defaultLocaleCode: "en",
fallback: "en",
converter: jsxConverter,
});
import { trans } from "@mongez/localization";
function PriceBanner() {
return (
<p>
{trans("minimumOrder", {
amount: <strong className="text-red-500">12 USD</strong>,
})}
</p>
);
}

Or, without flipping the global converter, use transX from @mongez/react-localization for the one call.

import { localizationEvents, setCurrentLocaleCode } from "@mongez/localization";
// On boot: read.
const match = document.cookie.match(/(?:^|; )locale=([^;]+)/);
if (match) setCurrentLocaleCode(decodeURIComponent(match[1]));
// On switch: write.
localizationEvents.onChange("localeCode", (next) => {
document.cookie = `locale=${encodeURIComponent(next)};path=/;max-age=31536000`;
});

For SSR you’d parse the cookie from the request on the server, call setCurrentLocaleCode(value) before rendering, and rely on the same browser-side write on subsequent switches.

Pluralize a counter

extend("en", {
notification_zero: "No new notifications",
notification_one: "1 new notification",
notification_many: ":count new notifications",
notification_other: ":count new notifications",
});
function NotificationBadge({ count }: { count: number }) {
return <span>{trans("notification", { count })}</span>;
}

Pass count: 0, count: 1, count: 7 — each picks the right string.

Mix global and feature translations

// Global keywords for the chrome/shell.
extend("en", { logout: "Sign Out", settings: "Settings" });
// Feature-local typed dictionary.
const t = transObject({
signIn: { en: "Sign In", ar: "تسجيل الدخول" },
});
function UserMenu() {
return (
<>
<button>{t.signIn}</button>
<button>{trans("logout")}</button>
<button>{trans("settings")}</button>
</>
);
}

Both flow through the same fallback chain and locale-switching events.