Via path
curl http://localhost:8787/ar/home
Via query parameter
curl http://localhost:8787/?lang=ar
Via cookie
curl -H 'Cookie: language=ja' http://localhost:8787/
Via header
curl -H 'Accept-Language: ar,en;q=0.9' http://localhost:8787/
## Default Configuration [](#default-configuration)
ts
export const DEFAULT_OPTIONS: DetectorOptions = { order: ['querystring', 'cookie', 'header'], lookupQueryString: 'lang', lookupCookie: 'language', lookupFromHeaderKey: 'accept-language', lookupFromPathIndex: 0, caches: ['cookie'], ignoreCase: true, fallbackLanguage: 'en', supportedLanguages: ['en'], cookieOptions: { sameSite: 'Strict', secure: true, maxAge: 365 * 24 * 60 * 60, httpOnly: true, }, debug: false, }
## Key Behaviors [](#key-behaviors)
### Detection Workflow [](#detection-workflow)
1. **Order**: Checks sources in this sequence by default:
* Query parameter (?lang=ar)
* Cookie (language=ar)
* Accept-Language header
2. **Caching**: Stores detected language in a cookie (1 year by default)
3. **Fallback**: Uses `fallbackLanguage` if no valid detection (must be in `supportedLanguages`)
## Advanced Configuration [](#advanced-configuration)
### Custom Detection Order [](#custom-detection-order)
Prioritize URL path detection (e.g., /en/about):
ts
app.use( languageDetector({ order: ['path', 'cookie', 'querystring', 'header'], lookupFromPathIndex: 0, // /en/profile → index 0 = 'en' supportedLanguages: ['en', 'ar'], fallbackLanguage: 'en', }) )
### Progressive Locale Matching [](#progressive-locale-matching)
When a detected locale code like `ja-JP` is not in `supportedLanguages`, the middleware progressively truncates subtags to find a match. For example, `zh-Hant-CN` will try `zh-Hant`, then `zh`. An exact match is always preferred.
ts
app.use( languageDetector({ supportedLanguages: ['en', 'ja', 'zh-Hant'], fallbackLanguage: 'en', }) )
// Accept-Language: ja-JP → matches 'ja' // Accept-Language: zh-Hant-CN → matches 'zh-Hant'
### Language Code Transformation [](#language-code-transformation)
Normalize complex codes (e.g., en-US → en):
ts
app.use( languageDetector({ convertDetectedLanguage: (lang) => lang.split('-')[0], supportedLanguages: ['en', 'ja'], fallbackLanguage: 'en', }) )
### Cookie Configuration [](#cookie-configuration)
ts
app.use( languageDetector({ lookupCookie: 'app_lang', caches: ['cookie'], cookieOptions: { path: '/', // Cookie path sameSite: 'Lax', // Cookie same-site policy secure: true, // Only send over HTTPS maxAge: 86400 * 365, // 1 year expiration httpOnly: true, // Not accessible via JavaScript domain: '.example.com', // Optional: specific domain }, }) )
To disable cookie caching:
ts
languageDetector({ caches: false, })
### Debugging [](#debugging)
Log detection steps:
ts
languageDetector({ debug: true, // Shows: "Detected from querystring: ar" })
## Options Reference [](#options-reference)
### Basic Options [](#basic-options)
Option
Type
Default
Required
Description
`supportedLanguages`
`string[]`
`['en']`
Yes
Allowed language codes
`fallbackLanguage`
`string`
`'en'`
Yes
Default language
`order`
`DetectorType[]`
`['querystring', 'cookie', 'header']`
No
Detection sequence
`debug`
`boolean`
`false`
No
Enable logging
### Detection Options [](#detection-options)
Option
Type
Default
Description
`lookupQueryString`
`string`
`'lang'`
Query parameter name
`lookupCookie`
`string`
`'language'`
Cookie name
`lookupFromHeaderKey`
`string`
`'accept-language'`
Header name
`lookupFromPathIndex`
`number`
`0`
Path segment index
### Cookie Options [](#cookie-options)
Option
Type
Default
Description
`caches`
`CacheType[] | false`
`['cookie']`
Cache settings
`cookieOptions.path`
`string`
`'/'`
Cookie path
`cookieOptions.sameSite`
`'Strict' | 'Lax' | 'None'`
`'Strict'`
SameSite policy
`cookieOptions.secure`
`boolean`
`true`
HTTPS only
`cookieOptions.maxAge`
`number`
`31536000`
Expiration (seconds)
`cookieOptions.httpOnly`
`boolean`
`true`
JS accessibility
`cookieOptions.domain`
`string`
`undefined`
Cookie domain
### Advanced Options [](#advanced-options)
Option
Type
Default
Description
`ignoreCase`
`boolean`
`true`
Case-insensitive matching
`convertDetectedLanguage`
`(lang: string) => string`
`undefined`
Language code transformer
## Validation & Error Handling [](#validation-error-handling)
* `fallbackLanguage` must be in `supportedLanguages` (throws error during setup)
* `lookupFromPathIndex` must be ≥ 0
* Invalid configurations throw errors during middleware initialization
* Failed detections silently use `fallbackLanguage`
## Common Recipes [](#common-recipes)
### Path-Based Routing [](#path-based-routing)
ts
app.get('/:lang/home', (c) => { const lang = c.get('language') // 'en', 'ar', etc. return c.json({ message: getLocalizedContent(lang) }) })
### Multiple Supported Languages [](#multiple-supported-languages)
ts
languageDetector({ supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'], convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize })