Skip to main content

Hướng dẫn đầy đủ về bản địa hóa trò chơi Godot

Từ TranslationServer đến phông chữ dự phòng: bản địa hóa trò chơi Godot bằng tệp CSV, PO, GDScript và bản dịch AI tự động.

1

Kiến thức cơ bản về TranslationServer

TranslationServer tích hợp sẵn của Godot là thành phần cốt lõi trong hệ thống bản địa hóa. Công cụ này tải tài nguyên dịch khi khởi động và phân giải khóa qua hàm tr(). Mọi lệnh gọi tr() trong GDScript đều đi qua TranslationServer mà không cần thư viện bổ sung.

TranslationServer basics
# TranslationServer is Godot's built-in localization system.
# It loads translations at startup and resolves keys via tr().

# Set the game's locale
TranslationServer.set_locale("ja")

# Get the current locale
var current = TranslationServer.get_locale()  # "ja"

# Translate a key — works everywhere in GDScript
var text = tr("MENU_START")  # "ゲームスタート"

# Translate with context (Godot 4.x)
var text = tr("OPEN", "verb")    # "Open" (action)
var text = tr("OPEN", "adj")     # "Open" (state)
TranslationServer hỗ trợ các định dạng CSV, PO (Gettext) và .translation (nhị phân). Công cụ này tự động phát hiện ngôn ngữ hệ thống qua OS.get_locale() rồi chọn tài nguyên dịch tương ứng. Bạn có thể thay đổi ngôn ngữ bất cứ lúc nào bằng TranslationServer.set_locale().
2

Tệp dịch CSV

CSV là định dạng đơn giản nhất để dịch trong Godot. Một tệp chứa tất cả ngôn ngữ theo từng cột. Cột đầu tiên là khóa và mỗi cột tiếp theo là một ngôn ngữ. Godot tự động nhập tệp .csv và tạo tài nguyên .translation.

translations.csv
# translations.csv
# First column = key, subsequent columns = locale codes
keys,en,ja,de,es
MENU_START,Start Game,ゲームスタート,Spiel starten,Iniciar juego
MENU_SETTINGS,Settings,設定,Einstellungen,Configuración
MENU_QUIT,Quit,終了,Beenden,Salir
ITEM_SWORD,Sword,剣,Schwert,Espada
ITEM_SHIELD,Shield,盾,Schild,Escudo
DIALOG_GREETING,"Hello, adventurer!",冒険者よ、こんにちは!,"Hallo, Abenteurer!","¡Hola, aventurero!"
Importing CSV translations
# In Godot Editor:
# 1. Place your .csv file in the project (e.g., res://translations.csv)
# 2. Godot auto-imports it — creates .translation resources
# 3. Go to Project > Project Settings > Localization > Translations
# 4. Add the generated .translation files
Đặt các giá trị chứa dấu phẩy hoặc ký tự xuống dòng trong dấu ngoặc kép. Với giá trị chứa dấu ngoặc kép, hãy thoát ký tự bằng "". Giữ khóa ngắn gọn và dễ hiểu: MENU_START phù hợp hơn menu_start_button_text_label.
3

Tệp dịch PO / Gettext

Tệp PO (Portable Object) là tiêu chuẩn ngành để bản địa hóa phần mềm. Godot 4.x hỗ trợ PO nguyên bản, bao gồm dạng số nhiều, phân biệt ngữ cảnh và ghi chú cho người dịch. Hãy tạo một tệp .po cho mỗi ngôn ngữ trong thư mục locale/.

locale/ja.po
# translations.po — Gettext format for Godot
# Place in res://locale/ja.po

msgid ""
msgstr ""
"Language: ja\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=1; plural=0;\n"

# Simple translation
msgid "MENU_START"
msgstr "ゲームスタート"

# Translation with context (disambiguates identical source strings)
msgctxt "verb"
msgid "OPEN"
msgstr "開く"

msgctxt "adj"
msgid "OPEN"
msgstr "開いている"

