Language Detection Middleware

Automatic language detection and URL routing based on user preferences

Middleware automatically detects users’ language preferences and redirects them to the appropriate localised version of your site. It uses browser settings, geolocation, and user preferences to serve the right language before any content loads.

File location: Place middleware.ts in your project root, at the same level as your app/ directory — not inside it.

Set-up

Step 1: Create a Dynamic Route

Create a [locale] directory in your app/ folder and move all pages into it:

middleware.ts
layout.tsx
page.tsx
next.config.js

Step 2: Add middleware

Create middleware.ts in your project root:

import { createNextMiddleware } from 'gt-next/middleware';

export default createNextMiddleware();

export const config = {
  // Match all paths except API routes, static files, and Next.js internals
  matcher: ['/((?!api|static|.*\\..*|_next).*)']
};

This enables automatic language detection and URL routing with locale prefixes such as /en/about, /es/about.

Language detection

The middleware detects users’ language preferences in this order:

  1. URL locale/es/about → Spanish
  2. User cookie — previous language selection
  3. Browser headersAccept-Language header
  4. Default locale — configured fallback language

The middleware automatically handles browser language detection and cookie persistence without additional configuration.

Localised Paths

Customise URL paths for different languages:

export default createNextMiddleware({
  pathConfig: {
    // English: /en/products, Chinese: /zh/产品
    "/products": {
      "zh": "/产品"
    },
    
    // Dynamic routes: /en/product/123, /zh/产品/123
    "/product/[id]": {
      "zh": "/产品/[id]"
    }
  }
});

URL structure

By default, your default locale will not be prefixed with a locale code:

/about     → /about        (default locale: English)
/about     → /es/about     (Spanish)
/about     → /fr/about     (French)

Add a prefix to the default locale

To prefix all locales, including the default:

export default createNextMiddleware({
  prefixDefaultLocale: true
});

Result:

/about     → /en/about     (English, with prefix)
/about     → /es/about     (Spanish, with prefix)
/about     → /fr/about     (French, with prefix)

Common issues

Missing Dynamic Route

All pages must be within [locale]/:

❌ Incorrect:
app/
├── page.tsx
└── about/page.tsx

✅ Correct:
app/
└── [locale]/
    ├── page.tsx
    └── about/page.tsx

Matcher Configuration

Exclude API routes and static files:

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

Test your middleware configuration thoroughly — incorrect matchers can cause infinite redirects or break static assets.

Next steps

  • SSG Guide - Static generation with locale routing
  • RTL Support - Right-to-left languages
  • API references:

How is this guide?

Language Detection Middleware