Skip to main content

Godot 遊戲本地化完整指南

從 TranslationServer 到字型回退:使用 CSV、PO 檔案、GDScript 和自動化 AI 翻譯本地化 Godot 遊戲。

1

TranslationServer 基礎

Godot 內置的 TranslationServer 是本地化系統的核心。它在啟動時載入翻譯資源,並通過 tr() 函數解析鍵。GDScript 對 tr() 的每次呼叫都會經過 TranslationServer,無需額外的庫。

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 支援 CSV、PO(Gettext)和 .translation(二進制)格式。它通過 OS.get_locale() 自動檢測系統地區設定並選擇匹配的翻譯資源。你可隨時使用 TranslationServer.set_locale() 覆蓋地區設定。
2

CSV 翻譯檔案

CSV 是 Godot 翻譯最簡單的格式。一個檔案按欄儲存所有語言。第一欄是鍵,之後每欄是一種地區設定。Godot 會自動匯入 .csv 檔案並產生 .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
用雙引號包裹包含逗號或換行符的值。對於包含雙引號的值,將其轉義為 ""。鍵應簡短且具有描述性:MENU_START 優於 menu_start_button_text_label。
3

PO/Gettext 翻譯檔案

PO(Portable Object)檔案是軟件本地化的行業標準。Godot 4.x 原生支援 PO,包括複數、上下文消歧和譯者注釋。在 locale/ 目錄中為每種語言建立一個 .po 檔案。

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)
PO 檔案支援用於上下文消歧的 msgctxt(例如 'OPEN' 用作動詞或形容詞)、用於複數形式的 msgid_plural,以及為譯者提供字串使用位置和方式上下文的注釋(#. 行)。
4

在 GDScript 中使用翻譯

在 GDScript 的任意位置使用 tr() 翻譯字串。結合 GDScript 的 % 運算符進行字串格式化。為穩健地管理地區設定,可建立一個 Autoload 腳本,通過信號處理地區設定檢測、持久化和切換。

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()
使用 TranslationServer.set_locale() 更改地區設定後,場景中已呈現的文字不會自動更新。地區設定更改後,必須手動對所有可見標籤、按鈕和文字節點重新應用 tr()。使用地區設定管理器的信號通知 UI 場景重新整理。
5

複數和預留位置

Godot 通過 PO 檔案的複數形式處理複數。每種語言都在 PO 標頭中定義自己的複數公式。GDScript 的 % 運算符處理位置預留位置(%s 用於字串,%d 用於整數)。對於命名預留位置,請使用 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))
切勿硬編碼 'if count == 1' 等複數邏輯。不同語言的複數規則差異很大:英語有 2 種形式,俄語有 3 種,阿拉伯語有 6 種,日語有 1 種。請讓 PO 複數系統自動選擇。CSV 檔案不支援複數,需要複數形式的內容應使用 PO 檔案。
6

CJK、阿拉伯文等文字的字型回退

遊戲的主要字型很可能不包含日文、韓文、中文、阿拉伯文或泰文字元。Godot 4.x 支援字型回退鏈:主要字型缺少某個字形時,Godot 會按順序檢查回退字型。若未設定,非拉丁文字會呈現為空方框。

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
使用 Google 的 Noto 字型系列,它涵蓋幾乎所有 Unicode 文字。新增 Noto Sans JP、Noto Sans KR、Noto Sans SC 和 Noto Sans Arabic 作為回退。對於像素風遊戲,可考慮 Noto Sans Mono 或包含 CJK 子集的位圖字型。請留意字型總大小,完整 CJK 字型每個可達 15-20 MB。
7

場景和 UI 本地化

本地化 Godot 場景有三種方法:在 _ready() 中使用 tr() 翻譯;使用編輯器中的 Auto Translate 屬性;或為需要不同佈局的語言(例如 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 僅適用於節點的 text 屬性。如果在 _ready() 之後通過程式碼動態設定 text,自動翻譯會被覆蓋。對於動態更新的文字,請始終在程式碼中顯式使用 tr()。另請注意,自動翻譯會對字面 text 值應用 tr(),因此 text 屬性必須包含翻譯鍵,而不是供人閱讀的來源字串。
8

使用 LocaleChain 實現智能地區設定回退

地區變體缺失時,Godot 的 TranslationServer 會直接回退到項目的預設地區設定。如果只有 pt-PT 翻譯,pt-BR 玩家會看到英語而非葡萄牙語。LocaleChain 在設定時將可設定回退鏈中的翻譯合併到 TranslationServer,解決此問題。

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 是純 GDScript 外掛程式,無需原生擴展或修改引擎。從 Godot AssetLib 安裝它,或將 addons/locale_chain/ 資料夾複製到項目中。它適用於 CSV、PO 和 .translation 檔案。
9

自動翻譯遊戲

完成本地化設定後,使用 AI 翻譯 CSV 或 PO 檔案。直接從 IDE 或 CI/CD 管線自動翻譯遊戲字串、UI 文字、物品說明和對話。

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
採用漸進式翻譯。向來源檔案新增新鍵時,只翻譯差異內容,不要重新產生全部內容。這樣可保留敘事對話或文化敏感內容中經人工審核的翻譯。

自動保證翻譯質素

使用 i18n-validate 在發佈前發現缺失鍵和損壞的預留位置。真實譯文完成前,可使用 i18n-pseudo 產生偽譯文來測試 UI。

常見問題

未匯入翻譯檔案

Godot 必須先匯入 .csv 和 .po 檔案才能使用。如果翻譯未顯示,請檢查檔案是否列在 Project Settings > Localization > Translations 中。對於 CSV,請確保 Godot 已在 .godot/imported/ 目錄中產生 .translation 檔案。

包含逗號或引號的 CSV 值導致解析失敗

包含逗號的值必須用雙引號包裹。包含雙引號的值必須將其轉義為 ""。缺少一個引號就會導致整行解析錯誤,通常還會在無提示的情況下使之後的所有欄發生偏移。

CJK 或阿拉伯文顯示為空方框

主要字型不包含這些文字的字形。請在 Theme 或 LabelSettings 資源中新增字型回退。如果沒有回退,缺失字形會呈現為空矩形。使用 Noto Sans 變體獲得全面的 Unicode 覆蓋。

PO 標頭中的複數形式數量不正確

如果 PO 標頭中的 nplurals 值與 msgstr 條目的實際數量不匹配,Godot 可能崩潰或顯示錯誤的複數形式。請始終確認每種目標語言的 Plural-Forms 標頭符合 CLDR 規範。

自動翻譯被程式碼覆蓋

在 _ready() 之後通過 GDScript 設定節點的 text 屬性,會覆蓋自動翻譯結果。請僅使用自動翻譯(在編輯器中設定 text,程式碼中不再設定),或僅在程式碼中使用 tr()。混用兩者會導致行為不一致。

推薦的項目結構

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

立即試用 i18n Agent

將翻譯檔案拖放到此處

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

或點擊選擇檔案

目標語言

無需註冊即時估價

使用 locale-chain-godot 實現地區設定回退

當 pl_PL 等地區地區設定缺少翻譯鍵時,Godot 會直接跳到項目的預設地區設定,而不會先檢查父級地區設定 pl。

Terminal
# Install from Godot Asset Library
# Search: 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"],
})

查看語言回退指南,瞭解受支援框架的完整列表和 75 條內置回退鏈。 Learn more →

Godot 本地化常見問題