Skip to main content

คู่มือการทำให้ React รองรับหลายภาษาอย่างครบถ้วน

จากศูนย์สู่หลายภาษา : ตั้งค่า i18n ในแอป React แล้วทำให้การแปลเป็นอัตโนมัติด้วย AI

1

ติดตั้งแพ็กเกจ

คุณต้องใช้สามแพ็กเกจ ได้แก่ react-i18next (การเชื่อมโยงกับ React), i18next (ไลบรารีหลัก) และ i18next-browser-languagedetector ซึ่งเป็นตัวเลือกเสริมสำหรับตรวจหาภาษาอัตโนมัติ

react-i18next มีฮุกและคอมโพเนนต์สำหรับ React ส่วน i18next เป็นเอนจินหลักที่จัดการการโหลดคำแปล การแทรกค่า และพหูพจน์ ปลั๊กอินตรวจหาภาษาจะอ่านภาษาที่เบราว์เซอร์ต้องการโดยอัตโนมัติ
Terminal
npm install react-i18next i18next i18next-browser-languagedetector i18next-http-backend
2

กำหนดค่าอินสแตนซ์ i18n

สร้างไฟล์กำหนดค่า i18n ที่เริ่มต้น i18next ด้วยภาษาเริ่มต้น ทรัพยากรคำแปล และลำดับปลั๊กอิน ต้องนำเข้าไฟล์นี้ที่จุดเริ่มต้นของแอปก่อนเรนเดอร์คอมโพเนนต์ใดๆ

src/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)  // Must come before .init()
  .init({
    fallbackLng: 'en',
    debug: process.env.NODE_ENV === 'development',
    interpolation: {
      escapeValue: false,  // React already escapes
    },
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json',
    },
  });

export default i18n;
ข้อผิดพลาด “You will need to pass in an i18next instance by using initReactI18next” หมายความว่าคุณลืมเรียก i18n.use(initReactI18next) ก่อน i18n.init() โดยต้องเรียก .use() ก่อน .init()
3

ครอบแอปด้วย I18nextProvider

นำเข้าไฟล์กำหนดค่า i18n ที่รากของแอป แล้วครอบโครงสร้างคอมโพเนนต์ด้วย I18nextProvider หากไม่ทำเช่นนี้ useTranslation() จะคืนคีย์ดิบแทนข้อความที่แปลแล้ว

src/main.tsx
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';  // Import your config
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Suspense fallback={<div>Loading...</div>}>
      <I18nextProvider i18n={i18n}>
        <App />
      </I18nextProvider>
    </Suspense>
  </React.StrictMode>
);
หากคำแปลแสดงคีย์ดิบอย่าง “welcome” แทน “Welcome to our app” สาเหตุที่พบบ่อยที่สุดคือไม่มี I18nextProvider หรือไม่ได้นำเข้าไฟล์กำหนดค่า i18n
4

สร้างไฟล์แปล

สร้างไฟล์ JSON หนึ่งไฟล์ต่อภาษา ใช้คีย์ซ้อนเพื่อจัดระเบียบข้อความตามฟีเจอร์หรือหน้า และให้ภาษาต้นฉบับ ซึ่งมักเป็นภาษาอังกฤษ เป็นแหล่งข้อมูลจริงเพียงแห่งเดียว

public/locales/en/translation.json
// public/locales/en/translation.json
{
  "nav": {
    "home": "Home",
    "about": "About",
    "settings": "Settings"
  },
  "greeting": "Hello, {{name}}!",
  "cart": {
    "itemCount_one": "{{count}} item",
    "itemCount_other": "{{count}} items"
  }
}

// public/locales/de/translation.json
{
  "nav": {
    "home": "Startseite",
    "about": "Über uns",
    "settings": "Einstellungen"
  },
  "greeting": "Hallo, {{name}}!",
  "cart": {
    "itemCount_one": "{{count}} Artikel",
    "itemCount_other": "{{count}} Artikel"
  }
}
ตั้งชื่อคีย์ตามสิ่งที่อธิบาย ไม่ใช่ตำแหน่งที่แสดง เช่น “cart.itemCount” ดีกว่า “homepageCartLabel” คีย์ควรยังใช้ได้หลังออกแบบ UI ใหม่
5

ใช้คำแปลในคอมโพเนนต์

