Intl-T

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:

translation.ts
translation.ts

app/protected/i18n/translation.ts i18n/translation.ts app/docs/i18n/translation.ts

Each of these files can export its own translation instance:

protected/i18n/translation.ts
import { createTranslation } from "intl-t";
import en from "./locales/en.json";

export const { t: protectedT } = createTranslation({ id: "protected", locales: { en } });
docs/i18n/translation.ts
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.

On this page