Golden Crayon Translation System

This file is the rawdata level. It says what a .lng file is, what its sections mean, how each kind of line is written, and the order the engine resolves them in. Its companion is translation_system.txt, which is the GUI level, covering how to browse, download and work on a translation with other people through the game's /languages panel.
The two intertwine rather than compete. Every entry added through that panel becomes exactly the kind of line described below, and knowing what these lines mean is what tells you which section an entry belongs in. Equally, none of it is required. A .lng file written in a text editor and dropped into translations/ works exactly as described here. Read this file to know what you are writing, and read translation_system.txt to know who you are writing it with.

Overview
Golden Crayon features a comprehensive translation system that supports both direct phrase mapping and advanced pattern matching. The system automatically translates all speech output and can handle complex text transformations.

Features
- Direct translation, an exact phrase-to-phrase mapping
- Aliases and pointers, one translation reused by many English phrases (key = @otherKey)
- Reusable variables, defined once in [VARS] and referenced with ${name}
- Pattern matching, plain substring replacement for dynamic text
- Wildcard patterns, whole-phrase matching with capture groups (* and $1..$9)
- Automatic speech integration, so all spoken text is translated for you
- Hot language switching, changing languages instantly through settings

File Format
Translation files use the .lng extension and are stored in the translations/ folder. Each language has its own file (e.g., Spanish.lng, French.lng).

File Structure
[VARS]
// Reusable terms (optional). Define before they are referenced.
name = "value"

[DIRECT]
// Direct phrase translations and aliases
English phrase = "Translated phrase"

[PATTERN]
// Pattern-based replacements (plain and wildcard)
"English pattern" => "Translated pattern"

[REGEX]
// Regular-expression rules
"regex pattern" => "replacement with $1 backreferences"

[CONFIG]
// Per-file options, e.g. whether to log untranslated strings
nolog

