어쩌다 알게 된 ƪ(•̃͡•̃͡ ƪ

[CRA/Plain React SPA] 다국어 설정 i18n.js 본문

개발/🟦 React

[CRA/Plain React SPA] 다국어 설정 i18n.js

비니_ 2025. 11. 14. 14:21
728x90

 

📂/src/i18n.js

import * as i18n from 'i18next'; // 다국어 핵심 라이브러리
import { initReactI18next } from 'react-i18next'; // react용 다국어 초기화

import Backend from 'i18next-http-backend'; // json파일을 HTTP로 불러오는 기능
import LanguageDetector from 'i18next-browser-languagedetector'; // 브라우저에서 언어 감지(navigator, localStorage, cookie 등)

import { getCurrentLang } from './helpers/locales'; // 프로젝트에서 현재 언어 가져오는 커스텀 함수

const defaultNS = 'translation'; // 기본 다국어 json 파일 명
const fallbackNS = [
  'menus',
  // 등등 폴더 내에 있는 파일들 써줘야 가져다가 쓸 수 있음
];
const ns = [defaultNS, ...fallbackNS]; // 실제 i18n 초기화 시 전체 등록할 네임스페이스 목록

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    lng: getCurrentLang(), // 초기 언어 설정
    debug: false,
    returnObjects: false,
    returnEmptyString: false,
    ns, // 등록할 모든 네임스페이스 배열
    defaultNS, // 기본 참조 네임스페이스
    fallbackNS, // 기본 참조에 없으면 순서대로 검색할 네임스페이스
    backend: {
      loadPath: `/locales/{{lng}}/{{ns}}.json?ts=${Date.now()}`, // json 파일 경로 // ts=${Date.now() 캐시 방지
    },
    compatibilityJSON: 'v3',
    interpolation: {
      escapeValue: false,
    },
    react: {
      useSuspense: false, // suspense 없이 렌더링
      transSupportBasicHtmlNodes: true, // <strong>, <br> 등 HTML 태그 번역 지원
    },
    saveMissing: false,
    missingKeyHandler: false,
    saveMissingTo: 'current',
    parseMissingKeyHandler: (key) => key, // 키가 없으면 그대로 반환 ex: t('없음') -> '없음'
    reloadResources: true, // 런타임에서 JSON으로 다시 로드 가능
    cleanCode: true,
    partialBundledLanguages: false,
  });

export default i18n;

 

 

 

📂/src/index.js

import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';

import './styles/styles.scss';
import '@nosferatu500/react-sortable-tree/style.css';
import './i18n'; // 다국어 import
import App from './App';
import { ThemeContextProvider } from './contexts/themeContext'; // 테마 사용할 시

window.progressbar = null;

const container = document.getElementById('root');
const root = createRoot(container);

moment.tz.setDefault('Asia/Seoul');

root.render(
	<Router>
		<React.StrictMode>
			<ThemeContextProvider>
				<App />
			</ThemeContextProvider>
		</React.StrictMode>
	</Router>,
);

 

-- index.js (상위 페이지)에서 import 해주면 하위 페이지에 알아서 다국어 적용 가능

 

  • i18n.js에서 i18next.init()가 실행되면서 전역(i18next singleton) 설정이 완료됨
  • 이 시점 이후에 어떤 컴포넌트에서 useTranslation()을 호출해도 동일한 i18n 인스턴스를 참조함
  • 하위 컴포넌트는 useTranslation()만 쓰면 번역 가능

 

** 하위 컴포넌트 다국어 사용 방법

import { useTranslation } from 'react-i18next';

export default function Header(){
	const { t } = useTranslation();
    
    return(
    	<div>{t('test')}</div>	// 기본 다국어.json에서 찾아서 나옴
    	<div>{t('menu.test')}</div>	// menu.json에서 찾아서 나옴
    )
}

 

 

 

** 기본 json, json 경로 등 설정 custom 컴포넌트 예시

import { useContext } from 'react';
import { useTranslation } from 'react-i18next';

import moment from 'moment';

import ThemeContext from '../contexts/themeContext';

import { getItem, setItem } from '../lib/localstorage';


const LANG_KEY = 'i18nextLng';
const LANGUAGE_LIST = {
	en: {
		text: 'English',
		lng: 'en',
		icon: 'CustomUsa',
	},
	ko: {
		text: '한국어',
		lng: 'ko',
		icon: 'CustomKor',
	},

};

export const getCurrentLang = () => getItem(LANG_KEY) ? getItem(LANG_KEY) : 'ko';
export const setCurrentLang = (lang) => setItem(LANG_KEY, lang);

export const getLangWithKey = (key) =>
	LANGUAGE_LIST[key];

export const useChangeLanguage = () => {
  const { i18n } = useTranslation();
  const { setLang } = useContext(ThemeContext);

  const updateLanguage = async (lang) => {
    i18n.services.resourceStore.data = {};
    await i18n.changeLanguage(lang);
    moment.locale(lang);
    setLang(lang);
    setCurrentLang(lang);
  }

  return { updateLanguage };
}

 

 

 

 

 

728x90
Comments