i18n이란?
국제화(internationalization; i18n)의 줄임말이다. 언어 및 문화권 등이 다른 여러 환경에 대해 사용할 수 있도록 지원하는 것으로, 보통 텍스트 번역을 의미한다.
i18n 적용하기
i18n 설치
npm install i18next i18next-browser-languagedetector react-i18next
// or
yarn add i18next i18next-browser-languagedetector react-i18next
언어별로 json 파일 만들기
지원할 언어별로 폴더를 만들어서 번역할 내용을 관리해준다.
utils/locale/en/front.json
{
"gnb.home": "Home",
"gnb.weather": "Weather"
}
utils/locale/ko/front.json
{
"gnb.home": "홈",
"gnb.weather": "날씨"
}
같은 key를 지정해주고, 언어별로 번역할 내용을 value로 작성해주면 된다.
이 때 babel edit이라는 프로그램을 사용하면 파일 내용들의 관리를 쉽게 할 수 있다.
babel edit은 json이나 yaml 확장자는 물론 다양한 파일을 여러 언어로 작성 및 관리할 수 있게 해주는 에디터이다.
https://www.codeandweb.com/babeledit
i18next를 클릭한다.
아까 생성한 ko와 en 폴더의 상위폴더인 locale을 선택해준다.
Configure languages에서 언어를 추가해준다.
각 key값의 언어마다 값들을 편하게 지정해줄 수 있다.
Save project를 클릭해 변경사항을 저장해준다.
저장을 완료하면 .babel 파일이 생긴다.
i18n 기본 설정
import i18n, { InitOptions } from 'i18next'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import enFront from './en/front.json'
import koFront from './ko/front.json'
i18n
.use(LanguageDetector)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources: {
en: {
front: enFront,
},
ko: {
front: koFront,
},
},
fallbackLng: 'en',
debug: false,
ns: ['front'],
defaultNS: 'front',
keySeparator: false, // we use content as keys
allowObjectInHTMLChildren: true,
interpolation: {
escapeValue: false, // not needed for react
formatSeparator: ',',
},
} as InitOptions)
export default i18n
기본적인 react i18next 세팅방법은 위와 같다.
LanguageDetector를 사용하면 자동으로 사용자 브라우저의 언어를 인식해 해당 언어로 컨텐츠를 번역해준다.
아까 지정한 언어별 json 파일을 불러와 resources 옵션에 같은 key로 지정해준다.
useI18n hook 만들기
자주 쓰이는 t 함수를 따로 빼서 만들어준다.
import { TFunction } from 'i18next'
import { useTranslation } from 'react-i18next'
export function useI18n(): TFunction {
const { t } = useTranslation()
return t
}
컴포넌트에서 사용하기
위와 같이 GNB에서 언어 변경 버튼으로 언어를 변경하도록 만들어보자.
routes/index.tsx
// ...
import i18n from 'utils/locale'
const App = () => {
// ...
useEffect(() => {
i18n.changeLanguage() // 컴포넌트가 마운트될 때 브라우저의 언어를 감지해서 로컬스토리지에 저장.
}, [])
// ...
return (
<div className={styles.container}>
<GNB />
<main className={styles.app}>
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='weather' element={<Weather />} />
</Routes>
</main>
</div>
)
}
export default App
language detector는 'querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag', 'path', 'subdomain' 순서대로 유저의 locale을 탐색한다.
(브라우저의 기본 언어 세팅은 브라우저 dev tool의 콘솔에서 window.navigator.language에서 확인가능하다.)
탐색이 완료되면 로컬스토리지에 i18nextLng라는 key에 언어 정보가 저장이 된다.
routes/_shared/GNB/index.tsx
// ...
import { useEffect, useI18n, useState } from 'hooks'
import i18n from 'utils/locale'
const navData = ['home', 'weather']
const navURI = ['/', '/weather']
const GNB = () => {
const t = useI18n()
const [theme, setTheme] = useRecoilState(themeState)
const [language, setLanguage] = useState('')
useEffect(() => {
const i18nLang = localStorage.getItem('i18nextLng')
if (i18nLang !== null) {
setLanguage(i18nLang.toUpperCase().substring(0, 2) === 'EN' ? 'KO' : 'EN')
}
}, [])
// ...
const handleLocaleClick = () => {
setLanguage(language === 'EN' ? 'KO' : 'EN')
i18n.changeLanguage(language.toLowerCase())
}
return (
<nav className={styles.gnb}>
<ul>
{navData.map((data, i) => {
return (
<li key={data}>
<NavLink to={navURI[i]} className={({ isActive }) => cx({ [styles.isActive]: isActive })}>
{`${t(`front:gnb.${data}`)}`}
</NavLink>
</li>
)
})}
<li>
<button type='button' onClick={handleThemeClick} className={styles.themeBtn}>
{theme === 'light' ? <Sun /> : <Moon />}
</button>
</li>
<li>
<button type='button' onClick={handleLocaleClick}>
{language}
</button>
</li>
</ul>
</nav>
)
}
export default GNB
컴포넌트가 마운트될 때 i18nextLng의 값을 로컬스토리지에서 가져와준 후, 현재 언어가 한국어이면 다음 바뀔 언어로 EN을 띄워줄 수 있도록 language state를 설정해준다.
이 때 substring(0, 2)을 해주는 이유는 navigator.language가 크롬에서는 ko이지만, 파이어폭스에서는 ko-KR이기 때문이다.
navigator.language | Internet Explorer | Chrome | Firefox |
브라우저별 지원 여부 | X | 지원 | 지원 |
브라우저 언어값이 한국어일 경우 출력되는 값 | undefined | ko | ko-KR |
그리고 각 key값을 이용해서 언어를 바꿔줄 부분에서는 {`${t(`front:gnb.${data}`)}`}와 같은 형식으로 써주면 된다. 이 때 front는 처음에 i18n 설정에서 ns로 설정해준 값이고, 그 뒤의 값은 json파일에서 지정해준 key값이다.
json 파일에서 gnb.todo, gnb.weather와 같이 gnb에서 사용할 값들은 모두 gnb.~~로 통일해주었기 때문에 map을 돌 때 gnb.뒤의 값만 바꿔주는 식으로 구현을 했다.
간단하게 nav메뉴에서만 적용을 해봤는데, 이런 방식으로 babel edit과 i18n을 이용해서 편리하게 다국어 지원을 할 수 있다.
References
https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-react-app-with-react-i18next
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=quasimodo__&logNo=110179247680
'FE > React' 카테고리의 다른 글
[React] Storybook 사용해보기 (0) | 2022.12.02 |
---|---|
React Infinite Carousel 만들기(TS) (1) | 2022.11.24 |
React 스톱워치 구현하기(TS) (0) | 2022.11.17 |
React 다크 모드 구현하기(with Recoil, SCSS) (0) | 2022.11.06 |
React의 LifeCycle (0) | 2021.06.30 |