เรียก useTranslation() ในคอมโพเนนต์ใดก็ได้เพื่อรับฟังก์ชัน t() ใช้กับข้อความทั่วไป ตัวแปรแทรกค่า และคำแปลที่ฝัง JSX ผ่านคอมโพเนนต์ Trans

Greeting.tsx
import { useTranslation } from 'react-i18next';

function Greeting({ userName }: { userName: string }) {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t('greeting', { name: userName })}</h1>
      <nav>
        <a href="/">{t('nav.home')}</a>
        <a href="/about">{t('nav.about')}</a>
      </nav>
    </div>
  );
}
Trans component for JSX
import { Trans, useTranslation } from 'react-i18next';

// For JSX inside translations:
// "terms": "By signing up, you agree to our <link>Terms</link>."

function SignUp() {
  const { t } = useTranslation();
  return (
    <Trans i18nKey="terms" components={{
      link: <a href="/terms" className="underline" />
    }} />
  );
}
คีย์แบบไดนามิกอย่าง t(`error.$'{code}'`) ใช้งานได้ขณะรัน แต่เครื่องมืออย่าง i18next-scanner ไม่สามารถแยกแบบสถิตได้ หากใช้เครื่องมือแยกคีย์ ให้ระบุคีย์แบบไดนามิกอย่างชัดเจนหรือใช้ความคิดเห็นเป็นคำใบ้
6

จัดการพหูพจน์และตัวแปร

i18next จัดการพหูพจน์ด้วยกฎ CLDR ไม่ใช่เพียงเอกพจน์กับพหูพจน์ ภาษาอาหรับมี 6 รูปแบบ (zero, one, two, few, many, other) ส่วนภาษาญี่ปุ่นมี 1 รูปแบบ (other) ให้กำหนดรูปแบบที่จำเป็นทั้งหมดในไฟล์แปล แล้ว i18next จะเลือกค่าที่ถูกต้องโดยอัตโนมัติ

Plural forms by language
// English: 2 forms (one, other)
{
  "itemCount_one": "{{count}} item",
  "itemCount_other": "{{count}} items"
}

// Arabic: 6 forms (zero, one, two, few, many, other)
{
  "itemCount_zero": "لا عناصر",
  "itemCount_one": "عنصر واحد",
  "itemCount_two": "عنصران",
  "itemCount_few": "{{count}} عناصر",
  "itemCount_many": "{{count}} عنصرًا",
  "itemCount_other": "{{count}} عنصر"
}

// Japanese: 1 form (other)
{
  "itemCount_other": "{{count}}個のアイテム"
}
อย่าฮาร์ดโค้ด count === 1 เพื่อตรวจหาเอกพจน์ ภาษาอย่างฝรั่งเศสมอง 0 เป็นเอกพจน์ ส่วนรัสเซีย อาหรับ และโปแลนด์มีรูปแบบที่ภาษาอังกฤษไม่มี ให้ i18next จัดการกฎพหูพจน์
7

เพิ่มการสลับและตรวจหาภาษา

สร้างตัวเลือกภาษาที่เรียก i18n.changeLanguage() ใช้ร่วมกับตัวตรวจหาภาษาของเบราว์เซอร์เพื่อตรวจหาภาษาที่ผู้ใช้ต้องการโดยอัตโนมัติในการเข้าชมครั้งแรก แล้วบันทึกตัวเลือกที่ผู้ใช้เลือกเอง

LanguageSwitcher.tsx
import { useTranslation } from 'react-i18next';

const LANGUAGES = [
  { code: 'en', label: 'English' },
  { code: 'de', label: 'Deutsch' },
  { code: 'ja', label: '日本語' },
  { code: 'es', label: 'Español' },
];

function LanguageSwitcher() {
  const { i18n } = useTranslation();

  return (
    <select
      value={i18n.language}
      onChange={(e) => i18n.changeLanguage(e.target.value)}
    >
      {LANGUAGES.map(({ code, label }) => (
        <option key={code} value={code}>{label}</option>
      ))}
    </select>
  );
}
หากใช้ SSR (Next.js, Remix) เซิร์ฟเวอร์อาจตรวจพบคนละภาษากับไคลเอ็นต์ เพราะเซิร์ฟเวอร์ไม่มีการตั้งค่าเบราว์เซอร์ ซึ่งทำให้ hydration ไม่ตรงกัน วิธีแก้ : ส่งภาษาที่ตรวจพบจากเซิร์ฟเวอร์ไปยังไคลเอ็นต์ผ่าน prop หรือคุกกี้ เพื่อให้ทั้งสองฝั่งเรนเดอร์ภาษาเดียวกัน
8

