4. 다국어 지원 (next‐i18next) - SaekKkanDa/OppaManyColorTone GitHub Wiki
프로젝트에 다국어 기능 추가하기
📈 개요
우리 사이트 이용자들의 유입경로 확인 결과
생각보다 다양한 국가에서 우리 사이트에 접속한다는 점이 발견되었고
기존 다국어 기능의 안정성을 높여 더 많은 유저 확보를 하자는 안건이 나왔다!
(이미지는 구글 애널리틱스의 인구통계 - 우선은 2순위인 미국과 다른 나라들도 포용할 수 있는 영어부터!)
기존에 “react-intl”
라는 라이브러리를 사용하고 있었지만, 몇가지 불편한 점들이 있었다.
- 새로고침하면 선택했던 언어 설정이 날라가 다시 한국어로 돌아오는 문제
- Next.js를 도입하면서 생긴 서버사이드 렌더링 문제
가 발생해 이번에 새로운 라이브러리로 교체하자는 의견이 나왔고 최종적으로는 “next-i18next”
가 채택되었다!
🌐 Why “next-i18next” ?
1. 우선, Next.js와 호환성이 좋다.
어떤점에서 호환성이 좋다고 하는거냐면, Next.js는 pages directory (혹은 app directory)를 따르고, SSR (server-side rendering)과 CSR (client-side rendering)을 모두 고려해줘야 하는데, next-i18next
는 간단한 셋팅만으로도 이를 지원해준다.
2. Next.js의 translation 기능성을 보완
Next.js가 internationalised routing을 지원하긴 하지만, translation에 대한 기능을 따로 갖고 있지는 않다.
그저 모든 언어 파일들을 URL로 관리하고 이를 sync해 줄 뿐이다.
이를 보완하기 위해 next-i18next
는 translation 파일들을 관리하고 React components를 번역해줄 components/hooks를 관리해주고 있다.
=> Next.js의 디렉토리 구조와 SSR/CSR을 고민해야하는 프로젝트에 사용하기 적합해 보인다!
🎧 그래서 어떻게 사용하는가?
next-i18next.config.js
파일을 프로젝트 최상단에 위치하고, appWithTranslation
의 t
(translate) 함수를 컴포넌트에 사용하면 next-i18next
를 사용할 형태를 갖추게 된다고 한다.
그리고나서 서버 사이드 translation을 위해 getStaticProps
혹은 getServerSideProps
을 page-level 컴포넌트에서 사용해주면 된다.
🔧 Setup
1. 설치
yarn add next-i18next react-i18next i18next
2. 언어별 파일 추가
기본적으로 next-i18next
는 translations 파일들을 로컬 디렉토리 구조에서 로드하고, 서버 사이드에서 로드 한다.
언어 파일들은 json 형식으로 아래 디렉토리 구조를 따르면 된다.
.
└── public
└── locales
├── en
| └── common.json
└── ko
└── common.json
3. 프로젝트 셋업
next-i18next.config.js
파일을 프로젝트 최상단에 위치하는데, 이는 next-i18next
에게 defaultLocale
과 다른 locales를 알게 해주어, 서버에서 preload 할 수 있게 해준다.
next-i18next.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
/** @type {import('next-i18next').UserConfig} */
module.exports = {
i18n: {
locales: ['en', 'ko'], // 지원하는 언어 목록
defaultLocale: 'en', // 기본 언어 설정
},
localePath: path.resolve('./public/locales'), // 번역 파일 경로
fallbackLng: 'en', // 기본 언어 설정
ns: ['common'], // 사용하는 namespace 목록
defaultNS: 'common', // 기본 namespace
debug: process.env.NODE_ENV === 'development',
};
그리고, 위에서 만들어준 설정들을 next.config.js
에 넘겨준다. (localised URL routing을 가능하게 해준다.)
next.config.js
const { i18n } = require('./next-i18next.config.js');
const nextConfig = {
// ...
i18n,
};
module.exports = withSentryConfig(
nextConfig,
);
아래는 프로젝트에서 번역 기능을 위해 사용하게 될 next-i18next
의 세가지 함수이다.
- appWithTranslation
import { appWithTranslation } from 'next-i18next'
const App = ({ Component, pageProps }: AppProps) => (
<Component {...pageProps} />
)
export default appWithTranslation(MyApp);
- serverSideTranslations
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
return {
props: {
...(await serverSideTranslations(locale ?? 'en', ['common'])),
},
};
};
- useTranslation
import { useTranslation } from 'next-i18next'
export const Footer = () => {
const { t } = useTranslation('common')
return (<p>{t('description')}</p>);
}
더 자세한 사용법은 공식문서를 참고 next-i18next 공식문서
👩🏻💻 트러블슈팅
지금 생각하면 필요 이상으로 시간을 너무 많이 썼다는 생각이 들지만, 당시에는 자바스크립트 모듈에 대한 깊은 이해가 없었기 때문에 발생한 이슈라고 생각한다.
상황은 이러했다.
- “next-i18next” 라이브러리 사용을 위해
next-i18next.config.js
파일 추가,next.config.js
파일 (기존에는next.config.mjs
였음)을 수정했는데, “next-i18next” 라이브러리가 파일을 찾지 못한다는 에러 발생 - 파일 확장자를
.cjs
혹은.mjs
로 변경하거나 내부 코드를 수정하면 파일 문법이 맞지 않는다는 에러 발생 - 계속 파일 확장자와 파일 내 모듈 로드 방식을 번갈아가며 변경하였지만 두 에러가 계속 반복해서 발생함
원인은 이거였다.
package.json
파일에서 type이 "module"
로 되어있는데 (ESM module),
알고보니 “next-i18next”에서는 ESM module을 지원하지 않았던 것이다.
따라서 package.json
의 type을 지우고 (그럼 기본값인 CommonJS로 해석), 두 파일 모두 .js
확장자로 바꿔주었다.
자바스크립트 모듈 시스템이 기본으로 CJS로 설정된다는 것과, package.json
의 type
으로 이를 설정해줄 수 있다는 것을 옛날에 듣고 흘렸었는데, 이번 계기로 인해 확실히 알게 되었다.
그리고 ESM module과 CJS의 차이점에 대해 단순히 import / require 을 사용한다는 점 말고도 더 찾아보고 공부하게 되었다.
공부한 내용은 블로그에 기록 자바스크립트 모듈 | CJS와 ESM의 차이