blog 18ways
Một cách làm i18n trong Next.js theo hướng AI-native, mà không làm hỏng SEO.
Quốc tế hoá (thường viết tắt là i18n), bản địa hoá (l10n) và hỗ trợ đa ngôn ngữ đều mô tả cùng một ý: điều chỉnh sản phẩm của bạn để mọi người có thể dùng nó bằng ngôn ngữ của riêng họ.
Rốt cuộc thì mọi doanh nghiệp cũng nhận ra họ cần hỗ trợ nhiều ngôn ngữ. Đến lúc đó, nhóm sản phẩm ngả người ra sau như một bác thợ máy, hít một hơi qua kẽ răng rồi nói: “Ta đang nói về bao nhiêu trang? Ít nhất cũng phải bốn sprint.”

Cách nền tảng chúng ta triển khai i18n về cơ bản đã không thay đổi suốt 20 năm qua. Phần lớn “best practices” về i18n đã được hình thành từ rất lâu trước khi có các kiến trúc web hiện đại, và mọi thứ sau đó đơn giản chỉ là chồng thêm lên trên chúng.
I18n là phần duy nhất của phát triển web hiện đại vẫn còn liên quan đến việc xuất tệp và gửi chúng cho ai đó. Người ta coi đó là một thiết lập hiện đại nếu thay vì gửi email, bạn tải chúng lên qua API.
Trong một thế giới của các pipeline CI và các lần triển khai tính bằng phút thay vì ngày, có lẽ chúng ta nên tự thấy may mắn vì không phải fax bất cứ thứ gì.
Phát triển web hiện đại đã tiến rất nhanh.
Chúng ta có:
Thế nhưng quy trình quốc tế hoá vẫn cứ như thuộc về một thời đại khác.
Ngay cả những ứng dụng tương đối nhỏ cũng nhanh chóng phải xoay xở với:
Các lập trình viên không hề bắt đầu bằng ý định xây dựng những hệ thống như thế này. Họ lảo đảo bước vào nó, khi những rào cản i18n quen thuộc làm vấp ngã các lập trình viên không quen với việc các ngôn ngữ khác nhau cấu trúc câu hoàn toàn khác nhau… rồi miễn cưỡng chấp nhận hiện trạng đã được đặt ra từ 20 năm trước.
Theo thời gian, quốc tế hoá trở thành một trong những phần phức tạp nhất của stack.
Hãy xem một giao diện người dùng đơn giản:
<h1>Hello world!</h1>
<p>
<a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
</p>Điều này dễ hiểu. Nó đơn giản.
Đến một lúc nào đó, chúng tôi quyết định quốc tế hoá nó. Vì thế, chúng tôi trích văn bản ra một tệp dịch:
{
"mainPage": {
"greeting": "Hello world",
"cta": "<0>Click here</0> to view your <1>{{description}}</1>"
}
}Và ta tham chiếu nó trong code của mình:
<h1>{t('mainPage.greeting')}</h1>
<p>
<Trans key="mainPage.cta">
<a href="#/">Click here</a> to view your <b>{{ description: serverResponse.productDescription }}</b>
</Trans>
</p>Nhưng khoan, giờ chúng ta có văn bản tiếng Anh cả trong code lẫn trong tệp dịch của mình ư? Đúng vậy. Các thư viện i18n khác nhau có thể phủ lên nó lớp sơn khác nhau, nhưng cố dịch một đoạn văn bản đã được định dạng một phần bằng cách tách nó ra thành một tệp JSON dễ đọc cho con người là một cuộc chiến thua chắc.
Giao diện người dùng hiện đại có cấu trúc, nhưng hệ thống dịch thuật lại được xây trên nền những chuỗi ký tự phẳng.
Một khi markup, biến và nội dung động xuất hiện, lớp trừu tượng bắt đầu rò rỉ.
Mọi thứ còn khó hơn nữa khi nội dung không tĩnh.
Hãy lấy ví dụ này một lần nữa:
<b>{serverResponse.productDescription}</b>Chuỗi đó đến từ một API. Làm sao để biến nó thành một khóa dịch?
Bí mật là: không. Giờ backend của bạn cũng phải hỗ trợ quốc tế hoá nữa.
Backend của bạn giờ phải xử lý:
Dịch giả đột nhiên cũng cần quyền truy cập vào các hệ thống backend.
Và vấn đề chỉ càng lớn hơn khi nội dung do người dùng tạo ra xuất hiện.
Đánh giá. Bình luận. Danh sách trên chợ. Diễn đàn.
Các quy trình i18n truyền thống mặc định rằng lập trình viên kiểm soát toàn bộ văn bản. Ngày càng nhiều hơn, điều đó đơn giản là không đúng.
Việc những công ty như Amazon bắt đầu thử nghiệm đánh giá người dùng được dịch vào năm 2026 nói lên rất nhiều điều về tốc độ tiến hoá quá chậm của hệ sinh thái i18n.
Ngay cả việc xác định người dùng muốn ngôn ngữ nào cũng phức tạp một cách đáng ngạc nhiên.
Trình duyệt gửi tùy chọn ngôn ngữ qua tiêu đề Accept-Language:
Accept-Language: en-GB,en;q=0.9,fr;q=0.8Điều này nói với máy chủ:
Trong thực tế, để triển khai đúng cách việc này đòi hỏi:
en, en-GB, en-US)Nhiều framework để mặc phần lớn logic này cho nhà phát triển.
Ngay cả việc phát hiện locale cơ bản cũng thường trở thành mã ứng dụng tuỳ chỉnh.
Hạ tầng dịch thuật chỉ là một nửa vấn đề.
Người dùng vẫn cần một cách để đổi ngôn ngữ.
Hầu hết các thư viện i18n đều cung cấp lớp dịch thuật nhưng để lại phần còn lại của vấn đề cho lập trình viên:
hreflang cho SEONhững chi tiết này nghe có vẻ nhỏ, nhưng chúng nhanh chóng trở thành một khối lượng đáng kể phần “hạ tầng” của ứng dụng, nhất là nếu bạn không quen với mọi ngóc ngách của i18n. Phần lớn công ty và lập trình viên đều không quen.
Trước hết mức độ phức tạp này, một số đội ngũ chuyển sang các công cụ dịch website tự động. Những nền tảng này hứa hẹn sẽ tự động dịch trang web của bạn với rất ít công sức.
Một số công ty đang xây dựng những phiên bản ngày càng tinh vi của ý tưởng này. Tiếc là họ lại tạo ra một bộ vấn đề khác:
Kết quả thường là một trang web về mặt kỹ thuật có hỗ trợ nhiều ngôn ngữ nhưng lại mang đến trải nghiệm kém hơn rõ rệt, và là một hộp đen đối với các lập trình viên khi làm việc.
Chúng tôi bắt đầu bằng một thứ đẹp đẽ và đơn giản:
<h1>Hello world!</h1>
<p>
<a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
</p>Và rồi chúng ta có khóa dịch thuật, từ điển JSON, các pipeline bị phân mảnh, các lớp bản địa hoá backend và frontend tách rời, cùng công cụ dịch bên ngoài.
Các ứng dụng web hiện đại là những hệ thống cực kỳ tinh vi. Thế nhưng, công cụ i18n lại kỳ vọng những quy trình được thiết kế cho một thời đại khác, như một chiếc Ferrari mà phải quay tay mới nổ máy.
Nếu quốc tế hoá trông như thế này thì sao?
<h1><T>Hello world!</T></h1>
<p>
<T>
<a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
</T>
</p>Không có khóa dịch.
Không có từ điển JSON.
Không có quy trình thủ công.
Chỉ là văn bản.
Ý tưởng này là nền tảng đằng sau 18ways.
18ways xem dịch thuật như một mối quan tâm runtime được tối ưu hoá. Việc trích xuất khoá dịch và quản lý tệp dịch là việc dành cho máy móc, không phải con người.
Nhà phát triển chỉ cần đánh dấu phần văn bản họ muốn dịch.
Ở hậu trường, 18ways:
Mọi thứ vẫn thân thiện với SSR và an toàn cho SEO.
Các hệ thống i18n truyền thống đánh mất ngữ cảnh.
Một dịch giả có thể thấy thứ gì đó như thế này:
homeNhưng điều đó có nghĩa là gì?
Trong tiếng Đức, những nghĩa này đòi hỏi các từ hoàn toàn khác nhau:
Haus - một ngôi nhàStartseite - trang chủStart - một nhãn điều hướngzum Anfang - quay lại đầuNếu bạn phải dịch “home” sang tiếng Đức, bạn muốn thấy gì hơn? Một chuỗi rời rạc, hay trang nơi nó thực sự xuất hiện?

