Nếu bắt đầu xây dựng website, phải biết về đa ngôn ngữ (NextJs 15 + i18n next-intl )

0 0 0

Người đăng: Thao Hanh

Theo Viblo Asia

Điều quan trọng phải nhắc thêm 1 lần nữa: Nếu bạn đang phát triển 1 website mới, hãy cài đặt tính năng đa ngôn ngữ đầu tiên, tránh việc code xong hết mới đi dịch, vì ngôn ngữ là cách để website giao tiếp với user mà, hãy chăm chút nó đầu tiên nhá!

Mở đầu

form đăng ký code form đăng ký - ngôn ngữ Tiếng anh

Dưới đây là form đăng ký tôi mới code xong (tôi sử dụng Zod - code cơ bản tự gen và là default). Điều gì sẽ xảy ra nếu tôi cứ phát triển các tính năng tiếp theo, và thêm các text bằng tiếng anh, rồi bán cho khách hàng việt. Hay tôi nên fix cứng thành tiếng việt ngay từ đầu. Tôi dừng lại việc phát triển các tính năng tiếp theo, và tôi biết rằng, thứ tôi cần là cách triển khai đa ngôn ngữ ngay từ khi website còn sơ sinh.

Khi các dev muốn tích hợp thêm tính năng đa ngôn ngữ, họ sẽ đứng trước phân vân nên dịch thủ công hay sử dụng Google API để dịch tự động. Sau 1 thời gian tìm hiểu, tôi rút ra vài kết luận như sau: Google API sẽ tốn chi phí nếu có nhiều request, nội dung bản dịch thiếu tự nhiên, k sát nghĩa, gây hiểu lầm cho người đọc, nếu dịch động (real-time), mỗi lần đổi ngôn ngữ sẽ phải gọi API => tăng thời gian phản hồi, ảnh hưởng UX. #NextJS sinh ra để tối ưu SEO, SEO hiệu quả đòi hỏi phải có đường dẫn tĩnh cho từng ngôn ngữ (/vi, /en, ...), trong khi Google API chỉ dịch trên frontend. Vậy, giải pháp đưa ra là bạn cần dịch thủ công toàn bộ website của bạn, dưới sự giúp đỡ của 1 thư viện ngôn ngữ nào đó.

=> i18n sinh ra là để dành cho bạn < 3

i18n là gì?

👉 i18n (Internationalization) là kỹ thuật giúp ứng dụng hỗ trợ nhiều ngôn ngữ và tùy chỉnh nội dung theo từng khu vực (locale) mà không cần thay đổi code nhiều lần.

📌 Gọi là "i18n" vì "Internationalization" có 18 chữ cái giữa "I" và "N" 😄

Các khái niệm liên quan đến i18n:

🔹 1. i18n (Internationalization - Quốc tế hóa) Là quá trình chuẩn bị ứng dụng để hỗ trợ nhiều ngôn ngữ. Không dịch ngôn ngữ mà chỉ giúp dễ dàng thay đổi nội dung theo từng locale.

🔹 2. l10n (Localization - Địa phương hóa) Là việc dịch nội dung và điều chỉnh giao diện theo từng ngôn ngữ và văn hóa địa phương. Ví dụ: Ở Mỹ hiển thị $10, còn ở Việt Nam hiển thị 10.000₫.

🔹 3. t9n (Translation - Dịch thuật) Là việc dịch từng đoạn văn bản sang ngôn ngữ khác.

Tích hợp thư viện i18n (next-intl) vào project

Link tham khảo: https://www.youtube.com/watch?v=2Jh9olZXBfw

i18n tác động thế nào

Yêu cầu của chúng ta là giúp website vừa tích hợp đa ngôn ngữ, vừa tối ưu SEO bằng thẻ Meta. Vì vậy, thư viện ngôn ngữ sử dụng phải thay đổi được tên page + nội dung page.

Bước 1: Install thư viện next-intl

// npm
npm install next-intl // yarn
yarn add next-intl

Sau khi cài đặt thành công, mở file package-json kiểm tra xem nhá: next-intl được cài đặt thành công

Mở file next.config.ts và thêm vào đoạn code sau:

import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin(); const nextConfig: NextConfig = { /* config options here */
}; export default withNextIntl(nextConfig); 

Bước 2: Cấu hình thư viện

image.png

Tạo 2 file có nội dung như sau:

File i18n.ts - Cấu hình tải dữ liệu ngôn ngữ

import { notFound } from "next/navigation"; // Import hàm điều hướng đến trang 404
import { getRequestConfig } from "next-intl/server"; // Import hàm lấy cấu hình từ next-intl const locales = ["en", "vi"] as const; // Danh sách các ngôn ngữ được hỗ trợ export default getRequestConfig(async ({ locale }) => { if (!locales.includes(locale as (typeof locales)[number])) notFound(); // Nếu locale không hợp lệ, trả về 404 return { messages: (await import(`./messages/${locale}.json`)).default, // Import file JSON tương ứng với locale };
}); 

File này để: Kiểm tra xem locale có hợp lệ không (en hoặc vi); Nếu không hợp lệ, gọi notFound() để trả về trang 404. Nếu hợp lệ, tải file JSON chứa nội dung dịch cho ngôn ngữ đó.

File middleware.ts - Xử lý ngôn ngữ trong request

