Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn xây một trang web hoàn hảo về mặt accessibility: contrast đạt chuẩn AAA, keyboard navigation mượt mà, screen reader đọc từng thành phần rõ ràng. Nhưng tất cả những nỗ lực đó được làm cho phiên bản tiếng Anh. Đến khi công ty mở rộng sang thị trường Việt Nam, Thái Lan, và Nhật Bản, mọi thứ bắt đầu vỡ vụn: screen reader đọc tiếng Việt bằng giọng Anh nghe như tiếng vô nghĩa, chữ tiếng Đức tràn ra khỏi nút bấm, giao diện tiếng Ả Rập bị lệch hoàn toàn vì không hỗ trợ đọc từ phải sang trái.
Đây chính là lý do bài học này tồn tại. Internationalization (i18n) và accessibility không phải hai chủ đề tách rời — chúng đan xen chặt chẽ. Một sản phẩm chỉ thực sự "inclusive" khi nó phục vụ được người dùng ở mọi ngôn ngữ, mọi vùng miền, với mọi công cụ hỗ trợ mà họ dùng. Nếu bạn làm việc cho một công ty có tham vọng vươn ra khu vực Đông Nam Á hay toàn cầu — mà phần lớn công ty công nghệ Việt Nam ngày nay đều có tham vọng đó — thì kỹ năng kết hợp i18n với a11y sẽ là điểm khác biệt lớn trong hồ sơ của bạn.
Trong bài này, chúng ta tập trung riêng vào khía cạnh: làm sao để một sản phẩm đa ngôn ngữ vẫn giữ được tính accessibility. Đây không phải bài về cách dịch nội dung hay quản lý file dịch thuần túy — mà là về những điểm giao thoa nơi ngôn ngữ gặp công nghệ hỗ trợ.
Khái niệm cốt lõi
i18n và l10n — phân biệt để không nhầm lẫn
Hai thuật ngữ này thường bị dùng lẫn lộn, nhưng chúng khác nhau rõ ràng:
Internationalization (i18n) — là quá trình chuẩn bị ứng dụng để có thể phục vụ nhiều ngôn ngữ và vùng miền. Đây là công việc kiến trúc: tách nội dung ra khỏi code, không hard-code chuỗi văn bản, thiết kế layout co giãn được, hỗ trợ nhiều bảng mã ký tự. Con số "18" trong "i18n" chính là số chữ cái giữa "i" và "n" của từ "internationalization".
Localization (l10n) — là quá trình thực hiện việc thích nghi cho một vùng cụ thể: dịch văn bản, đổi định dạng ngày tháng, tiền tệ, đơn vị đo, thậm chí điều chỉnh hình ảnh và màu sắc cho phù hợp văn hóa. Nếu i18n là xây một cái tủ có nhiều ngăn, thì l10n là bỏ đồ đúng vào từng ngăn.
Điểm mấu chốt cho người làm accessibility: nếu i18n làm sai ngay từ đầu, mọi cố gắng a11y sau này đều bị phá vỡ khi chuyển ngôn ngữ.
Thuộc tính lang — nền tảng của a11y đa ngôn ngữ
Screen reader chọn giọng đọc và cách phát âm dựa trên thuộc tính lang trong HTML. Đây là điểm giao thoa quan trọng nhất giữa i18n và a11y.
<html lang="vi">
Khi bạn khai báo lang="vi", screen reader như NVDA hay VoiceOver sẽ dùng bộ tổng hợp giọng nói tiếng Việt để đọc "Xin chào" thành "xin chào" đúng thanh điệu, thay vì đọc theo kiểu tiếng Anh nghe méo mó.
Khi trong một trang có đoạn văn bản bằng ngôn ngữ khác, bạn phải đánh dấu cục bộ:
<p>Chúng tôi gọi đây là <span lang="en">design thinking</span> trong ngành.</p>
Nhờ vậy, screen reader sẽ chuyển sang giọng Anh đúng chỗ, rồi quay lại giọng Việt. Bài 33 đã giới thiệu về language attributes ở mức cơ bản; ở đây ta nhấn mạnh vai trò của nó trong bối cảnh sản phẩm đa ngôn ngữ, nơi việc chuyển đổi lang phải diễn ra động theo lựa chọn của người dùng.
Hướng đọc (directionality) — LTR và RTL
Tiếng Việt, tiếng Anh đọc từ trái sang phải (LTR — left-to-right). Nhưng tiếng Ả Rập, tiếng Do Thái, tiếng Ba Tư đọc từ phải sang trái (RTL — right-to-left). Nếu sản phẩm của bạn nhắm tới thị trường Trung Đông, toàn bộ layout phải "lật gương": menu, icon mũi tên, thứ tự các cột đều đảo chiều.
<html lang="ar" dir="rtl">
Với accessibility, dir không chỉ ảnh hưởng thị giác mà còn ảnh hưởng thứ tự focus khi dùng keyboard và cách screen reader duyệt nội dung. Dùng thuộc tính logic của CSS (như margin-inline-start thay cho margin-left) sẽ giúp layout tự thích nghi mà không phải viết lại.
Văn bản co giãn — text expansion
Cùng một câu, dịch sang các ngôn ngữ khác nhau sẽ dài ngắn khác nhau. Tiếng Đức thường dài hơn tiếng Anh 30–40%, tiếng Việt có dấu chiếm chiều cao dòng nhiều hơn. Nếu bạn thiết kế nút bấm hay nhãn form với kích thước cố định vừa khít tiếng Anh, khi dịch sang tiếng khác chữ sẽ bị cắt hoặc tràn — và với người dùng screen reader hay người phóng to chữ (liên hệ bài 31 về Text Resize & Reflow), điều này gây lỗi nghiêm trọng.
Định dạng theo vùng và trải nghiệm cho AT
Ngày tháng, số, tiền tệ phải được định dạng theo vùng. Ngày "03/04/2026" ở Việt Nam là mùng 3 tháng 4, nhưng ở Mỹ lại là mùng 4 tháng 3. Với screen reader, việc đánh dấu ngữ nghĩa đúng (dùng thẻ <time datetime="...">) giúp công nghệ hỗ trợ đọc ngày tháng rõ ràng, không mơ hồ.
Tình huống thực tế
Tình huống 1 — Sàn thương mại điện tử Việt mở rộng ra Đông Nam Á
Một sàn TMĐT giả định tên "ChợViệt" ban đầu chỉ có tiếng Việt. Khi họ mở rộng sang Thái Lan và Philippines, đội kỹ thuật thêm cơ chế chuyển ngôn ngữ bằng cách thay nội dung qua JavaScript. Vấn đề: họ đổi text hiển thị nhưng quên cập nhật thuộc tính lang trên thẻ <html>. Kết quả, khi người dùng Thái Lan mù dùng screen reader chọn tiếng Thái, phần mềm vẫn đọc bằng bộ giọng tiếng Việt — nghe hoàn toàn vô nghĩa với họ.
Đội QA phát hiện lỗi khi một cộng tác viên khiếm thị người Thái phản hồi rằng "trang web nói thứ tiếng tôi không hiểu, dù chữ trên màn hình là tiếng Thái". Cách sửa: mỗi khi người dùng đổi ngôn ngữ, JavaScript cập nhật đồng thời document.documentElement.lang và, với tiếng Ả Rập trong tương lai, cả dir.
Bài học rút ra: Chuyển ngôn ngữ không chỉ là đổi chữ hiển thị. Thuộc tính lang phải được cập nhật đồng bộ, nếu không screen reader sẽ đọc sai giọng — một lỗi vô hình với người sáng mắt nhưng phá hỏng hoàn toàn trải nghiệm của người khiếm thị.
Tình huống 2 — Ứng dụng ngân hàng số và bài toán text expansion
Một ngân hàng số tại Việt Nam (giả định tên "TechBank") ra mắt app song ngữ Việt–Anh. Nhóm thiết kế làm các nút hành động với chiều rộng cố định vừa khít cụm từ tiếng Việt ngắn như "Gửi tiền". Khi bật tiếng Anh, cụm "Transfer money now" bị cắt cụt thành "Transfer mon...". Tệ hơn, họ đặt nhãn nút chỉ bằng hình ảnh chữ (image text) nên screen reader đọc ra một chuỗi vô nghĩa từ tên file.
Sau một đợt audit, đội thiết kế chuyển sang nút co giãn theo nội dung, dùng văn bản thật (không phải ảnh chữ) để screen reader đọc được, và đặt aria-label rõ ràng cho các nút chỉ có icon. Họ cũng kiểm thử với chuỗi giả lập dài gấp 1.5 lần để đảm bảo layout không vỡ. Sau thay đổi, số lượng khiếu nại từ người dùng lớn tuổi (vốn hay bật cỡ chữ lớn) giảm khoảng 25% trong quý tiếp theo.
Bài học rút ra: Thiết kế đa ngôn ngữ phải giả định văn bản có thể dài hơn nhiều so với bản gốc. Kết hợp text expansion với việc dùng văn bản thật thay cho ảnh chữ giúp vừa co giãn tốt vừa thân thiện với screen reader.
Tình huống 3 — Grab và trải nghiệm chuyển ngôn ngữ có thể tìm thấy
Các siêu ứng dụng như Grab phục vụ hàng chục thị trường với nhiều ngôn ngữ. Một điểm accessibility thường bị bỏ sót là: nút chuyển đổi ngôn ngữ phải tự nó có thể truy cập được ngay cả khi người dùng đang ở ngôn ngữ họ không đọc được. Nếu một người Indonesia vô tình mở app ở tiếng Việt, họ cần tìm được nút đổi ngôn ngữ mà không cần hiểu tiếng Việt.
Cách làm tốt là hiển thị mỗi ngôn ngữ bằng chính tên bản địa của nó (endonym): "Tiếng Việt", "English", "ไทย", "Bahasa Indonesia" — thay vì dịch tên ngôn ngữ. Đồng thời, phần tử chọn ngôn ngữ đó phải đánh dấu từng lựa chọn bằng lang tương ứng để screen reader đọc đúng tên mỗi ngôn ngữ.
Bài học rút ra: Bộ chọn ngôn ngữ là "cửa thoát hiểm" cho người dùng bị lạc ngôn ngữ. Nó phải tìm được, dùng được, và đọc được độc lập với ngôn ngữ hiện tại của giao diện.
Hướng dẫn từng bước
Dưới đây là quy trình thực tế để tích hợp i18n với a11y trong một dự án web:
Bước 1 — Khai báo ngôn ngữ mặc định đúng. Đặt lang trên thẻ <html> khớp với ngôn ngữ nội dung chính. Với sản phẩm Việt Nam, đó là lang="vi", không phải để mặc định lang="en" như nhiều template quốc tế.
Bước 2 — Cập nhật lang (và dir) khi đổi ngôn ngữ. Khi người dùng chuyển ngôn ngữ, dùng JavaScript cập nhật đồng thời:
document.documentElement.lang = 'th';
document.documentElement.dir = isRTL(locale) ? 'rtl' : 'ltr';
Bước 3 — Đánh dấu các đoạn ngôn ngữ phụ. Với văn bản chèn ngôn ngữ khác trong trang (tên riêng, thuật ngữ), bọc bằng <span lang="..."> để screen reader chuyển giọng đúng.
Bước 4 — Thiết kế cho text expansion. Không cố định chiều rộng nút/nhãn. Kiểm thử với chuỗi giả lập dài hơn 30–50%. Đảm bảo văn bản không bị cắt, và vẫn reflow tốt khi phóng to.
Bước 5 — Dùng văn bản thật, không phải ảnh chữ. Mọi nhãn, nút, tiêu đề nên là text để screen reader đọc được và để bản dịch dễ thay thế. Với icon-only button, thêm aria-label đã dịch.
Bước 6 — Định dạng ngày/số/tiền theo locale với ngữ nghĩa đúng. Dùng API như Intl.DateTimeFormat và Intl.NumberFormat, kết hợp thẻ ngữ nghĩa (<time>) để công nghệ hỗ trợ đọc chính xác.
Bước 7 — Làm bộ chọn ngôn ngữ dễ tìm và tự truy cập. Hiển thị tên bản địa (endonym), đặt lang cho từng lựa chọn, đảm bảo dùng được bằng keyboard và screen reader.
Bước 8 — Kiểm thử trên từng ngôn ngữ. Bật screen reader ở từng locale, dùng keyboard duyệt, kiểm tra RTL nếu có. Đừng chỉ test bản tiếng Anh rồi cho rằng các bản khác cũng ổn.
Lỗi thường gặp & mẹo
Lỗi 1 — Quên cập nhật lang khi đổi ngôn ngữ. Đây là lỗi phổ biến nhất. Text đổi nhưng lang giữ nguyên khiến screen reader đọc sai giọng. Mẹo: Đưa việc set lang vào đúng hàm chuyển locale, coi nó là một phần bắt buộc của quy trình.
Lỗi 2 — Hard-code chuỗi văn bản trong code. Khi chuỗi bị nhúng cứng, không thể dịch, và thường cũng khó thêm aria-label dịch được. Mẹo: Tách toàn bộ text ra file translation ngay từ đầu, kể cả text dành cho screen reader.
Lỗi 3 — Nối chuỗi (string concatenation) để tạo câu. Ví dụ ghép "Bạn có " + số + " tin nhắn" — cấu trúc ngữ pháp mỗi ngôn ngữ khác nhau nên câu ghép ra sẽ sai, và screen reader đọc rời rạc. Mẹo: Dùng chuỗi có placeholder hoàn chỉnh ("Bạn có {count} tin nhắn") để giữ ngữ pháp và ngữ điệu đúng.
Lỗi 4 — Dùng cờ quốc gia để đại diện ngôn ngữ. Cờ đại diện cho quốc gia, không phải ngôn ngữ (tiếng Tây Ban Nha nói ở hàng chục nước). Với người khiếm thị màu hoặc screen reader, cờ cũng không truyền tải rõ. Mẹo: Dùng tên ngôn ngữ bằng chữ (endonym) thay cho cờ.
Lỗi 5 — Bỏ quên chiều RTL trong focus order. Ở giao diện RTL, thứ tự tab và thứ tự đọc phải đảo theo. Mẹo: Dùng thuộc tính logic của CSS và test keyboard trong chế độ RTL thật.
Mẹo tổng quát: Hãy coi mỗi ngôn ngữ như một "người dùng" riêng cần được kiểm thử accessibility đầy đủ, thay vì cho rằng làm tốt một ngôn ngữ là đủ.
Bài tập thực hành
- Kiểm tra
lang: Lấy một trang web đa ngôn ngữ bất kỳ (ví dụ trang có nút đổi ngôn ngữ), đổi sang ngôn ngữ khác, rồi mở DevTools kiểm tra xem thuộc tínhlangtrên thẻ<html>có được cập nhật không. Ghi lại kết quả.
- Test screen reader: Bật VoiceOver (Mac) hoặc NVDA (Windows), mở một trang tiếng Việt có
lang="vi"và một trang tiếng Việt để mặc địnhlang="en". So sánh cách screen reader phát âm và ghi lại sự khác biệt.
- Mô phỏng text expansion: Lấy một nút hoặc nhãn form trong dự án của bạn, thay nội dung bằng một chuỗi dài gấp 1.5 lần. Quan sát xem layout có vỡ, chữ có bị cắt không. Đề xuất cách sửa.
- Thiết kế bộ chọn ngôn ngữ: Phác thảo (hoặc code) một language switcher hiển thị endonym, có
langcho từng lựa chọn, dùng được bằng keyboard. Tự kiểm thử bằng cách chỉ dùng phím Tab và Enter.
- Đánh dấu ngôn ngữ phụ: Viết một đoạn HTML tiếng Việt có chèn 2–3 thuật ngữ tiếng Anh, bọc chúng bằng
<span lang="en">. Nghe screen reader đọc và xác nhận nó chuyển giọng đúng chỗ.
Tóm tắt
Internationalization và accessibility là hai mặt của cùng một mục tiêu: phục vụ mọi người dùng, dù họ nói ngôn ngữ nào và dùng công cụ hỗ trợ gì. i18n chuẩn bị hạ tầng để đa ngôn ngữ, l10n thực hiện việc thích nghi cho từng vùng — nhưng cả hai chỉ trọn vẹn khi giữ được tính accessibility.
Những điểm cốt lõi cần nhớ: luôn cập nhật thuộc tính lang (và dir cho RTL) mỗi khi đổi ngôn ngữ; đánh dấu các đoạn ngôn ngữ phụ bằng <span lang>; thiết kế cho text expansion để chữ dịch không bị cắt; dùng văn bản thật thay cho ảnh chữ; định dạng ngày/số/tiền theo locale với ngữ nghĩa đúng; và làm bộ chọn ngôn ngữ tự truy cập được bằng tên bản địa. Tránh các bẫy như hard-code chuỗi, nối chuỗi tạo câu, và dùng cờ đại diện cho ngôn ngữ.
Quan trọng nhất: hãy kiểm thử accessibility trên từng ngôn ngữ, không cho rằng làm tốt một bản là đủ. Đó là tư duy khác biệt giữa một sản phẩm "có nhiều ngôn ngữ" và một sản phẩm thực sự inclusive trên toàn cầu.