Cách dịch tệp ngôn ngữ JSON mà không làm hỏng khóa hoặc placeholder
Xem những giá trị nào trong tệp en.json được dịch, trình dịch tệp viết lại những gì (chú thích, thứ tự khóa, ID lớn) và cách kiểm tra placeholder, số nhiều ICU và khóa bằng một script miễn phí và một tệp kiểm thử.

Để dịch một tệp ngôn ngữ JSON như en.json, hãy đưa toàn bộ tệp cho một trình dịch hiểu JSON. Dán văn bản thô vào cửa sổ trò chuyện hoặc trình dịch web là cách làm rủi ro. Một trình dịch hiểu JSON sẽ đọc tệp, chỉ dịch các giá trị văn bản và ghi lại đúng các khóa, để ứng dụng của bạn vẫn có thể tải tệp. Sao chép và dán có thể dịch một khóa, thay đổi dấu ngoặc kép hoặc làm mất một dấu ngoặc nhọn. Bất kỳ điều nào trong số đó đều làm hỏng tệp.
Giữ nguyên các khóa là phần dễ. Hầu hết rắc rối đến sau đó: một placeholder {name} trả về thành {nom}, một số nhiều ICU mất trường hợp other, một URL bị gửi đi dịch như bất kỳ văn bản nào khác, và một vài thay đổi mà trình dịch thực hiện trên chính tệp. Đó là những gì hướng dẫn này đề cập.
Chúng tôi đã tạo một tệp kiểm thử chứa tất cả các trường hợp đó và viết một trình kiểm tra miễn phí so sánh tệp nguồn với bản dịch của nó. Chúng tôi cũng đã chạy tệp kiểm thử qua một bản sao cục bộ của mã phân tích cú pháp và ghi JSON mà BookTranslator sử dụng, với một dấu đánh dấu thay cho mô hình, để bạn có thể thấy chính xác những giá trị nào được gửi đi và tệp quay về như thế nào.
Trả lời nhanh: Điều gì xảy ra với từng phần của tệp
| Phần của tệp | Ví dụ từ thử nghiệm của chúng tôi | BookTranslator làm gì | Cần kiểm tra gì |
|---|---|---|---|
| Khóa | "greeting", "error" | Giữ nguyên như chúng vốn có | Không có gì |
| Giá trị văn bản thuần | "Reading list" | Đã dịch | Ý nghĩa, độ dài trên màn hình |
| Giá trị có placeholder | "Welcome back, {name}!", "Saved %s of %d chapters" | Đã dịch, placeholder được bao gồm trong văn bản gửi đi | Tên và số lượng placeholder (trình kiểm tra thực hiện việc này) |
| Thông điệp số nhiều ICU | "{count, plural, one {# book} other {# books}}" | Được gửi dưới dạng một chuỗi | Trường hợp other, và các phạm trù số nhiều cho ngôn ngữ đích |
| HTML nội tuyến | "accept the <b>Terms</b>." | Đã dịch; số lượng thẻ được kiểm tra sau khi dịch | Tên thẻ và số lượng (công cụ kiểm tra thực hiện việc này) |
| URL được lưu dưới dạng văn bản | "https://example.com/help" | Được gửi để dịch | Rằng nó đã trở về không thay đổi |
| Giá trị không có chữ cái | "2.4.1", "—", "" | Được giữ nguyên như cũ | Không có gì |
Số, true/false, null | 20, true | Được giữ nguyên như cũ | ID rất lớn (xem bên dưới) |
| Mảng chuỗi | ["Open", "Save", "Export as EPUB"] | Được dịch trong tệp đã dịch; được giữ nguyên không dịch trong tệp song ngữ | Tệp song ngữ, nếu bạn sử dụng nó |
| Chú thích (JSONC) | // Shown on the home screen | Đã bị xóa. Nếu một giá trị cũng chứa //, tệp có thể không tải được | Xóa chú thích trước khi tải lên |
Phần còn lại của hướng dẫn này sẽ chỉ ra nguồn gốc của từng hàng và cách xử lý.
Bước 1: Kiểm tra tệp nguồn trước khi tải lên
Chạy công cụ kiểm tra trên tệp bạn định dịch. Nó cần Python 3 và không cần gì khác:
python3 check_i18n_json.py en.json
Trên tệp thử nghiệm của chúng tôi, nó đã in ra:
WARN limits.projectId: 9007199254740993 is above 2^53 and can change in a JavaScript round trip; store IDs as strings
WARN error.network: a flat key with dots and a nested key resolve to the same dot path; tools that address strings by dot path can mix them up
INFO (root): 3 keys look like integers; JavaScript sorts those first and in numeric order when it rewrites the object
INFO source: 17 strings, 2 numbers
Hai cảnh báo và ghi chú về các khóa giống số nguyên đều khớp với một thay đổi mà chúng tôi thấy khi tệp thử nghiệm của mình đi qua trình dịch. Nếu tệp của bạn không gặp phải bất kỳ điều nào trong số này, bạn có thể bỏ qua đến Bước 3.
Công cụ kiểm tra cũng từ chối một tệp không phải là JSON nghiêm ngặt, chẳng hạn như tệp có chú thích hoặc dấu phẩy cuối, vì đó là vấn đề tiếp theo.
Những gì đã trở về từ tệp thử nghiệm của chúng tôi
Thử nghiệm được chạy vào ngày 2 tháng 11 năm 2026, dựa trên cách xử lý JSON hiện tại trong phần phụ trợ của BookTranslator. Thay vì gọi một mô hình, thử nghiệm đã thay thế mọi giá trị sẽ được gửi để dịch bằng [ZH] cộng với văn bản gốc. Điều đó giữ cho kết quả có thể dự đoán được: bất kỳ thứ gì có [ZH] ở phía trước đều đã được gửi, bất kỳ thứ gì không có đều không được gửi. Cả hai đầu ra đều được công bố: tệp đã dịch và tệp song ngữ.
Tệp đã dịch
Mọi khóa đều trở về không thay đổi và tệp vẫn phân tích cú pháp được. Các giá trị có chữ cái đã được gửi, bao gồm {name}, %s, thông điệp ICU, thẻ <b> và URL trợ giúp. Chuỗi phiên bản "2.4.1", dấu gạch ngang và chuỗi rỗng không được gửi vì chúng không chứa chữ cái. Số và boolean được giữ nguyên.
Bốn điều khác đã thay đổi:
- Một chuỗi đã đi vào sai khóa. Trong thử nghiệm của chúng tôi, tệp có một khóa phẳng
"error.network"với văn bản "Check your connection." và cả một khóa lồng nhau"error": { "network": "Network error" }. Trình dịch nhận diện từng chuỗi bằng đường dẫn của nó với dấu chấm giữa các phần, nên cả hai chuỗi đều có cùng địa chỉerror.network. Trong đầu ra, cả hai khóa đều chứa bản dịch của "Network error". "Check your connection." đã biến mất. Điều này đòi hỏi cả hai kiểu trong cùng một tệp, hiếm gặp, nhưng nó thất bại âm thầm. Trình kiểm tra sẽ cảnh báo về điều này trước khi bạn tải lên. - Các khóa giống số nguyên đã bị sắp xếp lại.
"steps": { "10": ..., "2": ..., "1": ... }trở về thành"1","2","10". JavaScript luôn liệt kê các khóa giống số nguyên trước, theo thứ tự số, khi nó dựng lại một đối tượng. Các khóa và giá trị vẫn khớp, nên ứng dụng của bạn vẫn ổn, nhưng so sánh với tệp nguồn sẽ rất lộn xộn. - Một ID lớn đã mất độ chính xác.
9007199254740993trở về thành9007199254740992. JavaScript lưu số dưới dạng dấu phẩy động 64-bit, và các số nguyên trên 2^53 không thể biểu diễn chính xác hoàn toàn (MDN vềNumber.MAX_SAFE_INTEGER). Hãy lưu ID dài dưới dạng chuỗi hoặc giữ chúng ngoài tệp ngôn ngữ. - Định dạng đã được viết lại. Đầu ra dùng thụt lề hai dấu cách và kết thúc bằng một dòng mới. Nếu nguồn của bạn dùng tab hoặc bốn dấu cách, hãy mong đợi thay đổi khoảng trắng trong so sánh.
Tệp thử nghiệm của chúng tôi không có khóa trùng lặp, nhưng chúng đáng để biết: trình phân tích JSON của JavaScript chỉ giữ giá trị cuối cùng cho một khóa lặp lại, nên văn bản trước đó không bao giờ được dịch. Ứng dụng của bạn sẽ tải cùng giá trị cuối cùng, nên không có gì hiển thị bị hỏng, nhưng trình kiểm tra liệt kê các khóa trùng lặp để bạn có thể dọn dẹp chúng.
Tệp song ngữ
Tệp song ngữ giữ mỗi giá trị gốc và thêm một khóa anh em với _translated ở cuối:
"greeting": "Welcome back, {name}!",
"greeting_translated": "[ZH] Welcome back, {name}!",
Điều đó rất hữu ích để xem xét, vì nguồn và bản dịch nằm cạnh nhau. Đây không phải là tệp ứng dụng của bạn có thể tải, vì mọi khóa giờ đều có một khóa sinh đôi. Hãy dùng tệp đã dịch trong ứng dụng của bạn.
Một lỗ hổng: các chuỗi bên trong mảng không có khóa anh em _translated, vì các mục mảng không có khóa để thêm vào. Trong thử nghiệm của chúng tôi, "menu": ["Open", "Save", "Export as EPUB"] trở về bằng tiếng Anh trong tệp song ngữ, nhưng được dịch trong tệp đã dịch. Nếu bạn xem xét từ tệp song ngữ, hãy kiểm tra mảng trong tệp đã dịch.
Tệp có chú thích
Nhiều dự án giữ chú thích trong tệp ngôn ngữ của họ (JSONC), điều mà JSON nghiêm ngặt không cho phép. Trình dịch trước tiên thử JSON nghiêm ngặt. Nếu thất bại, nó xóa chú thích và dấu phẩy cuối rồi thử lại. Xóa chú thích nghĩa là xóa mọi thứ sau // trên một dòng, và điều đó bao gồm cả // trong https://.
Tệp có chú thích và URL của chúng tôi không tải được chút nào; phân tích cú pháp thất bại ở chú thích đầu tiên. Một tệp có chú thích nhưng không có // bên trong bất kỳ giá trị nào đã tải tốt, và các chú thích bị loại bỏ khỏi đầu ra.
Vậy: nếu tệp của bạn có chú thích, hãy xóa chúng trước khi tải lên, hoặc giữ một bản sao có chú thích và chấp nhận rằng bản dịch sẽ không có chúng.
Bước 2: Chuẩn bị tệp
Bạn có thể khắc phục hầu hết các vấn đề này trước khi dịch:
- Chuyển sang JSON nghiêm ngặt. Xóa chú thích và dấu phẩy cuối. Nếu công cụ build của bạn đọc JSONC, hãy giữ tệp có chú thích làm nguồn và xuất một bản sao sạch để dịch.
- Dùng một kiểu khóa. Hoặc lồng khóa hoặc làm phẳng chúng bằng dấu chấm, không dùng cả hai trong một tệp. Nếu bạn dùng i18next, tùy chọn
keySeparatorcủa nó kiểm soát cách đọc dấu chấm trong khóa, nên hãy kiểm tra cài đặt đó trước khi làm phẳng bất cứ thứ gì. - Chuyển dữ liệu không phải văn bản ra ngoài. ID, URL, cờ tính năng và giới hạn thuộc về config, không phải tệp ngôn ngữ. Nếu một URL phải giữ lại, hãy giữ nó, nhưng mong đợi kiểm tra nó sau khi dịch. Hướng dẫn không dịch của chúng tôi đề cập cách quyết định những gì khác nên giữ nguyên ngôn ngữ gốc.
- Viết placeholder giống nhau ở mọi nơi. Một kiểu, chẳng hạn
{name}, dễ kiểm tra hơn hỗn hợp{name},{{name}}và%s.
Bước 3: Dịch toàn bộ tệp
Tải tệp đã làm sạch lên trình dịch tài liệu của BookTranslator, nơi liệt kê .json cùng các định dạng bản địa hóa và văn bản có cấu trúc khác mà nó chấp nhận. Giá được hiển thị trước khi thanh toán. Khi tác vụ hoàn tất, trang lịch sử của bạn cung cấp tệp đã dịch và tệp song ngữ dưới dạng các bản tải xuống riêng biệt.
Hai điều về cách văn bản được dịch ảnh hưởng đến kết quả:
- Mô hình không nhìn thấy khóa của bạn. Mỗi giá trị được gửi dưới dạng một phân đoạn được đánh số, không có tên khóa. Nếu
"button.open"và"status.open"đều ghi "Open", mô hình không có cách nào biết rằng một cái là động từ và cái kia là tính từ. Những chuỗi ngắn, mơ hồ là những chuỗi bạn nên kiểm tra thủ công. - Mỗi giá trị được dịch riêng lẻ. Không có gì liên kết hai khóa với nhau, vì vậy cùng một từ ở hai vị trí có thể trả về hai cách khác nhau, và một từ cần hai bản dịch khác nhau lại có thể trả về giống nhau. Bảng thuật ngữ tự động tùy chọn của BookTranslator thu thập tên riêng, địa danh và thuật ngữ chuyên ngành và giữ chúng nhất quán, nhưng bạn không thể cung cấp bản dịch cố định của riêng mình. Đối với các thuật ngữ phải được dịch theo một cách chính xác, hãy giữ một danh sách thuật ngữ và kiểm tra các chuỗi đó sau khi dịch; hướng dẫn về bảng thuật ngữ của chúng tôi giải thích cách lập kế hoạch.
Các thẻ HTML nội tuyến được đếm sau khi dịch, và một phân đoạn có thẻ không khớp sẽ được thử lại. Các placeholder dạng ngoặc nhọn, placeholder kiểu %s và cú pháp ICU được gửi như một phần của văn bản nhưng không được kiểm tra theo cùng cách. Hãy chạy Bước 4 trên mọi bản dịch.
Nếu bạn muốn dịch bằng một mô hình trò chuyện hoặc một mô hình cục bộ, chỉ gửi các giá trị, không bao giờ gửi tệp thô, và đặt chúng trở lại theo khóa bằng một script. Các kiểm tra bên dưới áp dụng cho cả hai cách.
Bước 4: Kiểm tra tệp đã dịch
So sánh bản dịch với nguồn:
python3 check_i18n_json.py en.json fr.json
Để cho thấy nó bắt được gì, chúng tôi đã tạo một tệp tiếng Pháp có lỗi cài sẵn. Nó được làm thủ công, không phải đầu ra của mô hình. Sau các cảnh báo nguồn giống như trong Bước 1, trình kiểm tra đã in ra:
ERROR app.greeting: placeholders differ; lost ['name'] extra ['nom']
ERROR app.help_url: urls differ; lost ['https://example.com/help'] extra ['https://example.com/aide']
ERROR app.items: ICU message has no 'other' case
ERROR app.legacy: placeholders differ; lost ['%d'] extra []
ERROR limits.maxFiles: type changed from int to str
ERROR limits.projectId: non-text value changed: 9007199254740993 -> 9007199254740992
INFO menu[1]: identical to source: 'Save'
INFO compared 20 source values, 1 unchanged strings
Mỗi lỗi trong số đó sẽ hiển thị như một lỗi trong ứng dụng: tên trống, liên kết hỏng, sự cố hoặc thông báo thô khi số nhiều được hiển thị, thiếu số chương, giới hạn được so sánh như văn bản. Dòng INFO liệt kê các chuỗi trả về không thay đổi. Một số là ổn, như tên thương hiệu; "Save" trong menu tiếng Pháp thì không.
Trình kiểm tra so sánh khóa, kiểu giá trị, giá trị không phải văn bản, placeholder ({name}, {{name}}, %s, %1$s, %@), tên đối số ICU, thẻ HTML và URL. Nó không đánh giá bản thân bản dịch.
Các trường hợp số nhiều được cho là sẽ thay đổi
Một số nhiều ICU liệt kê một thông điệp cho mỗi phạm trù số nhiều. Tiếng Anh có hai phạm trù, one và other, nhưng nhiều ngôn ngữ có một bộ khác. Một bản dịch tiếng Nga tốt cho thông điệp items của chúng tôi thường sẽ dùng one, few, many và other, còn bản dịch tiếng Nhật chỉ dùng other. Vì vậy, sự không khớp ở đây không phải lúc nào cũng là lỗi.
Các phạm trù đến từ quy tắc số nhiều CLDR của Unicode, và other là phạm trù mà mọi ngôn ngữ đều có. Đây là các phạm trù số đếm được báo cáo bởi Intl.PluralRules trong Node.js với CLDR 48:
| Ngôn ngữ | Phạm trù số nhiều |
|---|---|
| Tiếng Anh, tiếng Đức | one, other |
| Tiếng Pháp, tiếng Tây Ban Nha, tiếng Bồ Đào Nha | one, many, other |
| Tiếng Nga, tiếng Ba Lan | one, few, many, other |
| Tiếng Ả Rập | zero, one, two, few, many, other |
| Tiếng Nhật, tiếng Trung, tiếng Hàn | khác |
Tiếng Pháp, tiếng Tây Ban Nha và tiếng Bồ Đào Nha chỉ dùng many cho những số tròn rất lớn, chẳng hạn như một triệu, nên thông điệp cho số lượng nhỏ thường có thể bỏ qua dạng này. Công cụ kiểm tra báo thiếu other là lỗi, và mọi thay đổi khác về phạm trù là cảnh báo để bạn đối chiếu với bảng này. Bạn có thể tự in danh sách cho bất kỳ ngôn ngữ nào:
node -e 'console.log(new Intl.PluralRules("ru").resolvedOptions().pluralCategories)'
Người dịch được cung cấp các dạng tiếng Anh, nên hãy kiểm tra kết quả trả về. Nếu thông điệp tiếng Nga hoặc tiếng Ả Rập vẫn chỉ có one và other, hãy tự thêm các dạng còn thiếu hoặc nhờ người dịch làm việc đó.
Sau đó tải tệp vào ứng dụng
Công cụ kiểm tra chứng minh tệp an toàn để tải, chứ không chứng minh tệp đọc tốt. Hãy chuyển ứng dụng sang ngôn ngữ mới và xem các màn hình: những từ dài trong tiếng Đức hoặc tiếng Phần Lan tràn ra khỏi nút bấm, văn bản từ phải sang trái trong tiếng Ả Rập hoặc tiếng Do Thái, và ngày tháng hoặc số mà mã của bạn định dạng riêng tách khỏi tệp ngôn ngữ. Hướng dẫn của chúng tôi về dịch thuật so với bản địa hóa đề cập những gì khác thay đổi giữa các thị trường ngoài từ ngữ.
Các vấn đề thường gặp
Tệp đã dịch không tải được trong ứng dụng của tôi. Hãy chạy công cụ kiểm tra trên tệp đó. Nếu công cụ kiểm tra không phân tích được, tệp đã bị chỉnh sửa sau khi dịch hoặc vốn không hợp lệ ngay từ đầu. Nếu phân tích được, hãy tìm các dòng ERROR về khóa hoặc kiểu dữ liệu.
Một chuỗi hiển thị dưới dạng thông điệp ICU thô, như {count, plural, ...}. Cú pháp ICU bị hỏng trong quá trình dịch, thường là thiếu dấu ngoặc nhọn hoặc thiếu dạng other. Hãy sao chép cấu trúc từ bản gốc và chỉ dịch phần từ ngữ bên trong mỗi dạng.
Tệp của tôi là YAML, tệp .po hoặc Markdown. BookTranslator cũng chấp nhận các định dạng đó, nhưng mỗi định dạng có quy tắc riêng. Đối với tài liệu, hãy xem hướng dẫn dịch Markdown của chúng tôi; đối với trang web, hãy xem hướng dẫn dịch tệp HTML.
Tôi có thể chỉ dịch các khóa mới không? Không thể trong một lần tải lên; toàn bộ tệp sẽ được dịch. Hãy đặt các khóa mới vào một tệp riêng, dịch tệp đó, rồi hợp nhất kết quả theo khóa. Công cụ kiểm tra sẽ xác nhận tệp sau khi hợp nhất có đầy đủ mọi khóa.
Tôi có nên dịch các khóa không? Không. Khóa là các định danh mà mã của bạn tra cứu. Chỉ có giá trị là văn bản để người đọc hiểu.
Bài viết liên quan