import createMiddleware from "next-intl/middleware"; // Import middleware để xử lý i18n export default createMiddleware({ locales: ["en", "vi"], // Danh sách các ngôn ngữ được hỗ trợ defaultLocale: "vi", // Ngôn ngữ mặc định nếu không có locale trong URL
}); export const config = { matcher: ["/", "/(en|vi)/:path*"], // Chỉ áp dụng middleware cho các đường dẫn này
};

✅ Chức năng: Xác định locale từ URL (ví dụ: /vi hoặc /en). Nếu người dùng truy cập /, tự động chuyển hướng đến /vi (ngôn ngữ mặc định).

Bước 3: Thêm file dịch và cấu hình Dynamic Segment cho đa ngôn ngữ

Thêm folder messages và các file dịch

image.png

Ví dụ, file vi.json có nội dung như sau:

{ "Nav": { "home": "Trang chủ", "about": "Giới thiệu", "contact": "Liên hệ", "login": "Đăng nhập", "register": "Đăng ký" }, "Home": { "title": "Chào mừng đến với trang web của chúng tôi", "description": "Đây là trang chủ" }, "Register": { "title": "Đăng ký", "description": "Đây là trang đăng ký", "email": "Email", "password": "Mật khẩu", "confirmPassword": "Xác nhận mật khẩu", "register": "Đăng ký" }, "Login": { "title": "Đăng nhập", "description": "Đây là trang đăng nhập" }
}

Cấu hình [locale] - dynamic segment cho route

src/ app/ [locale]/ -> Them folder nay layout.tsx ✅ page.tsx

Ta sẽ tạo ra app/[locale]/page.tsx → URL: /en hoặc /vi (Dấu ngoặc vuông [ ] tạo segment động, nghĩa là nó có thể nhận nhiều giá trị khác nhau)

Tạo thư mục [locale] trong src/app và move 2 file vào folder này image.png

Tại file layout.tsx, import và thêm locale vào thư mục:

// import các thư viện cần
import { NextIntlClientProvider } from "next-intl"; // import thêm thư viện
import { getMessages } from "next-intl/server"; // import thêm thư viện export default async function RootLayout({ children, params: { locale }, // Thêm dòng này
}: Readonly<{ children: React.ReactNode; params: { locale: string }; // Thêm dòng này
}>) { const messages = await getMessages(); // Thêm dòng này return ( <html lang={locale}> <body> <NextIntlClientProvider messages={messages}> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > <header> <Header /> </header> {children} <footer> Footer <ButtonRedirect /> </footer> </ThemeProvider> </NextIntlClientProvider> <Toaster /> </body> </html> );
}

Gòi xong , tôi bị như này các bác ạ... image.png

Nếu bạn gặp lỗi tương tự, move file middleware vào trong thư mục src thay vì để ngoài: image.png

Bạn sẽ thấy, đường link http://localhost:3000/vi sẽ không bị lỗi 404 nữa đâu.

Đó, vậy là tui và các bác vừa hoàn thành xong cách set up đa ngôn ngữ vào project NextJS 15. Kết quả như nào các bác tự trải nghiệm trên web của mình nha. Cảm ơn vì đã đọc bài viết này ❤️

Bình luận

Bài viết tương tự

- vừa được xem lúc

Thủ thuật nhỏ để căn chỉnh image với object-fit

Chào các bạn,. Có lẽ trong hành trình code của các bạn thì không ít lần gặp vấn đề méo ảnh do fix cứng cả width, height của ảnh nhỉ? Hoặc kể cả khi bạn set value cho 1 thuộc tính weigth hoặc height còn thuộc tính còn lại để auto thì nhiều lúc ảnh cũng không được hiển thị toàn vẹn cho lắm.

0 0 72

- vừa được xem lúc

Tìm hiểu về CSS framework - Bulma

Mở đầu:. Mấy bữa nay đang lướt web thấy có giới thiệu framework bulma này, được mọi người giới thiệu gọn nhẹ và dễ sử dụng, nên mình mới tìm hiểu thử và hôm nay xin viết 1 bài viết giới thiệu sơ qua với các bạn.

0 0 53

- vừa được xem lúc

Một số mẹo vặt "hay ho" của ES6 có thể bạn chưa biết - Phần 4

Xin chào, ở 3 bài trước của series "Một số mẹo vặt "hay ho" của ES6", mình đã chia sẻ 1 số tips/tricks nhỏ với ES6, hy vọng ít nhiều nó sẽ có ích với các bạn khi áp dụng vào thực tế. Hôm nay, xin mời các bạn theo dõi phần 4 của series này.

0 0 71

- vừa được xem lúc

Tìm hiểu về Jest Mocks Test phía frontend

Giới thiệu. Chắc hẳn không ai phủ nhận rằng UnitTest là 1 phần quan trọng trong giai đoạn phát triển phần mềm, đảm bảo cho code được coverage tránh các bug không mong muốn.

0 0 62

- vừa được xem lúc

Convert từ SVG sang Icon Font như thế nào?

Chào các bạn. Như câu hỏi trên title của bài viết, hôm nay mình sẽ hướng dẫn các bạn cách convert 1 file svg 1 cách khá đơn giản và vô cùng tiện lợi cho các bạn. https://icomoon.io/app/#/select.

0 0 76

- vừa được xem lúc

Một vài thủ thuật làm việc với các dạng layout - Phần 4

. Chào mọi người, cũng đã lâu rồi mình không thấy nhau. Để tiếp tục với series's về các dạng layout hôm nay mình sẽ chia sẻ thêm một trick thú vị nữa về step layout.

0 0 59