Bài 32.1: Cấu hình i18n trong Next.js

Bài 32.1: Cấu hình i18n trong Next.js
Chào mừng trở lại với chuỗi bài viết về lập trình Web Front-end! Hôm nay, chúng ta sẽ khám phá một chủ đề cực kỳ quan trọng khi xây dựng các ứng dụng web hiện đại: Internationalization (i18n), hay còn gọi là quốc tế hóa. Việc hỗ trợ đa ngôn ngữ giúp website hoặc ứng dụng của bạn tiếp cận được lượng lớn người dùng trên toàn cầu, mở rộng phạm vi và nâng cao trải nghiệm người dùng.
Và tin vui là, Next.js, framework mà chúng ta đang tìm hiểu, cung cấp sự hỗ trợ mạnh mẽ và tích hợp sẵn cho i18n, giúp việc triển khai trở nên đơn giản hơn bao giờ hết.
I18n là gì? Tại sao lại cần Internationalization và Localization?
Trước khi đi sâu vào cách cấu hình trong Next.js, hãy cùng làm rõ một chút về khái niệm:
- Internationalization (i18n - Quốc tế hóa): Là quá trình thiết kế và phát triển ứng dụng theo cách mà nó có thể thích ứng dễ dàng với các ngôn ngữ và vùng địa lý khác nhau mà không cần thay đổi code lõi. Chữ "18" ở giữa i và n là vì có 18 chữ cái giữa hai chữ này.
- Localization (L10n - Bản địa hóa): Là quá trình thực hiện sự thích ứng đó cho một ngôn ngữ và vùng địa lý cụ thể. Điều này bao gồm việc dịch văn bản, định dạng ngày giờ, tiền tệ, số, sắp xếp danh sách, v.v., sao cho phù hợp với văn hóa địa phương. Chữ "10" ở giữa L và n là vì có 10 chữ cái giữa hai chữ này.
Nói một cách đơn giản: i18n là khả năng hỗ trợ nhiều ngôn ngữ, còn L10n là việc làm cụ thể để hỗ trợ từng ngôn ngữ đó.
Tại sao cần chúng?
- Tiếp cận thị trường toàn cầu: Mở rộng đối tượng người dùng.
- Tăng trải nghiệm người dùng: Người dùng thích sử dụng sản phẩm bằng ngôn ngữ của họ.
- Tăng khả năng cạnh tranh: Đứng vững hơn trên thị trường quốc tế.
Cấu hình i18n trong Next.js
Next.js xử lý i18n bằng cách định tuyến (routing) dựa trên locale. Điều này có nghĩa là mỗi ngôn ngữ bạn hỗ trợ sẽ có một URL riêng, thường được biểu thị bằng tiền tố (prefix) trong đường dẫn (ví dụ: /en/about
, /vi/ve-chung-toi
).
Việc cấu hình i18n được thực hiện trong file next.config.js
ở thư mục gốc của dự án.
Bước 1: Cấu hình next.config.js
Bạn cần thêm một object i18n
vào cấu hình của mình. Object này có ba thuộc tính chính:
locales
: Một mảng các chuỗi, chứa mã ngôn ngữ/vùng địa lý được hỗ trợ (ví dụ: 'en', 'vi', 'en-US', 'fr'). Next.js sử dụng các mã BCP 47.defaultLocale
: Chuỗi, là mã ngôn ngữ mặc định. Khi người dùng truy cập trang mà không có tiền tố locale, Next.js sẽ sử dụng locale này.localeDetection
(tùy chọn): Boolean, mặc định làtrue
. Nếu làtrue
, Next.js sẽ tự động phát hiện ngôn ngữ ưa thích của người dùng từ headerAccept-Language
và chuyển hướng họ đến locale tương ứng (nếu có tronglocales
). Nếu làfalse
, Next.js sẽ không tự động chuyển hướng mà chỉ dùngdefaultLocale
khi không có tiền tố.
Đây là ví dụ về cấu hình cơ bản:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
i18n: {
// Danh sách các ngôn ngữ (locales) mà ứng dụng của bạn hỗ trợ
locales: ['en', 'vi', 'fr'],
// Ngôn ngữ mặc định nếu không có locale nào được chỉ định trong URL
defaultLocale: 'vi',
// Tùy chọn: Bật/tắt tự động phát hiện ngôn ngữ từ header Accept-Language
// và chuyển hướng người dùng đến locale phù hợp
localeDetection: true,
},
// ... các cấu hình Next.js khác của bạn có thể ở đây ...
}
module.exports = nextConfig
Giải thích code:
- Chúng ta export một object cấu hình Next.js.
- Thuộc tính
i18n
chứa cài đặt quốc tế hóa. locales: ['en', 'vi', 'fr']
khai báo rằng ứng dụng này hỗ trợ tiếng Anh, tiếng Việt và tiếng Pháp. Next.js sẽ tạo các route tương ứng như/en/...
,/vi/...
,/fr/...
.defaultLocale: 'vi'
chỉ định tiếng Việt là ngôn ngữ mặc định. Nếu người dùng truy cập/about
, họ sẽ thấy phiên bản tiếng Việt (URL vẫn là/about
, không có tiền tố/vi/
).localeDetection: true
cho phép Next.js kiểm tra headerAccept-Language
của trình duyệt. Nếu trình duyệt yêu cầufr
vàfr
có tronglocales
, người dùng truy cập trang gốc (/
) có thể sẽ được chuyển hướng đến/fr
.
Sau khi thêm cấu hình này và chạy lại ứng dụng (npm run dev
hoặc yarn dev
), Next.js sẽ tự động xử lý việc định tuyến dựa trên locale.
Bước 2: Routing và Lấy Locale Hiện Tại
Với cấu hình trên, Next.js sẽ tự động tạo các route cho từng locale. Ví dụ, nếu bạn có trang /pages/about.js
, Next.js sẽ xử lý các URL như:
/about
(sử dụngdefaultLocale
, ở đây làvi
)/en/about
(sử dụng localeen
)/vi/about
(sử dụng localevi
)/fr/about
(sử dụng localefr
)
Bạn có thể lấy thông tin về locale hiện tại trong các component React bằng hook useRouter
từ next/router
.
// pages/about.js (hoặc bất kỳ component nào) import { useRouter } from 'next/router'; import Link from 'next/link'; // Import Link để minh họa chuyển locale function AboutPage() { const router = useRouter(); const { locale, locales, defaultLocale, asPath } = router; return ( <div> <h1>Trang Về Chúng Tôi</h1> <p>Locale hiện tại: <strong>{locale}</strong></p> <p>Các locale được hỗ trợ: {locales?.join(', ')}</p> <p>Locale mặc định: {defaultLocale}</p> <p>Path hiện tại (bao gồm locale): {asPath}</p> {/* Ví dụ về Link sử dụng locale */} <p> <Link href="/"> Quay về Trang chủ (Locale hiện tại) </Link> </p> <p> {/* Chuyển hướng đến Trang chủ tiếng Anh */} <Link href="/" locale="en"> Go to Home (in English) </Link> </p> <p> {/* Chuyển hướng đến Trang chủ tiếng Pháp */} <Link href="/" locale="fr"> Aller à la page d'accueil (en Français) </Link> </p> </div> ); } export default AboutPage;
Giải thích code:
- Chúng ta import
useRouter
từnext/router
. - Gọi
useRouter()
để lấy đối tượng router. - Đối tượng router cung cấp các thuộc tính hữu ích như
locale
(locale hiện tại),locales
(danh sách tất cả các locales được cấu hình),defaultLocale
(locale mặc định), vàasPath
(đường dẫn hiển thị trên trình duyệt, bao gồm cả tiền tố locale nếu có). - Các thẻ
<Link>
từnext/link
cũng hỗ trợ proplocale
. Nếu không chỉ địnhlocale
, nó sẽ sử dụng locale hiện tại. Nếu chỉ địnhlocale="en"
, nó sẽ tạo link đến phiên bản tiếng Anh của route đó (ví dụ:/en/
).
Bước 3: Bản địa hóa Nội dung (Localization - L10n)
Cấu hình i18n trong next.config.js
và việc định tuyến chỉ là khả năng hỗ trợ đa ngôn ngữ. Để thực sự hiển thị nội dung khác nhau cho từng ngôn ngữ, bạn cần triển khai phần L10n - tức là cung cấp các bản dịch.
Next.js không cung cấp một giải pháp dịch nội dung có sẵn (built-in). Bạn cần tự quản lý các bản dịch của mình. Cách phổ biến nhất là sử dụng các thư viện chuyên dụng hoặc tự tạo một hệ thống đơn giản.
Cách tiếp cận phổ biến:
- Sử dụng thư viện: Các thư viện như
react-i18next
hoặcnext-i18next
(được xây dựng dựa trênreact-i18next
và tích hợp tốt với Next.js) là lựa chọn được khuyến khích vì chúng cung cấp nhiều tính năng mạnh mẽ (quản lý file dịch, interpolation, fallback ngôn ngữ, hooks...). - Tự triển khai đơn giản: Với các ứng dụng nhỏ, bạn có thể tự quản lý các object chứa chuỗi dịch và sử dụng hook
useRouter
để lấy locale hiện tại, sau đó chọn đúng object dịch.
Hãy minh họa cách tự triển khai rất đơn giản (chỉ để hiểu concept, không dùng cho ứng dụng lớn):
Đầu tiên, tạo các file chứa bản dịch cho từng ngôn ngữ. Ví dụ, trong thư mục src/locales
:
// src/locales/en.js
const en = {
home: {
title: 'Welcome to our site!',
greeting: 'Hello, user!',
description: 'This is a multi-language example in Next.js.',
learnMore: 'Learn More',
},
about: {
title: 'About Us',
content: 'We are a company dedicated to...',
},
// ... các phần khác
};
export default en;
// src/locales/vi.js
const vi = {
home: {
title: 'Chào mừng đến với trang web của chúng tôi!',
greeting: 'Xin chào, người dùng!',
description: 'Đây là ví dụ đa ngôn ngữ trong Next.js.',
learnMore: 'Tìm hiểu thêm',
},
about: {
title: 'Về Chúng Tôi',
content: 'Chúng tôi là một công ty chuyên về...',
},
// ... các phần khác
};
export default vi;
Tiếp theo, trong component của bạn, sử dụng useRouter
để lấy locale
và truy cập bản dịch phù hợp:
// pages/index.js (hoặc component Trang Chủ) import { useRouter } from 'next/router'; import Link from 'next/link'; // Import các file dịch (hoặc có cơ chế tải động tốt hơn) // Trong thực tế, bạn sẽ dùng Context hoặc thư viện để quản lý state này import en from '../locales/en'; import vi from '../locales/vi'; // Tập hợp tất cả các bản dịch const translations = { en, vi, }; function HomePage() { const router = useRouter(); const { locale } = router; // Lấy object bản dịch cho locale hiện tại, fallback về defaultLocale nếu không tìm thấy const t = translations[locale] || translations[router.defaultLocale]; // Xử lý trường hợp bản dịch chưa sẵn sàng hoặc locale không hợp lệ (tùy ứng dụng) if (!t) { return <div>Loading translations...</div>; // Hoặc hiển thị nội dung mặc định } return ( <div> <h1>{t.home.title}</h1> <p>{t.home.description}</p> <p>{t.home.greeting}</p> <p> <Link href="/about">{t.home.learnMore}</Link> </p> {/* Component chuyển đổi ngôn ngữ (xem phần tiếp theo) */} <LanguageSwitcher /> </div> ); } export default HomePage;
Giải thích code:
- Chúng ta import các object bản dịch cho tiếng Anh (
en
) và tiếng Việt (vi
). - Tạo một object
translations
để dễ dàng truy cập bản dịch theolocale
. - Trong component, dùng
useRouter
để lấylocale
hiện tại. - Sử dụng
locale
để chọn đúng object bản dịch từtranslations
. Ta dùng fallback|| translations[router.defaultLocale]
để đảm bảo luôn có bản dịch (ít nhất là bản dịch mặc định). - Truy cập các chuỗi dịch bằng cách dùng key (ví dụ:
t.home.title
).
Cách làm này chỉ là minh họa. Với các ứng dụng thực tế có nhiều nội dung và nhiều ngôn ngữ, việc quản lý các file dịch, tải chúng, và xử lý các trường hợp phức tạp (số nhiều, ngày tháng, tham số trong chuỗi...) đòi hỏi một thư viện i18n chuyên nghiệp.
Bước 4: Tạo Component Chuyển Đổi Ngôn Ngữ
Để người dùng có thể tự chọn ngôn ngữ, bạn cần tạo một component đơn giản cho phép họ làm điều đó. Cách dễ nhất là sử dụng component <Link>
với prop locale
.
// components/LanguageSwitcher.js import Link from 'next/link'; import { useRouter } from 'next/router'; function LanguageSwitcher() { const router = useRouter(); const { locales, locale: currentLocale, asPath } = router; // Lấy danh sách locales, locale hiện tại và path return ( <div> <p>Chọn ngôn ngữ:</p> <ul> {/* Duyệt qua danh sách các locales được cấu hình */} {locales?.map((locale) => ( <li key={locale}> {/* Tạo Link đến cùng một path hiện tại, nhưng với locale khác */} <Link href={asPath} locale={locale}> {/* Hiển thị tên ngôn ngữ hoặc mã locale (có thể làm đẹp hơn) */} {locale.toUpperCase()} </Link> {/* Có thể thêm dấu hiệu nhận biết ngôn ngữ hiện tại */} {locale === currentLocale && <span style={{ fontWeight: 'bold', marginLeft: '5px' }}>(Hiện tại)</span>} </li> ))} </ul> {/* Minh họa chuyển về locale mặc định (nếu không phải locale hiện tại) */} {currentLocale !== router.defaultLocale && ( <p> <Link href={asPath} locale={router.defaultLocale}> Quay về {router.defaultLocale.toUpperCase()} (Mặc định) </Link> </p> )} </div> ); } export default LanguageSwitcher;
Giải thích code:
- Import
Link
vàuseRouter
. - Lấy danh sách
locales
,locale
hiện tại (currentLocale
), vàasPath
(đường dẫn đầy đủ hiện tại) từ router. - Duyệt qua mảng
locales
. - Với mỗi
locale
, tạo một thẻ<Link>
.href={asPath}
: Giữ nguyên đường dẫn hiện tại (ví dụ:/about
hoặc/en/about
).locale={locale}
: Chỉ định locale mà link này sẽ chuyển đến. Next.js sẽ tự động thay đổi tiền tố URL (ví dụ: từ/en/about
sang/vi/about
nếu bấm vào link tiếng Việt).
- Hiển thị mã locale (có thể thay bằng tên ngôn ngữ đầy đủ nếu cần).
- Thêm một dấu hiệu nhỏ để người dùng biết ngôn ngữ nào đang được chọn.
Bạn có thể đặt component LanguageSwitcher
này ở bất kỳ đâu trong ứng dụng của mình (ví dụ: header, footer).
Các Điểm Cần Lưu Ý
- Server-Side Rendering (SSR) và Static Site Generation (SSG): Next.js i18n hoạt động tốt với cả SSR và SSG. Với SSG, Next.js sẽ tạo ra các phiên bản tĩnh của mỗi trang cho mỗi locale được cấu hình. Điều này có thể làm tăng đáng kể số lượng trang tĩnh được tạo ra.
- Dynamic Routes: i18n cũng hoạt động với dynamic routes. Ví dụ:
/pages/products/[slug].js
sẽ có các biến thể như/en/products/[slug]
,/vi/products/[slug]
. Khi lấy params tronggetStaticPaths
hoặcgetServerSideProps
, bạn cũng sẽ nhận được thông tin vềlocale
hiện tại. - Sử dụng thư viện: Như đã đề cập, với các ứng dụng lớn, nên sử dụng các thư viện i18n chuyên dụng để quản lý bản dịch một cách hiệu quả.
next-i18next
là một lựa chọn phổ biến vì nó kết hợpreact-i18next
và tích hợp tốt với các tính năng của Next.js (đặc biệt là SSR/SSG). - Locale Detection: Việc tự động phát hiện locale (
localeDetection: true
) dựa vào headerAccept-Language
có thể không chính xác 100% và có thể gây ra chuyển hướng không mong muốn cho một số người dùng. Hãy cân nhắc cẩn thận khi bật tính năng này.
Comments