Skip to content

Language 中间件

Language Detector 中间件会自动识别用户首选语言(Locale),并通过 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'], // 必须包含回退语言
    fallbackLanguage: 'en', // 必填
  })
)

app.get('/', (c) => {
  const lang = c.get('language')
  return c.text(`Hello! Your language is ${lang}`)
})

客户端示例

sh
# 通过路径
curl http://localhost:8787/ar/home

# 通过查询参数
curl http://localhost:8787/?lang=ar

# 通过 Cookie
curl -H 'Cookie: language=ja' http://localhost:8787/

# 通过请求头
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

高级配置

自定义检测顺序

优先从路径(如 /en/about)检测:

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

转换语言代码

对复杂的语言代码进行归一化(例如 en-USen):

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 路径
      sameSite: 'Lax', // SameSite 策略
      secure: true, // 仅通过 HTTPS 发送
      maxAge: 86400 * 365, // 有效期 1 年
      httpOnly: true, // 前端 JS 不可访问
      domain: '.example.com', // 可选:指定域名
    },
  })
)

若需禁用 Cookie 缓存:

ts
languageDetector({
  caches: false,
})

调试

打印检测日志:

ts
languageDetector({
  debug: true, // 输出示例:“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.httpOnlybooleantrue是否禁止 JS 访问
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' 等
  return c.json({ message: getLocalizedContent(lang) })
})

支持多种语言代码

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

Released under the MIT License.