Skills/godot-i18n/SKILL.md

238 lines
6.6 KiB
Markdown

---
name: godot-i18n
description: "Localization patterns for Godot 4.x: TranslationServer, CSV/PO/JSON translation formats, runtime language switching, RTL text support, font fallback chains, and pluralization. Use when implementing internationalization in Godot games."
license: MIT
compatibility:
- godot-4.0
- godot-4.1
- godot-4.2
- godot-4.3
- godot-4.4
- godot-4.5
- godot-4.6
- godot-4.7
metadata:
author: vl4dt
version: 0.1.0
tags:
- godot
- i18n
- localization
- translation
- rtl
created: 2026-06-21
---
# Localization in Godot
Patterns for internationalizing Godot games.
## TranslationServer Basics
Godot's built-in translation system uses `.translation` files (CSV-based):
```gdscript
# Mark strings for translation with _() shorthand
func _ready():
$Label.text = tr("Hello, Player!")
$Button.text = tr("Start Game")
# tr() calls TranslationServer.translate() internally
# Strings are looked up in loaded .translation resources
```
### Creating Translation Files
Use the editor: **Project → Tools → Translation** to create `.csv` files, then export as `.translation`.
Or create CSV manually:
```csv
msgid "Hello, Player!"
msgstr "¡Hola, Jugador!"
msgid "Start Game"
msgstr "Comenzar Juego"
msgid "Score: {0}"
msgstr "Puntuación: {0}"
```
### Registering Translations
In **Project Settings → Locale → Configured Locales**, add supported languages (e.g., `es`, `fr`, `ja`).
## Runtime Language Switching
```gdscript
class_name LanguageManager extends Node
signal language_changed(locale: String)
var available_locales: Array[String] = ["en", "es", "fr", "ja"]
var current_locale: String = "en"
func set_language(locale: String) -> void:
if not available_locales.has(locale):
push_error("LanguageManager: Unsupported locale '%s'" % locale)
return
TranslationServer.set_locale(locale)
current_locale = locale
language_changed.emit(locale)
_notify_ui_update()
func _notify_ui_update() -> void:
# Re-tr() all UI labels that need updating
# Connect to language_changed signal in UI nodes
pass
# Save/restore preference
func _ready() -> void:
var saved = ConfigFile.new()
if saved.load("user://settings.cfg") == OK:
var locale = saved.get_value("general", "language", "en")
set_language(locale)
func _save_preference() -> void:
var config = ConfigFile.new()
config.load("user://settings.cfg")
config.set_value("general", "language", current_locale)
config.save("user://settings.cfg")
```
### Language Selector UI
```gdscript
# LanguageSelector.gd — attached to OptionButton
extends OptionButton
func _ready():
for locale in LanguageManager.available_locales:
var display_name = TranslationServer.get_locale_name(locale)
add_item(display_name, locale)
selected = LanguageManager.available_locales.find(LanguageManager.current_locale)
item_selected.connect(LanguageManager.set_language)
```
## Pluralization
Godot supports plural forms via `trn()`:
```gdscript
# trn(singular, plural, count)
func show_item_count(count: int):
$Label.text = trn("You have {0} item.", "You have {0} items.", count) % count
# For complex plurals (Slavic languages with 3+ forms), use custom logic
func get_plural_string(msgid_singular: String, msgid_plural: String, n: int) -> String:
# Godot handles plural forms via .po format plural rules
return trn(msgid_singular, msgid_plural, n)
```
## RTL Text Support
For Arabic, Hebrew, and other right-to-left languages:
```gdscript
# In Label or RichTextLabel:
$Label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
$Label.text_direction = TextServer.DIRECTION_RTL
$Label.alignment = HORIZONTAL_ALIGNMENT_RIGHT
# For mixed LTR/RTL content, use RichTextLabel with bbcode
$RichTextLabel.text = "[rtl]مرحبا[/rtl] [ltr]Hello[/ltr]"
```
### RTL-Aware UI Layout
```gdscript
# Container layouts flip automatically with RTL
# Use Control.layout_mode for proper RTL support:
$PanelContainer.set_anchors_preset(Control.PRESET_FULL_RECT)
# For manual positioning, mirror anchors:
func mirror_for_rtl(control: Control) -> void:
if TranslationServer.get_locale() in ["ar", "he", "fa"]:
var left = control.anchor_left
control.anchor_left = control.anchor_right
control.anchor_right = left
```
## Font Fallback Chains
Handle characters not available in the primary font:
```gdscript
# Theme approach (recommended):
# 1. Create Theme resource
# 2. Set "FontFile" for Label/LineEdit
# 3. Add fallback fonts via DynamicFont
# Code approach:
var dynamic_font = DynamicFont.new()
dynamic_font.font_path = "res://fonts/primary.ttf"
dynamic_font.fallback_fonts.append(DynamicFontLoading.new())
$Label.add_theme_font_override("font", dynamic_font)
# For CJK + Latin support, chain fonts:
# Primary: Noto Sans (Latin) → Fallback: Noto Sans CJK
```
## Translation Best Practices
### Use Context Keys
```gdscript
# When the same word has different meanings:
tr("Attack [weapon]") # As a verb
tr("Attack [noun]") # As a noun/stat name
# In .po files, msgctxt provides context
# msgctxt "weapon" / msgid "Attack" / msgstr "Atacar"
# msgctxt "noun" / msgid "Attack" / msgstr "Ataque"
```
### Avoid String Concatenation for Translatable Text
```gdscript
# Bad — translators can't reorder
tr("You found ") + item_name + tr(" in the cave!")
# Good — translators control word order
tr("You found {0} in the cave!") % item_name
# Better — named placeholders for clarity
tr("You found {item} in {location}!") % {"item": item_name, "location": location_name}
```
### Externalize All UI Strings
```gdscript
# Hardcoded strings make translation impossible
$Label.text = "Score: 100" # BAD
# Use tr() for everything visible to players
$Label.text = tr("Score: {0}") % score # GOOD
```
## Godot 4.7 i18n Notes
- **TextServer** API improved for complex script handling
- **RichTextLabel** supports better RTL paragraph alignment
- Translation import pipeline handles `.po` files natively (no conversion needed)
## MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools can inspect and modify your project live:
- **`project_info`** — Check configured locales and translation file paths
- **`script_read`** / **`script_edit`** — Read and modify localization scripts
> **Note:** MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
## References
- [TranslationServer Deep Dive](references/translation-server.md)
- [RTL Layout Patterns](references/rtl-layout.md)
- [Font Fallback Strategies](references/font-fallbacks.md)