@better-intl/next
v0.11.1
Published
Next.js App Router integration for better-intl
Readme
@better-intl/next
Next.js App Router integration for better-intl.
Install
npm install @better-intl/next @better-intl/react @better-intl/core @better-intl/compilerQuick Start
1. Configure
// better-intl.config.json
{
"defaultLocale": "en",
"locales": ["en", "pt-BR", "es"],
"catalogs": "./locales/{locale}.json",
"include": ["app/**/*.tsx"]
}2. Plugin (next.config.js)
import { withBetterIntl } from "@better-intl/next/plugin";
export default withBetterIntl({
locales: ["en", "pt-BR", "es"],
defaultLocale: "en",
})({
// your next config
});3. Middleware
// middleware.ts
import { createIntlMiddleware } from "@better-intl/next/middleware";
export default createIntlMiddleware({
locales: ["en", "pt-BR", "es"],
defaultLocale: "en",
});
export const config = { matcher: ["/((?!api|_next|.*\\..*).*)"] };4. Layout
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from "@better-intl/next/client";
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
const messages = (await import(`../../locales/${locale}.json`)).default;
return (
<html lang={locale}>
<body>
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}5. Write normal JSX
// app/[locale]/page.tsx
export default function Page() {
return (
<section>
<h1>Hello world</h1>
<p>The best SaaS platform</p>
<button type="button">Get started</button>
</section>
);
}That's it. No t() calls, no hooks, no imports. The Babel plugin transforms your JSX automatically at build time:
// What the compiler generates behind the scenes:
import { useTranslation } from "@better-intl/react";
export default function Page() {
const { t: __t } = useTranslation();
return (
<section>
<h1>{__t("0rr2b7c", undefined, "Hello world")}</h1>
<p>{__t("1di71qw", undefined, "The best SaaS platform")}</p>
<button type="button">{__t("1gvrrqk", undefined, "Get started")}</button>
</section>
);
}Enable it via Babel:
// babel.config.js
module.exports = {
plugins: [["@better-intl/compiler/babel", { mode: "auto" }]],
};Or for zero-runtime (inlines translations at build time):
module.exports = {
plugins: [
["@better-intl/compiler/babel", {
locale: "pt-BR",
messages: require("./locales/pt-BR.json"),
}],
],
};Server Components (RSC)
For React Server Components where hooks aren't available, use getTranslator:
// app/[locale]/page.tsx
import { getTranslator } from "@better-intl/next/server";
export default async function Page({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
const t = await getTranslator(locale, () =>
import(`../../locales/${locale}.json`)
);
return <h1>{t("greeting")}</h1>;
}Metadata
import { getMetadataTranslator } from "@better-intl/next/server";
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
const t = await getMetadataTranslator(locale, () =>
import(`../../locales/${locale}.json`)
);
return { title: t("page.title") };
}API
| Export | Entry point | Description |
|--------|-------------|-------------|
| NextIntlClientProvider | @better-intl/next/client | Client provider for layouts |
| createIntlMiddleware(config) | @better-intl/next/middleware | Locale detection and routing |
| getTranslator(locale, loader) | @better-intl/next/server | Server-side t() for RSC |
| getMetadataTranslator(locale, loader) | @better-intl/next/server | For generateMetadata |
| getMessages(loader) | @better-intl/next/server | Load messages for a locale |
| withBetterIntl(config) | @better-intl/next/plugin | next.config.js plugin |
License
MIT
