Skip to main content

Bản địa hóa liên tục không cần TMS

Mã của bạn được triển khai liên tục. Bản dịch cũng nên như vậy. Sau đây là cách tự động bản địa hóa trong quy trình phát triển.

Bản địa hóa liên tục là gì?

Bản địa hóa liên tục là cách tự động dịch chuỗi mới ngay trong quy trình phát triển thay vì gom bản dịch thành từng đợt phát hành định kỳ. Khi lập trình viên thêm tính năng mới có chuỗi UI, hệ thống sẽ dịch các chuỗi đó trước khi phát hành tính năng.

Phương pháp truyền thống

Phần lớn nhóm xử lý bản dịch theo đợt: thu thập chuỗi mới, xuất sang TMS, chờ biên dịch viên, nhập kết quả rồi phát hành. Cách này tạo nút thắt bản địa hóa, khiến bản phát hành quốc tế chậm vài ngày hoặc vài tuần.

1

Lập trình viên thêm chuỗi mới vào tệp locale nguồn

2

Chuỗi được xuất và tải lên nền tảng TMS

3

Biên dịch viên nhận thông báo và bắt đầu làm việc

4

Bản dịch được duyệt và phê duyệt

5

Tệp đã dịch được tải xuống và hợp nhất trở lại

6

Tính năng được phát hành kèm bản dịch (sau vài ngày đến vài tuần)

Days to weeks

Phương pháp tích hợp trong IDE

Với tính năng dịch tích hợp trong IDE, lập trình viên dịch chuỗi ngay trong quy trình viết mã thường ngày. Không cần xuất, tải lên hay chờ đợi — bản dịch được tạo trong cùng phiên phát triển.

1

Lập trình viên thêm chuỗi mới vào tệp locale nguồn

2

Lập trình viên yêu cầu trợ lý AI dịch tệp

3

Tệp đã dịch được ghi thẳng vào dự án

4

Tính năng và bản dịch được phát hành trong cùng một commit

Same commit

Phương pháp truyền thống

6 steps

Multiple tools and platforms

Days to weeks per cycle

Phương pháp tích hợp trong IDE

4 steps

Inside your editor

Same commit

3

Thiết lập quy trình dịch

Với nhóm muốn tự động dịch ngoài IDE, hãy thêm một bước CI/CD để dịch chuỗi mới hoặc đã thay đổi mỗi khi đẩy lên main. Cách này hoạt động với GitHub Actions, GitLab CI hoặc mọi hệ thống CI/CD.

Nhóm nhỏ nên bắt đầu bằng tính năng dịch tích hợp trong IDE. Hãy thêm tự động hóa CI/CD khi nhóm có hơn 3-4 lập trình viên hoặc khi bạn cần bảo đảm mọi lần hợp nhất vào main đều có bản dịch.

Thêm dịch thuật làm một bước trong quy trình CI/CD hiện có. Bước dịch chạy sau khi dựng thành công và trước khi triển khai, bảo đảm mọi bản phát hành đều có bản dịch mới nhất.
.github/workflows/translate.yml
# .github/workflows/translate.yml
name: Translate
on:
  push:
    branches: [main]
    paths: ['locales/en/**']

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Translate changed strings
        run: |
          npx i18n-agent translate locales/en.json \
            --to de,ja,es,fr,ko,zh-Hans \
            --api-key ${{ secrets.I18N_AGENT_API_KEY }}

      - name: Commit translations
        run: |
          git config user.name "github-actions"
          git config user.email "[email protected]"
          git add locales/
          git diff --cached --quiet || git commit -m "chore: update translations"
          git push
