Skip to main content

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

ตั้งค่า next-intl กับ App Router กำหนดค่าเส้นทางตามภาษา แล้วทำให้การแปลเป็นอัตโนมัติด้วย AI

1

ติดตั้ง next-intl

next-intl เป็นแพ็กเกจเดียวที่จัดการเส้นทางตามภาษา การโหลดข้อความ และฮุกคำแปลสำหรับ Next.js App Router

Terminal
npm install next-intl
เหตุใดจึงเลือก next-intl แทน next-i18next? next-intl สร้างมาสำหรับ App Router และคอมโพเนนต์เซิร์ฟเวอร์ ส่วน next-i18next ออกแบบมาสำหรับ Pages Router และรองรับ App Router อย่างจำกัด
2

สร้างการกำหนดค่าคำขอ i18n

สร้างสองไฟล์ ได้แก่ src/i18n/request.ts สำหรับโหลดข้อความ และ src/i18n/routing.ts สำหรับกำหนดภาษา ไฟล์เหล่านี้กำหนดวิธีที่ next-intl ค้นหาข้อความและเส้นทาง

src/i18n/routing.ts
// src/i18n/routing.ts
import { defineRouting } from 'next-intl/routing';
import { createNavigation } from 'next-intl/navigation';

export const routing = defineRouting({
  locales: ['en', 'de', 'ja', 'es'],
  defaultLocale: 'en',
  localePrefix: 'as-needed',  // /about for en, /de/about for de
});

export const { Link, redirect, usePathname, useRouter } =
  createNavigation(routing);
Turbopack ซึ่งเป็นตัวรวมแพ็กเกจเริ่มต้นใน Next.js 15 ต้องใช้ experimental.turbo.resolveAlias ใน next.config.js หากไม่มี คุณจะพบข้อผิดพลาด “Couldn't find next-intl config file”
3

กำหนดค่ามิดเดิลแวร์

เพิ่ม middleware.ts เพื่อจัดการการตรวจหาภาษา การเขียน URL ใหม่ และการเปลี่ยนเส้นทาง มิดเดิลแวร์จะดักทุกคำขอและตรวจให้ใช้ภาษาที่ถูกต้อง

middleware.ts
// middleware.ts  <- Must be in project ROOT, not src/
import createMiddleware from 'next-intl/middleware';
import { routing } from './src/i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
};
middleware.ts ต้องอยู่ในไดเรกทอรีรากของโปรเจกต์ ไม่ใช่ใน src/ นี่คือข้อผิดพลาดด้านการกำหนดค่าที่พบบ่อยที่สุดของ next-intl
4

ตั้งค่าโครงสร้างโฟลเดอร์ [locale]

ย้ายเส้นทางแอปไปไว้ใน app/[locale]/ เพิ่ม generateStaticParams เพื่อสร้างหน้าสำหรับแต่ละภาษาขณะบิลด์ วิธีนี้สร้างโครงสร้าง URL อย่าง /en/about, /de/about และอื่นๆ

app/[locale]/layout.tsx
// app/[locale]/layout.tsx
import { routing } from '@/i18n/routing';

export function generateStaticParams() {
  return routing.locales.map((locale) => ({ locale }));
}
คุณต้องเรียก setRequestLocale(locale) ในทุก page.tsx และ layout.tsx ที่ใช้คำแปล หากไม่เรียก Next.js จะถอยไปใช้การเรนเดอร์แบบไดนามิกและประสิทธิภาพบิลด์จะลดลงอย่างมาก
5

อัปเดตเลย์เอาต์ราก

โหลดข้อความด้วย getMessages() แล้วส่งให้ NextIntlClientProvider ในเลย์เอาต์ภาษาราก ตั้งค่าแอตทริบิวต์ lang ของ html จากพารามิเตอร์ภาษา

app/[locale]/layout.tsx
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { routing } from '@/i18n/routing';
import { notFound } from 'next/navigation';

