5. Internationalization

 

Next.js는 여러 언어를 지원하기 위해 라우팅 및 콘텐츠 렌더링을 구성할 수 있게 해준다. 여러 locale에 대응하는 사이트를 만들기 위해서는 번역된 콘텐츠(localization)와 internationalized routes가 포함된다.

 

Middleware (/src/middleware.ts)

미들웨어는 클라이언트의 요청에서 locale 을 추출하고, 이에 따라 적절한 리디렉션 또는 리라이트를 수행한다. 만약 클라이언트가 /about에 대한 요청을 하고 locale이 en으로 설정되어 있다면, 미들웨어는 이를 /en/about으로 리디렉션하거나 리라이트할 것이다.

이러한 처리를 통해 사용자는 적절한 언어로 번역된 페이지로 리디렉션되거나 리라이트되어 효과적으로 국제화된 콘텐츠를 제공받을 수 있다. 이것이 국제화를 지원하는 미들웨어의 주요 역할 중 하나다.

import createMiddleware from 'next-intl/middleware'
import { NextRequest } from 'next/server'
import { locales } from './navigation'

/**
 * 국제화 (i18n)
 * ko-KR : Korean (Korea)
 * en-US : English (United States)
 * zh-CN : Chinese (S)
 * zh-TW : Chinese (T)
 * ja-JP : Japanese (Japan)
 */

export default async function middleware(request: NextRequest) {
  const acceptLanguage =
    request.headers.get('accept-language') || process.env.DEFAULT_LOCALE
  const defaultLocales = locales.filter((locale) => {
    if (acceptLanguage.startsWith(locale)) return true
    else return false
  })
  const defaultLocale = defaultLocales[0] || process.env.DEFAULT_LOCALE

  const handleI18nRouting = createMiddleware({
    locales,
    defaultLocale,
    localePrefix: process.env.LOCALE_PREFIX || ('always' as any),
    localeDetection: true
  })
  const response = handleI18nRouting(request)

  if (response.cookies.get('NEXT_LOCALE' as any)) {
    response.cookies.delete('NEXT_LOCALE' as any)
  }

  return response
}

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)']
}

 

message(/src/data/i18n)

메시지는 로컬에서 제공하거나 원격 데이터 소스에서 로드할 수 있다. 가장 간단한 옵션은 locale을 기반으로 한 JSON 파일을 프로젝트에 추가하는 것이다.

// Json 파일 예시
{
  "common-page": {
    "loggedIn": "Logged In",
    "loggedOut": "Logged Out",
    "login": "Sign In",
    "logout": "Log Out",
    "Settings": "Settings",
    "title": "Account"
  }
}

다국어 Json파일은 i18n폴더에 언어별 폴더에 json 파일을 넣으면 되며, 각 언어와 json 파일을 매핑하기 위해서는 i18n파일에 해당 json 경로를 명시해주면 된다. 

 

/src/i18n.ts

import { getRequestConfig } from 'next-intl/server'

export default getRequestConfig(async ({ locale }) => {
  // messages json 파일들 경로를 명시
  const combinedMessages = {
    ...(await import(`/src/data/i18n/${locale}/common.json`)),
    ...(await import(`/src/data/i18n/${locale}/display.json`)),
    ...(await import(`/src/data/i18n/${locale}/event.json`)),
    ...(await import(`/src/data/i18n/${locale}/goods.json`)),
    ...(await import(`/src/data/i18n/${locale}/member.json`)),
    ...(await import(`/src/data/i18n/${locale}/order.json`)),
    ...(await import(`/src/data/i18n/${locale}/promotion.json`)),
    ...(await import(`/src/data/i18n/${locale}/search.json`))
  }

  return {
    messages: combinedMessages
  }
})

 

/src/data

 

message를 사용하기 위해 server와 client단에서 제공해주는 함수가 다른데 공통된 함수로 사용하기 위해 분기처리하여 해당하는 값을 retrun해 준다.

import { getTranslations } from 'next-intl/server'
import { useTranslations } from 'next-intl';
import { isNull } from '@/lib/x2bee-core';

export function getMessage(namespace: string): any {
  if (isNull(namespace)) {
    throw new Error('Message Client namespace value is not null.');
  }

  const isServerComponent: () => (boolean) = () => {
    return typeof window === 'undefined' ? true : false;
  };

  if (isServerComponent()) {
    let localeTranslations = {}
    try {
      localeTranslations = getTranslations(namespace);
    } catch(error) {
      localeTranslations = useTranslations(namespace);
    }
    return localeTranslations;
  } else {
    return useTranslations(namespace);
  }
}

 

Server에서 사용 방법

import { getMessage } from '@/lib/common/plugins/messageClient'

const TestPage = async () => {
  const t = await getMessage('account-page')
  console.log(t)
  return (
    <>
      <h1>{t('loggedIn')}</h1>
    </>
  )
}

export default TestPage

 

Client에서 사용 방법

'use client'

import { getMessage } from '@/lib/common/plugins/messageClient'

const TestPage = () => {
  const t = getMessage('account-page')
  console.log(t)
  return (
    <>
      <h1>{t('loggedIn')}</h1>
    </>
  )
}

export default TestPage

 

Navigation

next-intl은 사용자 locale을 자동으로 처리하는 공통 Next.js 네비게이션 API에 대한 솔루션을 제공한다.

import {
  createLocalizedPathnamesNavigation,
  Pathnames
} from 'next-intl/navigation'

export const locales = ['ko', 'en'] as const
export const localePrefix = 'always' // Default

export const pathnames = {} satisfies Pathnames<typeof locales>

export const { Link, redirect, usePathname, useRouter, getPathname } =
  createLocalizedPathnamesNavigation({ locales, localePrefix, pathnames })

Navigation을 사용하여 locale 변경 및 페이지 이동을 구현할 수 있다. 아래 예시 소스는 위에 설정한 navegation에서 route와 link를 사용하여 locale 변경 및 페이지 이동하는 소스이다.

'use client'
import { getMessage } from '@/lib/common/plugins/messageClient'
// next-intl navigation 가져오기
import { Link, useRouter, usePathname } from '@/navigation'
const TestPage = () => {
  const t = getMessage('common-page')
  const pathname = usePathname()
  const router = useRouter()

  //router를 사용한 방식
  const handleChange = (event) => {
    router.replace(pathname, { locale: event.target.value })
  }

  return (
    <>
      <h1>{t('loggedIn')}</h1>
      <select onChange={handleChange}>
        <option>ko</option>
        <option>en</option>
      </select>
      //Link를 사용한 방식
      <Link href="/goods" locale="en">
        Switch to German
      </Link>
    </>
  )
}

export default TestPage

 

 

출력 예시

ko로 설정 시

zh로 설정 시 

변경될 경로 예시

/src/app/[locale]/(root)
/src/app/[locale]/[...notFound]
/src/app/[locale]/fo
/src/app/[locale]/goods
/src/app/[locale]/ui

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유