.gitlab-ci.yml
# .gitlab-ci.yml
translate:
  stage: deploy
  only:
    changes:
      - locales/en/**
  script:
    - npx i18n-agent translate locales/en.json
        --to de,ja,es,fr --api-key $I18N_AGENT_API_KEY
    - git add locales/ && git commit -m "chore: translations" && git push
4

Thêm bước đảm bảo chất lượng bản dịch vào CI

Các bước kiểm tra tự động trong quy trình CI phát hiện bản dịch thiếu, placeholder hỏng và định dạng thông báo ICU không hợp lệ trước khi đưa lên production. Đây là các tập lệnh đơn giản dùng để so sánh tệp locale nguồn với mọi locale đích.

Nếu không xác thực bản dịch ở cấp CI, bản dịch thiếu sẽ âm thầm lên production. Người dùng sẽ thấy khóa thô như "settings.title" hoặc chuỗi trống thay vì nội dung đã dịch. Một bước kiểm tra CI kéo dài 5 phút có thể ngăn chặn hoàn toàn vấn đề này.
check-translations.sh
#!/bin/bash
# check-translations.sh — Run in CI to catch missing translations

SOURCE="locales/en.json"
LANGS=("de" "ja" "es" "fr")
EXIT_CODE=0

# Extract all keys from source
SOURCE_KEYS=$(jq -r '[paths(scalars)] | map(join(".")) | .[]' "$SOURCE" | sort)
SOURCE_COUNT=$(echo "$SOURCE_KEYS" | wc -l)

for lang in "${LANGS[@]}"; do
  TARGET="locales/$lang.json"
  if [ ! -f "$TARGET" ]; then
    echo "❌ Missing: $TARGET"
    EXIT_CODE=1
    continue
  fi

  TARGET_KEYS=$(jq -r '[paths(scalars)] | map(join(".")) | .[]' "$TARGET" | sort)
  MISSING=$(comm -23 <(echo "$SOURCE_KEYS") <(echo "$TARGET_KEYS"))

  if [ -n "$MISSING" ]; then
    COUNT=$(echo "$MISSING" | wc -l)
    echo "❌ $lang: $COUNT missing keys"
    echo "$MISSING" | head -5
    EXIT_CODE=1
  else
    echo "✅ $lang: all $SOURCE_COUNT keys present"
  fi
done

exit $EXIT_CODE
check-placeholders.sh
#!/bin/bash
# check-placeholders.sh — Validate placeholder consistency

SOURCE="locales/en.json"
LANGS=("de" "ja" "es")

for lang in "${LANGS[@]}"; do
  TARGET="locales/$lang.json"

  # Compare placeholders like {{name}}, {count}, %s, %d
  jq -r 'paths(scalars) as $p | [($p | join(".")), (getpath($p))]
    | @tsv' "$SOURCE" | while IFS=$'\t' read -r key value; do
    SOURCE_PH=$(echo "$value" | grep -oE '\{\{[^}]+\}\}|%[sd@]' | sort)
    TARGET_VAL=$(jq -r "getpath($(echo $key | jq -R 'split(".")'))" "$TARGET")
    TARGET_PH=$(echo "$TARGET_VAL" | grep -oE '\{\{[^}]+\}\}|%[sd@]' | sort)

    if [ "$SOURCE_PH" != "$TARGET_PH" ]; then
      echo "❌ $lang/$key: placeholder mismatch"
      echo "   Source: $SOURCE_PH"
      echo "   Target: $TARGET_PH"
    fi
  done
done

Xác thực bản dịch trong CI

Thêm i18n-validate vào quy trình CI để tự động phát hiện khóa thiếu, placeholder hỏng và lỗi số nhiều. Dùng i18n-pseudo để tạo bản dịch giả phục vụ kiểm thử hồi quy giao diện.
5

Phát hiện và xử lý độ lệch bản dịch

Độ lệch bản dịch xảy ra khi chuỗi nguồn thay đổi nhưng bản dịch không được cập nhật. Tiếng Anh ghi "Save changes" nhưng tiếng Đức vẫn giữ nội dung cũ. Cơ chế phát hiện độ lệch so sánh dấu thời gian hoặc hàm băm nội dung của tệp nguồn và tệp dịch.

Bản dịch lỗi thời còn tệ hơn bản dịch bị thiếu. Bản dịch thiếu rõ ràng là sai — người dùng thấy khóa hoặc ngôn ngữ dự phòng. Bản dịch lỗi thời truyền đạt sai một cách thuyết phục, có thể khiến người dùng hiểu nhầm hoặc phát sinh yêu cầu hỗ trợ.
detect-drift.sh
#!/bin/bash
# detect-drift.sh — Find stale translations

SOURCE="locales/en.json"
SOURCE_HASH=$(md5sum "$SOURCE" | cut -d' ' -f1)
HASH_FILE=".translation-hashes"

# Compare current source hash with stored hash
if [ -f "$HASH_FILE" ]; then
  STORED_HASH=$(grep "^en:" "$HASH_FILE" | cut -d: -f2)
  if [ "$SOURCE_HASH" != "$STORED_HASH" ]; then
    echo "⚠️  Source strings changed since last translation"
    echo "   Run translations to update all locales"

    # Show which keys changed
    git diff HEAD~1 "$SOURCE" | grep '^[+-]' | grep -v '^[+-][+-]'
  fi
fi

# Update stored hash
echo "en:$SOURCE_HASH" > "$HASH_FILE"
6

Chọn phương pháp dịch

Không phải mọi nội dung đều cần cùng một phương pháp dịch. AI xử lý tốt chuỗi và nhãn UI. Nội dung tiếp thị nên kết hợp AI với người duyệt. Văn bản pháp lý và tuân thủ luôn cần biên dịch viên chuyên nghiệp.

AI xử lý hơn 80% chuỗi app điển hình với chất lượng production. Hãy dành biên dịch viên cho văn bản pháp lý, giọng điệu thương hiệu và nội dung nhạy cảm về văn hóa, nơi sắc thái có vai trò quan trọng nhất.
Cost Comparison
Translation Approach Comparison:

Content Type         | Approach          | Cost/word  | Quality
─────────────────────┼───────────────────┼────────────┼──────────
UI strings, labels   | AI/LLM            | $0.001-01  | Production
Tooltips, help text  | AI/LLM            | $0.001-01  | Production
Marketing copy       | AI + human review | $0.05-0.15 | High
Legal / compliance   | Human translator  | $0.15-0.40 | Certified
Brand voice content  | Human translator  | $0.20-0.50 | Premium
7

Quy trình Git cho bản dịch

Hãy dịch trên nhánh tính năng trước khi hợp nhất. Cách này bảo đảm mọi lần hợp nhất vào main đều có bản dịch đầy đủ. Thêm bước kiểm tra trước khi hợp nhất để xác minh độ đầy đủ của bản dịch cho mọi locale được hỗ trợ.

Tệp locale JSON lớn thường gây xung đột hợp nhất khi nhiều nhánh cùng sửa. Hãy sắp xếp khóa theo bảng chữ cái và đặt mỗi khóa trên một dòng để giảm kích thước diff, giúp giải quyết xung đột dễ hơn.

Tệp dịch nằm trong kho mã cùng với mã nguồn. Chúng được quản lý phiên bản, có thể duyệt trong pull request và được triển khai qua cùng quy trình với mọi thành phần khác.
.gitattributes + package.json
# .gitattributes — reduce merge conflicts in locale files
locales/*.json merge=union

# Sort keys alphabetically to minimize diffs:
# package.json script:
"sort-locales": "node -e \"
  const fs = require('fs');
  const f = process.argv[1];
  const d = JSON.parse(fs.readFileSync(f));
  const s = (o) => Object.keys(o).sort().reduce((r,k) =>
    ({...r, [k]: typeof o[k]==='object' ? s(o[k]) : o[k]}), {});
  fs.writeFileSync(f, JSON.stringify(s(d), null, 2)+'\\n');
\""

Lỗi thường gặp

Chỉ bản địa hóa sau khi phát triển

Sai lầm lớn nhất là chờ phát triển xong mới dịch. Cách này tạo nút thắt, trì hoãn phát hành và biến bản dịch thành phần việc bổ sung muộn màng. Hãy dịch trong lúc phát triển.

Không xác thực bản dịch ở cấp CI

Nếu không kiểm tra tự động, bản dịch thiếu, placeholder hỏng và định dạng không hợp lệ sẽ lên production. Hãy thêm bước xác thực độ đầy đủ và định dạng vào quy trình CI.

Phức tạp hóa quá mức bằng TMS

TMS được thiết kế để quản lý quy trình của biên dịch viên. Nếu dùng AI để dịch, bạn có thể không cần phần quản lý cồng kềnh của TMS. Hãy bắt đầu đơn giản — dịch trong IDE hoặc CI/CD — và chỉ thêm TMS khi cần quản lý biên dịch viên.

Không theo dõi độ mới của bản dịch

Nếu không theo dõi độ mới, bạn sẽ không biết bản dịch nào đã lỗi thời. Chuỗi nguồn thay đổi nhưng bản dịch cũ vẫn còn. Hãy tích hợp cơ chế phát hiện độ lệch vào quy trình để tìm nội dung lỗi thời.

Dùng thử i18n Agent ngay

Thả tệp bản dịch của bạn vào đây

JSON, YAML, PO, XML, CSV, Markdown, Properties

hoặc nhấp để duyệt

Ngôn ngữ đích

Không cần đăng kýBáo giá tức thì

Câu hỏi thường gặp