본문 바로가기
JavaScript/React

다국어 라이브러리 react-i18next

by curious week 2025. 6. 9.
 

Introduction | react-i18next documentation

Last updated 2 months ago

react.i18next.com


1단계: 기본 설정 및 번역 시스템 이해

목표

  • 번역 키를 관리하는 JSON 구조를 익히고
  • react-i18next를 사용해 컴포넌트에서 번역하는 흐름을 완전히 이해

1. i18n이란?

  • i18n = internationalization의 줄임말 (i와 n 사이에 18글자)
  • 앱을 여러 언어로 지원할 수 있도록 구성하는 기술
  • 문자열, 날짜, 단위 등을 사용자 언어에 맞게 자동 출력하도록 지원

2. i18next와 react-i18next

  • i18next: 국제화 핵심 엔진 (JS 기반)
  • react-i18next: i18next를 React에서 쉽게 쓰도록 만든 래퍼 라이브러리

3. 설치

npm install i18next react-i18next

4. 디렉토리 구조 

src/
├── i18n/
│   └── config.ts
├── locales/
│   ├── en/
│   │   └── translation.json
│   └── ko/
│       └── translation.json

5. 번역 JSON 파일 구조

locales/en/translation.json

{
  "greeting": "Hello, {{name}}!",
  "user": {
    "profile": {
      "name": "Name",
      "email": "Email"
    }
  },
  "button": {
    "save": "Save",
    "cancel": "Cancel"
  }
}

locales/ko/translation.json

{
  "greeting": "안녕하세요, {{name}}님!",
  "user": {
    "profile": {
      "name": "이름",
      "email": "이메일"
    }
  },
  "button": {
    "save": "저장",
    "cancel": "취소"
  }
}

6. i18n 초기화 (src/i18n/config.ts)

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import en from '../locales/en/translation.json';
import ko from '../locales/ko/translation.json';

i18n
  .use(initReactI18next) // React에 연결
  .init({
    resources: {
      en: { translation: en },
      ko: { translation: ko },
    },
    lng: 'ko', // 초기 언어
    fallbackLng: 'en', // 없는 경우 대체
    interpolation: {
      escapeValue: false, // React에선 XSS 방지 불필요
    },
  });

export default i18n;

7. i18n 초기화 연결 (src/main.tsx 또는 index.tsx)

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './i18n/config'; // 반드시 App보다 먼저 import

const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(<App />);

8. 컴포넌트에서 번역 사용 예시

import { useTranslation } from 'react-i18next';

function Profile() {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t('user.profile.name')}</h1>
      <p>{t('user.profile.email')}</p>
      <button>{t('button.save')}</button>
    </div>
  );
}

9. 폴더 구조/네이밍 팁

  • 도메인 단위로 분리 가능 (예: auth.json, dashboard.json)
  • 통합형 translation.json vs. 모듈별 네임스페이스 방식 선택 가능 (→ 2단계에서 다룸)

 

2단계: 언어 전환 및 커스텀 설정

목표

  • 사용자가 직접 언어를 선택할 수 있게 만들고
  • 브라우저 설정, 로컬스토리지, URL 등 다양한 방식으로 언어 상태를 감지하고 반영하기

1. 언어 변경 및 확인

언어 변경

import i18n from 'i18next';

i18n.changeLanguage('ko'); // 즉시 한국어로 전환

현재 언어 확인

console.log(i18n.language); // 예: "en"

2. 컴포넌트

import { useTranslation } from 'react-i18next';

export const LanguageSelector = () => {
  const { i18n } = useTranslation();

  const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    i18n.changeLanguage(e.target.value);
  };

  return (
    <select onChange={handleChange} value={i18n.language}>
      <option value="en">English</option>
      <option value="ko">한국어</option>
    </select>
  );
};

이 컴포넌트를 헤더나 앱의 상단에 배치하면 됩니다.


3. 기본 언어, fallback 언어 설정

i18n.init({
  lng: 'ko', // 기본 언어
  fallbackLng: 'en', // 번역 키가 없을 경우 대체 언어
  // ...
});

4. 브라우저 언어 감지 (i18next-browser-languagedetector)

설치

npm install i18next-browser-languagedetector

설정 (i18n/config.ts)

import LanguageDetector from 'i18next-browser-languagedetector';

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    // ...
    detection: {
      order: ['localStorage', 'cookie', 'navigator', 'htmlTag'],
      caches: ['localStorage'], // 설정한 언어를 localStorage에 저장
    },
  });
  • 순서대로 감지합니다: localStorage → 쿠키 → 브라우저 → <html lang="">
  • 캐시 저장을 통해 사용자가 선택한 언어를 유지

5. 다국어 URL 설계 (/en/about, /ko/about 등)

방식 1: react-router나 wouter에서 경로를 언어별로 구성

<Route path="/:lang/about">
  <AboutPage />
</Route>

방식 2: 언어에 따라 리디렉션

const { lang } = useParams();
i18n.changeLanguage(lang); // URL에서 언어를 읽어 설정

정리

