mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmWeb Frontendupdated 08 Aug 2026

next-intl

next-intl is an internationalization toolkit built specifically for Next.js. It renders ICU messages with interpolation, plurals, selects, and rich React content; formats dates, numbers, lists, and relative times; loads request-scoped messages in Server Components; and provides locale-aware routing, middleware, and navigation wrappers. Version 4.13.5 supports the App Router and Pages Router across Next.js 12 through 16, with current setup guidance centered on Next.js 16 proxy files and next/root-params.

Verdict

The strongest integrated choice for serious i18n in a Next.js App Router project, especially when routing and Server Components matter. It is excessive for simple formatting, and its Next-version-sensitive setup plus experimental extraction deserve deliberate adoption.

API stability4/5Translation hooks, ICU messages, request configuration, and routing definitions form a coherent version 4 API, while the package supports five Next.js major lines and React 16.8 through 19. Still, setup follows Next.js platform changes: middleware.ts became proxy.ts in Next 16, current static rendering guidance prefers next/root-params, and setRequestLocale is now labeled legacy. Experimental extraction APIs are intentionally not stability promises.
Docs5/5The dedicated site has separate App Router and Pages Router starts, runnable examples, routing configuration, middleware composition, ICU syntax, formatting, TypeScript augmentation, static rendering, and troubleshooting. It calls out Next-version differences, matcher exclusions, locale-cookie behavior, client message payload decisions, and experimental features. The breadth can feel like a course, but the difficult integration details are documented rather than implied.
Maintenance5/5Version 4.13.5 was published August 4, 2026, the repository was pushed August 7, 2026, and it is not archived. GitHub reports 48 open issues and pull requests combined, a reasonable active queue for a framework integration with broad surface area. Same-week release and repository activity, Next.js 16.3 guidance, React 19 support, and current extractor work show maintenance tracking the platform closely.
Ecosystem5/5npm records 4,873,339 downloads in the latest week and GitHub reports 4,336 stars. The package integrates with Next.js routing, Server Components, static rendering, ICU, TypeScript augmentation, local JSON, remote message sources, and translation-management workflows. Next 12 through 16 and React 16.8 through 19 peer ranges give it unusually broad project coverage, though its ecosystem is intentionally confined to Next.js.

Use it if

  • You are building a Next.js application and want translations, formatting, Server Components, and locale routing designed as one system
  • You need ICU plural and select rules instead of hand-written singular versus plural conditionals
  • You want localized pathnames, locale negotiation, alternate-language links, and type-aware navigation wrappers
  • You keep a canonical locale catalog and want TypeScript autocomplete for locale names, message keys, and format names
Skip it if

Setup reality

The first translated string requires more than npm install. For the App Router you normally create messages for each locale, an i18n/request.ts file using getRequestConfig, add createNextIntlPlugin to next.config, and wrap client consumers in NextIntlClientProvider. Locale-prefixed routing adds i18n/routing.ts, a top-level app/[locale] segment, a proxy.ts file using createMiddleware, and navigation wrappers created from the same routing object. Next.js called proxy.ts middleware.ts before version 16, so copy the example for your installed Next major. The current recommended routing setup reads next/root-params, which is available by default in Next.js 16.3 and later; earlier compatible versions need experimental.rootParams or the legacy setRequestLocale path. Static generation still needs generateStaticParams for the locale segment. The proxy matcher must exclude API routes, Next internals, monitoring endpoints, and static files, while explicitly including valid dotted paths such as user names with periods. Client Components only see messages passed through NextIntlClientProvider, so sending the entire catalog can inflate the response; pass a selected subtree when practical. ICU apostrophe escaping surprises newcomers, and translation keys are type-safe only after optional module augmentation. Message catalogs, translator workflow, fallback policy, and CMS synchronization remain your responsibility. The package is ESM-first but exposes a CommonJS plugin entry, and its install includes @swc/core and @parcel/watcher platform packages because extraction and build integration live in the main distribution.

Patterns

Load messages for each requestconfigure-request

// src/i18n/request.ts
import { getRequestConfig } from 'next-intl/server';

export default getRequestConfig(async () => {
  const locale = 'en';
  return {
    locale,
    messages: (await import(`../../messages/${locale}.json`)).default,
  };
});

The conventional path is src/i18n/request.ts or i18n/request.ts; pass a custom path to the plugin if you move it.

