FE/React

React 프로젝트에 i18n으로 다국어 지원하기

SH_Roh 2022. 10. 31. 17:16
반응형

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://stackoverflow.com/questions/51883593/detect-browsers-language-with-i18next-browser-languagedetector-for-reactjs-web

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=quasimodo__&logNo=110179247680 

https://all-dev-kang.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B8%80%EB%A1%9C%EB%B2%8C%ED%95%9C-%EC%9B%B9%EC%9D%84-%ED%96%A5%ED%95%98%EC%97%AC-react-i18n-%EB%8B%A4%EA%B5%AD%EC%96%B4%EC%A7%80%EC%9B%90

 

 

반응형