Internationalization (i18n) Routing
Examples
Next.js는 v10.0.0
부터 기본적으로 국제화(i18n (opens in a new tab)) 라우팅을 지원합니다. 로케일 목록, 기본 로케일 및 도메인별 로케일을 제공하면 Next.js가 자동으로 라우팅을 처리합니다.
현재 i18n 라우팅 지원은 라우트 및 로케일 구문 분석을 간소화하여 react-intl
(opens in a new tab), react-i18next
(opens in a new tab), lingui
(opens in a new tab), rosetta
(opens in a new tab), next-intl
(opens in a new tab), next-translate
(opens in a new tab), next-multilingual
(opens in a new tab), tolgee
(opens in a new tab), paraglide-next
(opens in a new tab) 등과 같은 기존 i18n 라이브러리 솔루션을 보완하기 위한 것입니다.
Getting started
시작하기 위해 next.config.js
파일에 i18n
설정을 추가합니다.
로케일은 UTS 로케일 식별자 (opens in a new tab)로, 로케일을 정의하기 위한 표준화된 형식입니다.
일반적으로 로케일 식별자는 언어, 지역 및 스크립트로 구성되며, 이들은 대시로 구분됩니다: language-region-script
. 지역과 스크립트는 선택 사항입니다. 예시:
en-US
- 미국에서 사용되는 영어nl-NL
- 네덜란드에서 사용되는 네덜란드어nl
- 특정 지역이 없는 네덜란드어
사용자의 로케일이 nl-BE
이고, 해당 로케일이 설정에 포함되지 않은 경우, 사용자는 nl
이 사용 가능하다면 nl
로 리디렉션되거나 그렇지 않으면 기본 로케일로 리디렉션됩니다.
특정 국가의 모든 지역을 지원할 계획이 아니라면, fallback 역할을 할 국가 로케일을 포함하는 것이 좋습니다.
module.exports = {
i18n: {
// 애플리케이션에서 지원하려는 모든 로케일이 포함됩니다.
locales: ['en-US', 'fr', 'nl-NL'],
// 로케일 접두사가 없는 경로를 방문할 때 사용할 기본 로케일입니다.
// 예: `/hello`
defaultLocale: 'en-US',
// 로케일 도메인 목록과 각 도메인이 처리해야 할 기본 로케일입니다. (이는 도메인 라우팅을 설정할 때만 필요합니다.)
// 주의: 하위 도메인 또한 도메인 값에 포함되어야 합니다. 예: "fr.example.com"
domains: [
{
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
// 선택적으로 http 필드를 사용하여 로컬에서 https 대신 http로 로케일 도메인을 테스트할 수 있습니다.
http: true,
},
],
},
}
Locale Strategies
로케일 처리 전략에는 두 가지가 있습니다: 하위 경로 라우팅과 도메인 라우팅입니다.
Sub-path Routing
하위 경로 라우팅은 URL 경로에 로케일을 포함시킵니다.
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL'],
defaultLocale: 'en-US',
},
}
위 설정에서는 en-US
, fr
, nl-NL
로 라우팅이 가능하며, en-US
가 기본 로케일입니다. pages/blog.js
가 있는 경우, 다음과 같은 URL을 사용할 수 있습니다:
/blog
/fr/blog
/nl-nl/blog
기본 로케일에는 접두사가 없습니다.
Domain Routing
도메인 라우팅을 사용하면 서로 다른 도메인에서 로케일을 제공하도록 설정할 수 있습니다:
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
defaultLocale: 'en-US',
domains: [
{
// 주의: 하위 도메인 또한 도메인 값에 포함되어야 합니다.
// 예: 예상되는 호스트명이 www.example.com인 경우 해당 도메인을 사용해야 합니다.
domain: 'example.com',
defaultLocale: 'en-US',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
{
domain: 'example.nl',
defaultLocale: 'nl-NL',
// 이 도메인으로 리디렉션되어야 하는 로케일을 지정합니다.
locales: ['nl-BE'],
},
],
},
}
예를 들어 pages/blog.js
가 있는 경우, 다음과 같은 URL을 사용할 수 있습니다:
example.com/blog
www.example.com/blog
example.fr/blog
example.nl/blog
example.nl/nl-BE/blog
Automatic Locale Detection
사용자가 애플리케이션의 루트(일반적으로 /
)를 방문하면, Next.js는 Accept-Language
(opens in a new tab) 헤더와 현재 도메인을 기반으로 사용자가 선호하는 로케일을 자동으로 감지합니다.
기본 로케일이 아닌 다른 로케일이 감지되면, 사용자는 다음 중 하나로 리디렉션됩니다:
- 하위 경로 라우팅을 사용하는 경우: 로케일 접두사가 있는 경로
- 도메인 라우팅을 사용하는 경우: 해당 로케일이 기본값으로 지정된 도메인
도메인 라우팅을 사용하는 경우, Accept-Language
헤더가 fr;q=0.9
인 사용자가 example.com
을 방문하면, 해당 도메인이 기본적으로 fr
로케일을 처리하기 때문에 example.fr
로 리디렉션됩니다.
하위 경로 라우팅을 사용하는 경우, 사용자는 /fr
로 리디렉션됩니다.
Prefixing the Default Locale
Next.js 12와 미들웨어를 사용하면 우회 방법 (opens in a new tab)을 통해 기본 로케일에 접두사를 추가할 수 있습니다.
예를 들어, 다음은 몇 가지 언어를 지원하는 next.config.js
파일입니다. 여기서 "default"
로케일은 의도적으로 추가되었습니다.
module.exports = {
i18n: {
locales: ['default', 'en', 'de', 'fr'],
defaultLocale: 'default',
localeDetection: false,
},
trailingSlash: true,
}
다음으로, 미들웨어를 사용하여 사용자 정의 라우팅 규칙을 추가할 수 있습니다:
import { NextRequest, NextResponse } from 'next/server'
const PUBLIC_FILE = /\.(.*)$/
export async function middleware(req: NextRequest) {
if (
req.nextUrl.pathname.startsWith('/_next') ||
req.nextUrl.pathname.includes('/api/') ||
PUBLIC_FILE.test(req.nextUrl.pathname)
) {
return
}
if (req.nextUrl.locale === 'default') {
const locale = req.cookies.get('NEXT_LOCALE')?.value || 'en'
return NextResponse.redirect(
new URL(
`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`,
req.url,
),
)
}
}
이 미들웨어는 API 라우트 및 폰트나 이미지 같은 public 파일에 기본 접두사를 추가하지 않습니다. 기본 로케일로 요청이 들어오면, 접두사 /en
으로 리디렉션합니다.
Disabling Automatic Locale Detection
자동 로케일 감지는 다음과 같이 비활성화할 수 있습니다:
module.exports = {
i18n: {
localeDetection: false,
},
}
localeDetection
이 false
로 설정되면, Next.js는 사용자가 선호하는 로케일에 따라 자동으로 리디렉션하지 않습니다. 대신, 로케일 기반 도메인이나 로케일 경로에서 감지된 로케일 정보만을 사용하여 작동합니다.
Accessing the locale information
Next.js 라우터를 통해 로케일 정보를 접근할 수 있습니다. 예를 들어, useRouter()
훅을 사용하면 다음 속성에 접근할 수 있습니다:
locale
은 현재 활성화된 로케일을 포함합니다.locales
는 설정된 모든 로케일을 포함합니다.defaultLocale
은 설정된 기본 로케일을 포함합니다.
getStaticProps
나 getServerSideProps
를 사용하여 페이지를 사전 렌더링 할 때, 로케일 정보는 함수에 제공된 컨텍스트에서 확인할 수 있습니다.
getStaticPaths
를 활용하는 경우, 설정된 로케일은 함수의 컨텍스트 파라미터의 locales
에서 제공되며, 기본 로케일은 defaultLocale
에서 제공됩니다.
Transition between locales
next/link
또는 next/router
를 사용하여 로케일 간에 전환이 가능합니다.
next/link
의 경우, locale
속성을 제공하여 현재 활성화된 로케일에서 다른 로케일로 전환할 수 있습니다. locale
속성이 제공되지 않으면, 클라이언트 전환 중에 현재 활성화된 로케일이 사용됩니다. 예를 들어:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/another" locale="fr">
To /fr/another
</Link>
)
}
next/router
메서드를 직접 사용하는 경우, 전환 옵션을 통해 사용할 locale
을 지정할 수 있습니다. 예를 들어:
import { useRouter } from 'next/router'
export default function IndexPage(props) {
const router = useRouter()
return (
<div
onClick={() => {
router.push('/another', '/another', { locale: 'fr' })
}}
>
to /fr/another
</div>
)
}
locale
만 전환하면서 동적 라우트 쿼리 값이나 숨겨진 href 쿼리 값과 같은 모든 라우팅 정보를 유지하려면 href
파라미터를 객체로 제공할 수 있습니다.
import { useRouter } from 'next/router'
const router = useRouter()
const { pathname, asPath, query } = router
// 로케일만 변경하고 href의 쿼리를 포함한 모든 라우트 정보를 유지합니다.
router.push({ pathname, query }, asPath, { locale: nextLocale })
router.push
의 객체 구조에 대한 자세한 내용은 여기에서 확인할 수 있습니다.
이미 로케일이 포함된 href
를 가지고 있다면, 로케일 접두사를 자동으로 처리하지 않도록 선택할 수 있습니다:
import Link from 'next/link'
export default function IndexPage(props) {
return (
<Link href="/fr/another" locale={false}>
To /fr/another
</Link>
)
}
Leveraging the NEXT_LOCALE
cookie
Next.js에서는 NEXT_LOCALE=the-locale
쿠키를 설정할 수 있으며, 이 쿠키는 accept-language
헤더보다 우선적으로 적용됩니다. 이 쿠키는 언어 전환 기능을 통해 설정할 수 있으며, 사용자가 사이트를 다시 방문할 때 /
에서 해당 쿠키에 지정된 로케일로 리디렉션됩니다.
예를 들어, 사용자의 accept-language
헤더에서 fr
로케일이 선호된다고 하더라도 NEXT_LOCALE=en
쿠키가 설정되어 있으면 사용자는 /
를 방문할 때 en
로케일로 리디렉션됩니다. 이 쿠키가 제거되거나 만료될 때까지 사용자는 en
로케일로 리디렉션됩니다.
Search Engine Optimization
Next.js는 사용자가 사용하는 언어를 인식하기 때문에 <html>
태그에 자동으로 lang
속성을 추가합니다.
Next.js는 페이지의 여러 형태를 자동으로 인식하지 않으므로, next/head
를 사용하여 hreflang
메타 태그를 추가해야 합니다. hreflang
에 대한 자세한 내용은 Google 웹마스터 문서 (opens in a new tab)를 참조하세요.
How does this work with Static Generation?
국제화된 라우팅은 Next.js 라우팅 레이어를 사용하지 않기 때문에
output: 'export'
와 호환되지 않습니다.output: 'export'
를 사용하지 않는 하이브리드 Next.js 애플리케이션은 완전히 지원됩니다.
Dynamic Routes and getStaticProps
Pages
getStaticProps
와 동적 라우트를 사용하는 페이지의 경우, 사전 렌더링 하려는 페이지의 모든 로케일 정보를 getStaticPaths
에서 반환해야 합니다. paths
를 지정하는 params
객체와 함께, 렌더링 할 로케일을 나타내는 locale
필드를 반환할 수 있습니다. 예를 들어:
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// `locale`이 제공되지 않으면 기본 로케일만 생성됩니다.
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
}
}
자동 정적 최적화가 되면서 getStaticProps
를 사용하는 페이지의 경우, 각 로케일에 대한 페이지 버전이 생성됩니다. 이는 getStaticProps
에 설정된 로케일 수에 따라 빌드 시간이 증가할 수 있기 때문에 중요한 사항입니다.
예를 들어, 50개의 로케일이 설정된 상태에서 10개의 비동적 페이지가 getStaticProps
를 사용하는 경우, 빌드 시 10개의 페이지에 각각 50가지 버전이 생성되므로 getStaticProps
가 500번 호출됩니다.
동적 페이지의 빌드 시간을 줄이려면 getStaticProps
와 함께 fallback
모드를 사용하는 것이 좋습니다. 이렇게 하면 getStaticPaths
에서 가장 많이 사용되는 경로와 로케일을 반환할 수 있으며, Next.js는 나머지 페이지를 요청 시 런타임에 생성합니다.
Automatically Statically Optimized Pages
자동 정적 최적화된 페이지의 경우, 각 로케일에 대한 페이지 버전이 생성됩니다.
Non-dynamic getStaticProps Pages
비동적 getStaticProps
페이지의 경우, 위에서 설명한 대로 각 로케일에 대한 버전이 생성됩니다. getStaticProps
는 렌더링 할 각 로케일에 대해 호출됩니다. 특정 로케일에 대한 사전 렌더링을 제외하고 싶다면, getStaticProps
에서 notFound: true
를 반환하면 해당 로케일의 페이지는 생성되지 않습니다.
export async function getStaticProps({ locale }) {
// 외부 API 엔드 포인트를 호출하여 게시물을 가져옵니다.
// 원하는 데이터 fetching 라이브러리를 사용할 수 있습니다.
const res = await fetch(`https://.../posts?locale=${locale}`)
const posts = await res.json()
if (posts.length === 0) {
return {
notFound: true,
}
}
// { props: posts }를 반환하면, Blog 컴포넌트는
// 빌드 시 `posts`를 prop으로 받게 됩니다.
return {
props: {
posts,
},
}
}
Limits for the i18n config
locales
: 총 100개의 로케일domains
: 총 100개의 로케일 도메인 항목
알아두면 좋은 정보: 이 제한은 초기에 빌드 시 발생할 수 있는 성능 문제를 방지하기 위해 추가되었습니다. Next.js 12의 미들웨어를 사용하여 이 제한을 우회할 수 있습니다.