Bộ công cụ bản địa hoá (i18n)
Chuyển file bản dịch giữa JSON, gettext PO, XLIFF, Android, iOS, Flutter ARB, YAML và CSV, kiểm tra file locale để tìm key thiếu và placeholder sai, thử số nhiều ICU MessageFormat, khám phá các API Intl của trình duyệt và bản địa hoá giả chuỗi. Mọi thứ chạy ngay trong trình duyệt, không có gì được tải lên.
Các bước
- Chọn một tab: Chuyển đổi file, So sánh locale & kiểm tra key, Kiểm thử thông điệp ICU, Sân chơi Intl hoặc Bản địa hoá giả.
- Dán nội dung file, dùng Mở file, hoặc bấm Tải mẫu để xem ví dụ.
- Xem kết quả ở bên phải, rồi sao chép hoặc dùng Tải xuống.
Chuyển đổi file bản dịch
- Ở tab Chuyển đổi file, chọn định dạng đầu vào ở Từ và định dạng đầu ra ở Sang. Khi mở file, định dạng được nhận diện từ tên và nội dung file; Nhận diện làm việc tương tự với nội dung vừa dán.
- Điền Locale đích (ví dụ
vi,ruhaypt-BR) khi đầu ra cần: gettext dùng nó cho headerLanguage,Plural-Formsvà thứ tựmsgstr[n], XLIFF dùng cho ngôn ngữ đích, ARB cho@@localevà YAML cho key gốc kiểu Rails. Để trống thì giữ locale đọc được từ đầu vào. - Kết quả cập nhật ngay khi bạn gõ. Hoán đổi đưa kết quả trở lại ô nhập và đảo chiều chuyển đổi.
Các tuỳ chọn chỉ hiện khi phù hợp:
- Số nhiều trong JSON: Hậu tố i18next (key_one, key_other) gom
inbox_onevàinbox_otherthành một chuỗi số nhiều; Chuỗi ICU MessageFormat đọc và ghi dạng{count, plural, one {…} other {…}}, đúng như next-intl, react-intl và Flutter cần. - Giá trị đầu vào là (chuyển file một ngôn ngữ sang XLIFF, PO hoặc CSV): bản dịch (target) hoặc văn bản nguồn. Chọn văn bản nguồn khi chuẩn bị file gửi người dịch.
- Ghi ra (chuyển file song ngữ sang file một ngôn ngữ): giữ bản dịch (target) hoặc văn bản nguồn.
- Locale nguồn cho đầu ra XLIFF, và Bọc trong key locale gốc (Rails) cho đầu ra YAML.
Mỗi định dạng hiểu được những gì:
- JSON: object lồng nhau thành key có dấu chấm và ngược lại; danh sách chuỗi vẫn là danh sách.
- gettext PO và POT:
msgctxt,msgid_pluralvớimsgstr[n], comment của người dịch (#), comment trích xuất (#.), tham chiếu (#:) và cờ (#,) kể cảfuzzy, mục lỗi thời#~, chuỗi nhiều dòng và mã thoát kiểu C. Các ô số nhiều được ghép với nhóm CLDR bằng cách tính chính biểu thứcPlural-Formscủa file, nên ô 0, 1, 2 của tiếng Nga thành one, few và many. - XLIFF 1.2 và 2.0: source, target, note,
state,translate="no"và thẻ inline như<g>,<x/>hay<pc>. - Android strings.xml:
<string>,<plurals>,<string-array>,translatable="false", comment, CDATA, mã thoát\'và\", khoảng trắng trong dấu nháy và thẻ inline như<b>hay<xliff:g>. - iOS:
.stringscó comment và mã thoát\",\nhay\U00E9(mở được cả file UTF-16), và quy tắc số nhiều.stringsdict. - Flutter ARB:
@@locale, mô tả và placeholders trong@key; placeholder còn thiếu được bổ sung khi ghi file ARB. - YAML (Rails): key gốc
en:và map số nhiềuone:/other:. CSV: các cộtkey, source, targetcùng cột tuỳ chọncontextvànote.
Khi chuyển đổi không trọn vẹn
Không có gì bị bỏ âm thầm. Khung vàng dưới kết quả đếm những gì định dạng đích không chứa được: dạng số nhiều bị tách thành key key_one / key_other, comment và ghi chú, msgctxt, trạng thái duyệt như fuzzy, văn bản nguồn trong file một ngôn ngữ, và mảng. Khi chuỗi có ngữ cảnh mà định dạng đích không có chỗ lưu, ngữ cảnh được nối vào key bằng ký tự ẩn U+0004 — đúng quy ước của gettext — nên chuyển ngược về PO sẽ khôi phục được.
Placeholder được giữ nguyên: %d của Android không bị đổi thành {count} khi sang ARB, và khung cảnh báo sẽ nhắc bạn điều đó.
Kiểm tra file locale
- Chuyển sang So sánh locale & kiểm tra key. Dán nội dung vào các ô hoặc dùng Mở nhiều file để nạp nhiều file cùng lúc; Thêm file thêm một ô trống. Ô đầu tiên là Tham chiếu, còn Dùng làm tham chiếu đưa một ô khác lên đầu.
- Mỗi ô mặc định dùng Tự nhận diện định dạng và đọc locale từ tên file (
vi.json,values-vi/strings.xml,fr.lproj,app_vi.arb). Gõ locale nếu tên file không có. Các file có thể khác định dạng. - Bảng Tổng quan cho biết số key của từng file, mức hoàn thành và số lỗi mỗi loại. Lọc danh sách kết quả bên dưới theo loại lỗi hoặc theo file.
Những gì được kiểm tra:
- Thiếu key và Key thừa so với file tham chiếu, Giá trị rỗng và Trùng key — key lặp lại trong cùng một object JSON vẫn bị phát hiện dù JSON.parse sẽ che mất.
- Giống file tham chiếu: giá trị giống hệt văn bản tham chiếu, nhiều khả năng chưa dịch. Tắt bằng Đánh dấu giá trị giống hệt file tham chiếu; kiểm tra này tự bỏ qua khi hai file cùng ngôn ngữ, như en-US và en-GB.
- Lệch placeholder:
{name}so với{nom},{{count}},%sso với%1$s,%{name},${name}và tham số ICU. - Lệch thẻ HTML, và Khác khoảng trắng ở đầu hoặc cuối chuỗi (tắt bằng So sánh khoảng trắng đầu và cuối).
- Thiếu dạng số nhiều: chuỗi số nhiều, hoặc plural ICU nằm trong chuỗi, thiếu nhóm mà locale của file cần — tiếng Nga cần one, few và many.
Lưu danh sách bằng Báo cáo CSV hoặc Báo cáo Markdown, hoặc chọn file ở Key còn thiếu của rồi tải Khung JSON gồm các key file đó còn thiếu, điền văn bản tham chiếu hoặc để trống giá trị.
Kiểm thử thông điệp ICU
- Ở tab Kiểm thử thông điệp ICU, gõ thông điệp hoặc chọn một ví dụ trong danh sách phía trên ô soạn.
- Lỗi cú pháp được báo kèm dòng, cột và dấu mũ chỉ đúng chỗ sai.
- Chọn Locale. Tham số liệt kê mọi tham số thông điệp đọc — ô số cho tham số plural và number, ô chọn ngày cho ngày giờ, danh sách lựa chọn cho select — và Kết quả được hiển thị bằng Intl.PluralRules, Intl.NumberFormat và Intl.DateTimeFormat.
- Cảnh báo liệt kê các nhóm số nhiều locale cần nhưng thông điệp còn thiếu, và các lựa chọn locale không bao giờ dùng (
fewtrong tiếng Anh). Lựa chọn=1chỉ thay đượconeở locale màonenghĩa là đúng 1. - Cột bên cạnh có Cây phân tích, các nhóm của locale kèm số mẫu, mọi dạng số nhiều đã hiển thị, và Thông điệp này đã đủ ở các ngôn ngữ khác chưa? cho khoảng một chục locale phổ biến. Múi giờ quyết định cách hiển thị ngày và giờ.
Cú pháp được hỗ trợ: tham số đơn {name}, number (integer, percent, currency và skeleton :: như ::currency/EUR .00 hay ::compact-short), date và time (short đến full, hoặc skeleton như ::yMMMd), plural có offset: và =N, selectordinal, select, lồng nhau, # và cách đặt trong dấu nháy đơn.
Sân chơi Intl
Sân chơi Intl hiển thị, cho một mã locale, kết quả trực tiếp và đúng đoạn JavaScript của từng API Intl: NumberFormat (tiền tệ, phần trăm, đơn vị, dạng rút gọn), DateTimeFormat (kiểu ngày giờ, lịch như chinese hay dangi, hệ chữ số và múi giờ — Bây giờ điền thời điểm hiện tại), RelativeTimeFormat, ListFormat, PluralRules cho 0–110, DisplayNames, Collator (đặt cạnh Array.sort() thông thường, vốn xếp Đ sau z) và Segmenter, nơi emoji và chữ tiếng Việt cho thấy vì sao text.length không phải số ký tự.
Bản địa hoá giả
- Ở tab Bản địa hoá giả, đặt Đầu vào là một định dạng file bản dịch hoặc Văn bản thường (mỗi dòng một chuỗi).
- Dán chuỗi hoặc mở file. Kết quả giữ nguyên định dạng và tải xuống được.
- Điều chỉnh Chữ có dấu, Dấu ngoặc [!! … !!], Kéo dài chuỗi (30–40% cho thấy chỗ giao diện bị vỡ), Giữ cú pháp ICU và Phải sang trái (U+202E). Xem trước cho thấy hiệu ứng trên một chuỗi mẫu.
Placeholder, thẻ HTML, entity và URL được giữ nguyên, thông điệp ICU chỉ bị đổi phần chữ nên vẫn phân tích được. Bản dịch gettext còn trống được điền từ msgid.
Mẹo
- Dùng tuỳ chọn chuỗi ICU của Số nhiều trong JSON cho file next-intl hoặc react-intl, và hậu tố i18next cho i18next.
- Build thử một bản bản địa hoá giả trước khi có bản dịch thật: chữ nào không có dấu hoặc ngoặc là chữ bị viết cứng trong code.
- Kiểm tra trực tiếp file
.povới file JSON tiếng Anh — phần kiểm tra locale đọc được mọi định dạng mà trình chuyển đổi đọc được.
Câu hỏi thường gặp
File bản dịch của tôi có bị tải lên đâu không?
Không. Việc phân tích, chuyển đổi, kiểm tra và hiển thị đều diễn ra trong trình duyệt, không có gì được gửi lên máy chủ.
Vì sao tiếng Việt chỉ có một dạng số nhiều?
Danh từ tiếng Việt không đổi hình thức theo số lượng — "1 tệp" và "5 tệp" dùng cùng một từ — nên CLDR chỉ gán cho tiếng Việt một nhóm là other. Vì vậy header gettext của tiếng Việt là nplurals=1; plural=0;, thông điệp ICU cho vi chỉ cần other {…}, và công cụ kiểm tra không bao giờ đòi file tiếng Việt có one. Tiếng Nhật, tiếng Trung, tiếng Hàn, tiếng Thái và tiếng Indonesia cũng vậy, trong khi tiếng Nga cần one, few và many, còn tiếng Ả Rập cần đủ sáu nhóm.
Vì sao placeholder %d của tôi không thành {count}?
Các cú pháp placeholder không tương ứng một-một — tham số printf có kiểu và vị trí, tham số ICU có tên — nên trình chuyển đổi giữ nguyên và cảnh báo bạn. Hãy đổi tên thủ công, rồi dùng So sánh locale & kiểm tra key để chắc chắn mọi file đã khớp.
Ký tự vô hình trong một số key JSON là gì?
Đó là U+0004, ký tự gettext dùng để nối ngữ cảnh (msgctxt) với thông điệp. Các định dạng không có trường ngữ cảnh giữ cặp này trong key, nên chuyển ngược về PO sẽ khôi phục ngữ cảnh.
Vì sao một số key XLIFF 2.0 hoặc Android bị đổi tên?
Id của XLIFF 2.0 và tên tài nguyên Android không được chứa khoảng trắng hay hầu hết dấu câu, nên các ký tự đó thành _. Khung cảnh báo cho biết có bao nhiêu key bị đổi tên.