NetcaneTechnologies

Next.js

Internationalization in Next.js: Routing, Metadata, and SEO

Locale-based routing, translated metadata, and hreflang so internationalized Next.js sites are built and indexed correctly.

Yasir Haleem2 min read

Internationalization in the App Router involves routing (e.g. /en/blog, /es/blog), translated content and metadata, and signals for search engines. Here’s a practical approach.

Locale in the URL

Two common patterns: prefix the locale (e.g. /en/about, /es/about) or use a domain/subdomain (e.g. en.example.com, es.example.com). Prefixing keeps one codebase and one deployment; use middleware or a route group to read the locale and serve the right content. Store the current locale in a cookie or accept-language so you can redirect or render accordingly. Next.js doesn’t ship a built-in i18n router in the App Router, so you implement the segment (e.g. [locale]) and middleware yourself.

// app/[locale]/layout.tsx
export default function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  // Validate locale; redirect if invalid
  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  );
}

Use middleware to redirect / to a default locale (e.g. /en) or to set the locale from cookie/header and rewrite to the right segment.

Translating metadata

Each page’s title and description should be in the right language. Use generateMetadata and pass the locale (from params or context) so you can look up translated strings. Export a small dictionary or use a CMS for per-locale metadata. Set metadata.alternates.canonical to the current locale URL and add alternates.languages so search engines know about other language versions.

export async function generateMetadata({
  params,
}: {
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  const t = await getTranslations(locale, "metadata");
  const base = process.env.NEXT_PUBLIC_SITE_URL;
  return {
    title: t("home.title"),
    description: t("home.description"),
    alternates: {
      canonical: `${base}/${locale}`,
      languages: {
        en: `${base}/en`,
        es: `${base}/es`,
      },
    },
  };
}

That gives you correct titles and hreflang-style alternates for SEO.

Content and SEO

Serve translated content from your CMS or static files per locale. Use the same URL structure per locale (e.g. /en/blog/slug, /es/blog/slug) so you can map alternates. Add hreflang (via metadata or link elements) for each language version so search engines can show the right version to the right user. With routing, metadata, and hreflang in place, your i18n Next.js site is set up for SEO.

Summary

Put locale in the URL (e.g. [locale] segment), use middleware for redirects and locale detection, and translate metadata per locale. Set canonical and language alternates for each page. That covers routing, metadata, and SEO for internationalized Next.js.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.