ユーザーの言語選択の保持
Cookieを使用して選択を記憶する
問題
ユーザーがサイトで手動で「フランス語」を選択します。ブラウザを閉じて後でサイトのメインアドレス(例: example.com)を入力すると、アプリケーションはデフォルト(例: 英語)または自動検出された言語に戻ります。この選択を記憶しないことにより、ユーザーは新しいセッションを開始するたびに言語スイッチャーを見つけて言語を再選択する必要があります。
解決策
ユーザーが言語を選択したら、その選択をCookieに保存します。ミドルウェアで、ユーザーがルートパス(/)にアクセスしたときに、Accept-Languageヘッダーを確認する前にこのCookieを確認します。有効なCookieが見つかった場合、ブラウザのデフォルトを上書きして、ユーザーを選択した言語のルート(例: /fr)にリダイレクトします。
手順
1. 言語設定を定義する
サポートされている言語、デフォルト言語、およびユーザーの設定を保存するために使用するCookieの名前を保存する中央設定ファイルを作成します。
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';
2. 言語パーサーをインストールする
Cookieが設定されていない場合のフォールバックとして使用されるAccept-Languageヘッダー用のパーサーが必要です。
npm install accept-language-parser
3. ミドルウェアを作成する
プロジェクトのルートにmiddleware.tsファイルを作成します。このミドルウェアは、最初にNEXT_LOCALE Cookieを確認します。見つからない場合は、Accept-Languageヘッダーの確認にフォールバックします。このロジックは、ルートパス(/)へのリクエストにのみ適用されます。
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import {
locales,
defaultLocale,
localeCookieName,
} from './i18n.config';
function getPreferredLocale(request: NextRequest) {
// 1. Check for the cookie
const cookie = request.cookies.get(localeCookieName);
if (cookie) {
const locale = cookie.value;
if (locales.includes(locale)) {
return locale;
}
}
// 2. Check the Accept-Language header
const acceptLang = request.headers.get('Accept-Language');
if (acceptLang) {
const bestMatch = parser.pick(locales, acceptLang, {
loose: true,
});
if (bestMatch) {
return bestMatch;
}
}
// 3. Return the default
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 1. Check if the request is for the root path
if (pathname === '/') {
// Get the user's preferred language (from cookie or header)
const bestLocale = getPreferredLocale(request);
// Redirect to the best-matched language path
request.nextUrl.pathname = `/${bestLocale}`;
return NextResponse.redirect(request.nextUrl);
}
// 2. For all other paths, continue as normal
return NextResponse.next();
}
export const config = {
matcher: [
// We only want to run this on the root path for now
'/',
// We also need to match non-root paths to let them pass
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
このミドルウェアのロジックは、ユーザーがサイトのルートにアクセスした際に、暗黙的な設定(ブラウザヘッダー)よりも明示的な選択(Cookie)を正しく優先するようになりました。