Blog giờ đây đã có song ngữ

Blog giờ đây đã có song ngữ

Tin ngắn hàng ngày dành cho bạn
  • Đợt trước có nhắc đến openai/codex - một dạng agent nhưng chạy trong Terminal rất tiện lợi đến từ nhà OpenAI, đặc biệt đây là mã nguồn mở và đến nay họ đã hỗ trợ thêm các nhà cung cấp khác thay vì chỉ sử dụng model chatgpt như trước.

    Mới đây Anthropic cũng đã giới thiệu Claude Code gần như Codex, chỉ có điều không phải là mã nguồn mở và buộc phải sử dụng API của họ. Vì không có tiền trải nghiệm nên chỉ nghe nói dân trình khen nó quá trời, có khi còn bá hơn cả Cursor. Đổi lại là nguy cơ cháy ví bất kỳ lúc nào 😨

    » Xem thêm
  • Từ lâu rồi suy nghĩ làm thế nào để tăng sự hiện diện thương hiệu, cũng như người dùng cho blog. Nghĩ đi nghĩ lại thì chỉ có cách chia sẻ lên mạng xã hội hoặc trông chờ họ tìm kiếm, cho đến khi...

    In cái áo này được cái tắc đường khỏi phải lăn tăn, càng đông càng vui vì hàng trăm con mắt nhìn thấy cơ mà 🤓

    (Có tác dụng thật nha 🤭)

    » Xem thêm
  • Một vòng của sự phát triển nhiều dự án khá là thú vị. Tóm tắt lại trong 3 bước: Thấy một cái gì đó phức tạp -> Làm cho nó đơn giản đi -> Thêm thắt tính năng cho đến khi nó phức tạp... -> Quay trở lại vòng lặp mới.

    Tại sao lại như vậy? Để mình lấy 2 ví dụ cho các bạn thấy.

    Markdown ra đời với mục tiêu tạo ra một định dạng văn bản thô "dễ viết, dễ đọc, dễ dàng chuyển thành một dạng gì đó như HTML". Vì thời đó chẳng ai đủ kiên nhẫn mà vừa ngồi viết vừa thêm định dạng cho văn bản hiển thị ở trên web như thế nào. Ấy vậy mà giờ đây người ta đang "nhồi nhét" hoặc tạo ra các biến thể dựa trên markdown để bổ sung thêm nhiều định dạng mới đến mức... chẳng nhớ nổi hết cú pháp.

    React cũng là một ví dụ. Từ thời PHP, việc khát khao tạo ra một cái gì đó tách biệt hẳn giao diện người dùng và phần xử lý logic chính của ứng dụng thành 2 phần riêng biệt cho dễ đọc, dễ viết. Kết quả là các thư viện UI/UX phát triển rất mạnh mẽ, mang lại khả năng tương tác với người dùng rất tốt, còn phần logic ứng dụng thì nằm ở một máy chủ riêng biệt. Bộ đôi Front-end, Back-end cũng từ đấy mà thịnh hành, không thể thiếu anh bồi bàn REST API. Ấy vậy mà giờ đây React trông cũng không khác biệt gì so với PHP là mấy, kéo theo là cả Vue, Svelte... lại cùng quy tất cả về một mối.

    Cơ mà không phải vòng lặp là xấu, ngược lại vòng lặp này mang tính tiến hoá nhiều hơn là "cải lùi". Nhiều khi lại tạo ra được cái hay hơi cái cũ thế là người ta lại dựa trên cái hay đó để tiếp tục lặp. Nói cách khác là chắc lọc tinh hoa từng tí một tí một á 😁

    » Xem thêm

Vấn đề

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ó.

Quá trình

Đâ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ữ.

Khó khăn

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:

Cấu trúc thư mục

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!

Tiếp theo

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!

Cao cấp
Hello

5 bài học sâu sắc

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Ẩn danh10 tháng trước

chào bạn có thể hướng dẫn rõ hơn về cách cài đặt đa ngôn ngữ cho blog không ạ?

Trả lời