Examples & Strategies
Practical patterns and edge cases
Locales metadata
You can include metadata in your translation files to help you with localization and translation management.
For example
{
"meta": {
// It is a normal node
"code": "en",
"name": "English",
"dir": "ltr",
},
// ...
}And then you can access it from your translations.
import { Translation } from "@/i18n/translation";
import { match } from "intl-t/utils";
interface Props {
params: Promise<{ locale: string }>;
children: React.ReactNode;
}
export default async function RootLayout({ children, params }: Props) {
let { locale } = await params;
locale = match(locale, Translation.allowedLocales);
const { meta } = await t[locale];
return (
<html lang={locale} dir={meta.dir}>
<body>
<Translation>{children}</Translation>
</body>
</html>
);
}Fallbacks
When a translation node is executed and the translation is not found, it will fall back to the input text with injected variables. This could be useful when you receive a string from an external API or server. You might get either a translation key or the direct text.
t("Please try again"); // falls back to "Please try again"
t("messages.try_again"); // outputs the translation
t("Please try again, {name}", { name: "John" }); // falls back to "Please try again, John"
// these fallbacks are also type safe
typeof t("Please try again, {name}"); // `Please try again, ${string}`String Methods
<motion.p initial="hidden" animate="visible" transition={{ staggerChildren: 0.04 }}>
{t.description.split(" ").map((word, index, words) => {
return (
<motion.span key={word + index} transition={transition} variants={variants}>
{word + " "}
</motion.span>
);
})}
</motion.p>Template Strings
async function Greeting() {
"use server";
return t`greeting`({ name: await getName() });
}Namespaces
Namespaces are first-class: create one translation per domain, give each an id, and providers resolve the right instance
across the server/client boundary while nested providers inherit the active locale — the full pattern is documented in
Next.js → Namespaces. What follows is the file organization that scales with
it:
app/protected/i18n/translation.ts
i18n/translation.ts
app/docs/i18n/translation.ts
Each of these files can export its own translation instance:
import { createTranslation } from "intl-t";
import en from "./locales/en.json";
export const { t: protectedT } = createTranslation({ id: "protected", locales: { en } });import { createTranslation } from "intl-t";
import en from "./locales/en.json";
export const { t: docsT } = createTranslation({ id: "docs", locales: { en } });You can then import and use the appropriate translation object in each part of your app:
import { docsT } from "../../docs/i18n/translation";
import { protectedT } from "../i18n/translation";
protectedT("dashboard.title");
docsT("guide.intro");Each instance hydrates through its own provider, serializes only its own tree, and can pick its own loading strategy — preload one domain at boot, lazy-load another per visit.