Language Middleware
The Language Detector middleware automatically determines a user's preferred language (locale) from various sources and makes it available via c.get('language'). Detection strategies include query parameters, cookies, headers, and URL path segments. Perfect for internationalization (i18n) and locale-specific content.
Import
import { Hono } from 'hono' import { languageDetector } from 'hono/language'Basic Usage
Detect language from query string, cookie, and header (default order), with fallback to English:
const app = new Hono() app.use( languageDetector({ supportedLanguages: ['en', 'ar', 'ja'], // Must include fallback fallbackLanguage: 'en', // Required }) ) app.get('/', (c) => { const lang = c.get('language') return c.text(`Hello! Your language is ${lang}`) })Client Examples
# 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
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
Detection Workflow
Order: Checks sources in this sequence by default:
- Query parameter (?lang=ar)
- Cookie (language=ar)
- Accept-Language header
Caching: Stores detected language in a cookie (1 year by default)
Fallback: Uses
fallbackLanguageif no valid detection (must be insupportedLanguages)
Advanced Configuration
Custom Detection Order
Prioritize URL path detection (e.g., /en/about):
app.use( languageDetector({ order: ['path', 'cookie', 'querystring', 'header'], lookupFromPathIndex: 0, // /en/profile → index 0 = 'en' supportedLanguages: ['en', 'ar'], fallbackLanguage: 'en', }) )Language Code Transformation
Normalize complex codes (e.g., en-US → en):
app.use( languageDetector({ convertDetectedLanguage: (lang) => lang.split('-')[0], supportedLanguages: ['en', 'ja'], fallbackLanguage: 'en', }) )Cookie Configuration
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:
languageDetector({ caches: false, })Debugging
Log detection steps:
languageDetector({ debug: true, // Shows: "Detected from querystring: ar" })Options Reference
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
| 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
| 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
| Option | Type | Default | Description |
|---|---|---|---|
ignoreCase | boolean | true | Case-insensitive matching |
convertDetectedLanguage | (lang: string) => string | undefined | Language code transformer |
Validation & Error Handling
fallbackLanguagemust be insupportedLanguages(throws error during setup)lookupFromPathIndexmust be ≥ 0- Invalid configurations throw errors during middleware initialization
- Failed detections silently use
fallbackLanguage
Common Recipes
Path-Based Routing
app.get('/:lang/home', (c) => { const lang = c.get('language') // 'en', 'ar', etc. return c.json({ message: getLocalizedContent(lang) }) })Multiple Supported Languages
languageDetector({ supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'], convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize })