summaryrefslogtreecommitdiff
path: root/middleware.ts
blob: 4237bfb4190fc78925f33dd303fdaada0278c567 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/** middleware.ts */
export const runtime = 'nodejs';

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import acceptLanguage from 'accept-language';

import { fallbackLng, languages, cookieName } from '@/i18n/settings';

acceptLanguage.languages(languages);

export function middleware(request: NextRequest) {
  /**
   * 1. 쿠키에서 언어 가져오기
   */
  let lng = request.cookies.get(cookieName)?.value;

  /**
   * 2. 쿠키가 없다면 브라우저의 Accept-Language 헤더에서 언어를 추론
   */
  if (!lng) {
    const headerLang = request.headers.get('accept-language');
    lng = acceptLanguage.get(headerLang) || fallbackLng;
  }

  const { pathname, searchParams, origin } = request.nextUrl;

  /**
   * 3. "/"" 경로로 들어온 경우 -> "/{lng}"로 리다이렉트
   *    (예) / -> /en, / -> /ko ...
   */
  if (pathname === '/') {
    const redirectUrl = new URL(`/${lng}`, origin);
    // 쿼리 파라미터가 있을 경우도 붙여주기
    redirectUrl.search = searchParams.toString();
    return NextResponse.redirect(redirectUrl);
  }

  /**
   * 4. 현재 pathname이 언어 경로를 포함하고 있는지 확인
   *    - /en
   *    - /en/...  (다른 하위 경로들)
   *    - /ko
   *    - /ko/...
   */
  const hasValidLngInPath = languages.some(
    (language) => pathname === `/${language}` || pathname.startsWith(`/${language}/`),
  );

  /**
   * 5. 언어 경로가 누락된 경우 -> "/{lng}" + 기존 pathname 으로 리다이렉트
   *    예) /dashboard  -> /en/dashboard
   */
  if (!hasValidLngInPath) {
    const redirectUrl = new URL(`/${lng}${pathname}`, origin);
    redirectUrl.search = searchParams.toString();
    return NextResponse.redirect(redirectUrl);
  }

  /**
   * 6. 위 조건에 걸리지 않았다면 그대로 Next.js로 넘긴다.
   */
  const response = NextResponse.next();

  /**
   * 7. 쿠키에 저장된 언어와 현재 lng가 다르면 업데이트
   */
  const currentCookie = request.cookies.get(cookieName)?.value;
  if (lng && lng !== currentCookie) {
    response.cookies.set(cookieName, lng, { path: '/' });
  }

  return response;
}

/**
 * 8. (선택) 매칭할 경로 설정
 *    - _next, 정적파일(.*), API 등의 경로는 제외시키는 예시
 */
export const config = {
  matcher: [
    '/((?!_next|.*\\..*|api).*)',
  ],
};