跳转到正文

Language 中间件 ​

Language Detector 中间件从多个来源自动检测用户偏好的语言(区域设置),并通过 c.get('language') 提供访问。检测策略包括查询参数、Cookie、请求头和 URL 路径片段,非常适合国际化(i18n)及面向特定区域的内容。

导入 ​

ts
import { Hono } from 'hono'
import { languageDetector } from 'hono/language'

基本用法 ​

按默认顺序从查询字符串、Cookie 和请求头检测语言,并回退到英语:

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}`)
})

客户端示例 ​

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/

默认配置 ​

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,
}

主要行为 ​

检测流程 ​

  1. 顺序:默认按以下顺序检查来源:

    • 查询参数(?lang=ar)
    • Cookie(language=ar)
    • Accept-Language 请求头
  2. 缓存:将检测到的语言存入 Cookie(默认保存 1 年)

  3. 回退:无法有效检测时使用 fallbackLanguage(必须包含在 supportedLanguages 中)

高级配置 ​

自定义检测顺序 ​

优先根据 URL 路径检测(例如 /en/about):

ts
app.use(
  languageDetector({
    order: ['path', 'cookie', 'querystring', 'header'],
    lookupFromPathIndex: 0, // /en/profile → index 0 = 'en'
    supportedLanguages: ['en', 'ar'],
    fallbackLanguage: 'en',
  })
)

逐级匹配区域设置 ​

当检测到的区域代码(如 ja-JP)不在 supportedLanguages 中时,中间件会逐步截去子标签,寻找匹配项。例如,zh-Hant-CN 会先尝试 zh-Hant,再尝试 zh。始终优先精确匹配。

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'

语言代码转换 ​

规范化复杂代码(例如 en-US → en):

ts
app.use(
  languageDetector({
    convertDetectedLanguage: (lang) => lang.split('-')[0],
    supportedLanguages: ['en', 'ja'],
    fallbackLanguage: 'en',
  })
)
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 缓存:

ts
languageDetector({
  caches: false,
})

调试 ​

记录检测步骤:

ts
languageDetector({
  debug: true, // Shows: "Detected from querystring: ar"
})

选项参考 ​

基本选项 ​

选项类型默认值必填说明
supportedLanguagesstring[]['en']是允许的语言代码
fallbackLanguagestring'en'是默认语言
orderDetectorType[]['querystring', 'cookie', 'header']否检测顺序
debugbooleanfalse否启用日志

检测选项 ​

选项类型默认值说明
lookupQueryStringstring'lang'查询参数名称
lookupCookiestring'language'Cookie 名称
lookupFromHeaderKeystring'accept-language'请求头名称
lookupFromPathIndexnumber0路径片段索引
选项类型默认值说明
cachesCacheType[] | false['cookie']缓存配置
cookieOptions.pathstring'/'Cookie 路径
cookieOptions.sameSite'Strict' | 'Lax' | 'None''Strict'SameSite 策略
cookieOptions.securebooleantrue仅 HTTPS
cookieOptions.maxAgenumber31536000过期时间(秒)
cookieOptions.httpOnlybooleantrueJS 可访问性
cookieOptions.domainstringundefinedCookie 域名

高级选项 ​

选项类型默认值说明
ignoreCasebooleantrue不区分大小写的匹配
convertDetectedLanguage(lang: string) => stringundefined语言代码转换器

验证与错误处理 ​

  • fallbackLanguage 必须包含在 supportedLanguages 中(配置时抛出错误)
  • lookupFromPathIndex 必须 ≥ 0
  • 无效配置会在中间件初始化时抛出错误
  • 检测失败时自动使用 fallbackLanguage,不报错

常见实用示例 ​

基于路径的路由 ​

ts
app.get('/:lang/home', (c) => {
  const lang = c.get('language') // 'en', 'ar', etc.
  return c.json({ message: getLocalizedContent(lang) })
})

支持多种语言 ​

ts
languageDetector({
  supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'],
  convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize
})

基于 MIT 许可证发布。