i18n.changeLanguage() 언어 수동 전환
i18n.language 현재 설정된 언어 반환
LanguageDetector 브라우저/쿠키/로컬스토리지에서 감지 가능
LanguageSelector 사용자 언어 선택 UI
fallbackLng 번역 누락 시 대체 언어 설정
다국어 URL 지원 URL 경로에서 언어 구분

3단계: 복잡한 번역 처리

목적: 실제 UI에서 동적 데이터 및 HTML 포함된 텍스트를 정확하고 안전하게 처리하기


1. 문자열 보간 (Interpolation)

변수값을 문자열에 삽입

JSON:

{
  "greeting": "Hello, {{name}}!"
}

사용:

t('greeting', { name: userName });

2. 조건부 번역 (Plural Forms)

단수/복수 등 숫자에 따라 다르게 번역

JSON:

{
  "item": "{{count}} item",
  "item_plural": "{{count}} items"
}

사용:

t('item', { count: 1 }); // "1 item"
t('item', { count: 5 }); // "5 items"

3. <Trans> 컴포넌트 사용법

HTML 태그나 리액트 컴포넌트를 번역에 포함할 때 사용

JSON:

{
  "welcome": "Welcome to <strong>{{siteName}}</strong>!"
}

사용:

import { Trans } from 'react-i18next';

<Trans i18nKey="welcome" values={{ siteName: 'MySite' }} components={{ strong: <strong /> }} />

※ components 안에 들어간 태그가 <strong>처럼 그대로 번역문에 적용됩니다.


4. React 요소 포함된 텍스트 안전하게 출력

  • <br />, <strong>, 링크 같은 마크업 포함
  • <Trans>로 안전하게 구성
  • dangerouslySetInnerHTML은 지양 (XSS 위험)

5. 다국어 폼 에러 메시지 대응

JSON:

{
  "errors": {
    "required": "This field is required.",
    "email": "Please enter a valid email address."
  }
}

사용:

t('errors.required');

실제 react-hook-form이나 Zod의 에러 메시지 매핑 시 유용


6. 공통 메시지 분리

  • 단일 JSON에 몰아서 관리하지 않기
  • 기능별, 영역별로 나눔
    예:
└── locales/
    ├── en/
    │   ├── common.json
    │   ├── buttons.json
    │   └── errors.json
    └── ko/
        ├── common.json
        ├── buttons.json
        └── errors.json

초기화 예시:

resources: {
  en: {
    common: require('./locales/en/common.json'),
    buttons: require('./locales/en/buttons.json'),
    errors: require('./locales/en/errors.json')
  }
}

사용:

t('buttons.save'); // buttons.json에서 "save" 키 사용
t('errors.email'); // errors.json에서 이메일 에러

문자열 보간 사용자 이름, 날짜 등 동적 데이터 출력
복수형 처리 1 item vs 2 items
<Trans> <strong>, <br />, <a> 등 포함한 텍스트 번역
안전한 요소 출력 XSS 걱정 없는 방식으로 UI에 번역 삽입
에러 메시지 대응 폼 유효성 에러 다국어 대응
공통 JSON 파일 분리 메시지 재사용성과 유지 보수 향상

4단계: 최적화 및 고급 설정

목적: 성능 최적화, 비동기 번역 로딩, SSR 등 실무 확장 적용


1. resources 대신 backend 비동기 로딩

기본 개념

  • 모든 번역 JSON을 초기 번들에 포함하면 비효율적
  • i18next-http-backend를 이용해 필요할 때 번역 파일을 가져옴

설정 예시:

import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';

i18n
  .use(HttpBackend)
  .use(initReactI18next)
  .init({
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json', // 언어, namespace별 경로
    },
    fallbackLng: 'en',
    ns: ['common', 'auth'],
    defaultNS: 'common',
  });

2. 언어 변경 시 Flash 방지

문제: i18n.changeLanguage() 직후 빈 화면이나 영어가 보이는 순간 발생

해결:

  • useSuspense: true 설정
  • <Suspense fallback={<Loading />} /> 사용으로 딜레이 처리
import { Suspense } from 'react';

<Suspense fallback={<div>Loading translations...</div>}>
  <App />
</Suspense>

3. 번역 캐싱

브라우저에 번역 결과를 저장하여 재방문 시 빠르게 로딩

i18next-localstorage-backend 사용

  • 번역을 localStorage에 저장해 불필요한 요청 방지
import LocalStorageBackend from 'i18next-localstorage-backend';

i18n
  .use(LocalStorageBackend)
  .init({
    backend: {
      prefix: 'i18next_res_',
      expirationTime: 7 * 24 * 60 * 60 * 1000, // 7일
    }
  });

4. Lazy Load (언어별 코드 분할)

  • 초기 로딩 시 ko/common.json만 불러오고, 다른 언어/네임스페이스는 이후 로딩
  • 코드 스플리팅 + 성능 최적화
i18n.init({
  ns: ['common'],
  defaultNS: 'common',
  preload: ['en', 'ko'], // 사전 로딩하고 싶은 언어
});

