Skip to main content

Panduan react-intl: Penyiapan Internasionalisasi React

Siapkan react-intl dari FormatJS dalam aplikasi React dengan IntlProvider, FormattedMessage, useIntl, format pesan ICU, dan penerjemahan otomatis.

Menggunakan react-i18next? Lihat panduan react-i18next kami

1

Instal react-intl

react-intl adalah bagian dari proyek FormatJS. Library ini menyediakan komponen dan hook React untuk memformat string, angka, tanggal, dan bentuk jamak menggunakan standar ICU MessageFormat.

react-intl tidak memiliki dependensi runtime selain React. Library ini menggunakan Intl API bawaan browser untuk pemformatan angka dan tanggal serta menyertakan parser ICU MessageFormat sendiri untuk bentuk jamak, select, dan teks kaya.
Terminal
npm install react-intl
2

Konfigurasikan IntlProvider

Bungkus aplikasi dengan IntlProvider di root. Teruskan bahasa aktif dan objek messages datar. Setiap komponen di bawahnya kemudian dapat mengakses terjemahan melalui FormattedMessage atau useIntl.

src/main.tsx
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>
);
IntlProvider memerlukan objek messages kunci-nilai datar (misalnya { "app.greeting": "Hello" }). JSON bertingkat harus diratakan sebelum diteruskan ke IntlProvider, atau gunakan utilitas seperti flat untuk mengonversi struktur bertingkat.

File Pesan

Buat satu file JSON per bahasa. react-intl menggunakan sintaks ICU MessageFormat secara native—bentuk jamak, select, dan variabel semuanya dinyatakan inline dalam string pesan.

messages/en.json & messages/de.json
// 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"
}
Gunakan ID yang dipisahkan titik seperti "nav.home" untuk pengelompokan. Tidak seperti react-i18next, react-intl mengharapkan objek messages datar—Anda meratakan kunci, bukan strukturnya.
3

Gunakan Terjemahan dalam Komponen

react-intl menyediakan dua API utama: komponen FormattedMessage untuk merender JSX terjemahan dan hook useIntl untuk akses imperatif (placeholder, label aria, pemformatan programatis).

Komponen FormattedMessage

Gunakan FormattedMessage untuk terjemahan deklaratif dalam JSX. Teruskan ID pesan dan nilai interpolasi apa pun. Komponen merender string terjemahan secara langsung.

Greeting.tsx
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>
  );
}

Hook useIntl

Gunakan useIntl() saat memerlukan string terjemahan sebagai nilai biasa—untuk placeholder input, label aria, document.title, atau saat meneruskan string ke API non-React. Hook juga menyediakan formatNumber, formatDate, dan formatRelativeTime.

SearchBar.tsx
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>
  );
}

Teks Kaya (HTML dalam Terjemahan)

Sematkan JSX dalam terjemahan menggunakan tag seperti XML di string pesan. Teruskan penangan tag melalui prop values untuk merender tautan, teks tebal, atau komponen React apa pun di dalam pesan terjemahan.

SignUp.tsx
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>,
      }}
    />
  );
}
FormattedMessage merender React Fragment secara default. Jika memerlukan elemen pembungkus tertentu, teruskan prop textComponent ke IntlProvider atau bungkus FormattedMessage dalam elemen sendiri.

Ekstraksi Pesan dengan @formatjs/cli

FormatJS menyediakan CLI untuk mengekstrak ID pesan secara otomatis dari kode sumber ke file JSON. Ini memastikan file pesan tetap sinkron dengan komponen tanpa pencatatan manual.

Terminal
# 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
4

Bentuk Jamak dan ICU Select

react-intl menggunakan ICU MessageFormat secara native. Bentuk jamak, select berbasis gender, dan pemformatan bertingkat semuanya dinyatakan langsung dalam string pesan—tanpa konvensi sufiks atau kunci terpisah.

ICU plural syntax by language
// 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}個の商品があります"
}
Jangan pernah meng-hardcode logika bentuk jamak dalam JavaScript. Bahasa seperti Arab memiliki 6 bentuk jamak, Prancis menganggap 0 sebagai tunggal, dan Jepang tidak membedakan bentuk jamak. Biarkan ICU MessageFormat menangani aturan—cukup teruskan nilai count.

ICU Select untuk Gender dan Peran

Gunakan sintaks ICU select untuk terjemahan bergantung konteks seperti gender, peran pengguna, atau nilai status. Ekspresi select memilih varian yang benar berdasarkan nilai yang diberikan.

ICU select syntax
// 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 }}
/>

Otomatiskan Kualitas Terjemahan

Temukan kunci hilang dan placeholder rusak sebelum dirilis dengan i18n-validate. Uji UI dengan terjemahan semu menggunakan i18n-pseudo sebelum terjemahan asli tersedia.

Kesalahan Umum

Terlalu Mengandalkan defaultMessage

defaultMessage adalah fallback pengembangan, bukan strategi terjemahan. Jika menggunakan defaultMessage untuk semua string, keluaran ekstraksi pesan akan berisi teks bahasa Inggris, tetapi penerjemah mungkin melewatkan kunci baru. Selalu ekstrak dan pelihara file bahasa sumber yang lengkap.

Objek Bertingkat, Bukan Kunci Datar

IntlProvider mengharapkan Record&lt;string, string&gt; datar untuk messages. Jika Anda meneruskan JSON bertingkat seperti { nav: { home: "Home" } }, react-intl tidak akan menemukan kunci "nav.home". Ratakan messages sebelum meneruskannya atau gunakan library seperti flat.

IntlProvider Menyebabkan Render Ulang

Jika membuat objek messages secara inline dalam fungsi render, IntlProvider menerima referensi objek baru pada setiap render sehingga semua konsumen merender ulang. Memoisasi messages dengan useMemo atau tentukan di luar komponen.

IntlProvider Hilang dalam Pengujian

Komponen yang menggunakan FormattedMessage atau useIntl akan melempar kesalahan jika dirender tanpa induk IntlProvider. Dalam pengujian, bungkus komponen dengan IntlProvider menggunakan locale="en" dan objek messages kosong atau minimal.

Struktur File yang Disarankan

Project Structure
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

Coba i18n Agent Sekarang

Lepaskan file terjemahan Anda di sini

JSON, YAML, PO, XML, CSV, Markdown, Properties

atau klik untuk menjelajahi

Bahasa target

Tidak perlu mendaftarEstimasi instan

Fallback Bahasa dengan react-intl-locale-chain

Saat kunci terjemahan tidak ada dalam bahasa regional seperti pt-BR, react-intl langsung beralih ke bahasa default alih-alih memeriksa bahasa induk pt terlebih dahulu.

Terminal
npm install react-intl-locale-chain
Configuration
<LocaleChainProvider
  fallbacks={{
    'pt-BR': ['pt', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
  }}
  defaultLocale="en"
>
  <App />
</LocaleChainProvider>

Lihat Panduan Fallback Bahasa kami untuk daftar lengkap framework yang didukung dan 75 rantai bawaan. Learn more →

Pertanyaan yang Sering Diajukan