- Published on
- ·11 min read
Next.js - 다국어(i18n) 설정 및 static html로 내보내기 Part 2
개요
이전 글인 Part 1에서 Next.js 프로젝트를 생성하고 다국어(i18n) 설정을 했습니다. 하지만, npm run build
명령어를 실행하면 오류가 발생하는 상태에서 글이 끝났습니다.
이번 글에서는 다국어 Static Export까지 가능하도록 설정해보겠습니다.
내용이 길어져 Part 1, Part 2로 나누어 정리했습니다.
다국어 Static Export 설정
Static HTML Export with i18n compatibility in Next.js에 영어로(😥) 잘 설명되어있습니다.
저는 거의 그대로 진행했으며 약간의 순서 변경 및 실수했던 부분에 주의할 점을 추가했습니다.
next-language-detector 설치
아래 명령어로 next-language-detector
라이브러리를 설치합니다.
npm i next-language-detector
i18n 설정 제거
next.config.js
파일을 열어서i18n
설정을 아래와 같이 제거합니다.
//const { i18n } = require('./next-i18next.config') // 제거
const nextConfig = {
output: 'export',
reactStrictMode: true,
//i18n // 제거
}
module.exports = nextConfig
[locale] 폴더 생성
pages
폴더 하위에[locale]
폴더를 생성합니다. (pages/[locale]
)
⚠ 주의할 점:locale
이 아닌 대괄호가 포함된[locale]
폴더입니다.
[locale] 폴더로 파일 이동
pages
폴더 하위에 있는_app.js
,_document.js
파일을 제외한 모든 파일을[locale]
폴더로 이동합니다.- 이 예제에서는
index.js
와second.js
파일을[locale]
폴더로 이동시킵니다. - 결과적으로
pages/[locale]/index.js
,pages/[locale]/second.js
- 이 예제에서는
lib 폴더
최상위 폴더에
lib
폴더를 생성합니다.lib/getStatic.js
파일을 생성하고 아래 내용을 추가합니다.
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import i18nextConfig from '../next-i18next.config'
export const getI18nPaths = () =>
i18nextConfig.i18n.locales.map((lng) => ({
params: {
locale: lng
}
}))
export const getStaticPaths = () => ({
fallback: false,
paths: getI18nPaths()
})
export async function getI18nProps(ctx, ns = ['common']) {
const locale = ctx?.params?.locale
let props = {
...(await serverSideTranslations(locale, ns))
}
return props
}
export function makeStaticProps(ns = {}) {
return async function getStaticProps(ctx) {
return {
props: await getI18nProps(ctx, ns)
}
}
}
lib/languageDetector.js
파일을 생성하고 아래 내용을 추가합니다.
import languageDetector from 'next-language-detector'
import i18nextConfig from '../next-i18next.config'
export default languageDetector({
supportedLngs: i18nextConfig.i18n.locales,
fallbackLng: i18nextConfig.i18n.defaultLocale
})
lib/redirect.js
파일을 생성하고 아래 내용을 추가합니다.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import languageDetector from './languageDetector'
export const useRedirect = (to) => {
const router = useRouter()
to = to || router.asPath
// language detection
useEffect(() => {
const detectedLng = languageDetector.detect()
if (to.startsWith('/' + detectedLng) && router.route === '/404') { // prevent endless loop
router.replace('/' + detectedLng + router.route)
return
}
languageDetector.cache(detectedLng)
router.replace('/' + detectedLng + to)
})
return <></>
};
export const Redirect = () => {
useRedirect()
return <></>
}
// eslint-disable-next-line react/display-name
export const getRedirect = (to) => () => {
useRedirect(to)
return <></>
}
pages 폴더 하위에 파일 생성
[locale]
폴더로 이동시켰던 파일들과 동일한 파일명으로 pages
폴더 하위에 생성합니다.
pages/index.js
,pages/second.js
파일을 생성합니다. 내용은 아래와 같습니다.
import { Redirect } from '../lib/redirect'
export default Redirect
언어를 감지해 해당 언어로 리다이렉트 시키는 역할을 합니다.
components 폴더 생성
components
폴더를 생성합니다.components/Link.js
컴포넌트 파일을 생성합니다.
⚠ 주의할 점: 원문에는legacyBehavior
가 없는데, Next.js 13버전 이상에서는 없으면 오류가 발생합니다.
import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
const LinkComponent = ({ children, skipLocaleHandling, ...rest }) => {
const router = useRouter()
const locale = rest.locale || router.query.locale || ''
let href = rest.href || router.asPath
if (href.indexOf('http') === 0) skipLocaleHandling = true
if (locale && !skipLocaleHandling) {
href = href
? `/${locale}${href}`
: router.pathname.replace('[locale]', locale)
}
// legacyBehavior 없으면 오류 (13버전 이상)
return (
<>
<Link href={href} legacyBehavior>
<a {...rest}>{children}</a>
</Link>
</>
)
}
export default LinkComponent
페이지 수정
pages/[locale]/index.js
와pages/[locale]/second.js
파일을 수정합니다.Link
import를 경로를@/components/Link
로 수정합니다.- 기존 getStaticProps 함수를 제거하고
@/lib/getStatic
에 선언된 함수를 사용합니다. - 아래 코드와 주석을 확인하세요.
pages/[locale]/index.js
import {useTranslation} from "next-i18next";
import {getStaticPaths, makeStaticProps} from "@/lib/getStatic"; // 추가
import Link from "@/components/Link"; // import 경로 변경(기존"next/link")
// 기존 내용 제거
// export async function getStaticProps({locale}) {
// return {
// props: {
// ...(await serverSideTranslations(locale, ['common'])),
// },
// };
// }
const getStaticProps = makeStaticProps(['common']) // 추가
export { getStaticPaths, getStaticProps } // 추가
export default function Home() {
const {t} = useTranslation(['common']);
return (
<div>
<h1>
{t('first')}
</h1>
<div>
<p><Link href={'/second'}>{t('second')}</Link></p>
<p><Link href={'/'} locale={'en'}>to English</Link> | <Link href={'/'} locale={'ko'}>한국어로</Link></p>
</div>
</div>
)
}
pages/[locale]/second.js
import {useTranslation} from "next-i18next";
import {getStaticPaths, makeStaticProps} from "@/lib/getStatic"; // 추가
import Link from "@/components/Link"; // import 경로 변경(기존"next/link")
// 기존 내용 제거
// export async function getStaticProps({locale}) {
// return {
// props: {
// ...(await serverSideTranslations(locale, ['common'])),
// },
// };
// }
const getStaticProps = makeStaticProps(['common']) // 추가
export { getStaticPaths, getStaticProps } // 추가
export default function Second() {
const {t} = useTranslation(['common']);
return (
<div>
<h1>
{t('second')}
</h1>
<div>
<p><Link href={'/'}>{t('first')}</Link></p>
<p><Link href={'/second'} locale={'en'}>to English</Link> | <Link href={'/second'} locale={'ko'}>한국어로</Link></p>
</div>
</div>
)
}
_document.js 파일 수정
pages/_document.js
파일을 열고 아래와 같이 수정합니다.
import Document, { Html, Head, Main, NextScript } from 'next/document'
import i18nextConfig from '../next-i18next.config'
class MyDocument extends Document {
render() {
const currentLocale = this.props.__NEXT_DATA__.query.locale || i18nextConfig.i18n.defaultLocale
return (
<Html lang={currentLocale}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
이제 작업은 끝났고 브라우저 확인과 static export가 정상적으로 되는 지 확인 하는 것만 남았습니다.
브라우저에서 확인
npm run dev
명령어로 실행하여, 브라우저에서 정상적으로 동작하는 지 확인합니다.
기존 동작과 거의 동일하게 동작하는 것을 확인할 수 있습니다. 기존 동작과 다른 부분은 http://localhost:3000/
로 표시되던 기본 언어가 http://localhost:3000/ko
로 표시된다는 점입니다.
http://localhost:3000/ko
과http://localhost:3000/en
에 접속해서 각 페이지가 각 언어로 정상적으로 나오는지 확인합니다.http://localhost:3000/ko/second
와http://localhost:3000/en/second
에 접속해서 각 페이지가 각 언어로 정상적으로 나오는지 확인합니다.
아래 이미지는 브라우저에서 확인한 화면입니다.
참고
저의 경우 브라우저에서 정상 동작했지만, 실행창에서 아래와 같은 오류를 발견했습니다.
- error API Routes cannot be used with "output: export". See more info here:
프로젝트 생성 시에 자동으로 만들어진 api/hello.js 파일 때문에 발생하는 오류로 해당 파일을 삭제하면 오류가 사라집니다.
Static Export
드디어 정적 파일 내보내기를 해보겠습니다.
아래 명령어를 실행합니다.
npm run build
성공 메시지가 나온 후 프로젝트 폴더를 확인하면 out
폴더가 생성되어 있는 것을 볼 수 있습니다.
live-server
등을 이용해서 해당 폴더를 웹 서버에서 실행하면 정상적으로 동작하는 것을 확인할 수 있습니다.
live-server: 실시간 reload를 지원하는 간단한 개발용 웹서버
저는 이렇게 만들어진 프로젝트를 CloudFlare Pages에 배포했습니다.
CloudFlare Pages: 정적 웹사이트를 무료로 배포할 수 있는 서비스로 GitHub과 연결하여 git push로 자동 배포되는 구성이 가능합니다.
코드
지금까지 작업한 코드는 아래 GitHub 저장소에서 확인할 수 있습니다.
https://github.com/hlog-kr/sample-i18n-static-export
git clone https://github.com/hlog-kr/sample-i18n-static-export.git
으로 저장소를 복제합니다.프로젝트 폴더에서
npm install
로 필요한 패키지를 설치합니다.npm run dev
로 브라우저에서 동작 확인이 가능합니다.npm run build
로 정적 파일 생성을 확인할 수 있습니다.