Cách đây ít lâu, tôi có tham vấn anh CTO về việc có nên chạy quảng cáo cho Blog để nhiều người biết đến hay không. Anh ấy khựng lại một chút và trả lời “Anh nghĩ là không nên, nếu có thể em hãy thử mở rộng tập người dùng của em ra nước ngoài”. Trước đó, tôi nghĩ rằng mình có thể bỏ tiền ra chạy quảng cáo trên một số nền tảng mạng xã hội để quảng bá blog của mình đến nhiều người hơn, đồng thời tăng lượng truy cập để trang web được ưu tiên hiển thị trong kết quả tìm kiếm. Nhưng qua câu trả lời đó, tôi bỗng nhớ lại dự định từ trước khi viết: “Blog phải có tiếng Anh”.
Nhưng bạn biết đấy, tôi không phải là một người giỏi tiếng Anh, có thể đọc hiểu được một số tài liệu, nhưng mà nói hay viết thì… Đành gác lại toan tính còn dang dở, hy vọng một ngày nào đó có ai dịch bài viết cho mình - Tôi nghĩ.
Rồi bỗng nhiên một ngày ChatGPT xuất hiện, một mô hình ngôn ngữ hiện đại bậc nhất. Nói nôm na là nó có thể hiểu và viết ra câu trả lời như một con người, chứ không đơn thuần là những câu có nghĩa được ghép từ rô-bốt. Nếu nhờ nó dịch bài viết cho mình, khả năng cao văn phong tiếng Anh sẽ trở nên mượt mà hơn so với sử dụng công cụ truyền thống như Google dịch.
Thật ra tôi muốn tích hợp API của ChatGPT vào trang quản trị để nhờ nó dịch trực tiếp bài viết tiếng Việt sang tiếng Anh. Nhưng đợi mãi mà nó vẫn chưa chịu mở API ở khu vực Việt Nam. Không đợi được nữa, tuần vừa rồi là một tuần khá bận rộn khi vừa hoàn thành xong blog đa ngôn ngữ. Vâng, tôi vừa tích hợp thêm i18n cho nó.
Đây không phải là lần đầu tiên tôi làm một trang web hỗ trợ đa ngôn ngữ, trước đó tôi tham gia một số dự án nhưng là xây dựng ngay từ đầu hoặc là tham gia vào quá trình bảo trì, thêm tính năng mới.
2coffee.dev được tạo ra cách đây khá lâu và ngay từ đầu không tích hợp i18n, cho nên cần chút thời gian để nghiên cứu một vài modules hỗ trợ i18n.
Không khó để tìm kiếm, @nuxtjs/i18n là modules phổ biến cho Nuxt.js nên không có lý do gì để không dùng cả. Hãy lướt qua tài liệu để xem cách cài đặt cũng như cấu hình và những gì mà nó làm được.
Sau bước tìm hiểu, tôi bắt tay vào thử nghiệm một số trường hợp để xem cách hoạt động cũng như có đáp ứng được yêu cầu của mình hay không. Ví dụ như làm thế nào để thay đổi ngôn ngữ, có hỗ trợ chuyển ngôn ngữ theo path không (ví dụ /vi là tiếng Việt, /en là tiếng Anh, mặc định / là tiếng Việt chẳng hạn), cấu hình url dẫn đến bài viết khác nhau giữa 2 ngôn ngữ (ví dụ url bài viết hiện tại đang là /bai-viet/ten-bai-viet nhưng khi sang tiếng Anh, tôi muốn nó phải thành /articles/article-title), rồi các liên kết trong trang web cấu hình thế nào để nó trỏ đến đúng url theo ngôn ngữ… rất nhiều thứ cần phải làm.
Và cuối cùng, cái quan trọng nhất là cấu hình SEO cho website đa ngôn ngữ. Phải làm cách nào đó để nói cho các công cụ tìm kiếm biết trang web của mình có nhiều hơn một thứ tiếng, cũng như nội dung tiếng Việt và tiếng Anh của cùng một bài để nó tối ưu kết quả tìm kiếm. Thật may mắn vì trước đó tôi có đọc qua vấn đề này và Google cũng có bài viết rất chi tiết về cách khai báo một website đa ngôn ngữ tại Overview of international and multilingual site topics. Tùy theo trường hợp, ví dụ như tôi đang khai báo địa chỉ sitemap với Google Search Console để họ có thể nhanh chóng cập nhật thông tin, nhưng trước đó sitemap chỉ đang cấu hình một ngôn ngữ, do đó cần cập nhật thêm đa ngôn ngữ.
Tuần vừa rồi, tôi đã gấp rút thực hiện. Bạn thấy đấy, series bài viết về Rust đã bị tạm dừng để tập trung hoàn thiện tính năng song ngữ.
Mọi chuyện không bao giờ suôn sẻ như những gì mình mong muốn, việc tích hợp song ngữ cũng vậy, thậm chí tôi suýt phải bỏ dở vì một lỗi khó hiểu vào phút chót.
Do không tích hợp i18n ngay từ đầu cho nên phải đi tìm lại những nơi có ngôn ngữ hiển thị ở trong code, đưa nó vào các tệp cấu hình ngôn ngữ, dịch nó và thay thế bằng mã hỗ trợ hiển thị ngôn ngữ của module i18n.
Vấn đề mất nhiều thời gian nhất là tích hợp đường dẫn trong thẻ <nuxt-link>
, cụ thể là props to
. Thông thường chúng ta sử dụng <nuxt-link to=“/path”>
để tạo liên kết đến router /path
. Nhưng như tôi đã nói, đường đẫn đến bài viết giữa 2 ngôn ngữ Việt/Anh là khác nhau, cho nên phải sử dụng cú pháp mới: <nuxt-link :to="localePath({ name: 'bai-viet-id', params: { id: article.url } })”>
. Cấu trúc trong localePath
bao gồm name
là tên đường dẫn, params để khai báo params trong url đến liên kết mới. Để dễ hình dung, hãy nhìn vào cấu trúc thư mục hiện tại của tôi:
Oh, vậy thì name
phải là bai-viet
thôi chứ sao lại là bai-viet-id
. Vâng điều đó tiêu tốn kha khá thời gian của tôi để tìm ra cách làm thế khai báo router có chứa params, cụ thể ở đây là _id
.
Đến đây mọi thứ đã gần như xong xuôi cả rồi, đến bước npm run generate
để tạo trang web static thì nhiều lỗi lại xuất hiện, mọi nỗ lực lúc này là cố gắng “fix” chúng. Nếu như sử dụng SSR thì nhiều vấn đề trở nên đơn giản hơn nhưng tôi lựa chọn SSG để tăng tốc độ tải trang, SSG có cách xây dựng mã khác so với SSR nên cần sửa lại mã tương thích với nó.
Cuối cùng, tưởng rằng mọi thứ đã sẵn sàng để deploy rồi thì khi kiểm tra lại, tôi phát hiện ra ở lần tải đầu tiên, trang web của tôi bị vỡ giao diện trong khoảng thời gian rất ngắn, dù chỉ là nhấp nháy rất nhỏ nhưng nếu để ý sẽ thấy rất khó chịu, chưa kể điều này còn khiến công cụ tìm kiếm đánh giá thấp về mặt hiệu năng.
Nếu như nhiều lỗi khác có để để lại thông báo rõ ràng về nguyên nhân cũng như vị trí lỗi, thì với lỗi giao diện điều đó là quá xa xỉ. Tôi tin chắc nhiều người rơi vào trạng thái này và tuyệt vọng trong vấn đề suy đoán lỗi ở đâu. Riêng tôi thì checkout lại về từng commit để xem vấn đề bắt đầu từ chỗ nào. Quả không sai, nó xuất hiện từ commit thêm cấu hình module i18n.
Quái dị, i18n thì liên quan gì đến giao diện chứ? Tôi cũng nghĩ vậy, mất rất nhiều thời gian để tìm kiếm câu trả lời trên mạng nhưng hình như không có ai bị cả, chắc mình cần tạo một issue về vấn đề này trên Github? Hmm… có lẽ sẽ mất nhiều thời gian, chưa kể cần trình bày lại cấu trúc dự án lẫn vấn đề đang gặp phải để đợi người khác trả lời? ==‘
Đành phải tự thân vận động thôi, tôi debug sự khác nhau giữa 2 commit trước và sau khi thêm module i18n và phát hiện ra một dữ kiện, ở commit sau khi thêm module, có một file css được tải chậm hơn, nó cũng chính là css của thành phần vỡ giao diện, tiếp tục xem tại sao nó lại bị tải chậm hơn thì hóa ra, nó được thực thi sau khi một file js được tải xuống, hay nói cách khác, file js đó chứa đoạn mã tải xuống css. Vậy có nghĩa là module i18n đang cố thay đổi cấu trúc dự án build ra!?
Tôi mò đến cấu hình build trong file nuxt.config.js
, có một options đang bật là:
build: {
…
extractCSS: {
ignoreOrder: true,
},
}
Đây là tùy chọn tách các file css thành các file nhỏ hơn để tăng tốc độ tải, có lẽ việc tách css đã xung đột với i18n, khiến nó suy nghĩ file css kia không quan trọng và có thể tải sau cho nên dẫn đến giao diện bị vỡ trong một khoảng thời gian ngắn. Tôi tắt tùy chọn đấy đi thì… mọi thứ đã hoạt động trở lại. Thật may mắn!
Bước đầu là việc viết mã, sau đó là một hành trình dài.
Trước tiên, tôi sẽ nhờ ChatGPT dịch một số bài mà cảm thấy có thể dịch được sang tiếng Anh, sửa lại vài thứ như định dạng, hình ảnh… bởi vì tôi có thử dịch một bài nhưng nó không trả về định dạng markdown để cho mình copy được. Một số bài viết có định dạng mã trong các dấu nháy ngược (`), khi hiển thị trong ChatGPT thì bị lỗi… tóm lại, sau khi nhờ dịch, vẫn cần phải chỉnh sửa lại nhiều trước khi xuất bản bài viết hoàn chỉnh.
Đánh giá hiệu quả của việc SEO cũng như sửa lỗi hay vấn đề phát sinh sau này, bởi vì tôi chưa có nhiều kinh nghiệm về việc quản lý website đa ngôn ngữ.
Tuy là dịch, nhưng tôi sẽ cố gắng đọc lại bài viết đã dịch và hy vọng sẽ học hỏi thêm được nhiều điều thông qua cách này!
Bí mật ngăn xếp của Blog
Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (1)