Middleware
Automatic locale detection with TanStack Start middleware
Configure automatic locale detection using TanStack Start middleware.
Basic Setup
Create a middleware using the built-in helper:
import { createBetterI18nMiddleware } from "@better-i18n/use-intl/middleware"
import { i18nConfig } from "../i18n.config"
export const i18nMiddleware = createBetterI18nMiddleware({
project: i18nConfig.project,
defaultLocale: i18nConfig.defaultLocale,
}) Register Middleware
Add the middleware to your TanStack Start configuration:
import { createStart } from "@tanstack/react-start/server"
import { i18nMiddleware } from "./middleware/i18n"
export default createStart({
middleware: [i18nMiddleware],
})Configuration Options
export const i18nMiddleware = createBetterI18nMiddleware({
// Required
project: "org/project",
defaultLocale: "en",
// Optional detection settings
detection: {
// Use Accept-Language header
browserLanguage: true,
// Use locale cookie
cookie: true,
// Cookie name (default: "locale")
cookieName: "locale",
// Cookie max age in seconds (default: 1 year)
cookieMaxAge: 60 * 60 * 24 * 365,
},
})Detection Order
The middleware detects locale in this order:
- URL Path -
/tr/about→tr - Cookie -
locale=tr→tr - Accept-Language Header -
tr-TR,tr;q=0.9→tr - Default - Falls back to
defaultLocale
How It Works
Request: GET /about
Headers: Accept-Language: tr-TR,tr;q=0.9,en;q=0.8
1. Check URL path → No locale prefix
2. Check cookie → No cookie
3. Check Accept-Language → "tr" detected
4. Set context.locale = "tr"
5. Set-Cookie: locale=trCookie Persistence
The middleware sets a cookie to persist the user's preference:
// First visit (Accept-Language: tr)
// → Cookie set: locale=tr; path=/; max-age=31536000
// Second visit (cookie exists)
// → Locale from cookie, no header checkDisable Cookie
To disable cookie-based persistence:
export const i18nMiddleware = createBetterI18nMiddleware({
project: "org/project",
defaultLocale: "en",
detection: {
cookie: false,
browserLanguage: true,
},
})Disable Browser Detection
To ignore Accept-Language header:
export const i18nMiddleware = createBetterI18nMiddleware({
project: "org/project",
defaultLocale: "en",
detection: {
cookie: true,
browserLanguage: false,
},
})Custom Middleware
For advanced use cases, create a custom middleware:
import { createMiddleware } from "@tanstack/react-start/server"
export const i18nMiddleware = createMiddleware().server(async ({ next, request }) => {
// Extract locale from various sources
const pathLocale = new URL(request.url).pathname.split("/")[1]
const cookieLocale = getCookie(request, "locale")
const browserLocale = request.headers.get("accept-language")?.split(",")[0]?.slice(0, 2)
// Determine locale with priority
let locale = "en"
if (pathLocale && isValidLocale(pathLocale)) {
locale = pathLocale
} else if (cookieLocale && isValidLocale(cookieLocale)) {
locale = cookieLocale
} else if (browserLocale && isValidLocale(browserLocale)) {
locale = browserLocale
}
// Set cookie for persistence
if (locale !== cookieLocale) {
response.headers.set(
"Set-Cookie",
`locale=${locale}; path=/; max-age=31536000; SameSite=Lax`
)
}
// Continue with locale in context
return next({ context: { locale } })
})
function isValidLocale(locale: string): boolean {
const supportedLocales = ["en", "tr", "de", "es"]
return supportedLocales.includes(locale)
}
function getCookie(request: Request, name: string): string | null {
const cookies = request.headers.get("cookie")
if (!cookies) return null
const match = cookies.match(new RegExp(`${name}=([^;]+)`))
return match ? match[1] : null
}Accessing Locale in Routes
The middleware sets context.locale which is accessible in all routes:
export const Route = createRootRouteWithContext<{ locale: string }>()({
loader: async ({ context }) => {
console.log("Detected locale:", context.locale)
const messages = await getMessages({
project: "org/project",
locale: context.locale,
})
return { messages, locale: context.locale }
},
})Debugging
Enable logging to debug locale detection:
export const i18nMiddleware = createMiddleware().server(async ({ next, request }) => {
const pathLocale = new URL(request.url).pathname.split("/")[1]
const cookieLocale = getCookie(request, "locale")
const browserLocale = request.headers.get("accept-language")
console.log("Locale detection:", {
path: pathLocale,
cookie: cookieLocale,
browser: browserLocale,
url: request.url,
})
// ... rest of middleware
})Common Issues
Locale Not Persisting
Symptom: User's language preference resets on each visit.
Solution: Ensure cookie is set correctly:
detection: {
cookie: true,
cookieMaxAge: 60 * 60 * 24 * 365, // 1 year
}Wrong Locale Detected
Symptom: Browser in Turkish but app shows English.
Solution: Check detection order and cookie:
// Debug by logging
console.log({
cookie: request.headers.get("cookie"),
acceptLanguage: request.headers.get("accept-language"),
})