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,
}主な動作
検出の流れ
順番:デフォルトで次の順にソースを確認します。
- クエリパラメーター(?lang=ar)
- Cookie(language=ar)
- Accept-Language ヘッダー
キャッシュ:検出した言語を Cookie に保存します(デフォルトで 1 年間)
フォールバック:有効な言語が検出できなければ
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',
})
)Cookie の設定
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"
})オプションリファレンス
基本オプション
| オプション | 型 | デフォルト | 必須 | 説明 |
|---|---|---|---|---|
supportedLanguages | string[] | ['en'] | はい | 許可する言語コード |
fallbackLanguage | string | 'en' | はい | デフォルトの言語 |
order | DetectorType[] | ['querystring', 'cookie', 'header'] | いいえ | 検出順序 |
debug | boolean | false | いいえ | ログを有効にする |
検出オプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
lookupQueryString | string | 'lang' | クエリパラメーター名 |
lookupCookie | string | 'language' | Cookie 名 |
lookupFromHeaderKey | string | 'accept-language' | ヘッダー名 |
lookupFromPathIndex | number | 0 | パスセグメントのインデックス |
Cookie オプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
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 のドメイン |
高度なオプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
ignoreCase | boolean | true | 大文字小文字を区別しない照合 |
convertDetectedLanguage | (lang: string) => string | undefined | 言語コードの変換関数 |
検証とエラー処理
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
})