本文へ移動

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.securebooleantrueHTTPS のみ
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 ライセンスで公開されています。