[react]react-i18next 다국어 처리

K·2023년 7월 31일

react

목록 보기
2/8

웹페이지 작업 중 다국어 처리 기능이 필요하다고 하여
react-i18next 다국어 라이브러리를 사용하게 되었다.

i18next와 react-i18next는 둘 다 다국어화(i18n)를 쉽게 구현 할 수 있도록 도와주는 라이브러리로

i18next는 순수 자바스크립트 기반의 다국어 라이브러리,
react-i18next는 react에 더욱 최적화 된 다국어 라이브러리로 보면된다.

i18next는 react에서도 사용가능하나, react-i18next이 더 단순화 작업이 가능하였기에
react-i18next사용을 추천하고 싶다.

*사실 프로젝트 진행할 때는 두가지 모두를 설치하고 작업을 하였다.

react-i18next 설치

npm i react-i18next

react-i18next (자세한 설명 참고)

i18next.js 만들기

import LanguageDetector from 'i18next-browser-languagedetector'; // 언어 감지(언어 변경 부분)
import { initReactI18next } from 'react-i18next'; //react와 i18next를 통합
import KR from './kr.json'
import EN from './en.json'
const resource = {
    "ko": {
        translation: KR
    },
    "en": {
        translation: EN
    }
}
i18n
    .use(LanguageDetector)
    .use(initReactI18next)
    .init({
        resources: resource,
        fallbackLng: "en", // 기본감지 언어 en
        // debug: true,
        interpolation: {
            escapeValue: false // 특수문자를 이스케이프(escape)하지 않도록 하는 옵션
        },
        // react: {
        //     bindI18n: '', // prevent react-i18next rerender
        // },
    })
export default i18n

다중언어.json 만들기

나는 프로젝트 상 4개의 언어, 또 대량의 번역이 필요했기에 언어별로 json 파일을 따로 만들어서 작업하였다.
(기록물에는 편의상 한국어와 영어만 사용함)
이때 key 변수명에 띄어쓰기를 해도 출력이 되나,
추후 에러발생의 소지가 있기에 붙여쓰고, 간단한 핵심언어로 사용하는 것을 권장한다(특수문자 사용 역시 비추천).

(예. "korea language" : "한국어 " (x) / "korean": "한국어" (o))

{
    "LanguagePage": {
        "korean": "한국어",
        "english": "영어",
        "chinese": "중국어",
     }
}
 en.json //영어
{
    "LanguagePage": {
        "korean": "korean",
        "english": "english",
        "chinese": "chinese",
     }
}

언어 선택별 언어 감지

보통 언어를 선택하는 곳은 Topnav에 위치하기에 listbox를 만들어 언어를 구분했고, 해당 언어를 선택시
어떤언어인지 바로 감지 할 수 있도록 i18n.changeLanguage를 사용하였다. i18n.changeLanguage는 i18next 라이브러리에서 제공하는 메서드로 언어변경 시 사용되며, 이 메서드를 호출하면 어플리케이션의 언어가 동적으로 변경되고, 해당 언어에 맞는 다국어 번역 메시지가 적용됨.

export default function Topnav(){
	 const Language = [
        { name: '한국어', code:'ko', id:2},
        { name: 'English', code:'en', id:3 },
        ]
        
	  let initialLanguageCode = i18n.language;
      
	 const [selectedLag, setSelectedLag] = useState(() => {
          return Language.find(lang => lang.code === initialLanguageCode) || Language[0];
      		})
            
      const handleLanguageOptionClick = (option) => {
        setSelectedLag(option);
        switch (option.id) {
          case 2:
            i18n.changeLanguage('ko');
            break;
          case 3:
            i18n.changeLanguage('en');
            break;
          case 4:
            i18n.changeLanguage('zh-CN');
            break;
          case 5:
            i18n.changeLanguage('zh-TW');
            break;
          default:
            break;
        }
        window.location.reload()
      };
      
      **리스트박스 구현 코드
      return (
         <div className='w-44'>
          <Listbox value={selectedLag} onChange={setSelectedLag}>
            <div className='relative text-[]'>
              <Listbox.Button className='relative w-full cursor-default rounded-lg bg-white pl-3 pr-10 text-center'>
                <span className='block truncate'>{selectedLag.name}</span>
                <span className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-5'>
                  <RiArrowDownSFill className='h-6 w-6 text-black' aria-hidden='true' />
                </span>
              </Listbox.Button>

              <Transition
                as={Fragment}
                leave='transition ease-in duration-100'
                leaveFrom='opacity-100'
                leaveTo='opacity-0'
              >
                <Listbox.Options className='absolute mt-1 max-h-60 w-full overflow-auto rounded-md py-1'>
                  {Language.map((option) => (
                    <Listbox.Option
                      key={option.id}
                      className={({ active }) =>
                        `relative cursor-default select-none py-2 bg-white shadow-md ${
                          active ? 'bg-amber-100 text-amber-900 text-sm' : 'text-gray-900 text-sm'
                        }`
                      }
                      value={option}
                      onClick={() => handleLanguageOptionClick(option)}
                    >
                      {({ selectedLag }) => (
                        <>
                          <span
                            className={`block truncate ${selectedLag ? 'font-medium' : 'font-normal'}`}
                          >
                            {option.name}
                          </span>
                          {selectedLag ? (
                            <span className='absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600'>
                              {/* < className="h-5 w-5" aria-hidden="true" /> */}
                            </span>
                          ) : null}
                        </>
                      )}
                    </Listbox.Option>
                  ))}
                </Listbox.Options>
              </Transition>
            </div>
          </Listbox>
        </div>
        )
       }
      

useState 훅은 이 표현식의 결과로 초기 상태를 설정하고 selectedLag라는 상태 변수를 생성한다.
이 상태 변수는 현재 선택된 언어를 담게 되며,
이후에 언어 선택이 변경될 때마다 해당 값을 업데이트할 때 사용된다.
(즉, useState를 사용하는 이유는 상태값 변경에 따라 업데이트 하려고 사용)

Language.find(lang => lang.code === initialLanguageCode) 부분은 Language 배열에서 initialLanguageCode와 일치하는 언어 객체를 찾는다.
initialLanguageCode는 이전 코드에서 얻은 현재 애플리케이션의 언어 코드이다.

만약 initialLanguageCode와 일치하는 언어 객체가 없다면 Language[0]을 선택한다. 이는 배열의 첫 번째 언어 객체를 선택하라는 의미이다. 언어 설정이 유효하지 않을 경우를 대비하여 기본적인 언어를 설정하는데 사용된다.

return Language.find(lang => lang.code === initialLanguageCode) || Language[0];에서 Language에서 ()의 조건을 찾는 다는 건데 lang은 ()안에서만 사용하는 변수로 변수값으로 사용불가한 값을 제외한 모든 값을 다 쓸 수 있다.
lang.code를 사용한 이유는 initialLanguageCode와 비교해야하기 때문에 id 가 아닌 code로 일치값을 찾아 적용할 수 있기 때문이다.

리스트박스는 headless ui에서 listbox 코드를 사용했다.
headless ui

다국어 번역 작업

Main.js
import { useTranslation } from 'react-i18next'

const Main = forwardRef((props, ref)=> {
 const { t, i18n } = useTranslation()
 
  return (    
  <div>
   {t('LandingPage.korea')}
  </div>
)}

따라서 topnav에서 선택된 언어에 따라 {t('')}에 해당하는 언어가 번역된다.

1개의 댓글

comment-user-thumbnail
2023년 7월 31일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기