Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Localization Patterns

ASecurity

Localization code patterns: Godot tr() and TranslationServer usage, PO file structure, ICU plurals, font fallback chains for multi-script text, RTL layout mirroring, pseudo-localization, and string key naming. Use when making a game translatable or fixing text that breaks in another language.

65 stars
0 votes
0 copies
0 views
Added 10/1/2026
developmentgonodetesting

Security Analysis

A100/100

Scanned 10/1/2026

$npx -y skills add HermeticOrmus/claude-code-game-development --skill localization-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Localization Patterns?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Localization Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hermeticormus-localization-patterns/badge)](https://www.skillsdirectory.com/skills/hermeticormus-localization-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: localization-patterns
description: "Localization code patterns: Godot tr() and TranslationServer usage, PO file structure, ICU plurals, font fallback chains for multi-script text, RTL layout mirroring, pseudo-localization, and string key naming. Use when making a game translatable or fixing text that breaks in another language."
---

# Localization Patterns

## Godot TranslationServer and tr() Usage

```gdscript
# Correct tr() usage patterns
class_name LocalizableUI extends Control

func _ready() -> void:
    # Static string - wrap in tr()
    $Label.text = tr("MENU_PLAY_BUTTON")

    # Dynamic string with variable - use format, not concatenation
    var score := 1500
    $ScoreLabel.text = tr("SCORE_DISPLAY") % score
    # PO file: msgid "SCORE_DISPLAY" msgstr "Score: %d"

    # Plural-aware string
    var item_count := 3
    $ItemLabel.text = tr_n("ITEM_COUNT_SINGULAR", "ITEM_COUNT_PLURAL", item_count) % item_count
    # PO file:
    # msgid "ITEM_COUNT_SINGULAR"
    # msgid_plural "ITEM_COUNT_PLURAL"
    # msgstr[0] "%d item"
    # msgstr[1] "%d items"

    # Listen for locale changes (font swaps, layout updates)
    TranslationServer.connect("locale_changed", _on_locale_changed)

func _on_locale_changed() -> void:
    # Refresh all translatable strings
    _ready()
    _update_fonts_for_locale()

func _update_fonts_for_locale() -> void:
    var locale := TranslationServer.get_locale()
    if locale.begins_with("ja") or locale.begins_with("zh") or locale.begins_with("ko"):
        $Label.add_theme_font_override(&"font", preload("res://fonts/NotoSansCJK.ttf"))
    elif locale.begins_with("ar") or locale.begins_with("he"):
        $Label.add_theme_font_override(&"font", preload("res://fonts/NotoSansArabic.ttf"))
        # Mirror layout for RTL
        layout_direction = Control.LAYOUT_DIRECTION_RTL
    else:
        $Label.remove_theme_font_override(&"font")
        layout_direction = Control.LAYOUT_DIRECTION_AUTO
```

## PO File Structure

```po
# messages.pot (template - generated, not translated)
# and messages.fr.po (French translation)

# Header - required, specifies plural rules
msgid ""
msgstr ""
"Project-Id-Version: MyGame 1.0\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"

# Simple string
msgid "MENU_PLAY_BUTTON"
msgstr "Jouer"

# Contextual disambiguation
msgctxt "main_menu"
msgid "BACK"
msgstr "Retour"

msgctxt "character_anatomy"
msgid "BACK"
msgstr "Dos"

# Plural forms
msgid "ITEM_COUNT_SINGULAR"
msgid_plural "ITEM_COUNT_PLURAL"
msgstr[0] "%d objet"
msgstr[1] "%d objets"

# String with variables (preserve format specifiers)
msgid "SCORE_DISPLAY"
msgstr "Score : %d"

# String with named variables (safer than positional)
msgid "PLAYER_GREETING"
msgstr "Bonjour, {player_name} !"
```

## ICU Message Format for Plurals

```gdscript
# Custom ICU-style formatter for Godot (simplified)
# For full ICU support, use Unity Localization or custom GDExtension

class_name ICUFormatter
# Handles {count, plural, one{...} other{...}} syntax

static func format(template: String, vars: Dictionary) -> String:
    var result := template
    # Replace simple variables
    for key in vars:
        result = result.replace("{%s}" % key, str(vars[key]))
    return result

# Usage:
# tr("FOUND_ITEMS").format({"count": 5}) -> "Found 5 items"
# PO: msgid "FOUND_ITEMS" msgstr "Found {count} items"

# Plural with tr_n (Godot built-in):
func items_text(count: int) -> String:
    # tr_n selects singular or plural form based on count
    return tr_n("ITEM_SINGULAR", "ITEM_PLURAL", count) % count
```

## Font Fallback Chain for Multi-Script Support

```gdscript
# Set up font fallback chain covering Latin, CJK, and Arabic
func setup_global_font() -> void:
    var theme := ThemeDB.get_project_theme()
    var font := FontFile.new()
    font.load_dynamic_font("res://fonts/NotoSans-Regular.ttf")

    # CJK fallback - activates for CJK codepoints not in base font
    var cjk_font := FontFile.new()
    cjk_font.load_dynamic_font("res://fonts/NotoSansCJK-Regular.ttf")
    font.add_fallback(cjk_font)

    # Arabic fallback
    var arabic_font := FontFile.new()
    arabic_font.load_dynamic_font("res://fonts/NotoSansArabic-Regular.ttf")
    font.add_fallback(arabic_font)

    theme.default_font = font
    ThemeDB.get_project_theme().default_font = font
```

## RTL Layout Mirroring

```gdscript
# Apply RTL layout to all controls in scene
func apply_rtl_layout(root: Control, is_rtl: bool) -> void:
    var direction := Control.LAYOUT_DIRECTION_RTL if is_rtl else Control.LAYOUT_DIRECTION_LTR
    _set_layout_recursive(root, direction)

func _set_layout_recursive(node: Control, direction: Control.LayoutDirection) -> void:
    node.layout_direction = direction
    for child in node.get_children():
        if child is Control:
            _set_layout_recursive(child, direction)

# RTL detection from locale
func is_rtl_locale(locale: String) -> bool:
    const RTL_LANGUAGES: Array[String] = ["ar", "he", "ur", "fa", "dv", "yi"]
    var language := locale.split("_")[0]  # "ar_SA" -> "ar"
    return language in RTL_LANGUAGES
```

## Pseudo-Localization for Early Testing

```gdscript
# Manual pseudo-localization (Godot has built-in, but this shows the logic)
class_name PseudoLocalizer
const CHAR_MAP := {
    "a": "á", "b": "ƀ", "c": "ç", "d": "ď", "e": "é",
    "f": "ƒ", "g": "ĝ", "h": "ĥ", "i": "í", "j": "ĵ",
    "k": "ķ", "l": "ĺ", "m": "m", "n": "ñ", "o": "ó",
    "p": "ƥ", "q": "q", "r": "ŗ", "s": "ŝ", "t": "ţ",
    "u": "ú", "v": "v", "w": "ŵ", "x": "x", "y": "ý",
    "z": "ź",
}

static func pseudo_localize(text: String) -> String:
    var result := "["
    for char in text:
        result += CHAR_MAP.get(char.to_lower(), char)
    # Add 40% length padding to simulate German text expansion
    result += "_".repeat(int(text.length() * 0.4))
    result += "]"
    return result
```

## String Key Naming Convention

```
# Hierarchy: SCREEN_SECTION_ELEMENT_VARIANT
MENU_MAIN_PLAY_BUTTON          # Main menu, Play button
MENU_MAIN_SETTINGS_BUTTON      # Main menu, Settings button
HUD_HEALTH_LABEL               # HUD, health label
INVENTORY_ITEM_NAME_SWORD      # Inventory, sword item name
DIALOGUE_NPC_MERCHANT_GREETING # Merchant NPC greeting line
ERROR_SAVE_FAILED              # Error message for save failure
TUTORIAL_MOVEMENT_HINT         # Tutorial hint for movement
```

## Anti-Patterns

- **String concatenation**: `"Enemy: " + enemy.name` - word order varies by language. Use named substitution: `tr("ENEMY_LABEL").format({"name": enemy.name})`.
- **Hardcoded plural**: `"%d items"` doesn't work in languages with 3+ plural forms (Russian, Polish). Always use `tr_n()`.
- **Empty msgstr**: translators leaving msgstr blank - Godot falls back to msgid. Build QA catches this.
- **Format string reordering**: `"%s found %d items"` - translators need to reorder but `%s %d` positional args can't reorder. Use named: `"{player} found {count} items"`.

Attribution

HermeticOrmusHermeticOrmus
View sourceSee grades on GitHubMore from HermeticOrmus →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

286712 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Writing Plans

Use when you have a spec or requirements for a multi-step task, before touching code

2927051 votes
View all in development →