Aller au contenu

Middleware Language ​

Le middleware Language Detector détermine automatiquement la langue préférée de l’utilisateur (locale) à partir de différentes sources et la rend accessible via c.get('language'). Ses stratégies de détection incluent les paramètres de requête, les cookies, les en-têtes et les segments du chemin de l’URL. Il convient à l’internationalisation (i18n) et au contenu propre à chaque langue.

Importation ​

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

Utilisation de base ​

Détecter la langue à partir de la chaîne de requête, d’un cookie et d’un en-tête, dans cet ordre par défaut, avec repli sur l’anglais :

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

Exemples côté client ​

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/

Configuration par défaut ​

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

Comportements principaux ​

Processus de détection ​

  1. Ordre : Vérifie les sources dans cet ordre par défaut :

    • Paramètre de requête (?lang=ar)
    • Cookie (language=ar)
    • En-tête Accept-Language
  2. Mise en cache : Stocke la langue détectée dans un cookie, pendant un an par défaut

  3. Repli : Utilise fallbackLanguage si aucune langue valide n’est détectée ; cette valeur doit figurer dans supportedLanguages

Configuration avancée ​

Ordre de détection personnalisé ​

Donner la priorité à la détection dans le chemin de l’URL, par exemple /en/about :

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

Correspondance progressive des locales ​

Lorsqu’un code de locale détecté comme ja-JP ne figure pas dans supportedLanguages, le middleware supprime progressivement les sous-étiquettes pour trouver une correspondance. Par exemple, pour zh-Hant-CN, il essaie zh-Hant, puis zh. Une correspondance exacte est toujours privilégiée.

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'

Transformation des codes de langue ​

Normaliser les codes complexes, par exemple 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
    },
  })
)

Pour désactiver la mise en cache dans les cookies :

ts
languageDetector({
  caches: false,
})

Débogage ​

Journaliser les étapes de détection :

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

Référence des options ​

Options de base ​

OptionTypeValeur par défautObligatoireDescription
supportedLanguagesstring[]['en']OuiCodes de langue autorisés
fallbackLanguagestring'en'OuiLangue par défaut
orderDetectorType[]['querystring', 'cookie', 'header']NonOrdre de détection
debugbooleanfalseNonActiver la journalisation

Options de détection ​

OptionTypeValeur par défautDescription
lookupQueryStringstring'lang'Nom du paramètre de requête
lookupCookiestring'language'Nom du cookie
lookupFromHeaderKeystring'accept-language'Nom de l’en-tête
lookupFromPathIndexnumber0Index du segment de chemin
OptionTypeValeur par défautDescription
cachesCacheType[] | false['cookie']Paramètres de cache
cookieOptions.pathstring'/'Chemin du cookie
cookieOptions.sameSite'Strict' | 'Lax' | 'None''Strict'Politique SameSite
cookieOptions.securebooleantrueHTTPS uniquement
cookieOptions.maxAgenumber31536000Expiration en secondes
cookieOptions.httpOnlybooleantrueAccessibilité depuis JS
cookieOptions.domainstringundefinedDomaine du cookie

Options avancées ​

OptionTypeValeur par défautDescription
ignoreCasebooleantrueCorrespondance sans distinction de casse
convertDetectedLanguage(lang: string) => stringundefinedTransformation du code de langue

Validation et gestion des erreurs ​

  • fallbackLanguage doit figurer dans supportedLanguages, sinon une erreur est levée lors de la configuration
  • lookupFromPathIndex doit être ≥ 0
  • Les configurations invalides lèvent des erreurs lors de l’initialisation du middleware
  • En cas d’échec de la détection, fallbackLanguage est utilisé sans signaler d’erreur

Recettes courantes ​

Routage basé sur le chemin ​

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

Plusieurs langues prises en charge ​

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

Publié sous licence MIT.