Appearance
语言中间件
🌐 Language Middleware
语言检测中间件会自动从各种来源确定用户的首选语言(区域设置),并通过 c.get('language') 提供。检测策略包括查询参数、Cookies、请求头和 URL 路径段。非常适合国际化 (i18n) 和特定区域内容。
🌐 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
ts
import { Hono } from 'hono'
import { languageDetector } from 'hono/language'基本用法
🌐 Basic Usage
从查询字符串、cookie 和标头(默认顺序)检测语言,并回退到英语:
🌐 Detect language from query string, cookie, and header (default order), with fallback to English:
ts
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
sh
# 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
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
检测工作流程
🌐 Detection Workflow
- 顺序:默认按此顺序检查来源:
- 查询参数 (?lang=ar)
- Cookie(语言=ar)
- Accept-Language 标头
- 缓存:将检测到的语言存储在 cookie 中(默认 1 年)
- 回退:如果没有有效检测,则使用
fallbackLanguage(必须在supportedLanguages中)
高级配置
🌐 Advanced Configuration
自定义检测顺序
🌐 Custom Detection Order
优先考虑 URL 路径检测(例如,/en/about):
🌐 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
当检测到的区域代码如 ja-JP 不在 supportedLanguages 中时,中间件会逐步截断子标签以找到匹配。例如,zh-Hant-CN 会尝试 zh-Hant,然后是 zh。总是优先使用精确匹配。
🌐 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
规范化复杂代码(例如,en-US → en):
🌐 Normalize complex codes (e.g., en-US → en):
ts
app.use(
languageDetector({
convertDetectedLanguage: (lang) => lang.split('-')[0],
supportedLanguages: ['en', 'ja'],
fallbackLanguage: 'en',
})
)Cookie 配置
🌐 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
},
})
)要禁用 cookie 缓存:
🌐 To disable cookie caching:
ts
languageDetector({
caches: false,
})调试
🌐 Debugging
日志检测步骤:
🌐 Log detection steps:
ts
languageDetector({
debug: true, // Shows: "Detected from querystring: ar"
})选项参考
🌐 Options Reference
基本选项
🌐 Basic Options
| 选项 | 类型 | 默认值 | 必填 | 描述 |
|---|---|---|---|---|
supportedLanguages | string[] | ['en'] | 是 | 允许的语言代码 |
fallbackLanguage | string | 'en' | 是 | 默认语言 |
order | DetectorType[] | ['querystring', 'cookie', 'header'] | 否 | 检测顺序 |
debug | boolean | false | 否 | 启用日志 |
检测选项
🌐 Detection Options
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
lookupQueryString | string | 'lang' | 查询参数名称 |
lookupCookie | string | 'language' | Cookie 名称 |
lookupFromHeaderKey | string | 'accept-language' | 头部名称 |
lookupFromPathIndex | number | 0 | 路径段索引 |
Cookie 选项
🌐 Cookie Options
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
caches | CacheType[] | false | ['cookie'] | 缓存设置 |
cookieOptions.path | string | '/' | Cookie 路径 |
cookieOptions.sameSite | 'Strict' | 'Lax' | 'None' | 'Strict' | SameSite 策略 |
cookieOptions.secure | boolean | true | 仅 HTTPS |
cookieOptions.maxAge | number | 31536000 | 过期时间(秒) |
cookieOptions.httpOnly | boolean | true | JS 可访问性 |
cookieOptions.domain | string | undefined | Cookie 域 |
高级选项
🌐 Advanced Options
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
ignoreCase | boolean | true | 不区分大小写的匹配 |
convertDetectedLanguage | (lang: string) => string | undefined | 语言代码转换器 |
验证和错误处理
🌐 Validation & Error Handling
fallbackLanguage必须在supportedLanguages中(设置期间会抛出错误)lookupFromPathIndex必须 ≥ 0- 无效配置在中间件初始化期间抛出错误
- 失败的检测会默默使用
fallbackLanguage
常用秘诀
🌐 Common Recipes
基于路径的路由
🌐 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
ts
languageDetector({
supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'],
convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize
})