Nếu bạn đã từng làm việc với hệ thống dịch thuật của Django, bạn biết quy trình này rồi. Bạn đánh dấu các chuỗi bằng _(), chạy makemessages, mở file .po, và nhìn chằm chằm vào hàng trăm trường msgstr trống. Sau đó bạn lặp lại cho mổi ngôn ngữ mà ứng dụng hỗ trợ.
Quy trình này hoạt động. Nhưng nó cũng ngốn hàng giờ đồng hồ mà lẽ ra có thể dùng để phát triển tính năng. Một dự án có 200 chuỗi cần dịch cho năm ngôn ngữ nghỉa là 1.000 bản dịch cần quản lý. Thay đổi nhãn một nút bấm và bạn cần cập nhật cả năm file .po trước lần triển khai tiếp theo, hoặc chấp nhận giao diện dịch dở dang.
Quy trình thủ công
Hệ thống dịch thuật tích hợp của Django có bốn bước:
- Đánh dấu các chuỗi bằng
gettexthoặc{% trans %}trong mã nguồn và template - Chạy
makemessagesđể trích xuất chúng vào file.po - Dịch các mục
msgstrtrong từng file.po - Chạy
compilemessagesđể tạo file nhị phân.momà Django đọc
Bước 1 và 2 rất nhanh. Bước 4 chỉ là một lệnh. Bước 3 là nơi mọi thứ chậm lại.
Bước 3 trên thực tế
Mở file locale/de/LC_MESSAGES/django.po trong trình soạn thảo. Bạn sẽ thấy nội dung như sau:
#: myapp/views.py:42
msgid "Your subscription has been renewed."
msgstr ""
#: myapp/templates/dashboard.html:18
msgid "Welcome back, %(username)s"
msgstr ""
#: myapp/forms.py:15
msgid "This field is required."
msgstr ""
Với mỗi mục, bạn cần một bản dịch chính xác mà vẫn giử nguyên các biến placeholder như %(username)s hoặc {count}. Nếu placeholder bị sai, Django sẽ ném ra lỗi định dạng khi chạy.
Với năm ngôn ngữ và hàng trăm chuỗi mới, bạn sẽ mất cả buổi chiều để copy-paste giữa các dịch vụ dịch thuật, sửa định dạng placeholder, và hy vọng rằng không bỏ sót gì. Và sprint tiếp theo, bạn lại làm lại từ đầu.
Khi nào quy trình thủ công gặp vấn đề
Dự án nhỏ có thể dùng cách dịch thủ công. Khi dự án vượt qua một vài ngưỡng nhất định, quy trình bắt đầu gây khó khăn.
Số lượng chuỗi tăng dần
Một ứng dụng Django với vài chục template, vài form, và một số email thông báo có thể dễ dàng đạt 300 chuổi cần dịch. Nhân với số ngôn ngữ được hỗ trợ và tổng số tăng nhanh chóng. Đến một lúc nào đó, không ai muốn là người mở file .po đó.
Bản dịch lỗi thời
Khi lập trình viên thay đổi các chuỗi hiện có, makemessages đánh dấu các bản dịch cũ là fuzzy. Các mục fuzzy này cần được xem xét thủ công, và chúng có xu hướng chồng chất vì không ai nhận ra cho đến khi người dùng báo cáo thấy tiếng Anh trên trang tiếng Đức. Số lượng fuzzy tồn đọng âm thầm tăng lên giữa các lần phát hành.
Lỗi placeholder
Định dạng .po của Django sữ dụng nhiều kiểu placeholder: %(name)s cho định dạng % của Python, {name} cho .format() và f-string, và {{ name }} cho biến template. Người dịch (con người hoặc máy) đôi khi làm hỏng các placeholder này, và lổi chỉ xuất hiện khi chạy. Một %(username)s bị sai trong bản dịch tiếng Đức có thể làm crash view mà bản tiếng Anh vẫn chạy bình thường.
Chặn triển khai
Nếu dịch thuật là một bước thủ công trong pipeline triễn khai, nó trở thành nút thắt cổ chai. Hoặc bạn chờ bản dịch trước khi triển khai (chậm), hoặc bạn triển khai với bản dịch thiếu (trải nghiệm người dùng kém). Không lựa chọn nào tốt khi bạn phát hành hàng tuần.
Tự động hóa dịch thuật
Tự động hóa dịch thuật Django nghĩa là thay thế bước 3 (phần dịch thủ công) bằng một quy trình tự động trong khi giữ nguyên mọi thứ khác. Quy trình makemessages và compilemessages không thay đổi. Bạn chỉ ngừng dịch file .po bằng tay.
Lựa chọn 1: tự viết script
Bạn có thể viết script đọc file .po, gửi các chuỗi chưa dịch đến API dịch thuật, và ghi kết quả trở lại. Phiên bản cơ bản ngắn một cách đáng ngạc nhiên:
import polib
from openai import OpenAI
client = OpenAI()
po = polib.POFile("locale/de/LC_MESSAGES/django.po")
for entry in po.untranslated_entries():
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Translate to German. Preserve all "
"placeholder variables like %(name)s exactly as they appear."},
{"role": "user", "content": entry.msgid},
],
)
entry.msgstr = response.choices[0].message.content.strip()
po.save()
Cách này giải quyết đươc 80% vấn đề. 20% còn lại là phần phức tạp:
- Dạng số nhiều (
msgid_pluralvà nhiều mụcmsgstr[n]) cần xữ lý đặc biệt, vì các ngôn ngữ khác nhau có quy tắc số nhiều khác nhau - Chuỗi ngữ cảnh (
msgctxt) nên được truyền vào API để bản dịch phù hợp với ngữ cảnh sử dụng - Bạn cần kiểm tra rằng placeholder vẫn nguyên vẹn sau khi dịch
- Giới hạn tốc độ API và lỗi cần logic retry
- Script cần xử lý encoding file
.pođúng cách (UTF-8 với các trường header cụ thể) - Các mục fuzzy cần xử lý khác với các mục chưa dịch
Tự xây dựng tất cả những thứ này hoàn toàn khả thi, nhưng cần vài ngày làm việc và bảo trì liên tục. Mỗi khi API dịch thuật thay đổi hành vi hoặc giá cả, bạn cần cập nhật script.
Lựa chọn 2: nền tảng quản lý dịch thuật
Các dịch vụ như Crowdin, Transifex, và Lokalise tích hợp với hệ thống quản lý phiên bản và cung cấp giao diện web cho người dịch. Chúng xử lý file .po nguyên bản, hỗ trợ dạng số nhiều, và có thể thiết lập quy trình tự động với dịch máy làm bản nháp đầu tiên.
Đánh đổi là chi phí và độ phức tạp. Các nền tảng này được xây dựng cho đội ngũ có người dịch chuyên trách, và giá cả phản ánh điều đó. Với lập trình viên cá nhân hoặc nhóm nhỏ chỉ cần dịch file .po, chúng thêm nhiều hạ tầng hơn mức cần thiết.
Lựa chọn 3: công cụ chuyên dụng
Các công cụ được xây dựng riêng cho quy trình file .po của Django có thể tự động hóa dịch thuật mà vẫn tôn trọng các đặc thù của định dạng. Chúng xử lý dạng số nhiều, giữ nguyên placeholder, quản lý các mục fuzzy, và tích hợp với pipeline makemessages/compilemessages hiện có.
Ưu điểm là chúng được thiết kế chính xác cho trường hợp sử dụng này, nên ít mã kết nối cần viết và bảo trì hơn. Nhược điểm là bạn thêm một dependency vào quy trình dịch thuật.
Thiết lập dịch thuật tự động trong CI/CD
Bất kể bạn chọn công cụ nào, việc tích hợp với pipeline triển khai đều theo cùng một mô hình:
# .github/workflows/translate.yml
name: Update translations
on:
push:
branches: [main]
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install django
- name: Extract new strings
run: python manage.py makemessages -a --no-obsolete
- name: Translate .po files
run: python scripts/translate.py # your translation script
- name: Compile messages
run: python manage.py compilemessages
- name: Commit and push
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git add locale/
git diff --staged --quiet || git commit -m "chore: update translations"
git push
Workflow này trích xuất chuỗi mới, dịch chúng, biên dịch kết quả, và commit các file đã cập nhật. Kiểm tra git diff --staged --quiet tránh tạo commit trống khi không có gì thay đổi.
Bạn cũng có thể chạy quy trình này như pre-commit hook hoặc scheduled job. Ý tưởng chính vẫn giống nhau: dịch thuật xảy ra tự động như một phần của quy trình phát triển bình thường, không phải là bước thủ công riêng biệt.
Kiểm tra bản dịch
Bản dịch tự động cần được kiểm tra. Ngay cả các mô hình AI tốt nhất đôi khi cũng làm hỏng định dạng placeholder hoặc tạo ra bản dịch không phù hợp với ngữ cảnh giao diện.
Kiểm tra placeholder
Viết một kiểm tra so sánh placeholder giữa msgid và msgstr:
import re
import polib
PLACEHOLDER_RE = re.compile(r"%\(\w+\)[sd]|\{\w+\}|\{\{.*?\}\}")
def validate_po(path):
po = polib.POFile(path)
errors = []
for entry in po.translated_entries():
src = set(PLACEHOLDER_RE.findall(entry.msgid))
dst = set(PLACEHOLDER_RE.findall(entry.msgstr))
if src != dst:
errors.append(f"Mismatch: {entry.msgid!r}")
return errors
Chạy kiểm tra này trong CI và fail build nếu bất kỳ placeholder nào bị hỏng. Chỉ mất vài giây và bắt được loại lỗi dịch thuật nguy hiểm nhất trước khi chúng đến production.
Kiểm tra độ dài
Một số bản dịch dài hơn đáng kể so với chuỗi gốc. Văn bản tiếng Đức thường dài hơn tiếng Anh 20-30%, và điều này có thể phá vỡ các thành phần giao diện có chiều rộng cố định. Kiểm tra tỷ lệ độ dài đơn giản có thể đánh dấu các bản dịch có khả năng gây vấn đề bố cục.
Kiểm tra các mục fuzzy
Sau khi dịch tự động, hãy kiểm tra rằng không có mục nào còn ở trạng thái fuzzy. Các mục fuzzy có nghĩa là Django sẽ không sử dụng bản dịch đó, và sẽ quay về tiếng Anh. Pipeline CI nên coi các mục fuzzy là điều kiện fail, giống như cách nó coi test thất bại.
Giữ bản dịch đồng bộ
Phần khó nhất của việc bảo trì dịch thuật không phải là bản dịch ban đầu, mà là giữ bản dịch luôn cập nhật khi codebase phát triển.
Thay đổi chuỗi
Khi bạn thay đổi một chuỗi có thể dịch đã tồn tại, makemessages thực hiện một thao tác tinh tế: nó tìm chuỗi tương tự trong file .po và tạo một fuzzy match. Bản dịch cũ được giữ lại nhưng đánh dấu cần xem xét. Điều này hữu ích khi người dịch xem xét thay đổi, nhưng quy trình tự động cần xử lý các mục fuzzy một cách cụ thể.
Cách tiếp cận thực tế: xóa cờ fuzzy và dịch lại mục đó từ đầu. Fuzzy matching giúp tiết kiệm công sức cho người dịch, nhưng công cụ tự động có thể dịch trực tiếp chuỗi mới. Kết quả thường tốt hơn so với việc cố gắng sửa bản dịch cũ cho phù hợp với chuỗi nguồn mới.
Chuỗi mới
Chuỗi mới xuất hiện dưới dạng các mục chưa dịch sau khi chạy makemessages. Quy trình tự động nên dịch chúng và commit kết quả. Nếu bạn chạy makemessages trong CI, các chuỗi mới sẽ được dịch tự động khi merge.
Chuỗi bị xóa
Chạy makemessages --no-obsolete sẽ xóa các mục cho những chuỗi không còn tồn tại trong mã nguồn. Điều này giữ file .po sạch sẽ và tránh tích lũy các bản dịch thừa theo thời gian.
Những điều cần kiểm tra trước khi tự động hóa
Trước khi thiết lập dịch thuật tự động, hãy xác nhận một vài điều:
- Tất cả các chuỗi cần dịch đã được đánh dấu đúng bằng
_()hoặc{% trans %}. Chạymakemessagesvà kiểm tra các chuỗi bạn có thể đã bỏ sót. - Cài đặt
LOCALE_PATHSchính xác và cấu trúc thư mụclocale/đã sẵn sàng. compilemessageschạy không có lỗi trên các file.pohiện tại.- Bạn có cách xem xét bản dịch tự động trước khi chúng lên production, dù chỉ là môi trường staging nơi ai đó có thể dùng thử ứng dụng bằng từng ngôn ngữ.
Tự động hóa dịch thuật hoạt động tốt nhất khi phần còn lại của thiết lập i18n đã vững. Tự động hóa bước 3 sẽ không giúp gì nếu bước 1 và 2 bị lỗi.
Mẹo thực tế
Một vài điều tôi đã học được từ việc làm việc với dịch thuật Django tự động:
- Chạy
makemessagesvới--no-obsoleteđể giữ file.posạch sẽ. Các mục thừa gây nhầm lẫn cho cả người xem xét và công cụ tự động. - Sử dụng
msgctxtkhi cùng một chuỗi tiếng Anh cần bản dịch khác nhau trong các ngữ cảnh khác nhau. "Post" có thể là bài viết trên blog hoặc hành động đăng bài, và nếu không có ngữ cảnh, người dịch (con người hoặc tự động) phải đoán. - Kiểm tra với các ngôn ngữ có cấu trúc khác biệt so với tiếng Anh. Từ dài trong tiếng Đức và bộ ký tự tiếng Nhật phát hiện lỗi bố cục mà các ngôn ngữ châu Âu bỏ sót.
- Tạo file
TRANSLATING.mdvới thuật ngữ và hướng dẫn giọng văn riêng cho dự án. Nếu dự án gọi "workspace" là "project" trong giao diện, hãy ghi lại điều đó. Một số công cụ dịch tự động có thể đọc file này và áp dụng tùy chọn của bạn một cách nhất quán. - Commit file
.povà.mocùng nhau. Nếu bạn biên dịch message trong CI, hãy đảm bảo file.mođược bao gồm trong artifact triển khai. Django đọc file.mo, không phải file.po.
So sánh các phương pháp
Với lập trình viên cá nhân hoặc nhóm nhỏ có dự án Django hỗ trợ 3-5 ngôn ngữ:
Script tự viết phù hợp nếu bạn thích bảo trì nó và số lượng chuỗi dưới 200. Vượt qua ngưỡng đó, các trường hợp đặc biệt liên quan đến dạng số nhiều, placeholder, và các mục fuzzy bắt đầu ăn vào thời gian bạn đã tiết kiệm được.
Nền tảng quản lý dịch thuật hợp lý nếu bạn có đội ngũ người dịch làm việc trên nhiều dự án. Với một ứng dụng Django đơn lẻ, chúng thường quá dư thừa.
Công cụ chuyên dụng nằm ở giữa: chúng xử lý các đặc thù của file .po mà không yêu cầu bạn tự xây dựng và bảo trì phần tích hợp. Chi phí hàng tháng thường thấp hơn thời gian kỷ thuật bỏ ra cho giải pháp tự xây.
Chọn phương pháp nào phù hợp với quy mô đội ngũ và mức độ sẵn sàng bảo trì hạ tầng dịch thuật. Điều quan trọng là dịch thuật không còn là nút thắt thủ công mà trở thành phần tự nhiên của chu kỳ phát triển.