export function generateStaticParams() {
  return routing.locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  const { locale } = await params;
  if (!routing.locales.includes(locale as any)) notFound();

  setRequestLocale(locale);
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider locale={locale} messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
NextIntlClientProvider ต้องได้รับ prop locale อย่างชัดเจน การละเว้นทำให้เกิดข้อผิดพลาดที่สังเกตได้ยากในคอมโพเนนต์ไคลเอ็นต์และแก้ปัญหาได้ยาก
6

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

คอมโพเนนต์เซิร์ฟเวอร์ใช้ getTranslations (อะซิงโครนัสและใช้ await) ส่วนคอมโพเนนต์ไคลเอ็นต์ใช้ useTranslations (ฮุก) ให้เลือกตามตำแหน่งที่คอมโพเนนต์เรนเดอร์ คอมโพเนนต์เซิร์ฟเวอร์จะกันคำแปลออกจากชุด JavaScript ทั้งหมด

app/[locale]/page.tsx
// Server Component (default)
import { getTranslations, setRequestLocale } from 'next-intl/server';

export default async function AboutPage({
  params,
}: { params: { locale: string } }) {
  const { locale } = await params;
  setRequestLocale(locale);
  const t = await getTranslations('AboutPage');

  return <h1>{t('title')}</h1>;
}

// Client Component ('use client')
'use client';
import { useTranslations } from 'next-intl';

export default function SearchBar() {
  const t = useTranslations('SearchBar');
  return <input placeholder={t('placeholder')} />;
}
ควรใช้คอมโพเนนต์เซิร์ฟเวอร์กับเนื้อหาที่แปล เพราะช่วยกันข้อความคำแปลออกจากชุด JavaScript ของไคลเอ็นต์และลดเวลาโหลดสำหรับผู้ใช้
7

เพิ่ม SEO : เมทาดาทาและ Hreflang

ใช้ generateMetadata เพื่อสร้างชื่อหน้าและคำอธิบายเฉพาะภาษา เพิ่ม alternates.languages สำหรับแท็ก hreflang เพื่อให้เครื่องมือค้นหาพบทุกเวอร์ชันภาษาของทุกหน้า

app/[locale]/layout.tsx
// app/[locale]/layout.tsx or any page.tsx
import { getTranslations } from 'next-intl/server';
import { routing } from '@/i18n/routing';

export async function generateMetadata({
  params,
}: { params: { locale: string } }) {
  const { locale } = await params;
  const t = await getTranslations({ locale, namespace: 'Metadata' });

  return {
    title: t('title'),
    description: t('description'),
    alternates: {
      languages: Object.fromEntries(
        routing.locales.map((l) => [l, `/${l}`])
      ),
    },
  };
}
บน Vercel บิลด์อาจสร้าง localhost เป็น URL ตามรูปแบบบัญญัติหากไม่ได้ตั้ง metadataBase ให้ตั้ง metadataBase ในเลย์เอาต์รากเป็นโดเมนระบบจริงเสมอ
8

จัดการหน้าข้อผิดพลาดและไม่พบหน้า

error.tsx และ not-found.tsx ต้องจัดการเป็นพิเศษ เพราะอาจเรนเดอร์นอกเลย์เอาต์ภาษาปกติ not-found.tsx ที่รากต้องมีการตั้งค่าผู้ให้บริการ i18n ของตัวเองเพื่อแสดงข้อความข้อผิดพลาดฉบับโลคัลไลซ์

app/[locale]/error.tsx
// app/[locale]/error.tsx
'use client';
import { useTranslations } from 'next-intl';

export default function Error() {
  const t = useTranslations('Error');
  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

// app/not-found.tsx (root level -- needs own provider)
import { routing } from '@/i18n/routing';

export default async function GlobalNotFound() {
  return (
    <html lang={routing.defaultLocale}>
      <body>
        <h1>404 - Page Not Found</h1>
      </body>
    </html>
  );
}
Next.js จะเรนเดอร์หน้า 404 ฉบับโลคัลไลซ์เมื่อโค้ดเรียก notFound() อย่างชัดเจนเท่านั้น เส้นทางที่ไม่รู้จักและไม่มีหน้าตรงกันจะแสดงหน้า 404 เริ่มต้นของ Next.js ไม่ใช่เวอร์ชันที่โลคัลไลซ์
9

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

เมื่อตั้งค่า i18n เสร็จแล้ว ให้แปลไฟล์ข้อความด้วย AI จาก IDE โดยตรง หรือใช้ CLI ของ i18n Agent ในไปป์ไลน์ CI/CD เพื่อแปลอัตโนมัติทุกครั้งที่ดีพลอย

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

✓ messages/de.json created (1.1s)
✓ messages/ja.json created (1.4s)
✓ messages/es.json created (1.0s)
ใช้ next-intl-localechain สำหรับการใช้ภาษาสำรองอัจฉริยะ ผู้ใช้ pt-BR จะเห็นคำแปล pt-PT แทนการถอยไปใช้ภาษาอังกฤษเมื่อไม่มีภาษาโปรตุเกสบราซิล

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

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

เครื่องมือโอเพนซอร์สสำหรับ Next.js i18n

แพ็กเกจโอเพนซอร์สเหล่านี้แก้ปัญหาที่พบบ่อยในเวิร์กโฟลว์การทำให้ Next.js รองรับหลายภาษา

next-intl-localechain

next-intl มาตรฐานจะถอยไปใช้ภาษาเริ่มต้นทันทีเมื่อไม่มีคำแปล ผู้ใช้ภาษาโปรตุเกสบราซิลจึงเห็นภาษาอังกฤษแทนคำแปล pt-PT ที่ใช้ได้ดี next-intl-localechain เพิ่มลำดับการใช้ภาษาสำรองอัจฉริยะ โดยผสานคำแปลเชิงลึกจากภาษาที่เกี่ยวข้อง เพื่อให้ผู้ใช้แต่ละภูมิภาคเห็นคำแปลที่ใกล้เคียงที่สุดเสมอ

src/i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { withLocaleChain } from 'next-intl-localechain';

export default getRequestConfig(withLocaleChain({
  loadMessages: (locale) =>
    import(`../../messages/${locale}.json`).then(m => m.default),
  defaultLocale: 'en'
}));
ผสานคำแปลเชิงลึกตามลำดับภาษาโดยอัตโนมัติ
มีลำดับสำเร็จรูปสำหรับโปรตุเกส สเปน ฝรั่งเศส เยอรมัน และอื่นๆ
ข้ามไฟล์ข้อความที่หายไปอย่างราบรื่นโดยไม่มีข้อผิดพลาด
ตั้งค่าในบรรทัดเดียว โดยครอบ getRequestConfig เดิมของคุณ
ดูบน GitHub

@i18n-agent/cli

เครื่องมือบรรทัดคำสั่งสำหรับแปลไฟล์ข้อความ Next.js โดยไม่ออกจากเทอร์มินัล แปลไฟล์โดยตรง ตรวจสถานะงาน และดาวน์โหลดผลลัพธ์ ใช้งานในไปป์ไลน์ CI/CD พร้อมการยืนยันตัวตนด้วยคีย์ API สำหรับเวิร์กโฟลว์โลคัลไลเซชันอัตโนมัติเต็มรูปแบบ

Terminal
# Install the CLI
npm install -g @i18n-agent/cli

# Authenticate
i18nagent login

# Translate your message files
i18nagent translate ./messages/en.json --lang de,ja,es

# Or use in CI/CD with an API key
export I18N_AGENT_API_KEY=your-key-here
i18nagent translate ./messages/en.json --lang de,ja,es
แปลไฟล์ JSON, YAML, PO และไฟล์ i18n รูปแบบอื่นจากเทอร์มินัล
พร้อมสำหรับ CI/CD โดยยืนยันตัวตนผ่านตัวแปรสภาพแวดล้อมสำหรับไปป์ไลน์อัตโนมัติ
ติดตามสถานะงาน ทำงานที่ล้มเหลวต่อ และดาวน์โหลดผลลัพธ์
ผลลัพธ์ JSON ที่เครื่องอ่านได้สำหรับสคริปต์และระบบอัตโนมัติ
ดูบน GitHub

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

“Unable to find next-intl locale”

มิดเดิลแวร์ไม่ตรงกับคำขอ ให้ตรวจว่า middleware.ts อยู่ในรากโปรเจกต์หรือไม่ รูปแบบ matcher ไม่รวมไฟล์คงที่อย่างถูกต้องหรือไม่ และภาษาอยู่ในการกำหนดค่าเส้นทางหรือไม่

เรนเดอร์แบบไดนามิกโดยไม่คาดคิด

ไม่มี setRequestLocale(locale) ในหน้าหรือเลย์เอาต์ หากไม่มี next-intl จะใช้ส่วนหัว/คุกกี้ตรวจหาภาษา ซึ่งบังคับให้เรนเดอร์แบบไดนามิกและป้องกันการสร้างแบบสถิต

เส้นทางขนานใช้งานไม่ได้กับ i18n

เส้นทางขนาน (@modal) และเส้นทางดัก ((.)photo) มีปัญหาความเข้ากันได้ที่ทราบกับเซกเมนต์ไดนามิก [locale] ให้ใช้การกำหนดเส้นทางผ่านมิดเดิลแวร์เป็นทางแก้สำหรับรูปแบบเส้นทางขั้นสูงเหล่านี้

การสลับภาษาทำให้เส้นทางปัจจุบันหาย

เมื่อสลับภาษา ให้รักษา pathname ปัจจุบันด้วย usePathname() และแทนที่เฉพาะเซกเมนต์ภาษา ระวังพารามิเตอร์เส้นทางแบบไดนามิก เพราะต้องค้นหาค่าใหม่สำหรับภาษาใหม่

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

Project Structure
my-nextjs-app/
├── middleware.ts              # Locale routing (project root!)
├── next.config.mjs
├── messages/
│   ├── en.json                # Source messages
│   ├── de.json
│   └── ja.json
├── src/
│   ├── i18n/
│   │   ├── request.ts         # Message loading config
│   │   └── routing.ts         # Locale definitions
│   └── app/
│       └── [locale]/
│           ├── layout.tsx     # Root locale layout
│           ├── page.tsx       # Home page
│           ├── error.tsx      # Localized error page
│           ├── not-found.tsx  # Localized 404
│           └── about/
│               └── page.tsx
└── package.json

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

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

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

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

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

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

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

การใช้ภาษาสำรองด้วย next-intl-localechain

เมื่อไม่มีคีย์คำแปลในภาษาตามภูมิภาคอย่าง pt-BR next-intl จะข้ามไปใช้ภาษาเริ่มต้นทันทีแทนที่จะตรวจภาษาหลัก pt ก่อน

Terminal
npm install next-intl-localechain
Configuration
import { withLocaleChain } from 'next-intl-localechain';

export default withLocaleChain({
  fallbacks: {
    'pt-BR': ['pt', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
  },
  defaultLocale: 'en',
  loadMessages: (locale) => import(`./messages/${locale}.json`),
});

ดูคู่มือการใช้ภาษาสำรองของเราสำหรับรายการเฟรมเวิร์กที่รองรับทั้งหมดและลำดับสำเร็จรูป 75 รายการ Learn more →

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