Các hệ thống dịch chỉ thấy những chuỗi văn bản rời rạc sẽ gặp khó với những khác biệt này.
18ways phân tích bản dịch trong ngữ cảnh của chính giao diện thực tế.
Các tác nhân backend của chúng tôi xem xét bản dịch ngay khi chúng xuất hiện trên các trang thực tế và tối ưu cho:
Nếu bạn quen với danh từ ghép tiếng Đức thì đừng lo, phát hiện tràn cũng đã được tính đến.
Mọi thứ giúp AI tạo ra bản dịch tốt hơn cũng giúp ích cho dịch giả con người.
18ways cung cấp cho biên dịch viên cùng một môi trường hiểu ngữ cảnh mà các tác nhân AI của chúng tôi dùng.
Thay vì chỉnh sửa các tệp JSON, người dịch xem lại văn bản trực tiếp trên giao diện nơi nó xuất hiện.
Điều này cải thiện đáng kể độ chính xác của bản dịch và hiệu quả quy trình làm việc.
Quốc tế hoá không cần thêm một lớp khóa dịch, tệp chuỗi và keo nối đường ống.
Các ứng dụng hiện đại đã có sẵn cấu trúc, ngữ cảnh và thông tin thời gian chạy mà chúng ta cần để làm việc này tốt hơn.
Thế hệ i18n tiếp theo nên được xây dựng cho ứng dụng render phía máy chủ, nội dung động, quy trình làm việc gốc AI và dịch giả con người làm việc song song.
Đó là hướng mà 18ways đang đi theo.