diff --git a/mods/spanish_ui/README.md b/mods/spanish_ui/README.md new file mode 100644 index 00000000..b844ac80 --- /dev/null +++ b/mods/spanish_ui/README.md @@ -0,0 +1,27 @@ +# spanish_ui + +A Espanol translation of the game. + +Generated with `python3 tools/modkit.py translation spanish_ui`. See +`TRANSLATING.md` for how to work on it. + +## Status + +Nothing is translated yet: 601 strings are waiting in `lang/`. + +| Catalog | Entries | +|---|---| +| `lang/dialogue.lua` | 6 | +| `lang/strings.lua` | 577 | +| `lang/species_names.lua` | 3 | +| `lang/move_names.lua` | 4 | +| `lang/item_names.lua` | 5 | +| `lang/trainer_names.lua` | 1 | +| `lang/status_labels.lua` | 5 | + +## Layout + +- `manifest.json` - identity and the engine version range +- `main.lua` - registers whatever is filled in and skips whatever is not +- `lang/` - the catalogs; this is the whole job +- `assets/font/` - your glyph sheet diff --git a/mods/spanish_ui/TRANSLATING.md b/mods/spanish_ui/TRANSLATING.md new file mode 100644 index 00000000..f1cca9bc --- /dev/null +++ b/mods/spanish_ui/TRANSLATING.md @@ -0,0 +1,111 @@ +# Translating into Espanol + +Everything the player can read is one of two kinds of string, and they live +in different places for a reason. + +| lang/ file | What it is | Key | +|---|---|---| +| `dialogue.lua` | Every line of extracted script text | the original label, e.g. `_PalletTownText1` | +| `strings.lua` | Text the engine itself writes: battle messages, menus, link play | the English source string | +| `species.lua` `moves.lua` `items.lua` `trainers.lua` | Names | the vanilla id | +| `statuses.lua` | `PSN`, `BRN`, ... as they appear in the HUD | the status id | +| `font.lua` `charmap.lua` | Your glyph sheet and what draws what | see below | +| `naming.lua` | The letter grid for entering names | - | + +Fill in a value and it takes effect. Leave it `""` and that string stays in +English, so the game is playable at every point along the way. + +## Where the English is + +The catalogs hold keys and *your* text, never the original English. The +English lives next door, in `spanish_ui-worksheet/`, one tab-separated file per +catalog: + +``` +"_AbandonLearningText" "Abandon learning\n{RAM:wStringBuffer}?" +``` + +That directory is deliberately outside the mod. Extracted script text and +the vanilla names are ROM content, and `modkit pack` zips everything under +the mod directory, so a worksheet kept inside would end up in your release +whatever a `.gitignore` said. Keep it beside the mod, never in it. + +`lang/strings.lua` is the exception: those sources are the engine's own Lua +rather than anything out of the ROM, so there the key *is* the English and +you can translate straight from it. + +## Start with the font, not the text + +The engine draws from **glyph pages**: an image of 8x8 cells plus a charmap +saying which byte sequence draws which cell. The vanilla pages sit at `$60` +and `$80`. Anything from `0x100` up is free, so a new alphabet is added +rather than swapped in: + +```lua +-- lang/font.lua +return { + spanish_ui = { + image = "assets/font/spanish_ui.png", + base = 0x100, -- first code this page owns + glyphsPerRow = 16, + -- advance = 8, -- set this if your glyphs are not 8px wide + }, +} +``` + +```lua +-- lang/charmap.lua: sequence -> code, in the same order as the sheet +return { + ["A"] = 0x100, + ["B"] = 0x101, +} +``` + +The sheet is a plain PNG, 16 glyphs to a row by default, each cell 8x8, +black on white like `assets/generated/font.png`. Codes run left to right, +top to bottom from `base`. + +Sequences are matched **longest first**, so a multi-byte character and a +multi-character ligature both work and neither shadows the other: + +```lua +["\u{3042}"] = 0x120, -- one 3-byte character, one glyph +["ch"] = 0x121, -- two ASCII letters, one glyph +``` + +## Line length is counted in glyphs + +The dialogue box fits 18 glyphs a line, not 18 bytes. A 3-byte character +costs one column, and the engine will never cut a character in half. Your +own `\n` line breaks are respected exactly as written, so break lines where +they read best rather than where they fit English. + +If your glyphs are not 8px wide, set `advance` on the page and the box +re-measures. + +## Format directives must survive + +Some sources carry `%s` or `%d`: + +```lua +["Wild %s\nappeared!"] = "...", +``` + +Keep every directive, in a count that matches. Word order is yours to +change; the engine substitutes in the order the directives appear, so if +your language needs the name last, write the sentence with the `%s` last. +A translation whose directive count does not match the English is refused +at runtime and the English is drawn instead, with a line in the log saying +so - it will not crash a battle. + +## Checking your work + +```sh +python3 tools/modkit.py validate spanish_ui --base imported +python3 tools/modkit.py translation spanish_ui --refresh # pick up new engine strings +POKEPORT_DEV=1 scripts/run.sh # F5 hot-reloads lang/ +``` + +`--refresh` rewrites the catalogs from the current engine, keeping every +translation you have already written and reporting what changed. Run it +after pulling a new engine version. diff --git a/mods/spanish_ui/assets/font/README.md b/mods/spanish_ui/assets/font/README.md new file mode 100644 index 00000000..8f0f5890 --- /dev/null +++ b/mods/spanish_ui/assets/font/README.md @@ -0,0 +1,11 @@ +Put your glyph sheet here. + +A page is a PNG of 8x8 cells, 16 per row by default, black on white. Codes +run left to right and top to bottom starting at the page's `base`, so the +first cell is `base`, the second `base + 1`, and so on. + +`assets/generated/font.png` in the player's cache is the vanilla sheet at +the same scale; open it alongside yours to match weight and baseline. + +Declare the sheet in `lang/font.lua` and map sequences to codes in +`lang/charmap.lua`. diff --git a/mods/spanish_ui/lang/charmap.lua b/mods/spanish_ui/lang/charmap.lua new file mode 100644 index 00000000..ca0e3140 --- /dev/null +++ b/mods/spanish_ui/lang/charmap.lua @@ -0,0 +1,10 @@ +-- Which byte sequence draws which glyph code. +-- +-- Sequences are matched longest-first, so a multi-byte character and a +-- multi-character ligature both work: "ch" can be one glyph even though +-- "c" is also mapped. Codes here must land inside a page declared in +-- lang/font.lua. +return { + -- ["A"] = 0x100, + -- ["B"] = 0x101, +} diff --git a/mods/spanish_ui/lang/dialogue.lua b/mods/spanish_ui/lang/dialogue.lua new file mode 100644 index 00000000..8d3c16f3 --- /dev/null +++ b/mods/spanish_ui/lang/dialogue.lua @@ -0,0 +1,12 @@ +-- Script text +-- +-- Keyed by the original text label. The English is in the comment. + +return { + ["_FixMartText"] = "", + ["_FixRouteTrainerAfterText"] = "", + ["_FixRouteTrainerBattleText"] = "", + ["_FixRouteTrainerEndText"] = "", + ["_FixTownGreeterText"] = "", + ["_FixTownSignText"] = "", +} diff --git a/mods/spanish_ui/lang/font.lua b/mods/spanish_ui/lang/font.lua new file mode 100644 index 00000000..4b878079 --- /dev/null +++ b/mods/spanish_ui/lang/font.lua @@ -0,0 +1,13 @@ +-- Glyph pages this translation adds. Delete the entry if the vanilla +-- alphabet already covers your language. +-- +-- base is the first glyph code the page owns. 0x100 and up is free space +-- above the vanilla $60/$80 pages, so this adds an alphabet rather than +-- replacing one. Set `advance` if your glyphs are not 8px wide. +return { + -- spanish_ui = { + -- image = "assets/font/spanish_ui.png", + -- base = 0x100, + -- glyphsPerRow = 16, + -- }, +} diff --git a/mods/spanish_ui/lang/item_names.lua b/mods/spanish_ui/lang/item_names.lua new file mode 100644 index 00000000..2798d181 --- /dev/null +++ b/mods/spanish_ui/lang/item_names.lua @@ -0,0 +1,11 @@ +-- Item names +-- +-- Item names for Espanol. + +return { + ["FIX_BADGE_1"] = "", + ["FIX_BADGE_2"] = "", + ["FIX_BALL"] = "", + ["FIX_POTION"] = "", + ["FIX_TM"] = "", +} diff --git a/mods/spanish_ui/lang/move_names.lua b/mods/spanish_ui/lang/move_names.lua new file mode 100644 index 00000000..80abf686 --- /dev/null +++ b/mods/spanish_ui/lang/move_names.lua @@ -0,0 +1,10 @@ +-- Move names +-- +-- Move names for Espanol. + +return { + ["FIX_CUT"] = "", + ["FIX_EMBERISH"] = "", + ["FIX_SCRATCH"] = "", + ["FIX_TACKLE"] = "", +} diff --git a/mods/spanish_ui/lang/naming.lua b/mods/spanish_ui/lang/naming.lua new file mode 100644 index 00000000..adb0666b --- /dev/null +++ b/mods/spanish_ui/lang/naming.lua @@ -0,0 +1,41 @@ +-- The naming screen's letter grid. Return an empty table to keep the +-- English alphabet. +-- +-- Each entry is a row of cells; a cell is whatever sequence your charmap +-- maps, so a multi-byte character is one cell. The row holding a single +-- "lower case" / "UPPER CASE" cell is the case switch, and the cell +-- spelled "ED" is the confirm. +-- +-- The screen is 160x144 and NamingScreen draws cell `c` of row `r` at +-- (c * 16, 32 + r * 16), so the grid is capped at **9 columns and 6 rows**: +-- a 10th column lands at x=160 and a 7th row at y=144, both off screen. +-- That leaves 44 usable cells, exactly what vanilla uses, so Spanish +-- letters have to displace something rather than being added. +-- +-- What gives way is vanilla's `× ( ) : ; [ ]` row. Those are legal in a +-- Gen-1 nickname but nobody reaches for them, whereas Ñ is not optional in +-- Spanish -- and here it sits in its alphabetical place after N, which is +-- where a Spanish speaker will look for it. Space, and are kept. +-- +-- These glyphs exist in the Spanish cartridge's font ($CA Ñ, $BF Á, $C7 É, +-- $C9 Í, $CC Ó, $CE Ú, $C2 Ü and their lowercase). On an English ROM they +-- do not, so main.lua checks the running game's charmap first and keeps the +-- English grid rather than drawing blank cells. +return { + upper = { + { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + { "J", "K", "L", "M", "N", "Ñ", "O", "P", "Q" }, + { "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }, + { "Á", "É", "Í", "Ó", "Ú", "Ü", " ", "", "" }, + { "-", "?", "!", "♂", "♀", "/", ".", ",", "ED" }, + { "lower case" }, + }, + lower = { + { "a", "b", "c", "d", "e", "f", "g", "h", "i" }, + { "j", "k", "l", "m", "n", "ñ", "o", "p", "q" }, + { "r", "s", "t", "u", "v", "w", "x", "y", "z" }, + { "á", "é", "í", "ó", "ú", "ü", " ", "", "" }, + { "-", "?", "!", "♂", "♀", "/", ".", ",", "ED" }, + { "UPPER CASE" }, + }, +} diff --git a/mods/spanish_ui/lang/species_names.lua b/mods/spanish_ui/lang/species_names.lua new file mode 100644 index 00000000..7a4e5645 --- /dev/null +++ b/mods/spanish_ui/lang/species_names.lua @@ -0,0 +1,9 @@ +-- Species names +-- +-- Species names for Espanol. + +return { + ["FIXMON_A"] = "", + ["FIXMON_B"] = "", + ["FIXMON_C"] = "", +} diff --git a/mods/spanish_ui/lang/status_labels.lua b/mods/spanish_ui/lang/status_labels.lua new file mode 100644 index 00000000..b3cd3365 --- /dev/null +++ b/mods/spanish_ui/lang/status_labels.lua @@ -0,0 +1,11 @@ +-- Status labels +-- +-- Short enough for the battle HUD: the vanilla ones are three glyphs. + +return { + ["BRN"] = "", + ["FRZ"] = "", + ["PAR"] = "", + ["PSN"] = "", + ["SLP"] = "", +} diff --git a/mods/spanish_ui/lang/strings.lua b/mods/spanish_ui/lang/strings.lua new file mode 100644 index 00000000..c0858d3c --- /dev/null +++ b/mods/spanish_ui/lang/strings.lua @@ -0,0 +1,584 @@ +-- Engine text +-- +-- Keyed by the English source, which is also what draws if you leave +-- an entry empty. Keep any %s / %d directives. + +return { + ["%s\nflew up high!"] = "¡%s\nvoló muy alto!", + ["%s\ndug a hole!"] = "¡%s\ncavó un hoyo!", + ["%s\nmade a whirlwind!"] = "¡%s\ncreó un torbellino!", + ["%s\ntook in sunlight!"] = "¡%s\nabsorbió luz!", + ["%s\nlowered its head!"] = "¡%s\nbajó la cabeza!", + ["%s\nis glowing!"] = "¡%s\nestá brillando!", + ["The hooked\n%s\nattacked!"] = "¡El %s\nenganchado atacó!", + ["Wild %s\nappeared!"] = "¡Un %s\nsalvaje apareció!", + ["%s wants\nto fight!"] = "¡%s\nquiere luchar!", + ["The GHOST\nappeared!"] = "¡Apareció el\nFANTASMA!", + ["Go! %s!"] = "¡Ve, %s!", + ["Do it! %s!"] = "¡Hazlo, %s!", + ["Get'm! %s!"] = "¡A por él, %s!", + ["The enemy's weak!\nGet'm! %s!"] = "¡Está débil!\n¡A por él, %s!", + ["%s is out of\nuseable POKéMON!"] = "¡%s no tiene\nPOKéMON útiles!", + ["%s blacked\nout!"] = "¡%s se\ndebilitó!", + ["%s sent\nout %s!"] = "¡%s envió\na %s!", + ["PA: You're out of\nSAFARI BALLs!\nGame over!"] = "AV: ¡No te quedan\nSAFARI BALLs!\n¡Fin del juego!", + ["%s is too\nscared to move!"] = "¡%s tiene\ndemasiado miedo!", + ["%s has no\nmoves left!"] = "¡%s no tiene\nmovimientos!", + ["The move is\ndisabled!"] = "¡El movimiento\nestá anulado!", + ["No PP left for\nthis move!"] = "¡No quedan PP para\neste movimiento!", + ["But, it failed!"] = "¡Pero falló!", + ["%s\nlearned\n%s!"] = "¡%s\naprendió\n%s!", + ["POKé BALL"] = "POKé BALL", + ["%s used\nPOKé BALL!"] = "¡%s usó\nPOKé BALL!", + ["All right!\n%s was\ncaught!"] = "¡Bien!\n¡%s fue\ncapturado!", + ["GHOST: Get out...\nGet out..."] = "FANTASMA: Fuera...\nFuera...", + ["%s with-\ndrew %s!"] = "¡%s retiró\na %s!", + ["%s\nmust recharge!"] = "¡%s debe\nrecargarse!", + ["%s\nis fast asleep!"] = "¡%s está\nprofundamente dormido!", + ["%s\nis confused!"] = "¡%s está\nconfuso!", + ["%s\nwoke up!"] = "¡%s se\ndespertó!", + ["%s\nis frozen solid!"] = "¡%s está\ncongelado!", + ["%s\ncan't move!"] = "¡%s no\npuede moverse!", + ["%s\nflinched!"] = "¡%s se\namedrentó!", + ["It hurt itself in\nits confusion!"] = "¡Se hirió a sí\nmismo por confusión!", + ["%s\nused %s!"] = "¡%s usó\n%s!", + ["%s\nis charging up!"] = "¡%s está\ncargando energía!", + ["%s's\nattack missed!"] = "¡El ataque de %s\nfalló!", + ["%s's\nattack continues!"] = "¡El ataque de %s\ncontinúa!", + ["%s\nis storing energy!"] = "¡%s está\nacumulando energía!", + ["%s\nunleashed energy!"] = "¡%s liberó\nsu energía!", + ["%s's\nSUBSTITUTE broke!"] = "¡El SUSTITUTO de\n%s se rompió!", + ["The SUBSTITUTE\ntook damage for\n%s!"] = "¡El SUSTITUTO\nrecibió el daño\nde %s!", + ["%s's\nRAGE is building!"] = "¡La FURIA de %s\nva creciendo!", + ["%s\nfainted!"] = "¡%s se\ndebilitó!", + ["%s gained\n%d EXP. Points!"] = "¡%s ganó\n%d P. EXP.!", + ["%s gained\nwith EXP.ALL,\v%d EXP. Points!"] = "¡%s ganó\ncon EXP.TODOS,\v%d P. EXP.!", + ["%s gained\na boosted\v%d EXP. Points!"] = "¡%s ganó\nun extra de\v%d P. EXP.!", + ["%s grew\nto level %d!"] = "¡%s subió\nal nivel %d!", + ["%s is\nabout to use"] = "%s va a usar", + ["%s!"] = "¡%s!", + ["Will %s\nchange POKéMON?"] = "¿%s va a\ncambiar de POKéMON?", + ["%s defeated\n%s!"] = "¡%s venció\na %s!", + ["%s got ¥%d\nfor winning!"] = "¡%s ganó\n¥%d!", + ["%s learned\n%s!"] = "¡%s aprendió\n%s!", + ["{RIVAL}: Yeah! Am\nI great or what?"] = "{RIVAL}: ¡Sí! ¿Soy\ngenial o qué?", + ["Use next POKéMON?"] = "¿Sacar al siguiente?", + ["Got away safely!"] = "¡Escapaste!", + ["Can't escape!"] = "¡No puedes escapar!", + ["There's no will\nto fight!"] = "¡No hay ganas de\nluchar!", + ["%s used\nSAFARI BALL!"] = "¡%s usó\nSAFARI BALL!", + ["%s threw some\nBAIT."] = "%s echó\nCEBO.", + ["%s threw a\nROCK."] = "%s tiró una\nPIEDRA.", + ["Wild %s\nis eating!"] = "¡El %s\nsalvaje come!", + ["Wild %s\nis angry!"] = "¡El %s\nsalvaje se enfadó!", + ["Wild %s\nran!"] = "¡El %s\nsalvaje huyó!", + ["No! There's no\nrunning from a\vtrainer battle!"] = "¡No! ¡No puedes\nhuir de un combate\vcontra un entrenador!", + ["You missed the\nPOKéMON!"] = "¡Fallaste el tiro!", + ["Darn! The POKéMON\nbroke free!"] = "¡Vaya! ¡El POKéMON\nse escapó!", + ["Aww! It appeared\nto be caught!"] = "¡Oh! ¡Parecía que\nestaba capturado!", + ["Shoot! It was so\nclose too!"] = "¡Vaya! ¡Estuvo\nmuy cerca!", + ["Do you want to\ngive a nickname\nto %s?"] = "¿Quieres poner un\nmote a\n%s?", + ["NICKNAME?"] = "MOTE?", + ["New POKéDEX data\nwill be added for\n%s!"] = "¡Se añadirán datos\nnuevos a la POKéDEX\nde %s!", + ["someone's PC"] = "el PC de alguien", + ["%s was\ntransferred to\n%s!"] = "¡%s fue\ntransferido a\n%s!", + ["But every BOX\nis full!"] = "¡Pero todas las\nCAJAS están llenas!", + ["%s used\n%s!"] = "¡%s usó\n%s!", + ["The trainer\nblocked the BALL!"] = "¡El entrenador\nbloqueó la BALL!", + ["Don't be a thief!"] = "¡No seas ladrón!", + ["It dodged the\nthrown BALL!"] = "¡Esquivó la BALL!", + ["This POKéMON\ncan't be caught!"] = "¡Este POKéMON no\nse puede capturar!", + ["%s is\nalready out!"] = "¡%s ya\nestá fuera!", + ["%s picked up\n¥%d!"] = "¡%s recogió\n¥%d!", + ["FIGHT"] = "LUCHAR", + ["ITEM"] = "OBJETO", + ["RUN"] = "HUIR", + ["BALLx"] = "BALLx", + ["BAIT"] = "CEBO", + ["THROW ROCK"] = "TIRAR PIEDRA", + ["disabled!"] = "¡anulado!", + ["TYPE/"] = "TIPO/", + ["It doesn't affect\n%s!"] = "¡No afecta a\n%s!", + ["Critical hit!"] = "¡Golpe crítico!", + ["One-hit KO!"] = "¡KO en un golpe!", + ["It's super\neffective!"] = "¡Es muy eficaz!", + ["It's not very\neffective..."] = "¡No es muy\neficaz...", + ["Hit the enemy\n%d times!"] = "¡Golpeó al enemigo\n%d veces!", + ["Hit %d times!"] = "¡Golpeó %d veces!", + ["%s's\nhit with recoil!"] = "¡%s sufrió\nel retroceso!", + ["%s is\nprotected by MIST!"] = "¡%s está\nprotegido por NIEBLA!", + ["Nothing happened!"] = "¡No pasó nada!", + ["%s's\n%s\ngreatly rose!"] = "¡%s\nmejoró mucho su\n%s!", + ["%s's\n%s rose!"] = "¡%s mejoró\nsu %s!", + ["%s's\n%s fell!"] = "¡%s bajó\nsu %s!", + ["%s's\n%s\ngreatly fell!"] = "¡%s\nbajó mucho su\n%s!", + ["Fire defrosted\n%s!"] = "¡El fuego descongeló\na %s!", + ["%s\nbecame confused!"] = "¡%s se\nconfundió!", + ["%s\nwas seeded!"] = "¡%s recibió\nla DRENADORA!", + ["%s\nstarted sleeping!"] = "¡%s se\nquedó dormido!", + ["%s\nregained health!"] = "¡%s recuperó\nsalud!", + ["%s's\nprotected against\nspecial attacks!"] = "¡%s está\nprotegido de los\nataques especiales!", + ["%s\ngained armor!"] = "¡%s ganó\narmadura!", + ["%s's\nshrouded in mist!"] = "¡%s se\ncubrió de niebla!", + ["%s's\ngetting pumped!"] = "¡%s se\nestá animando!", + ["All STATUS changes\nare eliminated!"] = "¡Los cambios de\nESTADO desaparecen!", + ["%s\nhas a SUBSTITUTE!"] = "¡%s tiene\nun SUSTITUTO!", + ["Too weak to make\na SUBSTITUTE!"] = "¡Muy débil para\nhacer un SUSTITUTO!", + ["It created a\nSUBSTITUTE!"] = "¡Creó un SUSTITUTO!", + ["Converted type to\n%s's!"] = "¡Cambió su tipo al\nde %s!", + ["%s\ntransformed into\n%s!"] = "¡%s se\ntransformó en\n%s!", + ["%s's\n%s was\ndisabled!"] = "¡El %s\nde %s\nfue anulado!", + ["No effect!"] = "¡Sin efecto!", + ["Sucked health from\n%s!"] = "¡Absorbió salud de\n%s!", + ["%s's\ndream was eaten!"] = "¡Devoró el sueño\nde %s!", + ["%s\nkept going and\ncrashed!"] = "¡%s siguió\nadelante y se\nestrelló!", + ["Coins scattered\neverywhere!"] = "¡Las monedas se\ndesparramaron!", + ["%s\nran away scared!"] = "¡%s huyó\nasustado!", + ["%s\nwas blown away!"] = "¡%s salió\nvolando!", + ["%s\nran from battle!"] = "¡%s huyó\ndel combate!", + ["It didn't affect\n%s!"] = "¡No afectó a\n%s!", + ["%s\nis unaffected!"] = "¡%s no se\nvio afectado!", + ["The MIRROR MOVE\nfailed!"] = "¡El MOVIMIENTO\nESPEJO falló!", + ["%s\nfell asleep!"] = "¡%s se\nquedó dormido!", + ["%s\nwas frozen solid!"] = "¡%s se\ncongeló!", + ["%s's\nhurt by poison!"] = "¡El veneno hiere a\n%s!", + ["%s's\nbadly poisoned!"] = "¡%s está\ngravemente envenenado!", + ["%s\nwas poisoned!"] = "¡%s fue\nenvenenado!", + ["%s's\nhurt by the burn!"] = "¡La quemadura hiere\na %s!", + ["%s\nwas burned!"] = "¡%s se\nquemó!", + ["%s's\nfully paralyzed!"] = "¡%s está\ntotalmente paralizado!", + ["%s's\nparalyzed! It may\nnot attack!"] = "¡%s está\nparalizado! ¡Puede\nque no ataque!", + ["%s's\ndisabled no more!"] = "¡%s ya no\nestá anulado!", + ["%s\nsnapped out of\nconfusion!"] = "¡%s salió\nde su confusión!", + ["LEECH SEED saps\n%s!"] = "¡La DRENADORA\nabsorbe a %s!", + ["%s\nwas afflicted\nby %s!"] = "¡%s sufre\n%s!", + ["%s's\nprotected against\nstat changes!"] = "¡%s está\nprotegido de los\ncambios de estado!", + ["What will"] = "¿Qué va a hacer", + [" do?"] = "?", + ["You can't get off\nhere."] = "No puedes bajarte\naquí.", + ["%s got off\nthe BICYCLE."] = "%s se bajó\nde la BICICLETA.", + ["%s got on\nthe BICYCLE!"] = "¡%s se subió\na la BICICLETA!", + ["No cycling\nallowed here."] = "No se puede montar\naquí.", + ["No good! It's not\neven near water."] = "¡No sirve! No hay\nagua cerca.", + ["OAK: %s!\nThis isn't the\ntime to use that!"] = "OAK: ¡%s!\n¡No es momento\nde usar eso!", + ["The TOWN MAP is\nunreadable here."] = "El MAPA PUEBLO no\nse puede leer aquí.", + ["Yes! ITEMFINDER\nindicates there's\nan item nearby."] = "¡Sí! El BUSCAOBJ.\nindica que hay algo\ncerca.", + ["Nope! ITEMFINDER\nisn't responding."] = "¡No! El BUSCAOBJ.\nno responde.", + ["Booted up a TM!"] = "¡Se activó una MT!", + ["It contained\n%s!"] = "¡Contenía\n%s!", + ["USE"] = "USAR", + ["TOSS"] = "TIRAR", + ["That's too impor-\ntant to toss!"] = "¡Es demasiado\nimportante!", + ["Threw away\n%s."] = "Tiraste\n%s.", + ["PRESS A BUTTON"] = "PULSA UN BOTON", + ["ESC TO CANCEL"] = "ESC PARA CANCELAR", + ["%s :L%d"] = "%s :N%d", + ["STATS"] = "DATOS", + ["CANCEL"] = "CANCELAR", + ["What? There are\nno POKéMON here!"] = "¿Qué? ¡Aquí no hay\nningún POKéMON!", + ["You can't take\nany more POKéMON.\fDeposit POKéMON\nfirst."] = "No puedes llevar\nmás POKéMON.\fGuarda alguno\nprimero.", + ["BOX %d (WITHDRAW)"] = "CAJA %d (RETIRAR)", + ["%s is\ntaken out.\vGot %s."] = "Retirado\n%s.\vRecibes %s.", + ["You can't deposit\nthe last POKéMON!"] = "¡No puedes guardar\nel último POKéMON!", + ["Oops! This Box is\nfull of POKéMON."] = "¡Uups! Esta CAJA\nestá llena.", + ["You need at least\none POKéMON!"] = "¡Necesitas al menos\nun POKéMON!", + ["BOX %d is full!"] = "¡La CAJA %d está\nllena!", + ["%s was\nstored in Box %s."] = "%s se\nguardó en la CAJA %s.", + ["BOX %d (RELEASE)"] = "CAJA %d (SOLTAR)", + ["Once released,\n%s is\ngone forever. OK?"] = "Si lo sueltas,\n%s se\nirá para siempre. ¿OK?", + ["%s was\nreleased outside.\fBye %s!"] = "%s fue\nliberado.\f¡Adiós, %s!", + ["%sBOX %2d"] = "%sCAJA %2d", + ["When you change a\nPOKéMON BOX, data\nwill be saved. OK?"] = "Al cambiar de CAJA\nse guardarán los\ndatos. ¿OK?", + ["What?"] = "¿Qué?", + ["BOX No."] = "CAJA No.", + ["BOX No.%d"] = "CAJA No.%d", + ["Empty."] = "Vacía.", + [":L%d No.%03d"] = ":N%d No.%03d", + ["Printed BOX %d!\fSaved as\n%s\vin the save\nfolder."] = "¡CAJA %d impresa!\fGuardada como\n%s\ven la carpeta de\nguardado.", + ["Printer error!\n%s"] = "¡Error de impresión!\n%s", + ["WITHDRAW "] = "RETIRAR ", + ["DEPOSIT "] = "GUARDAR ", + ["RELEASE "] = "SOLTAR ", + ["CHANGE BOX"] = "CAMBIAR CAJA", + ["PRINT BOX"] = "IMPRIMIR CAJA", + ["SEE YA!"] = "HASTA LUEGO!", + ["YES"] = "SI", + ["NO"] = "NO", + ["GAME FREAK"] = "", + ["Nintendo"] = "", + ["Creatures inc."] = "", + ["GAME FREAK inc."] = "", + ["T H E E N D"] = "F I N", + ["HT %d′%02d″"] = "AL %d′%02d″", + ["WT %.1flb"] = "PE %.1flb", + ["Data unknown."] = "Datos desconocidos.", + [""] = "", + ["Player"] = "Jugador", + ["Huh? %s\nstopped evolving!"] = "¿Eh? ¡%s\ndejó de evolucionar!", + ["Congratulations!\nYour %s\nevolved into\n%s!"] = "¡Enhorabuena!\n¡Tu %s\nevolucionó a\n%s!", + ["evolving!"] = "evolucionando", + ["POKéDEX Seen:{NUM:wDexRatingNumMonsSeen, 1, 3}\n Owned:{NUM:wDexRatingNumMonsOwned, 1, 3}"] = "POKéDEX Vistos:{NUM:wDexRatingNumMonsSeen, 1, 3}\n Capturados:{NUM:wDexRatingNumMonsOwned, 1, 3}", + ["POKéDEX Rating{COLON}"] = "Nota POKéDEX{COLON}", + ["Keep it up!"] = "¡Sigue así!", + ["LEVEL/"] = "NIVEL/", + ["TYPE1/"] = "TIPO1/", + ["TYPE2/"] = "TIPO2/", + ["HALL OF FAME"] = "SALON DE LA FAMA", + ["PLAY TIME"] = "TIEMPO", + ["MONEY"] = "DINERO", + ["bois club games"] = "bois club games", + ["GENGAR VS NIDORINO"] = "", + ["bois club"] = "bois club", + ["Nothing here."] = "Aquí no hay nada.", + ["%s is\ntrying to learn\v%s!\fBut, %s\ncan't learn more\vthan 4 moves!\f"] = "¡%s está\nintentando aprender\v%s!\f¡Pero %s\nno puede aprender\vmás de 4!\f", + ["Delete an older\nmove to make room\vfor %s?"] = "¿Borrar un movi-\nmiento antiguo para\vaprender %s?", + ["HM techniques\ncan't be deleted!"] = "¡Los movimientos MO\nno se pueden borrar!", + ["Abandon learning\n%s?"] = "¿Dejar de aprender\n%s?", + ["1, 2 and... Poof!\f%s forgot\n%s!\fAnd...\f%s learned\n%s!"] = "¡1, 2 y... plaf!\f¡%s olvidó\n%s!\f¡Y...\f%s aprendió\n%s!", + ["%s\ndid not learn\v%s!"] = "¡%s no\naprendió\v%s!", + ["Which move should"] = "¿Qué movimiento", + ["be forgotten?"] = "hay que olvidar?", + ["YOUR NAME?"] = "TU NOMBRE?", + ["NEW NAME"] = "NUEVO NOMBRE", + ["Hello there!\nWelcome to the\vworld of POKéMON!\fMy name is OAK!\nPeople call me\vthe POKéMON PROF!"] = "¡Hola!\n¡Bienvenido al\vmundo POKéMON!\fMe llamo OAK.\nMe llaman el\vPROF. POKéMON.", + ["This world is\ninhabited by\vcreatures called\vPOKéMON!"] = "¡Este mundo está\nhabitado por unas\vcriaturas llamadas\vPOKéMON!", + ["\fFor some people,\nPOKéMON are\vpets. Others use\vthem for fights.\fMyself...\fI study POKéMON\nas a profession."] = "\fPara algunos, los\nPOKéMON son masco-\vtas. Otros luchan\vcon ellos.\fYo...\fEstudio los POKéMON\ncomo profesión.", + ["{PLAYER}!\fYour very own\nPOKéMON legend is\vabout to unfold!\fA world of dreams\nand adventures\vwith POKéMON\vawaits! Let's go!"] = "¡{PLAYER}!\f¡Tu propia leyenda\nPOKéMON está a\vpunto de comenzar!\f¡Un mundo de sueños\ny aventuras con\vPOKéMON te espera!\v¡Vamos!", + ["First, what is\nyour name?"] = "¿Cómo te llamas?", + ["This is my grand-\nson. He's been\vyour rival since\vyou were a baby.\f...Erm, what is\nhis name again?"] = "Este es mi nieto.\nHa sido tu rival\vdesde que erais\vbebés.\f...Mmm, ¿cómo se\nllamaba?", + ["HIS NAME?"] = "SU NOMBRE?", + ["_OakSpeechText2A"] = "", + ["TEXT SPEED"] = "VEL TEXTO", + ["BATTLE ANIMATION"] = "ANIMACIONES", + ["OFF"] = "NO", + ["ON"] = "SI", + ["BATTLE STYLE"] = "ESTILO COMBATE", + ["SET"] = "FIJO", + ["SHIFT"] = "CAMBIO", + ["BATTLE LAYOUT"] = "DISENO COMBATE", + ["WIDE"] = "ANCHO", + ["OG"] = "OG", + ["RULESET"] = "REGLAS", + ["MUSIC VOL"] = "VOL MUSICA", + ["SFX VOL"] = "VOL SONIDO", + ["PIKACHU VOL"] = "VOL PIKACHU", + ["MUSIC FILTER"] = "FILTRO MUSICA", + ["COLORS"] = "COLORES", + ["TILT"] = "INCLINACION", + ["GBC FX"] = "EFECTO GBC", + ["ZOOM"] = "ZOOM", + ["VOID FILL"] = "RELLENO VACIO", + ["VIDEO MODE"] = "MODO VIDEO", + ["MAX FPS"] = "FPS MAXIMO", + ["GAME SPEED"] = "VELOCIDAD JUEGO", + ["MODS"] = "MODS", + ["%d INSTALLED"] = "%d INSTALADOS", + ["CONTROLS"] = "CONTROLES", + ["TOUCH PAD"] = "CONTROL TACTIL", + ["SURE? AGAIN"] = "SEGURO? OTRA VEZ", + ["AUTO HIDE PAD"] = "OCULTAR AUTO", + ["A blinding FLASH\nlights the area!"] = "¡Un DESTELLO\nilumina la zona!", + ["No SURFing here!"] = "¡Aquí no se puede\nSURFEAR!", + ["Nothing to CUT!"] = "¡Nada que CORTAR!", + ["{RAM:wNameBuffer} used\nSTRENGTH."] = "{RAM:wNameBuffer} usó\nFUERZA.", + ["{RAM:wNameBuffer} can\nmove boulders."] = "{RAM:wNameBuffer} puede\nmover rocas.", + ["It won't have\nany effect."] = "No tendrá ningún\nefecto.", + ["%s's HP\nwas restored!"] = "¡Los PS de %s\nse recuperaron!", + ["SWITCH"] = "CAMBIAR", + ["FLY"] = "VUELO", + ["FLASH"] = "DESTELLO", + ["CUT"] = "CORTE", + ["SURF"] = "SURF", + ["STRENGTH"] = "FUERZA", + ["SOFTBOILED"] = "HUEVO SUERTE", + ["TELEPORT"] = "TELETRANSPORTE", + ["DIG"] = "EXCAVAR", + ["Use TM on which\nPOKéMON?"] = "¿Usar la MT en qué\nPOKéMON?", + ["Bring out which\nPOKéMON?"] = "¿Qué POKéMON\nquieres sacar?", + ["Choose a POKéMON."] = "Elige un POKéMON.", + ["No POKéMON!"] = "¡Ningún POKéMON!", + ["ABLE"] = "PUEDE", + ["NOT ABLE"] = "NO PUEDE", + ["FNT"] = "DEB", + ["Move to where?"] = "¿Mover a dónde?", + ["Use on which one?"] = "¿Usar en cuál?", + ["You can't carry\nany more items."] = "No puedes llevar\nmás objetos.", + ["Withdrew\n%s."] = "Retirado\n%s.", + ["No room left to\nstore items."] = "No queda sitio para\nguardar objetos.", + ["%s was\nstored via PC."] = "%s se\nguardó en el PC.", + ["Toss %s?"] = "¿Tirar %s?", + ["Threw away %s."] = "Tiraste %s.", + ["WITHDRAW ITEM"] = "RETIRAR OBJETO", + ["DEPOSIT ITEM"] = "GUARDAR OBJETO", + ["TOSS ITEM"] = "TIRAR OBJETO", + ["LOG OFF"] = "SALIR", + ["SEEN %d OWNED %d"] = "VISTOS %d CAPT. %d", + ["DATA"] = "DATOS", + ["CRY"] = "VOZ", + ["AREA"] = "ZONA", + ["PRNT"] = "IMPR", + ["Printed %s's\ndata!\fSaved as\n%s\vin the save\nfolder."] = "¡Datos de %s\nimpresos!\fGuardado como\n%s\ven la carpeta de\nguardado.", + ["QUIT"] = "SALIR", + ["%s (%s)"] = "%s (%s)", + ["%s x%d"] = "%s x%d", + ["%s to box %d"] = "%s a caja %d", + ["LOAD REPORT"] = "CARGAR PARTIDA", + ["A:CONTINUE"] = "A:CONTINUAR", + ["You don't have\nenough money."] = "No tienes dinero\nsuficiente.", + ["%s?\nThat will be\n¥%d. OK?"] = "¿%s?\nSon ¥%d.\n¿OK?", + ["Here you are!\nThank you!"] = "¡Aquí tienes!\n¡Gracias!", + ["I can't put a\nprice on that."] = "No puedo ponerle\nprecio a eso.", + ["I can pay you\n¥%d for that."] = "Te doy ¥%d\npor eso.", + ["BUY"] = "COMPRAR", + ["SELL"] = "VENDER", + ["%s lined up!\nScored %d coins!"] = "¡%s alineados!\n¡%d fichas!", + ["Darn!\nRan out of coins!"] = "¡Vaya!\n¡Sin fichas!", + ["Not enough\ncoins!"] = "¡Fichas\ninsuficientes!", + ["SLOT MACHINE"] = "MAQUINA TRAGAPERRAS", + ["COINS %4d"] = "FICHAS %4d", + ["POKéDEX"] = "POKéDEX", + ["POKéMON"] = "POKéMON", + ["SAVE"] = "GUARDAR", + ["PLAYER %s\nBADGES %d\nPOKéDEX %3d\nTIME %6d:%02d"] = "JUGADOR %s\nMEDALLAS %d\nPOKéDEX %3d\nTIEMPO %6d:%02d", + ["\fWould you like to\nSAVE the game?"] = "\f¿Quieres GUARDAR\nla partida?", + ["Now saving..."] = "Guardando...", + ["%s saved\nthe game!"] = "¡%s guardó\nla partida!", + ["OPTION"] = "OPCION", + ["LINK"] = "LINK", + ["RETURN TO MAIN\nMENU?"] = "¿VOLVER AL MENU\nPRINCIPAL?", + ["BALL"] = "BALL", + ["STATUS/"] = "ESTADO/", + ["OT/"] = "EO/", + ["EXP POINTS"] = "P. EXP.", + ["LEVEL UP"] = "SUBE NIVEL", + ["PP"] = "PP", + ["SCORE %d"] = "PUNTOS %d", + ["New record!"] = "¡Nuevo récord!", + ["HI %d"] = "MAX %d", + ["A: done"] = "A: listo", + ["PLAYER"] = "JUGADOR", + ["BADGES"] = "MEDALLAS", + ["TIME"] = "TIEMPO", + ["CONTINUE"] = "CONTINUAR", + ["NEW GAME"] = "NUEVA PARTIDA", + ["EXIT GAME"] = "SALIR DEL JUEGO", + ["POKéMON RED"] = "", + ["2026 bois club games"] = "", + ["OT/%s"] = "EO/%s", + ["NAME/%s"] = "NOMBRE/%s", + ["In battle"] = "En combate", + ["Wild battle"] = "Combate salvaje", + ["Trainer battle"] = "Combate entrenador", + ["Link battle"] = "Combate link", + ["Title screen"] = "Pantalla de título", + ["Level %d"] = "Nivel %d", + ["What?\n%s is\nevolving!\fCongratulations!\nYour %s\nevolved into\n%s!"] = "¿Qué?\n¡%s está\nevolucionando!\f¡Enhorabuena!\n¡Tu %s\nevolucionó a\n%s!", + ["Not even a nibble!"] = "¡Ni un mordisco!", + ["Oh!\nIt's a bite!"] = "¡Oh!\n¡Ha picado!", + ["It's a sculpture\nof DIGLETT."] = "Es una escultura\nde DIGLETT.", + ["Crammed full of\nPOKéMON books!"] = "¡Repleto de libros\nsobre POKéMON!", + ["There's a slew of\nPOKéMON stuff!"] = "¡Hay un montón de\ncosas POKéMON!", + ["An elevator!"] = "¡Un ascensor!", + ["INDIGO PLATEAU"] = "MESETA ANIL", + ["POKéMON LEAGUE HQ"] = "SEDE DE LA LIGA\nPOKéMON", + ["You can't carry\nany more items!"] = "¡No puedes llevar\nmás objetos!", + ["%s found\n%s!"] = "¡%s encontró\n%s!", + ["%s found\n%d coins!"] = "¡%s encontró\n%d fichas!", + ["OUT OF ORDER\nThis is broken."] = "FUERA DE SERVICIO\nEsto está roto.", + ["OUT TO LUNCH\nThis is reserved."] = "CERRADO POR COMIDA\nEsto está reservado.", + ["Someone's keys!\nThey'll be back."] = "¡Las llaves de\nalguien! Volverá.", + ["A COIN CASE is\nrequired!"] = "¡Se necesita un\nMONEDERO!", + ["You don't have\nany coins!"] = "¡No tienes fichas!", + ["{RAM}\nPOKéMON GYM\nLEADER: {RAM}"] = "{RAM}\nGIMNASIO POKéMON\nLIDER: {RAM}", + ["Nope, there's\nonly trash here."] = "No, aquí solo hay\nbasura.", + ["Darn! It needs a\nCARD KEY!"] = "¡Vaya! ¡Necesita\nuna LLAVE MAGNET.!", + ["Bingo!"] = "¡Bingo!", + ["\nThe CARD KEY\nopened the door!"] = "\n¡La LLAVE MAGNET.\nabrió la puerta!", + ["Hey! There's a\nswitch under the\ntrash!\fThe 1st electric\nlock opened!"] = "¡Hay un interruptor\nbajo la basura!\f¡Se abrió el 1er\ncierre eléctrico!", + ["The 2nd electric\nlock opened!\fThe motorized door\nopened!"] = "¡Se abrió el 2o\ncierre eléctrico!\f¡La puerta se\nabrió!", + ["Nope! There's\nonly trash here.\fHey! The electric\nlocks were reset!"] = "¡No! Aquí solo hay\nbasura.\f¡Los cierres se\nreiniciaron!", + ["TELEPORTER is\ndisplayed on the\nPC monitor."] = "El TELETRANSPORTE\naparece en el\nmonitor del PC.", + ["{PLAYER} initiated\nTELEPORTER's Cell\nSeparator!"] = "¡{PLAYER} activó el\nSeparador de Células\ndel TELETRANSPORTE!", + ["BILL's favorite\nPOKéMON list!"] = "¡La lista de POKéMON\nfavoritos de BILL!", + ["{PLAYER} got on\n{RAM:wNameBuffer}!"] = "¡{PLAYER} se subió\na {RAM:wNameBuffer}!", + ["{RAM:wNameBuffer} hacked\naway with CUT!"] = "¡{RAM:wNameBuffer} cortó\ncon CORTE!", + ["Gyaoo!"] = "¡Gyaoo!", + ["Hi there!\nMay I help you?"] = "¡Hola!\n¿Puedo ayudarte?", + ["SOMEONE'S PC"] = "EL PC DE ALGUIEN", + ["PROF.OAK's PC"] = "EL PC DEL PROF.OAK", + ["POKéDEX comp-\nletion is:\f{NUM:hDexRatingNumMonsSeen} POKéMON seen\n{NUM:hDexRatingNumMonsOwned} POKéMON owned\fPROF.OAK's\nRating:"] = "La POKéDEX está\nasí:\f{NUM:hDexRatingNumMonsSeen} POKéMON vistos\n{NUM:hDexRatingNumMonsOwned} POKéMON capturados\fNota del\nPROF.OAK:", + ["We hope to see\nyou again!"] = "¡Esperamos verte\nde nuevo!", + ["Welcome to our\nPOKéMON CENTER!"] = "¡Bienvenido a\nnuestro CENTRO\nPOKéMON!", + ["Shall we heal your\nPOKéMON?"] = "¿Curamos a tus\nPOKéMON?", + ["OK. We'll need\nyour POKéMON."] = "Bien. Necesitamos\ntus POKéMON.", + ["Your POKéMON are\nfighting fit!"] = "¡Tus POKéMON están\nen plena forma!", + ["Welcome to the\nCable Club!"] = "¡Bienvenido al Club\nde Cable!", + ["We're making\npreparations.\vPlease wait."] = "Estamos preparando\ntodo.\vEspera un momento.", + ["Please apply here.\fBefore opening\nthe link, we have\vto save the game."] = "Solicítalo aquí.\fAntes de abrir el\nlink hay que\vguardar la partida.", + ["Please come\nagain!"] = "¡Vuelve pronto!", + ["I like shorts!\nThey're comfy and\neasy to wear!"] = "¡Me gustan los\npantalones cortos!\n¡Son cómodos!", + ["%s received\nthe %s!"] = "¡%s recibió\nel %s!", + ["%s received\n%s!"] = "¡%s recibió\n%s!", + ["REPEL's effect\nwore off."] = "El efecto del REPEL\nse ha pasado.", + ["Go right ahead!"] = "¡Adelante!", + ["You don't have the\nBOULDERBADGE yet!"] = "¡Aún no tienes la\nMEDALLA ROCA!", + ["Oh! That is the\n{RAM}!"] = "¡Oh! ¡Eso es el\n{RAM}!", + ["You don't have the\n{RAM} yet!"] = "¡Aún no tienes el\n{RAM}!", + ["You need a\nBICYCLE for the\nCycling Road!"] = "¡Necesitas una\nBICICLETA para el\nCarril Bici!", + ["The boulder fell\nthrough the hole!"] = "¡La roca cayó por\nel agujero!", + ["PA: Ding-dong!\nTime's up!"] = "AV: ¡Ding-dong!\n¡Se acabó el tiempo!", + ["PA: Your SAFARI\nGAME is over!"] = "AV: ¡Tu JUEGO\nSAFARI ha terminado!", + ["PA: You're out of\nSAFARI BALLs!"] = "AV: ¡No te quedan\nSAFARI BALLs!", + ["{PLAYER} got\n%s!"] = "¡{PLAYER} consiguió\n%s!", + ["There's no more\nroom for POKéMON!\v%s was\vsent to POKéMON\vBOX %s on PC!"] = "¡No hay sitio para\nmás POKéMON!\v¡%s fue\venviado a la CAJA\vPOKéMON %s del PC!", + ["contribution is not a table"] = "", + [" [%s %s.%s]"] = " [%s %s.%s]", + ["Link battle needs\nthe same mods on\nboth games."] = "El combate link\nnecesita los mismos\nmods en los dos\njuegos.", + ["Your %s can't\nbattle on the\nother game."] = "Tu %s no puede\nluchar en el otro\njuego.", + ["Their %s isn't\nin this game.\n(%s)"] = "Su %s no está\nen este juego.\n(%s)", + ["%s wants\nto battle!"] = "¡%s quiere\nluchar!", + ["Link desync!\n%s differs.\fAre both games\nrunning the same\nmods?"] = "¡Link desincroni-\nzado!\n%s difiere.\f¿Están los dos\njuegos con los\nmismos mods?", + ["%s ran from\nthe battle!"] = "¡%s huyó del\ncombate!", + ["Items can't be\nused in a link\nbattle!"] = "¡No se pueden usar\nobjetos en un\ncombate link!", + ["%s is out of\nPOKéMON!\f%s wins!"] = "¡%s no tiene\nPOKéMON!\f¡%s gana!", + ["%s left the\nbattle."] = "%s dejó el\ncombate.", + ["%s ran out of\ntime!"] = "¡%s se quedó\nsin tiempo!", + ["Time's up! You\nforfeit the match."] = "¡Se acabó el tiempo!\nPierdes el combate.", + ["%s's %s can't\nbattle on this\ngame."] = "El %s de %s\nno puede luchar en\neste juego.", + ["%s's %s can't\nbattle on this\ngame.\n(%s)"] = "El %s de %s\nno puede luchar en\neste juego.\n(%s)", + ["%s vs %s!"] = "¡%s contra %s!", + ["Link error:\n%s"] = "Error de link:\n%s", + ["Online play runs\nvanilla for both\nplayers.\fTurn off %s\nand restart?"] = "El juego en línea\nva sin mods para\nlos dos jugadores.\f¿Desactivar %s\ny reiniciar?", + ["The link was\nbroken."] = "Se ha perdido el\nlink.", + ["Link battle\ncan't start."] = "El combate link no\npuede empezar.", + ["The trade stopped:\n%s."] = "El intercambio se\ndetuvo:\n%s.", + ["The trade was\ncancelled."] = "El intercambio se\nha cancelado.", + ["Trade completed!\f%s received\n%s!"] = "¡Intercambio hecho!\f¡%s recibió\n%s!", + ["LINK CABLE (LAN)"] = "CABLE LINK (LAN)", + ["ONLINE MATCH"] = "PARTIDA EN LINEA", + ["TOURNAMENT"] = "TORNEO", + ["HOST A GAME"] = "CREAR PARTIDA", + ["JOIN A GAME"] = "UNIRSE A PARTIDA", + ["UDP port %s"] = "Puerto UDP %s", + ["HOST ONLINE"] = "CREAR EN LINEA", + ["JOIN ONLINE"] = "UNIRSE EN LINEA", + ["Tell your friend"] = "Dile a tu amigo", + ["the code:"] = "el código:", + ["Waiting for join..."] = "Esperando...", + ["A: connect B: back"] = "A: conectar B: atrás", + ["Calling..."] = "Llamando...", + ["Friend joins at:"] = "Tu amigo entra en:", + ["Port: %s"] = "Puerto: %s", + ["TRADE"] = "INTERCAMBIO", + ["BATTLE"] = "COMBATE", + ["LEVELS:"] = "NIVELES:", + ["A: continue B: back"] = "A: seguir B: atrás", + ["Checking the"] = "Comprobando el", + ["other game..."] = "otro juego...", + ["Waiting for the"] = "Esperando a que", + ["host to choose..."] = "el anfitrión elija...", + ["A: trade anyway"] = "A: intercambiar igual", + ["YOURS"] = "TUYO", + ["THEIRS"] = "SUYO", + ["X: not on theirs"] = "X: no en el suyo", + ["A: trade B: cancel"] = "A: cambiar B: cancelar", + ["Exchanging data..."] = "Intercambiando...", + ["can't reach relay %s:%d\n(%s)"] = "", + ["That code wasn't\nfound."] = "Ese código no se\nha encontrado.", + ["That game already\nhas two players."] = "Esa partida ya\ntiene dos jugadores.", + ["That code has\nexpired."] = "Ese código ha\ncaducado.", + ["Couldn't join:\n%s"] = "No se pudo unir:\n%s", + ["no answer from\n%s"] = "", + ["That tournament\nhas already begun."] = "Ese torneo ya ha\nempezado.", + ["Can't host:\nneed %d Pokemon\nLv %s-%s."] = "No puedes crearlo:\nnecesitas %d Pokemon\nNv %s-%s.", + ["Couldn't host\nthat tournament."] = "No se pudo crear\nese torneo.", + ["Your party needs\n%d Pokemon, Lv\n%s-%s."] = "Tu equipo necesita\n%d Pokemon, Nv\n%s-%s.", + ["Couldn't join\nthat tournament."] = "No se pudo unir a\nese torneo.", + ["Link error:\nversion mismatch\nwith opponent."] = "Error de link:\nversión distinta a\nla del rival.", + ["The tournament\nconnection was\nlost."] = "Se perdió la\nconexión del torneo.", + ["Can't watch this\nmatch."] = "No se puede ver\neste combate.", + ["HOST"] = "CREAR", + ["JOIN"] = "UNIRSE", + ["START: create"] = "START: crear", + ["A: join B: back"] = "A: unirse B: atrás", + ["B: cancel"] = "B: cancelar", + ["TOURNAMENT %s"] = "TORNEO %s", + ["ROUND %d"] = "RONDA %d", + ["%s (bye)"] = "%s (pasa)", + ["%s%s vs %s%s"] = "%s%s contra %s%s", + ["(organizing --"] = "(organizando --", + ["not playing)"] = "no juega)", + ["Waiting for"] = "Esperando a que", + ["players to join:"] = "entren jugadores:", + ["A: START B: cancel"] = "A: START B: cancelar", + ["%s is the"] = "¡%s es el", + ["champion!"] = "campeón!", + ["A: continue"] = "A: continuar", + ["{PLAYER} played the\nPOKé FLUTE."] = "{PLAYER} tocó la\nFLAUTA POKé.", + ["Played the POKé\nFLUTE.\fNow, that's a\ncatchy tune!"] = "Tocaste la FLAUTA\nPOKé.\f¡Qué melodía tan\npegadiza!", + ["%s played the\nPOKé FLUTE."] = "%s tocó la\nFLAUTA POKé.", + ["All sleeping\nPOKéMON woke up!"] = "¡Todos los POKéMON\ndormidos despertaron!", + ["%s's\nhits will never\nmiss!"] = "¡Los golpes de %s\nnunca fallarán!", + ["The wild POKéMON\nran away!"] = "¡El POKéMON salvaje\nhuyó!", + ["%s's PP\nwas restored!"] = "¡Los PP de %s\nse recuperaron!", + ["%s's\nstatus returned\nto normal!"] = "¡El estado de %s\nvolvió a la\nnormalidad!", + ["%s\nis revitalized!"] = "¡%s se ha\nrevitalizado!", + ["%s\nis refusing!"] = "¡%s se\nniega!", + ["%s's %s\nrose!"] = "¡El %s de %s\nsubió!", + ["%s's PP\nincreased!"] = "¡Los PP de %s\naumentaron!", + ["%s can't\nlearn that move!"] = "¡%s no puede\naprender ese\nmovimiento!", + ["It knows that\nmove already!"] = "¡Ya conoce ese\nmovimiento!", + ["Coin count:\n%d"] = "Fichas:\n%d", + ["NO MODS INSTALLED"] = "NO HAY MODS", + ["SAVE CURRENT AS.."] = "GUARDAR ACTUAL..", + ["OPTIONS.."] = "OPCIONES..", + ["PERMISSIONS.."] = "PERMISOS..", + ["VIEW ERROR.."] = "VER ERROR..", + ["BACK"] = "ATRAS", + ["APPLY & RESTART"] = "APLICAR Y REINICIAR", + ["DISCARD CHANGES"] = "DESCARTAR CAMBIOS", + ["DATA & API ONLY"] = "SOLO DATOS Y API", + ["DISABLE BOTH?"] = "DESACTIVAR AMBOS?", + ["PROFILE NAME?"] = "NOMBRE DEL PERFIL?", + ["RENAME?"] = "RENOMBRAR?", + ["RESET DEFAULTS"] = "VALORES POR DEFECTO", + ["NO CHANGES"] = "SIN CAMBIOS", + ["A:OK"] = "A:OK", + ["B:DONE (NO RESTART)"] = "B:LISTO (SIN REINICIAR)", + ["MOD MANAGER"] = "GESTOR DE MODS", + ["Choose a mod .zip"] = "Elige un .zip de mod", + ["Choose a .sav save file"] = "Elige un archivo .sav", + ["An update is available"] = "Hay una actualización", + ["Name save slot"] = "Nombra la ranura", + ["Enter to save - Esc to cancel - empty clears"] = "Enter para guardar - Esc para cancelar - vacío la borra", + ["Add a mod index"] = "Añadir un índice de mods", + ["Paste the index URL, or its owner/repo."] = "Pega la URL del índice, o su owner/repo.", + ["Enter to add - Esc to cancel"] = "Enter para añadir - Esc para cancelar", + ["Import a ROM to play"] = "Importa una ROM para jugar", + ["RED"] = "ROJO", + ["BLUE"] = "AZUL", + ["YELLOW"] = "AMARILLO", + ["FIND MODS"] = "BUSCAR MODS", + ["%d of 3 ready"] = "%d de 3 listos", + ["Or drop the .gb/.gbc file here."] = "O arrastra aquí el archivo .gb/.gbc.", + ["ROM imported"] = "ROM importada", + ["That ROM could not be imported."] = "No se pudo importar esa ROM.", + ["Open folder"] = "Abrir carpeta", + ["%d badges - %s - %d caught"] = "%d medallas - %s - %d capturados", + ["%d of %d enabled"] = "%d de %d activados", + ["Or drop a mod .zip onto the window."] = "O arrastra un .zip de mod a la ventana.", + ["No mods installed - drop a mod .zip here to add one."] = "No hay mods - arrastra aquí un .zip para añadir uno.", + ["Refreshed - %d mods listed"] = "Actualizado - %d mods listados", + ["Added %s"] = "Añadido %s", + ["Index removed"] = "Índice eliminado", + ["Downloading %s..."] = "Descargando %s...", + ["Installed %s %s"] = "Instalado %s %s", + ["%d mods listed"] = "%d mods listados", + ["%d of %d mods"] = "%d de %d mods", + ["Mods here are listed, not reviewed - read the source and trust the author."] = "Los mods aquí se listan, no se revisan - lee el código y confía en el autor.", + ["No mod index added"] = "No hay índice de mods", + ["Add an index to browse mods. An index is a published list; paste its URL or its owner/repo."] = "Añade un índice para explorar mods. Un índice es una lista publicada; pega su URL o su owner/repo.", + ["Search mods"] = "Buscar mods", + ["This index lists no mods yet."] = "Este índice aún no lista mods.", + ["No mods match that search."] = "Ningún mod coincide con esa búsqueda.", +} diff --git a/mods/spanish_ui/lang/trainer_names.lua b/mods/spanish_ui/lang/trainer_names.lua new file mode 100644 index 00000000..c6537732 --- /dev/null +++ b/mods/spanish_ui/lang/trainer_names.lua @@ -0,0 +1,7 @@ +-- Trainer class names +-- +-- Trainer class names for Espanol. + +return { + ["OPP_FIX_YOUNGSTER"] = "", +} diff --git a/mods/spanish_ui/main.lua b/mods/spanish_ui/main.lua new file mode 100644 index 00000000..ada371d1 --- /dev/null +++ b/mods/spanish_ui/main.lua @@ -0,0 +1,127 @@ +-- spanish_ui: a translation of the game into Espanol. +-- +-- Nothing here is translated yet. Every table under lang/ starts with +-- empty strings; fill one in and it takes effect on the next boot, and +-- anything still empty keeps rendering in English. That means a +-- half-finished translation is always playable, so you can ship early and +-- fill the long tail in later. +-- +-- Read TRANSLATING.md before the first edit; the font is the part people +-- get wrong. +return function(mod) + -- mod:read is the supported way into your own directory; the catalogs are + -- plain Lua tables, so read and run them rather than require()ing them. + local function catalog(name) + local rel = "lang/" .. name .. ".lua" + local body = mod:read(rel) + if not body then return {} end + local chunk, err = loadstring(body, rel) + if not chunk then + mod.log:warn("%s has a syntax error: %s", rel, tostring(err)) + return {} + end + local ok, table_ = pcall(chunk) + if not ok or type(table_) ~= "table" then + mod.log:warn("%s did not return a table: %s", rel, tostring(table_)) + return {} + end + return table_ + end + + -- An empty value means "not translated yet", never "translate to blank". + local function each(name, apply) + local n = 0 + for key, value in pairs(catalog(name)) do + if type(value) == "string" and value ~= "" then + apply(key, value) + n = n + 1 + end + end + return n + end + + -- ---- glyphs ------------------------------------------------------- + -- Register the sheet BEFORE anything asks for a glyph on it. base is + -- the first code the page owns; 0x100 and up is free space above the + -- vanilla pages, so a new alphabet never collides with them. + for id, page in pairs(catalog("font")) do + mod.content.font:register(id, page) + end + -- charmap: which byte sequence draws which code + for seq, code in pairs(catalog("charmap")) do + mod.content.font:register("charmap:" .. seq, { seq = seq, code = code }) + end + + -- ---- text --------------------------------------------------------- + local counts = {} + counts.dialogue = each("dialogue", function(id, value) + mod.content.text:override(id, value) + end) + counts.strings = each("strings", function(source, value) + mod.content.strings:override(source, value) + end) + counts.species = each("species_names", function(id, value) + mod.content.pokemon:patch(id, { name = value }) + end) + counts.moves = each("move_names", function(id, value) + mod.content.moves:patch(id, { name = value }) + end) + counts.items = each("item_names", function(id, value) + mod.content.items:patch(id, { name = value }) + end) + counts.trainers = each("trainer_names", function(id, value) + mod.content.trainers:patch(id, { name = value }) + end) + counts.statuses = each("status_labels", function(id, value) + mod.content.statuses:patch(id, { label = value }) + end) + + -- ---- name entry --------------------------------------------------- + -- The naming screen's letter grid. Leave lang/naming.lua returning nil + -- to keep the English alphabet. + local grid = catalog("naming") + if grid.upper then + -- Only offer the accented cells when the running cartridge can actually + -- draw them. A Spanish ROM has Ñ and the accented vowels in its font + -- and the manifest maps them; an English one does not, and an + -- unmappable cell renders blank -- a naming screen with six empty keys + -- is worse than an English one. So check the charmap and fall back. + local function drawable(cells, ctx) + local font = ((ctx.game or {}).data or {}).font + local charmap = font and font.charmap + if not charmap then return false end + local have = {} + for _, entry in ipairs(charmap) do have[entry.seq] = true end + for _, row in ipairs(cells) do + for _, cell in ipairs(row) do + -- Only the non-ASCII cells are at risk; A-Z and punctuation are + -- on every page. + if cell:byte(1) and cell:byte(1) > 127 and not have[cell] then + return false + end + end + end + return true + end + local warned = false + mod.hooks:on("ui.naming.grid", function(base, ctx) + local want = ctx.lower and grid.lower or grid.upper + if not want then return base end + if not drawable(want, ctx) then + if not warned then + warned = true + mod.log:info("naming grid: this ROM has no accented glyphs, " + .. "keeping the English alphabet") + end + return base + end + return want + end) + end + + mod.events:on("game.ready", function() + local total = 0 + for _, n in pairs(counts) do total = total + n end + mod.log:info("Espanol: %d strings translated", total) + end) +end diff --git a/mods/spanish_ui/manifest.json b/mods/spanish_ui/manifest.json new file mode 100644 index 00000000..b3e45f04 --- /dev/null +++ b/mods/spanish_ui/manifest.json @@ -0,0 +1,17 @@ +{ + "id": "spanish_ui", + "name": "Espanol (interfaz)", + "version": "0.1.0", + "api": 2, + "entry": "main.lua", + "profile": "content", + "game_version": ">=0.0.0-dev <1.0.0", + "category": "LANGUAGE", + "priority": 100, + "dependencies": [], + "optional_dependencies": [], + "conflicts": [], + "incompatible": [], + "experimental": false, + "description": "Spanish for the app's own settings and menus. The game's text comes from your ROM and is untouched, so an English cartridge stays an English adventure with Spanish menus." +} \ No newline at end of file