ทำให้การแปลเป็นอัตโนมัติ

เมื่อตั้งค่า i18n เสร็จแล้ว ให้แปลไฟล์ภาษาด้วย AI โดยบอกผู้ช่วย AI ใน IDE ให้แปลไฟล์ต้นฉบับ หรือใช้ CLI ของ i18n Agent ในไปป์ไลน์ CI/CD

Terminal
# In your IDE, ask your AI assistant:
> Translate public/locales/en/translation.json to German, Japanese, and Spanish

✓ de/translation.json created (1.2s)
✓ ja/translation.json created (1.5s)
✓ es/translation.json created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate public/locales/en/translation.json --lang de,ja,es
แปลแบบเพิ่มทีละส่วน เมื่อเพิ่มคีย์ใหม่ในไฟล์ต้นฉบับ ให้แปลเฉพาะส่วนต่างแทนการสร้างทุกไฟล์ใหม่ วิธีนี้ช่วยรักษาคำแปลที่มนุษย์ตรวจทานแล้ว

ทำให้คุณภาพการแปลเป็นอัตโนมัติ

ใช้ i18n-validate จับคีย์ที่หายไปและตัวยึดตำแหน่งเสียหายก่อนส่งขึ้นใช้งาน แล้วทดสอบ UI ด้วยคำแปลจำลองผ่าน i18n-pseudo ก่อนคำแปลจริงจะมาถึง

ข้อผิดพลาดที่พบบ่อย

คำแปลแสดงคีย์ดิบ

สาเหตุ ได้แก่ ไม่มี I18nextProvider ไม่ได้นำเข้าการกำหนดค่า i18n ที่รากแอป ไม่ได้โหลดเนมสเปซ หรือคำแปลยังโหลดแบบอะซิงโครนัสอยู่ ตรวจดูเบาะแสในคอนโซลของเบราว์เซอร์ด้วย debug: true

ข้อผิดพลาด Suspense เมื่อไม่มี fallback

ข้อผิดพลาด “A component suspended while responding to synchronous input” แก้ได้ด้วยการเพิ่มขอบเขต '&lt;Suspense&gt;' รอบแอป หรือตั้ง useSuspense: false ในการกำหนดค่า init ของ i18next

Hydration ของ SSR ไม่ตรงกัน

เซิร์ฟเวอร์เรนเดอร์ภาษาหนึ่ง แต่ไคลเอ็นต์ hydrate อีกภาษา ตรวจให้ทั้งสองฝั่งใช้แหล่งภาษาเดียวกัน โดยส่งเป็น prop จากเซิร์ฟเวอร์และอย่าพึ่งการตรวจหาของเบราว์เซอร์เพียงอย่างเดียว

ไม่มีการเติมข้อความอัตโนมัติสำหรับคีย์คำแปล

ขยายโมดูล i18next ด้วยชนิดทรัพยากรของคุณ : declare module 'i18next' '{ interface CustomTypeOptions { resources: typeof resources } }' วิธีนี้ทำให้การเรียก t() ปลอดภัยด้านชนิดและมีการเติมข้อความอัตโนมัติ

โครงสร้างไฟล์ที่แนะนำ

Project Structure
my-react-app/
├── public/
│   └── locales/
│       ├── en/
│       │   ├── translation.json    # Default namespace
│       │   ├── common.json         # Shared strings
│       │   └── dashboard.json      # Feature namespace
│       ├── de/
│       │   ├── translation.json
│       │   ├── common.json
│       │   └── dashboard.json
│       └── ja/
│           └── ...
├── src/
│   ├── i18n.ts                     # i18n configuration
│   ├── main.tsx                    # App entry with Provider
│   ├── App.tsx
│   └── components/
│       └── LanguageSwitcher.tsx
└── package.json

แปลรายการต่อไปนี้ได้ด้วย :

ลองใช้ i18n Agent ตอนนี้

ลากและวางไฟล์แปลของคุณที่นี่

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

หรือคลิกเพื่อเลือกไฟล์

ภาษาเป้าหมาย

ไม่ต้องลงทะเบียนประเมินราคาได้ทันที

คำถามที่พบบ่อย