Phiên bản tiếng Anh của một trình phân tích Cache-Control mới phát hành đã hiển thị văn bản tiếng Nhật—trạng thái của nó hiển thị “新鮮” thay vì “Fresh.” Sai sót này bắt nguồn từ logic dùng chung trả về các chuỗi tiếng Nhật được viết cứng (hard-coded), trong khi trang web chỉ cung cấp các nhãn tiếng Anh.

Nhà phát triển xây dựng một loạt các công cụ trình duyệt nhẹ, mỗi công cụ đều có một trang tiếng Anh và một trang tiếng Nhật sử dụng chung các hàm phân tích và logic cốt lõi. Chỉ có phần chữ hiển thị là cần khác nhau. Khi trình phân tích Cache-Control được xuất bản, giao diện tiếng Anh hiển thị các nhãn chính xác, nhưng các giá trị mà nó hiển thị lại đến từ lớp logic, nơi vẫn còn chứa các ký tự tiếng Nhật nguyên bản. Không có lỗi console nào xuất hiện; trang web trông vẫn bình thường, nhưng thông tin trình bày cho người dùng nói tiếng Anh lại bị sai.

Tại sao logic dùng chung có thể phản bội việc dịch thuật

Lỗi này bắt nguồn từ một lựa chọn thiết kế: hàm cốt lõi quyết định nội dung hiển thị lại trả về các chuỗi ký tự trực tiếp bằng tiếng Nhật. Lớp trang (page layer), chịu trách nhiệm cho các văn bản tiếng Anh xung quanh, đã không có cơ hội để thay thế các giá trị đó. Vì logic và giao diện người dùng (UI) được tách biệt rõ ràng, vấn đề này đã không bị phát hiện trong quá trình kiểm thử—về mặt kỹ thuật thì mọi thứ đều "hoạt động" bình thường, mặc dù ngôn ngữ hiển thị cho người dùng là không chính xác.

Nhược điểm là ngôn ngữ được sử dụng bên trong module dùng chung sẽ trở thành ngôn ngữ mặc định cho mọi front-end sử dụng nó. Nếu cần một ngôn ngữ khác, giá trị mặc định này sẽ trở thành một lỗi tiềm ẩn.

Cách khắc phục: các khóa (keys), gói (packs) và một lưới an toàn

Tác giả đã viết lại kiến trúc để tách biệt các trách nhiệm:

  • Message packs hiện lưu trữ tất cả các chuỗi văn bản có thể đọc được cho mỗi ngôn ngữ.
  • Shared logic chỉ trả về các khóa ký hiệu (symbolic keys), không bao giờ trả về văn bản thô.
  • Pages sẽ tra cứu từ ngữ phù hợp từ gói tương ứng dựa trên khóa.

Khi một thông báo cần bao gồm một con số, mã mới sử dụng một hàm nhỏ thay vì chuỗi mẫu (template string). Điều này cho phép mỗi ngôn ngữ tự quyết định vị trí của con số, nhằm thích ứng với sự khác biệt về thứ tự từ.

Một bước phân tích tĩnh đơn giản cũng đã được thêm vào: quá trình build sẽ quét các tệp dùng chung để tìm các ký tự tiếng Nhật. Nếu có bất kỳ ký tự nào xuất hiện, nhà phát triển sẽ được cảnh báo ngay lập tức, ngăn chặn việc các văn bản tiếng nước ngoài được viết cứng lọt trở lại vào mã nguồn.

Những gì kinh nghiệm đã dạy cho tác giả

  1. Dịch thuật đóng vai trò như một bước kiểm duyệt. Trong khi viết các thông báo tiếng Anh, tác giả nhận thấy một số từ tương đương trong tiếng Nhật khá mơ hồ. Việc dịch thuật đã buộc tác giả phải diễn đạt rõ ràng hơn ở cả hai ngôn ngữ.
  2. Các hàm dùng chung trả về chuỗi văn bản sẽ áp đặt một ngôn ngữ cho tất cả mọi người. Nếu một hàm tự quyết định ngôn ngữ, bất kỳ bên sử dụng nào mong đợi một ngôn ngữ khác đều sẽ gặp phải lỗi tương tự. Đây không phải là một lỗi hiển thị UI; đó là một lỗi logic.

Khuyến nghị cho bất kỳ ai đang duy trì các công cụ đa ngôn ngữ

  • Trả về các khóa thay vì chuỗi văn bản từ các hàm cốt lõi. Hãy để lớp UI xử lý việc bản địa hóa (localization).
  • Hoặc truyền các chuỗi mong muốn vào hàm dưới dạng tham số. Điều này giúp logic không bị phụ thuộc vào ngôn ngữ.
  • Kiểm tra các module dùng chung để tìm văn bản ngôn ngữ gốc được viết cứng. Một lệnh tìm kiếm nhanh các ký tự không phải ASCII có thể giúp phát hiện các vấn đề tiềm ẩn.
  • Thêm bước kiểm tra ký tự nước ngoài trong mã dùng chung tại thời điểm build. Phát hiện sớm sẽ tốt hơn là sự nhầm lẫn sau khi phát hành.

Những điều cần lưu ý tiếp theo

Bài học rút ra: Nếu dự án của bạn chia sẻ mã nguồn giữa các phiên bản ngôn ngữ khác nhau, hãy đảm bảo rằng phần dùng chung không bao giờ tự quyết định cách dùng từ. Hãy để mỗi trang tự cung cấp từ ngữ của riêng nó, và bạn sẽ tránh được tình huống trớ trêu khi một trang tiếng Anh lại vô tình nói tiếng Nhật.