Skip to main content

Internationalization (i18n)

Introduction

The i18n configuration enables the auth package to work with localized pathnames and routing strategies.

Do you need i18n configuration?

The auth package's i18n configuration is required when locale influences URL pathnames in your application.

Common scenarios:

  • Using Next.js built-in i18n with sub-path routing (/fr/dashboard)
  • Translating route segments (/fr/tableau-de-bord instead of /fr/dashboard) with libraries like next-intl
  • Implementing custom locale-based routing strategies

Skip this configuration if:

  • Your app is single-language only
  • Routes use identical pathnames across all locales

Getting started

Before you begin

Complete the Getting Started: Pages Router or Getting Started: App Router guide. This i18n configuration builds on top of the base authentication setup.

Configuration

OptionTypeRequiredDescription
i18n.localeCookiestringName of the cookie storing user's locale preference
i18n.getLocalizedPathname<Pathname extends string>(options: { locale?: string; pathname: Pathname; url: URL }) => string | Promise<string>Callback function to resolve localized pathnames
i18n-related redirects

The auth middleware needs to operate on the final localized pathname to accurately match routes and redirect users using localized pathnames from getLocalizedPathname.

All i18n-related redirects must be handled before the auth middleware processes the request. If your i18n middleware needs to redirect (e.g., sub-path routing, pathnane translation), you need to detect redirect responses and return early.

Learn more
proxy.ts
import { createAuthMiddleware } from "@krakentech/blueprint-auth/middleware";
import createNextIntlMiddleware from "next-intl/middleware";
import { routing } from "@/i18n/routing";
import { authConfig } from "@/lib/auth/config";

const nextIntlMiddleware = createNextIntlMiddleware(routing);
const authMiddleware = createAuthMiddleware(authConfig);

export function proxy(request: NextRequest) {
const response = nextIntlMiddleware(request);
// If i18n middleware is redirecting, return early
if (!response.ok) return response;

return authMiddleware(request, response);
}

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

Examples

If your project uses Next.js built-in i18n features with the default sub-path routing strategy, you can define a custom getLocalizedPathname function to emulate sub-path routing.

export const LOCALES = ["en", "fr", "de"] as const;
export const DEFAULT_LOCALE = "en";

export type Locale = (typeof LOCALES)[number];

export function hasLocale(locale: string | undefined): locale is Locale {
return !!localeCookie && LOCALES.includes(localeCookie as Locale);
}

export function resolveLocale(
localeCookie: string | undefined,
url: URL,
): Locale {
if (hasLocale(cookieLocale)) return cookieLocale;
const pathnameLocale = url.pathname.split("/")[1];
if (hasLocale(pathnameLocale)) return pathnameLocale;
return DEFAULT_LOCALE;
}
lib/auth/config.ts
import { createAuthConfig } from "@krakentech/blueprint-auth";
import { DEFAULT_LOCALE, resolveLocale } from "@/lib/i18n";

export const authConfig = createAuthConfig({
appRoutes: {
dashboard: { pathname: "/dashboard" },
login: { pathname: "/login" },
home: { pathname: "/" },
},
i18n: {
localeCookie: "NEXT_LOCALE",
getLocalizedPathname({ locale, pathname, url }) {
const resolvedLocale = resolveLocale(locale, url);

// By default, Next.js does not prefix the default locale
// Remove this if you opted-in to prefix the default locale
if (resolvedLocale === DEFAULT_LOCALE) {
return pathname;
}

return `/${resolvedLocale}${pathname}`;
},
},
// ... other config
});

FAQ

How does the middleware use getLocalizedPathname?

The middleware calls getLocalizedPathname for each configured route in appRoutes to build a list of localized pathnames to match against:

  1. Extract locale from cookie (if localeCookie is configured)
  2. Call getLocalizedPathname for each route: dashboard, login, anon, masquerade
  3. Match incoming request pathname against localized paths
  4. Apply appropriate authentication logic

For example, with French locale and translated routes:

  • /dashboard/fr/tableau-de-bord
  • /login/fr/connexion
  • Middleware matches incoming /fr/tableau-de-bord request to dashboard route
Do I need to update my middleware matcher?

Yes, if you use localized route segments. Your middleware matcher should include all localized variations:

middleware.ts
export const config = {
matcher: [
// English routes
"/dashboard/:path*",
"/login",
"/anon/:key",
// French routes
"/fr/tableau-de-bord/:path*",
"/fr/connexion",
"/fr/anon/:key",
// German routes
"/de/armaturenbrett/:path*",
"/de/anmelden",
"/de/anon/:key",
],
};

Alternatively, use a catch-all pattern:

export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};
How do I use allowList with i18n routing?

The allowList glob patterns are matched against localized pathnames. This means your globs should include every supported locale or translated pathname.

Use extglob ?(pattern) syntax to match paths with or without locale prefixes:

lib/auth/config.ts
import { createAuthConfig } from "@krakentech/blueprint-auth";
import { DEFAULT_LOCALE, resolveLocale } from "@/lib/i18n";

export const authConfig = createAuthConfig({
appRoutes: {
dashboard: {
pathname: "/dashboard",
// Matches: /dashboard/switch-tariff/*, /fr/dashboard/switch-tariff/*, /de/dashboard/switch-tariff/*
allowList: ["/?({fr/,de/})dashboard/switch-tariff/*"],
},
},
i18n: {
localeCookie: "NEXT_LOCALE",
getLocalizedPathname({ locale, pathname, url }) {
const resolvedLocale = resolveLocale(locale, url);

// By default, Next.js does not prefix the default locale
if (resolvedLocale === DEFAULT_LOCALE) {
return pathname;
}

return `/${resolvedLocale}${pathname}`;
},
},
});

See the picomatch docs for all supported glob patterns.

Next Steps

Now that you have i18n configured, explore these related guides:

API Reference

Quick reference to i18n-related configuration and functions:

Need Help?

If the i18n API doesn't meet your specific requirements or doesn't integrate well with your i18n tooling, please reach out to the Blueprint team. We're happy to discuss your use case and potentially extend the API to support additional patterns.