Resolution Order
When a string is translated it is checked in this order, and the first hit wins.
1. Direct translations (exact match)
2. Case-insensitive direct match (a fallback for #1, and it can be disabled)
3. Aliases (key = @otherKey)
4. Wildcard patterns (whole-phrase match, resolving once and never re-translated)
5. Regex rules (applied in order over the text)
6. Plain substring patterns (applied repeatedly until no further change)

If none of the above touch the string, it is left in English, and for a non-English language it is recorded to the missing-translation log, described below.

Sections

VARS Section
Variables let you define a term once and reuse it anywhere with ${name}. They are expanded when the file loads, in every section, so they work in DIRECT values, PATTERN replacements, and even other variables. Because the file is read top to bottom, put [VARS] at the top of the file (before it is referenced).

Format
name = "value"

Example
[VARS]
app = "Crayón Dorado"

[DIRECT]
Welcome to Golden Crayon = "Bienvenido a ${app}"

DIRECT Section
Direct translations provide exact phrase-to-phrase mapping. These are processed first and take priority over pattern matches.

Format
English text = "Translated text"

Example
[DIRECT]
Loading. Please wait... = "Cargando. Por favor espere..."
Settings saved. = "Configuración guardada."
Golden Crayon Settings = "Configuración de Golden Crayon"

Aliases / Pointers
Inside [DIRECT], a value of the form @otherKey makes this phrase reuse another key's translation. This avoids repeating (and having to keep in sync) the same translation for phrases that should always read the same. Aliases may chain, with one alias pointing at another, and cycles are detected and stop safely.

Format
English text = @AnotherEnglishKey

Example
[DIRECT]
on = "encendido"
enabled = @on        // "enabled" now translates to "encendido"
active = @enabled    // chains through to "encendido"

PATTERN Section
Pattern translations handle dynamic text that contains variables or UI suffixes. There are two kinds.

1. Plain patterns (no *) are a straight substring replacement. These are applied repeatedly, so a string may be touched by several plain patterns in turn.

2. Wildcard patterns (contain *) are matched against the WHOLE string. Each * captures whatever appears in that position, and the captured spans are inserted into the replacement as $1, $2, ... (in left-to-right order, up to $9). A wildcard pattern resolves once and is not fed back through the other patterns, which prevents accidental double-translation. Because the match is anchored to the whole phrase, "Level *" matches "Level 7" but not "Go to Level 7 now".

Format
"Search pattern" => "Replacement pattern"

Plain example
[PATTERN]
"Master Volume" => "Volumen Principal"
"Sign sounds " => "Sonidos de señal "
" FPS" => " CPS"

Wildcard example
[PATTERN]
"You have * coins" => "Tienes $1 monedas"
"* of *" => "$1 de $2"
"Level *" => "Nivel $1"

Pitfall, a plain pattern whose replacement contains its own search text
Plain patterns are applied repeatedly until nothing changes (so several patterns can each take a turn on the same string). This creates one trap. If a plain pattern's replacement CONTAINS its own search text, it keeps matching its own output and grows without bound.

Example, translating "Map" to Spanish "Mapa"
[PATTERN]
"Map" => "Mapa"

"Mapa" contains "Map", so applied to "Map tab" it would loop.
"Map tab" -> "Mapa tab" -> "Mapaa tab" -> "Mapaaa tab" -> ... -> "Mapaaaaaaaaaaaaaaaaaaaa tab"

The engine guards against this. A plain pattern whose replacement contains its search text is applied at most once, so the result is simply correct.

"Map tab" -> "Mapa tab"

The guard stops the runaway, but a self-referential plain pattern is usually a sign you want a different tool.
- For a whole word or phrase spoken on its own, use [DIRECT], an exact match that is never looped. Map = "Mapa"
- For a role word at the end of a control string (e.g. List -> "Lista", Edit -> "Edicion"), use a wildcard, which also never re-loops. "* List" => "$1 Lista"
The same trap applies whenever the translation merely adds letters around the original word (List/Lista, Cancel/Cancelar, Confirm/Confirmar), so reach for [DIRECT] or a wildcard there.

Plurals (in wildcard replacements)
A wildcard replacement may pick between a singular and a plural word based on one of its captures, using the token {n|singular|plural}. Here n is the capture number. If that capture equals 1 the singular form is used, otherwise the plural.

Example
[PATTERN]
"You have * coins" => "Tienes $1 {1|moneda|monedas}"
// "You have 1 coins"  -> "Tienes 1 moneda"
// "You have 5 coins"  -> "Tienes 5 monedas"

This is the common two-form case (used by English, Spanish, French, etc.). For a language with more complex plural rules, write specific wildcard patterns for the exact numbers that differ.

REGEX Section
For advanced cases, [REGEX] rules run a full regular-expression replacement over the text. Captured groups are written back into the replacement as $1, $2, ... Rules are applied in order, each over the result of the previous one. An invalid pattern is ignored rather than crashing.

Format
"regex pattern" => "replacement"

Example
[REGEX]
// Reformat "3:45" into spoken words
"(\d+):(\d+)" => "$1 horas y $2 minutos"
// Accept either spelling, normalise to one
"\bcolou?r\b" => "color"

Use [REGEX] sparingly. Direct, alias and wildcard entries are easier to read and faster. Reach for regex only when a rule genuinely needs pattern logic.

Missing-Translation Log
While a non-English language is loaded, any string that no rule translated is appended to translations/<LanguageName>.missing.txt, already formatted as a [DIRECT] entry.

    Some untranslated text. = "Some untranslated text."

Each unique string is recorded once (it remembers what it already logged, even across sessions). To finish a translation, play through the game, then open that file, translate the right-hand sides, and paste the lines into your .lng file. This turns "find everything that still needs translating" into fill-in-the-blanks.

Logging is OFF by default. There are two ways to control it, in order of precedence.

1. The language file itself, in the [CONFIG] section described below. This wins over everything, so the file author decides.
2. The in-game setting at Settings > General > "Log untranslated text...". This is how a translator who is not a developer turns logging on while working, and it is remembered between sessions.

CONFIG Section
The optional [CONFIG] section lets a language file configure its own behaviour, so everything is controlled from the .lng file itself. For logging, a [CONFIG] directive overrides the in-game setting for that file, so a finished translation can ship with logging off (and a work in progress can force it on).

Directives
    nolog                         // never log missing strings for this language
    log                           // always log missing strings for this language
    log_missing = true|false      // explicit form of the two above
    case_insensitive = true|false // case-insensitive direct fallback (default on)

Example, a completed translation that should never write a log
[CONFIG]
nolog

Creating a New Translation
By hand
1. Copy the template. Start with translations/template.lng
2. Rename the file. Use the language name (e.g., German.lng)
3. Translate the content. Replace English text with your translations
4. Test the translation. Load it in game through Settings > General > Language
In game, type /languages and choose "Start a new language". You get the same starting point, which is the template above seeded for you, plus somewhere for other people to help and a way to reach the players who will read it. See translation_system.txt.
The two routes produce the same file, so starting one way does not stop you continuing the other.

Translation Guidelines
- Keep quotes. Maintain quote marks around pattern translations
- Preserve spacing. Keep leading and trailing spaces in patterns
- Test thoroughly. Verify all UI elements translate correctly
- Use UTF-8 encoding. Ensure proper character support

File Comments
- Lines starting with // are comments and are ignored
- Empty lines are ignored
- Use comments to organize sections and provide context

Language Selection
The language can be changed in game.
1. Go to Settings > General
2. Select Language from the list
3. Click Save Settings

The change takes effect immediately for all new speech output.

Integration
Everything the game says is translated automatically as you play, and you only ever edit the .lng file. Adding or completing a language never requires any changes to the game itself.

Performance
- Direct translations are fastest (dictionary lookup)
- Pattern matching is optimized for common cases
- Large texts are automatically skipped to prevent delays

Troubleshooting

Translation Not Working
- Check file is in translations/ folder
- Verify .lng extension
- Ensure proper file format with [DIRECT] and [PATTERN] sections

Partial Translation
- Check for missing entries in template comparison
- Verify pattern syntax uses => not =
- Test with simple direct translations first

Text Still in English
- Confirm language is selected in settings
- Check if text exists in translation file
- Verify pattern matching covers dynamic text

Contributing Translations
Translations are shared through the server's /languages panel, which is what puts one in front of the players who need it. Start one there, or ask to help with a language somebody has already begun, since anyone already working on it can bring you in. Making it public is what lists it for everyone else. translation_system.txt covers the whole of that side, including joining a language, the three ways to edit one, why edits reach readers immediately, and what happens to your local copy when they do.
A file you have written entirely by hand can still be brought in that way. Create the language, then paste your work in through the line browser.

New translations help make Golden Crayon accessible to more users worldwide!
