> For the complete documentation index, see [llms.txt](https://tech.x2bee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tech.x2bee.com/dev-guide/pjt-prepare/publish-your-docs/store-front-framewok-next.js/02./4.-internationalization-i18n.md).

# 4. Internationalization와 i18n

이 문서는 Nextjs 프로젝트에서 다국어 사이트를 개발하는 방법에 대해 설명합니다.

미들웨어를 사용하여 언어를 추출하고, 다국어 콘텐츠를 관리하기 위해 JSON 파일을 관리, i18n 설정과 언어 변경 및 이동 방법을 설명합니다.

***

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

## Middleware (/src/middleware.ts)

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

이러한 처리를 통해 사용자는 적절한 언어로 번역된 페이지로 redirect되거나 rewrite되어 효과적으로 국제화된 콘텐츠를 제공받을 수 있습니다.

{% code title="/src/middleware.ts" %}

```ts
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|.*\\..*).*)']
}
```

{% endcode %}

***

## message (/src/data/i18n)

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

예시 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

{% code title="/src/i18n.ts" %}

```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 }
})
```

{% endcode %}

### /src/data

<div align="left"><figure><img src="/files/HryXbWIfDWh7cxhcT5ZG" alt=""><figcaption></figcaption></figure></div>

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

{% code title="messageClient (예시)" %}

```ts
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;
  };

  // server와 client 분기처리
  if (isServerComponent()) {
    let localeTranslations = {}
    try {
      localeTranslations = getTranslations(namespace);
    } catch(error) {
      localeTranslations = useTranslations(namespace);
    }
    return localeTranslations;
  } else {
    return useTranslations(namespace);
  }
}
```

{% endcode %}

***

## Server에서 사용 방법

{% code title="Server Component (예시)" %}

```tsx
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
```

{% endcode %}

## Client에서 사용 방법

{% code title="Client Component (예시)" %}

```tsx
'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
```

{% endcode %}

***

## Navigation

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

{% code title="/src/navigation.ts" %}

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

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

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

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

{% endcode %}

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

{% code title="Locale 변경 예시 (Client Component)" %}

```tsx
'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
```

{% endcode %}

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tech.x2bee.com/dev-guide/pjt-prepare/publish-your-docs/store-front-framewok-next.js/02./4.-internationalization-i18n.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