5. SSR 대응 (Next.js 등)

클라이언트 이전에 서버에서 번역된 결과를 내려줌

핵심 이슈:

  • 번역 준비 전 렌더링 → mismatch 발생
  • hydration 오류 등

6. next-i18next 활용 (Next.js 공식 통합 모듈)

기능:

  • SSR 자동 처리
  • 페이지별 namespace 지정
  • getStaticProps로 사전 번역 fetch

구조 예시:

/public/locales/en/common.json
/public/locales/ko/common.json

사용 예시:

// pages/index.tsx
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common', 'home'])),
    },
  };
}

7. namespace 모듈화

  • JSON 파일을 기능 단위로 분할 (auth, profile, settings 등)
  • 유지보수성과 협업 효율 향상

사용 예:

t('auth.login'); // auth.json
t('profile.name'); // profile.json
i18n.init({
  ns: ['common', 'auth', 'profile'],
  defaultNS: 'common',
});

비동기 로딩 서버에서 JSON 파일을 fetch로 불러옴
Flash 방지 Suspense로 로딩 처리
번역 캐싱 localStorage 백엔드 사용
Lazy load 필요한 언어만 나중에 로딩
SSR 서버에서 번역 처리 (Next.js)
next-i18next SSR + 번역 통합 지원
namespace 모듈화 기능별 JSON 분할

5단계: 다국어 프로젝트 실전 적용 예시

목적: 실무 수준의 다국어 시스템을 실제 프로젝트에 통합 적용


1. 실시간 언어 전환 가능한 Header 구현

  • useTranslation()과 i18n.changeLanguage() 사용
  • 드롭다운/버튼으로 언어 선택 UI 구성
  • 선택 언어를 상태로 저장 (예: localStorage)
const LanguageSelector = () => {
  const { i18n } = useTranslation();
  const changeLanguage = (lng: string) => {
    i18n.changeLanguage(lng);
    localStorage.setItem('lng', lng);
  };
  return (
    <select onChange={(e) => changeLanguage(e.target.value)} value={i18n.language}>
      <option value="en">English</option>
      <option value="ko">한국어</option>
    </select>
  );
};

2. 각 페이지 별 다국어 대응

  • 페이지 제목, 버튼 텍스트, 메시지 전부 t() 함수로 처리
  • 공통 키는 common.json, 기능별로 auth.json, dashboard.json 등 분리
const { t } = useTranslation(['auth']);
return <button>{t('auth.login_button')}</button>;

3. Zustand / Recoil 과 연계한 언어 상태 관리

  • i18n.language → 전역 상태로 저장
  • 앱 전반에서 현재 언어를 참조하거나 조건 처리 가능
// Zustand 예시
import { create } from 'zustand';
export const useLanguageStore = create((set) => ({
  language: 'en',
  setLanguage: (lng: string) => {
    i18n.changeLanguage(lng);
    set({ language: lng });
  },
}));

4. <title> 태그 등 메타정보 번역 (SEO 대응)

  • react-helmet or next/head와 t() 결합
  • 페이지마다 다국어로 타이틀/설명 표시
import { Helmet } from 'react-helmet';
<Helmet>
  <title>{t('page.home.title')}</title>
  <meta name="description" content={t('page.home.desc')} />
</Helmet>

5. SEO 대응 (Next.js or SSG 환경)

  • next-i18next와 getStaticProps() 사용
  • SSR로 미리 번역된 HTML 렌더링 → 크롤러 최적화
export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['home'])),
    },
  };
}
  • hreflang 태그로 언어별 SEO 강화
<Head>
  <link rel="alternate" hrefLang="en" href="https://example.com/en" />
  <link rel="alternate" hrefLang="ko" href="https://example.com/ko" />
</Head>

6. 마이크로 프론트엔드(MFE)에서 공유된 다국어 시스템 설계

  • 각 마이크로 앱에서 공통 i18n 인스턴스를 참조하거나 공유 모듈화
  • 글로벌 i18n 설정은 컨테이너 앱이 주입하거나 CDN 기반으로 통합
// 공통 i18n.ts
export const initI18n = (lng = 'en') =>
  i18n
    .use(initReactI18next)
    .init({
      lng,
      fallbackLng: 'en',
      resources: {
        en: { translation: en },
        ko: { translation: ko },
      },
    });
  • 상태 공유를 위해 zustand, redux, 또는 event bus 사용

실전 적용 시 주요 체크리스트

실시간 언어 전환 UX 개선, 글로벌 유저 지원
페이지별 번역 기능별 모듈화 및 가독성
전역 상태 연동 동기화된 앱 환경 구성
메타 번역 SEO 대응 및 접근성
SSR/SSG 대응 검색엔진 최적화
MFE 구조 설계 대규모 시스템 확장 대비

 

'JavaScript > React' 카테고리의 다른 글

npm React 라이브러리 배포  (0) 2025.06.13
template 자동화  (0) 2025.06.12
상태 관리 라이브러리 valtio  (0) 2025.06.09
wouter  (0) 2025.06.09
React-Spring  (3) 2025.05.23