
Hướng dẫn đầy đủ về quốc tế hóa Next.js
Thiết lập next-intl với App Router, cấu hình định tuyến theo locale và tự động dịch bằng AI.
Cài đặt next-intl
next-intl là một gói duy nhất xử lý định tuyến theo locale, tải thông báo và cung cấp hook dịch cho Next.js App Router.
npm install next-intlTạo cấu hình yêu cầu i18n
Tạo hai tệp: src/i18n/request.ts để tải thông báo và src/i18n/routing.ts để định nghĩa locale. Các tệp này quy định cách next-intl phân giải thông báo và tuyến đường.
// 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);Cấu hình middleware
Thêm middleware.ts để phát hiện locale, viết lại URL và chuyển hướng. Middleware chặn mọi yêu cầu và bảo đảm áp dụng đúng locale.
// 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|.*\\..*).*)'],
};Thiết lập cấu trúc thư mục [locale]
Chuyển các tuyến của app vào app/[locale]/. Thêm generateStaticParams để tạo trang cho từng locale khi dựng ứng dụng. Cách này tạo cấu trúc URL /en/about, /de/about, v.v.
// app/[locale]/layout.tsx
import { routing } from '@/i18n/routing';
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}Cập nhật layout gốc
Tải thông báo bằng getMessages() rồi truyền vào NextIntlClientProvider trong layout locale gốc. Đặt thuộc tính html lang theo tham số locale.
// 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>
);
}Dùng bản dịch trong thành phần
Thành phần máy chủ dùng getTranslations (async, await). Thành phần máy khách dùng useTranslations (hook). Hãy chọn theo nơi thành phần được kết xuất — thành phần máy chủ không đưa bản dịch vào gói JavaScript.
// 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')} />;
}Thêm SEO: metadata và hreflang
Dùng generateMetadata để tạo tiêu đề và mô tả trang theo từng locale. Thêm alternates.languages cho các thẻ hreflang để công cụ tìm kiếm phát hiện mọi phiên bản ngôn ngữ của từng trang.
// 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}`])
),
},
};
}Xử lý trang lỗi và trang không tìm thấy
error.tsx và not-found.tsx cần được xử lý riêng vì có thể kết xuất bên ngoài layout locale thông thường. not-found.tsx ở thư mục gốc cần cấu hình trình cung cấp i18n riêng để hiển thị thông báo lỗi đã bản địa hóa.
// 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>
);
}Tự động hóa bản dịch
Sau khi hoàn tất thiết lập i18n, hãy dùng AI để dịch tệp thông báo ngay trong IDE hoặc dùng i18n Agent CLI trong quy trình CI/CD để tự động dịch mỗi lần triển khai.
# 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)Tự động kiểm soát chất lượng bản dịch
Công cụ mã nguồn mở cho i18n Next.js
Các gói mã nguồn mở này giải quyết những vướng mắc phổ biến trong quy trình quốc tế hóa Next.js.
next-intl-localechain
next-intl tiêu chuẩn chuyển thẳng về locale mặc định khi thiếu bản dịch. Người dùng tiếng Bồ Đào Nha Brazil sẽ thấy tiếng Anh dù có bản dịch pt-PT hoàn toàn phù hợp. next-intl-localechain bổ sung chuỗi dự phòng thông minh — công cụ hợp nhất sâu bản dịch từ các locale liên quan để người dùng từng khu vực luôn thấy bản dịch gần nhất hiện có.
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'
}));@i18n-agent/cli
Công cụ dòng lệnh giúp dịch tệp thông báo Next.js mà không cần rời terminal. Bạn có thể dịch trực tiếp, kiểm tra trạng thái tác vụ và tải kết quả xuống. Công cụ hoạt động trong quy trình CI/CD với cơ chế xác thực bằng khóa API để tự động hóa hoàn toàn quy trình bản địa hóa.
# 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,esLỗi thường gặp
"Unable to find next-intl locale"
Middleware không khớp với yêu cầu. Hãy kiểm tra: middleware.ts có nằm ở thư mục gốc dự án không? Mẫu matcher đã loại trừ đúng các tệp tĩnh chưa? Cấu hình định tuyến có chứa locale không?
Kết xuất động ngoài dự kiến
Một trang hoặc layout đang thiếu setRequestLocale(locale). Nếu thiếu, next-intl dùng header/cookie để phát hiện locale, buộc ứng dụng kết xuất động và ngăn tạo tĩnh.
Tuyến song song xung đột với i18n
Tuyến song song (@modal) và tuyến chặn ((.)photo) có các vấn đề tương thích đã biết với phân đoạn động [locale]. Hãy dùng định tuyến dựa trên middleware để khắc phục các mẫu định tuyến nâng cao này.
Chuyển ngôn ngữ làm mất tuyến hiện tại
Khi chuyển locale, hãy giữ nguyên pathname hiện tại bằng usePathname() và chỉ thay phân đoạn locale. Hãy thận trọng với tham số tuyến động — bạn cần phân giải lại chúng cho locale mới.
Cấu trúc tệp đề xuất
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.jsonDùng thử i18n Agent ngay
Thả tệp bản dịch của bạn vào đây
JSON, YAML, PO, XML, CSV, Markdown, Properties
hoặc nhấp để duyệt
Ngôn ngữ đích
Chuyển dự phòng locale bằng next-intl-localechain
Khi locale khu vực như pt-BR thiếu khóa dịch, next-intl chuyển thẳng về locale mặc định thay vì kiểm tra locale cha pt trước.
npm install next-intl-localechainimport { 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`),
});Xem Hướng dẫn chuyển dự phòng locale để biết danh sách đầy đủ các framework được hỗ trợ và 75 chuỗi tích hợp sẵn. Learn more →