코딩 공부 일지/React JS
React에서 react-i18next 사용하는 방법
헬로코딩
2023. 4. 20. 11:05
728x90
회사에서 맡은 프로젝트 중 처음으로 다국어 지원 웹사이트를 만들게 되었다. 관리자 페이지여서 SEO는 따로 필요 없었기 때문에 React로 진행하기로 했고, 그에 따라 react-i18next 사용방법을 정리해보기로 했다.
서버에서 다국어 지원을 제공하는 next-i18next 라이브러리도 있다.
다른 큰 회사에서는 어떤 방식으로 번역문을 관리하는지 모르겠지만 작은 프로젝트 단위에서는 json 방식으로 직접 보관하는 방법을 사용할 수 있다. (더 규모가 큰 프로젝트에서는 개발자가 아닌 번역 담당자가 번역문을 수정할 수 있도록 다른 방법을 찾아봐야 할 것 같긴 하다.)
단계 1: 패키지 설치
우선, 프로젝트 내에 react-i18next와 i18next 패키지를 설치한다.
$ npm i react-i18next i18next
단계 2: i18n config 설정
src 폴더 아래에 locales 라는 폴더를 새로 만들고 그 안에 i18n.ts (or i18n.js) 파일을 생성한다.
// src/locales/i18n.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import translationEN from "locales/en/translation.json";
import translationKO from "locales/ko/translation.json";
const resources = {
en: {
translation: translationEN
},
ko: {
translation: translationKO
}
};
i18n
.use(initReactI18next)
.init({
resources,
lng: "ko", // 기본 설정 언어, 'cimode'로 설정할 경우 키 값으로 출력된다.
fallbackLng: "en", // 번역 파일에서 찾을 수 없는 경우 기본 언어
interpolation: {
escapeValue: false
}
});
export default i18n;
이렇게 작성하면, translationEN과 translationKO를 import 하는 데서 에러가 날 텐데, 이제 번역 파일을 생성해줄 차례다.
단계 3: 번역 파일 생성
locales 폴더 아래에 번역 국가 폴더를 만들고 각 폴더 아래에 translation.json 파일을 생성한다.
// src/locales/ko/translation.json
{
"header": {
"mypage": "마이페이지",
"logout": "로그아웃",
"login": "로그인",
"register": "회원가입",
"language": "언어설정",
"help": "고객센터"
},
"nav": {
"dashboard": "대시보드",
"shopping-mall": "쇼핑몰 관리"
}
}
// src/locales/en/translation.json
{
"header": {
"mypage": "My page",
"logout": "Sign Out",
"login": "Sign In",
"register": "Sign Up",
"language": "Languages",
"help": "Help"
},
"nav": {
"dashboard": "Dashboard",
"shopping-mall": "Shopping Mall"
}
}
단계 4: 번들링을 위해 index.tsx에 import 하기
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './global.scss';
import App from './App';
import reportWebVitals from './reportWebVitals';
import "locales/i18n"; // 이렇게 import 한다.
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
단계 5: 번역 사용하기
// 사용할 컴포넌트
import React, { useState, useRef, useCallback, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import i18n from "locales/i18n";
export default function Header() {
const { t } = useTranslation();
const languageRef = useRef<null | HTMLDivElement>(null);
const navigate = useNavigate();
const [isLanguageMenuOpen, setLanguageMenuOpen] = useState<boolean>(false);
// 외부 클릭 시 닫기
const handleUserClose = useCallback((e: any) => {
if (isLanguageMenuOpen && languageRef.current !== null && !languageRef.current.contains(e.target)) setLanguageMenuOpen(false);
}, [isUserMenuOpen, isLanguageMenuOpen]);
useEffect(() => {
document.addEventListener("click", handleUserClose);
return () => document.removeEventListener("click", handleUserClose)
}, [handleUserClose]);
// 언어 변경하기
const changeLanguage = (lang: string) => {
i18n.changeLanguage(lang);
setLanguageMenuOpen(false);
};
return (
<header className="header">
<div className="header-gnb">
<div className="header-inner">
<nav className="header-gnb-nav">
<div className="header-gnb-nav-link" onClick={() => navigate("/user/signin")}>{t(`header.login`)}</div>
<div className="header-gnb-nav-link" onClick={() => navigate("/user/register")}>{t(`header.register`)}</div>
<div ref={languageRef} className="header-gnb-nav-link lang-en" onClick={() => setLanguageMenuOpen(prev => !prev)}>
{t(`header.language`)}
{isLanguageMenuOpen && (
<ul className="header-gnb-nav-link-dropDown">
<li className="header-gnb-nav-link-dropDown-item" onClick={() => changeLanguage("ko")}>한국어</li>
<li className="header-gnb-nav-link-dropDown-item" onClick={() => changeLanguage("en")}>English</li>
</ul>
)}
</div>
<div className="header-gnb-nav-link">{t(`header.help`)}</div>
</nav>
</div>
</div>
{isLoggingIn && (
<div className="header-snb" onMouseEnter={() => setSubMenuOpen(true)} onMouseLeave={() => setSubMenuOpen(false)}>
<div className="header-inner">
<nav className="header-snb-nav">
<div className="header-snb-nav-link">{t(`nav.dashboard`)}</div>
<div className="header-snb-nav-link">{t(`nav.shopping-mall`)}</div>
</nav>
</div>
</div>
)}
</header>
)
}
처음엔 다국어 지원이라고 해서 어려울 것 같아서 겁먹었는데 생각보다 너무 간편하게 라이브러리 구성이 되어 있어서 사용하기 굉장히 편리했다. 다음번엔 SSR에서도 다국어 지원을 구성하는 방법을 사용해봐야겠다.
728x90