# Plural form
msgid "You collected %d coin."
msgid_plural "You collected %d coins."
msgstr[0] "%d枚のコインを集めました。"
Loading PO files
# Project Settings > Localization > Translations
# Add each .po file:
#   res://locale/en.po
#   res://locale/ja.po
#   res://locale/de.po
#   res://locale/es.po

# Or load programmatically:
func _ready():
    var translation = load("res://locale/ja.po")
    TranslationServer.add_translation(translation)
Tệp PO hỗ trợ msgctxt để phân biệt ngữ cảnh (ví dụ: 'OPEN' là động từ hay tính từ), msgid_plural cho dạng số nhiều và ghi chú cho người dịch (các dòng #.) để cung cấp ngữ cảnh về vị trí và cách dùng chuỗi.
4

Sử dụng bản dịch trong GDScript

Dùng tr() ở bất cứ đâu trong GDScript để dịch chuỗi. Kết hợp với toán tử % của GDScript để định dạng chuỗi. Để quản lý ngôn ngữ ổn định, hãy tạo một tập lệnh Autoload phụ trách phát hiện, lưu và chuyển đổi ngôn ngữ bằng tín hiệu.

Using tr() in scenes
extends Control

func _ready():
    # Simple key lookup
    $TitleLabel.text = tr("MENU_START")

    # With string formatting (positional)
    $GreetingLabel.text = tr("DIALOG_GREETING_NAME") % [player_name]

    # With multiple placeholders
    $StatusLabel.text = tr("PLAYER_STATUS") % [player_name, level, health]

    # Context-aware translation (Godot 4.x)
    $ActionButton.text = tr("OPEN", "verb")

    # Update UI when locale changes
    TranslationServer.set_locale("de")
    _update_ui()

func _update_ui():
    # Re-apply all translated strings
    $TitleLabel.text = tr("MENU_START")
    $GreetingLabel.text = tr("DIALOG_GREETING_NAME") % [player_name]
locale_manager.gd (Autoload)
# locale_manager.gd — Register as Autoload in Project Settings
extends Node

signal locale_changed(new_locale: String)

const SUPPORTED_LOCALES = ["en", "ja", "de", "es", "fr", "ko", "zh"]

func _ready():
    var system_locale = OS.get_locale_language()
    if system_locale in SUPPORTED_LOCALES:
        set_locale(system_locale)
    else:
        set_locale("en")

func set_locale(locale: String):
    TranslationServer.set_locale(locale)
    locale_changed.emit(locale)

func get_locale() -> String:
    return TranslationServer.get_locale()
Việc đổi ngôn ngữ bằng TranslationServer.set_locale() không tự động cập nhật văn bản đã hiển thị trong cảnh. Sau khi đổi ngôn ngữ, bạn phải tự áp dụng lại tr() cho mọi nhãn, nút và nút văn bản đang hiển thị. Dùng tín hiệu từ trình quản lý ngôn ngữ để yêu cầu các cảnh UI làm mới.
5

Dạng số nhiều và phần giữ chỗ

Godot xử lý dạng số nhiều qua các dạng số nhiều trong tệp PO. Mỗi ngôn ngữ xác định công thức số nhiều riêng trong phần đầu PO. Toán tử % của GDScript xử lý phần giữ chỗ theo vị trí (%s cho chuỗi, %d cho số nguyên). Với phần giữ chỗ có tên, hãy dùng String.replace().

Plural forms by language
# Godot uses Gettext PO plural rules.
# Each language defines its own plural formula.

# English (2 forms: one, other)
msgid "You collected %d coin."
msgid_plural "You collected %d coins."
msgstr[0] "You collected %d coin."
msgstr[1] "You collected %d coins."

# Japanese (1 form: other — no singular/plural distinction)
msgid "You collected %d coin."
msgid_plural "You collected %d coins."
msgstr[0] "%d枚のコインを集めました。"

# Russian (3 forms: one, few, many)
msgid "You collected %d coin."
msgid_plural "You collected %d coins."
msgstr[0] "Вы собрали %d монету."
msgstr[1] "Вы собрали %d монеты."
msgstr[2] "Вы собрали %d монет."
Placeholder formatting
# GDScript string formatting with tr()

# Positional placeholders with %
var msg = tr("SCORE_MSG") % [score]        # "Score: %d" → "Score: 1500"
var msg = tr("STATS") % [name, level, hp]  # "%s — Lv %d — HP: %d"

# Named placeholders (manual replacement)
var template = tr("WELCOME_BACK")
var msg = template.replace("{player}", name).replace("{days}", str(days))
Không bao giờ viết cứng logic số nhiều như 'if count == 1'. Quy tắc số nhiều giữa các ngôn ngữ rất khác nhau: tiếng Anh có 2 dạng, tiếng Nga có 3, tiếng Ả Rập có 6 còn tiếng Nhật có 1. Hãy để hệ thống số nhiều PO tự động chọn dạng. Tệp CSV không hỗ trợ dạng số nhiều, vì vậy hãy dùng tệp PO cho mọi nội dung cần dạng số nhiều.
6

Phông chữ dự phòng cho CJK, tiếng Ả Rập và các hệ chữ khác

Phông chữ chính của trò chơi có thể không chứa ký tự tượng hình cho hệ chữ tiếng Nhật, Hàn, Trung, Ả Rập hoặc Thái. Godot 4.x hỗ trợ chuỗi phông chữ dự phòng: khi phông chữ chính thiếu một ký tự tượng hình, Godot sẽ lần lượt kiểm tra các phông chữ dự phòng. Nếu không thiết lập, văn bản không dùng chữ Latinh sẽ hiển thị thành ô vuông trống.

Font fallback setup
# Godot 4.x supports font fallback chains.
# When a glyph is missing from the primary font, fallbacks are checked in order.

# In the Editor:
# 1. Create a LabelSettings or Theme resource
# 2. Set the primary font (e.g., Noto Sans for Latin)
# 3. Add fallback fonts: Noto Sans JP, KR, SC, Arabic

# Programmatically:
func setup_fonts():
    var font = FontFile.new()
    font.load_dynamic_font("res://fonts/NotoSans-Regular.ttf")

    var fallback_jp = FontFile.new()
    fallback_jp.load_dynamic_font("res://fonts/NotoSansJP-Regular.ttf")
    font.add_fallback(fallback_jp)

    $Label.add_theme_font_override("font", font)
RTL support
# Right-to-left (RTL) support for Arabic, Hebrew, etc.

# In the Editor:
# Select your Control node > Layout > Text Direction = RTL

# Programmatically:
func setup_rtl():
    var locale = TranslationServer.get_locale()
    var rtl_locales = ["ar", "he", "fa", "ur"]

    if locale.substr(0, 2) in rtl_locales:
        $Label.text_direction = Control.TEXT_DIRECTION_RTL
        $Container.layout_direction = Control.LAYOUT_DIRECTION_RTL
Dùng họ phông chữ Noto của Google vì họ này bao phủ gần như mọi hệ chữ Unicode. Thêm Noto Sans JP, Noto Sans KR, Noto Sans SC và Noto Sans Arabic làm phông chữ dự phòng. Với trò chơi pixel art, hãy cân nhắc Noto Sans Mono hoặc phông chữ bitmap có các tập con CJK. Lưu ý tổng dung lượng phông chữ vì mỗi phông chữ CJK đầy đủ có thể chiếm 15-20MB.
7

Bản địa hóa cảnh và UI

Có ba cách bản địa hóa cảnh Godot: dịch trong _ready() bằng tr(), dùng thuộc tính Auto Translate trong trình chỉnh sửa hoặc tải các cảnh hoàn toàn khác cho ngôn ngữ cần bố cục riêng (như ngôn ngữ RTL).

Scene localization approaches
# Approach 1: Translate in _ready() using tr()
extends Control

func _ready():
    $StartButton.text = tr("MENU_START")
    $SettingsButton.text = tr("MENU_SETTINGS")
    $QuitButton.text = tr("MENU_QUIT")

# Approach 2: Use auto-translate in the editor
# Set the Text property to the translation key (e.g., "MENU_START")
# and enable "Auto Translate" on the node.

# Approach 3: Locale-specific scenes for complex layouts
func load_localized_scene():
    var locale = TranslationServer.get_locale().substr(0, 2)
    var path = "res://ui/main_menu_%s.tscn" % locale
    if ResourceLoader.exists(path):
        add_child(load(path).instantiate())
    else:
        add_child(load("res://ui/main_menu_en.tscn").instantiate())
Auto Translate chỉ hoạt động với thuộc tính text của nút. Nếu bạn đặt text linh hoạt trong mã sau _ready(), kết quả dịch tự động sẽ bị ghi đè. Với văn bản cập nhật linh hoạt, luôn gọi tr() trực tiếp trong mã. Ngoài ra, tính năng dịch tự động áp dụng tr() cho giá trị văn bản nguyên trạng, vì vậy thuộc tính text phải chứa khóa dịch chứ không phải chuỗi nguồn dễ đọc.
8

Dự phòng ngôn ngữ thông minh bằng LocaleChain

TranslationServer của Godot chuyển thẳng sang ngôn ngữ mặc định của dự án khi thiếu một biến thể vùng. Người chơi pt-BR sẽ thấy tiếng Anh thay vì tiếng Bồ Đào Nha nếu chỉ có bản dịch pt-PT. LocaleChain khắc phục vấn đề này bằng cách hợp nhất bản dịch từ các chuỗi dự phòng có thể cấu hình vào TranslationServer tại thời điểm cấu hình.

LocaleChain plugin
# LocaleChain for Godot — smart locale fallback
# Install from Godot AssetLib or copy addons/locale_chain/ into your project

# Problem: Godot's TranslationServer falls back directly to the default locale.
# A pt-BR player with only pt-PT translations sees English, not Portuguese.

# Solution: One-line setup
func _ready():
    LocaleChain.configure()  # Uses built-in fallback chains

# Now pt-BR falls back to pt-PT → pt → default
# es-MX falls back to es-419 → es → default
# zh-Hant-HK falls back to zh-Hant-TW → zh-Hant → default

# Custom configuration:
func _ready():
    # Override specific chains
    LocaleChain.configure({"pt-BR": ["pt"]})

    # Full custom — only your chains
    LocaleChain.configure(
        {"pt-BR": ["pt-PT", "pt"], "es-MX": ["es-419", "es"]},
        false  # don't merge defaults
    )

    # Reset to original state
    LocaleChain.reset()
LocaleChain là tiện ích bổ sung thuần GDScript, không có phần mở rộng gốc hay thay đổi engine. Cài đặt từ Godot AssetLib hoặc sao chép thư mục addons/locale_chain/ vào dự án. Tiện ích hoạt động với tệp CSV, PO và .translation.
9

Tự động dịch trò chơi

Sau khi hoàn tất thiết lập bản địa hóa, hãy dịch tệp CSV hoặc PO bằng AI. Tự động dịch chuỗi trò chơi, văn bản UI, mô tả vật phẩm và lời thoại ngay từ IDE hoặc quy trình CI/CD.

Terminal
# Translate your Godot locale files with AI
# CSV files:
# In your IDE, ask your AI assistant:
> Translate translations.csv to Japanese, Korean, and German

# PO files:
> Translate locale/en.po to ja, ko, de

# Or use the CLI in CI/CD:
npx i18n-agent translate locale/en.po --lang ja,ko,de

# The tool preserves:
# - CSV column structure and delimiters
# - PO msgctxt, msgid_plural, and plural forms
# - Placeholder syntax (%s, %d, {name})
# - Comments and metadata headers
Dịch từng phần. Khi thêm khóa mới vào tệp nguồn, chỉ dịch phần thay đổi thay vì tạo lại mọi bản dịch. Cách này giữ nguyên các bản dịch lời thoại tự sự hoặc nội dung nhạy cảm về văn hóa mà con người đã duyệt.

Tự động bảo đảm chất lượng bản dịch

Phát hiện khóa thiếu và phần giữ chỗ hỏng trước khi phát hành bằng i18n-validate. Thử nghiệm UI bằng bản dịch giả từ i18n-pseudo trước khi có bản dịch thật.

Lỗi thường gặp

Chưa nhập tệp dịch

Godot phải nhập tệp .csv và .po trước khi có thể sử dụng. Nếu bản dịch không xuất hiện, hãy kiểm tra xem các tệp có trong Project Settings > Localization > Translations hay chưa. Với CSV, hãy bảo đảm Godot đã tạo tệp .translation trong thư mục .godot/imported/.

Giá trị CSV chứa dấu phẩy hoặc dấu ngoặc kép làm lỗi phân tích cú pháp

Phải đặt giá trị chứa dấu phẩy trong dấu ngoặc kép. Giá trị chứa dấu ngoặc kép phải thoát ký tự bằng "". Thiếu một dấu ngoặc kép sẽ khiến toàn bộ hàng được phân tích sai và thường âm thầm làm lệch mọi cột tiếp theo.

Văn bản CJK hoặc tiếng Ả Rập hiển thị ô vuông trống

Phông chữ chính không chứa ký tự tượng hình cho các hệ chữ này. Thêm phông chữ dự phòng vào tài nguyên Theme hoặc LabelSettings. Nếu không có phông chữ dự phòng, ký tự tượng hình bị thiếu sẽ hiển thị thành hình chữ nhật trống. Dùng các biến thể Noto Sans để bao phủ Unicode toàn diện.

Số dạng số nhiều trong phần đầu PO không chính xác

Nếu giá trị nplurals trong phần đầu PO không khớp với số mục msgstr thực tế, Godot có thể gặp sự cố hoặc hiển thị sai dạng số nhiều. Luôn xác minh trường Plural-Forms khớp với đặc tả CLDR của từng ngôn ngữ đích.

Mã ghi đè tính năng Auto Translate

Việc đặt thuộc tính text của một nút trong GDScript sau _ready() sẽ ghi đè kết quả dịch tự động. Chỉ dùng riêng tính năng dịch tự động (đặt text trong trình chỉnh sửa, không bao giờ đặt trong mã) hoặc chỉ dùng tr() trong mã. Kết hợp cả hai sẽ dẫn đến hành vi thiếu nhất quán.

Cấu trúc dự án đề xuất

Project Structure
my_godot_game/
├── addons/
│   └── locale_chain/              # LocaleChain plugin (optional)
│       ├── fallback_map.gd
│       ├── locale_chain.gd
│       └── plugin.cfg
├── fonts/
│   ├── NotoSans-Regular.ttf       # Primary font (Latin)
│   ├── NotoSansJP-Regular.ttf     # Japanese fallback
│   ├── NotoSansKR-Regular.ttf     # Korean fallback
│   └── NotoSansArabic-Regular.ttf # Arabic fallback
├── locale/
│   ├── en.po                      # English (source)
│   ├── ja.po                      # Japanese
│   ├── de.po                      # German
│   ├── es.po                      # Spanish
│   └── ar.po                      # Arabic
├── translations.csv               # Alternative: CSV format
├── scenes/
│   └── ui/
│       ├── main_menu.tscn
│       └── settings_menu.tscn
├── scripts/
│   ├── locale_manager.gd          # Autoload for locale management
│   └── ui/
│       └── main_menu.gd
├── project.godot
└── export_presets.cfg

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ì

Dự phòng ngôn ngữ bằng locale-chain-godot

Khi thiếu khóa dịch trong một ngôn ngữ vùng như pl_PL, Godot chuyển thẳng sang ngôn ngữ mặc định của dự án thay vì kiểm tra ngôn ngữ cha pl trước.

Terminal
# Cài đặt từ Godot Asset Library
# Tìm kiếm: locale-chain-godot
Configuration
var lc = LocaleChain.new()
lc.configure({
    "pl": ["pl_PL", "en"],
    "pt_BR": ["pt", "en"],
    "zh_Hant_HK": ["zh_Hant", "zh", "en"],
})

Xem Hướng dẫn dự phòng ngôn ngữ của chúng tôi để biết danh sách đầy đủ các framework được hỗ trợ và 75 chuỗi tích hợp sẵn. Learn more →

Câu hỏi thường gặp về bản địa hóa Godot