
react-intl 가이드: React 국제화 설정
IntlProvider, FormattedMessage, useIntl, ICU 메시지 형식, 자동 번역으로 React 앱에 FormatJS react-intl을 설정하세요.
react-i18next를 사용하고 있나요? react-i18next 가이드 보기
react-intl 설치
react-intl은 FormatJS 프로젝트의 일부예요. ICU MessageFormat 표준을 사용해 문자열, 숫자, 날짜, 복수형의 서식을 지정하는 React 컴포넌트와 훅을 제공해요.
npm install react-intlIntlProvider 구성
앱 루트에서 IntlProvider로 앱을 감싸세요. 활성 로케일과 플랫 messages 객체를 전달하세요. 그러면 아래의 모든 컴포넌트가 FormattedMessage나 useIntl로 번역에 접근할 수 있어요.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { IntlProvider } from 'react-intl';
import App from './App';
import enMessages from './messages/en.json';
import deMessages from './messages/de.json';
const messages: Record<string, Record<string, string>> = {
en: enMessages,
de: deMessages,
};
// Detect locale from browser or your routing layer
const locale = navigator.language.split('-')[0] || 'en';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<IntlProvider locale={locale} messages={messages[locale] || messages.en}>
<App />
</IntlProvider>
</React.StrictMode>
);메시지 파일
로케일마다 JSON 파일을 하나씩 만드세요. react-intl은 ICU MessageFormat 구문을 기본으로 사용하므로 복수형, select, 변수를 모두 메시지 문자열 안에서 표현해요.
// messages/en.json
{
"app.greeting": "Hello, {name}!",
"nav.home": "Home",
"nav.about": "About",
"nav.settings": "Settings",
"cart.itemCount": "{count, plural, one {# item} other {# items}} in your cart"
}
// messages/de.json
{
"app.greeting": "Hallo, {name}!",
"nav.home": "Startseite",
"nav.about": "Über uns",
"nav.settings": "Einstellungen",
"cart.itemCount": "{count, plural, one {# Artikel} other {# Artikel}} in Ihrem Warenkorb"
}컴포넌트에서 번역 사용
react-intl은 두 가지 주요 API를 제공해요. 번역된 JSX를 렌더링하는 FormattedMessage 컴포넌트와 플레이스홀더, aria 레이블, 프로그래밍 방식의 서식에 직접 접근하는 useIntl 훅이에요.
FormattedMessage 컴포넌트
JSX의 선언적 번역에는 FormattedMessage를 사용하세요. 메시지 ID와 보간 값을 전달하면 번역된 문자열을 바로 렌더링해요.
import { FormattedMessage } from 'react-intl';
function Greeting({ userName }: { userName: string }) {
return (
<div>
<h1>
<FormattedMessage
id="app.greeting"
values={{ name: userName }}
/>
</h1>
<nav>
<a href="/"><FormattedMessage id="nav.home" /></a>
<a href="/about"><FormattedMessage id="nav.about" /></a>
</nav>
</div>
);
}useIntl 훅
입력 플레이스홀더, aria-label, document.title, React 외 API에 전달할 문자열처럼 번역된 문자열을 일반 값으로 사용해야 할 때 useIntl()을 사용하세요. formatNumber, formatDate, formatRelativeTime도 제공해요.
import { useIntl } from 'react-intl';
function SearchBar() {
const intl = useIntl();
return (
<input
type="search"
placeholder={intl.formatMessage({ id: 'search.placeholder' })}
aria-label={intl.formatMessage({ id: 'search.ariaLabel' })}
/>
);
}
// useIntl also gives you formatNumber, formatDate, formatRelativeTime:
function PriceTag({ amount, currency }: { amount: number; currency: string }) {
const intl = useIntl();
return (
<span>{intl.formatNumber(amount, { style: 'currency', currency })}</span>
);
}리치 텍스트(번역 내 HTML)
메시지 문자열의 XML 유사 태그로 번역 안에 JSX를 삽입하세요. values prop으로 태그 처리기를 전달해 번역된 메시지 안에 링크, 굵은 텍스트 또는 React 컴포넌트를 렌더링할 수 있어요.
import { FormattedMessage } from 'react-intl';
// Message: "By signing up, you agree to our <link>Terms</link>."
// Key: "signup.terms"
// Value: "By signing up, you agree to our <link>Terms</link>."
function SignUp() {
return (
<FormattedMessage
id="signup.terms"
values={{
link: (chunks) => <a href="/terms" className="underline">{chunks}</a>,
}}
/>
);
}@formatjs/cli를 활용한 메시지 추출
FormatJS는 소스 코드에서 메시지 ID를 JSON 파일로 자동 추출하는 CLI를 제공해요. 직접 관리하지 않아도 메시지 파일을 컴포넌트와 동기화할 수 있어요.
# Install the CLI
npm install -g @formatjs/cli
# Extract messages from source code into a JSON file
formatjs extract 'src/**/*.tsx' --out-file messages/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'
# Or use explicit IDs (recommended):
formatjs extract 'src/**/*.tsx' --out-file messages/en.json
# Compile messages for production (optional, improves perf)
formatjs compile messages/en.json --out-file compiled/en.json
formatjs compile messages/de.json --out-file compiled/de.json복수형 및 ICU Select
react-intl은 ICU MessageFormat을 기본으로 사용해요. 복수형, 성별 기반 select, 중첩 서식을 모두 메시지 문자열에 직접 표현하므로 접미사 규칙이나 별도 키가 필요 없어요.
// ICU MessageFormat syntax — react-intl uses this natively
// English
{
"cart.itemCount": "{count, plural, one {# item} other {# items}} in your cart",
"inbox.unread": "You have {count, plural, =0 {no unread messages} one {# unread message} other {# unread messages}}"
}
// Arabic — 6 plural forms
{
"cart.itemCount": "{count, plural, zero {لا عناصر} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}} في سلتك"
}
// Japanese — 1 form (other)
{
"cart.itemCount": "カートに{count}個の商品があります"
}성별 및 역할용 ICU Select
성별, 사용자 역할, 상태 값처럼 문맥에 따른 번역에는 ICU select 구문을 사용하세요. select 표현식이 제공된 값에 따라 올바른 변형을 선택해요.
// Gender-dependent messages using ICU select
{
"user.greeting": "{gender, select, male {He} female {She} other {They}} liked your post.",
"user.invitation": "{role, select, admin {You can manage all settings.} editor {You can edit content.} other {You can view content.}}"
}
// Usage:
<FormattedMessage
id="user.greeting"
values={{ gender: user.gender }}
/>번역 품질 자동화
흔한 실수
defaultMessage에 과도하게 의존
플랫 키 대신 중첩 객체 사용
IntlProvider로 인한 재렌더링
테스트에서 IntlProvider 누락
권장 파일 구조
my-react-app/
├── messages/
│ ├── en.json # Source of truth (English)
│ ├── de.json # German
│ ├── ja.json # Japanese
│ └── es.json # Spanish
├── compiled/ # Optional: compiled messages for prod
│ ├── en.json
│ └── ...
├── src/
│ ├── main.tsx # App entry with IntlProvider
│ ├── App.tsx
│ └── components/
│ ├── Greeting.tsx # Uses FormattedMessage
│ └── SearchBar.tsx # Uses useIntl
└── package.json지금 i18n Agent 사용해 보기
번역 파일을 여기에 드롭
JSON, YAML, PO, XML, CSV, Markdown, Properties
또는 클릭하여 파일 선택
대상 언어
react-intl-locale-chain을 활용한 로케일 폴백
pt-BR 같은 지역 로케일에 번역 키가 없으면 react-intl은 상위 로케일 pt를 먼저 확인하지 않고 기본 로케일로 바로 이동해요.
npm install react-intl-locale-chain<LocaleChainProvider
fallbacks={{
'pt-BR': ['pt', 'en'],
'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
}}
defaultLocale="en"
>
<App />
</LocaleChainProvider>지원 프레임워크와 내장 체인 75개의 전체 목록은 로케일 폴백 가이드에서 확인하세요. Learn more →