Connect request configuration to Next.jsenable-next-plugin

// next.config.ts
import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';

const withNextIntl = createNextIntlPlugin();
const nextConfig: NextConfig = {};

export default withNextIntl(nextConfig);

The plugin locates the conventional request file; supply its relative path to createNextIntlPlugin when using another location.

Expose request configuration to Client Componentsprovide-client-messages

import { NextIntlClientProvider } from 'next-intl';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang='en'>
      <body>
        <NextIntlClientProvider>{children}</NextIntlClientProvider>
      </body>
    </html>
  );
}

Client Components require the provider; consider passing a selected messages object instead of shipping every namespace.

Interpolate and pluralize a translationrender-icu-message

// messages/en.json
// { "Profile": { "followers": "{count, plural, =0 {No followers} =1 {One follower} other {# followers}}" } }

import { useTranslations } from 'next-intl';

function Followers({ count }: { count: number }) {
  const t = useTranslations('Profile');
  return <p>{t('followers', { count })}</p>;
}

ICU selects the plural branch using locale rules; # expands to the formatted count inside a plural clause.

Map trusted ICU tags to React elementsrender-rich-text

// messages/en.json: { "About": { "cta": "Read the <link>documentation</link>." } }
const t = useTranslations('About');

return t.rich('cta', {
  link: (chunks) => <a href='/docs'>{chunks}</a>,
});

Use t.rich for React elements; plain t returns a string and t.raw bypasses message processing.

Translate inside an async Server Componenttranslate-server-component

import { getTranslations } from 'next-intl/server';

export default async function Page() {
  const t = await getTranslations('HomePage');
  return <h1>{t('title')}</h1>;
}

Async components use the awaitable server API; hooks cannot be called after an await in an async component.

Declare supported and default localesdefine-locale-routing

// src/i18n/routing.ts
import { defineRouting } from 'next-intl/routing';

export const routing = defineRouting({
  locales: ['en', 'de'] as const,
  defaultLocale: 'en',
  localePrefix: 'as-needed',
});

Keep this object shared by the proxy and navigation wrappers so redirects and generated links follow one policy.

Negotiate and rewrite locale routesconfigure-locale-proxy

// src/proxy.ts for Next.js 16
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: '/((?!api|trpc|_next|_vercel|.*\..*).*)',
};

Next.js used middleware.ts before version 16; the matcher excludes dotted paths, so add explicit entries when dots are valid route data.

Create locale-aware navigation wrapperscreate-localized-navigation

// src/i18n/navigation.ts
import { createNavigation } from 'next-intl/navigation';
import { routing } from './routing';

export const { Link, redirect, usePathname, useRouter, getPathname } =
  createNavigation(routing);

// In a component:
// <Link href='/about' locale='de'>Deutsch</Link>

Import these generated wrappers instead of next/link and next/navigation when the destination should follow locale routing.

Reject unsupported locale segmentsvalidate-route-locale

import { hasLocale, NextIntlClientProvider } from 'next-intl';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';

export default async function LocaleLayout({ children, params }) {
  const { locale } = await params;
  if (!hasLocale(routing.locales, locale)) notFound();
  return <NextIntlClientProvider>{children}</NextIntlClientProvider>;
}

Validate a dynamic locale before using it for message imports or request configuration.

Pre-render each configured localegenerate-locale-pages

import { routing } from '@/i18n/routing';

export function generateStaticParams() {
  return routing.locales.map((locale) => ({ locale }));
}

A top-level [locale] dynamic segment needs generateStaticParams for the locales you want rendered at build time.

Type locale names and message keysaugment-message-types

// global.ts
import { routing } from '@/i18n/routing';
import messages from './messages/en.json';

declare module 'next-intl' {
  interface AppConfig {
    Locale: (typeof routing.locales)[number];
    Messages: typeof messages;
  }
}

Use a canonical locale catalog for the Messages type; very large catalogs can add TypeScript and editor work.

Alternatives

PackageRegistryPick it when
next-i18nextnpmChoose it when an existing i18next catalog, plugin, or translation-service workflow must remain the center of the app
react-intlnpmChoose it for framework-neutral React ICU messages and formatting without next-intl's routing layer
@lingui/reactnpmChoose it when compile-time message extraction and a broader React ecosystem matter more than Next-specific routing