Merge pull request #1759 from thibautbus/fix/translate-gold-title-and-save-menus

Translate Gold's title menu and SAVE screen
This commit is contained in:
bryanthaboi
2026-08-24 08:29:00 -04:00
committed by GitHub
5 changed files with 386 additions and 24 deletions
+11 -10
View File
@@ -24,6 +24,7 @@ local Logger = require("src.core.Logger")
local Music = require("src.core.Music")
local Runtime = require("src.mods.Runtime")
local Save = require("src.core.gen2.Save")
local Strings = require("src.core.Strings")
local MainMenu = {}
MainMenu.__index = MainMenu
@@ -73,14 +74,14 @@ local function sameItems(_, items) return items end
function MainMenu:buildList()
local items = {}
if self.hasSave then
items[#items + 1] = { label = "CONTINUE", value = "continue" }
items[#items + 1] = { label = Strings("CONTINUE"), value = "continue" }
end
items[#items + 1] = { label = "NEW GAME", value = "new" }
items[#items + 1] = { label = "OPTION", value = "option" }
items[#items + 1] = { label = Strings("NEW GAME"), value = "new" }
items[#items + 1] = { label = Strings("OPTION"), value = "option" }
-- Not on the cart: a cartridge is left by switching the console off, and
-- there is no console here. Mirrors the Gen 1 port's title menu
-- (src/ui/TitleState.lua), which adds the same row for the same reason.
items[#items + 1] = { label = "EXIT GAME", value = "exit" }
items[#items + 1] = { label = Strings("EXIT GAME"), value = "exit" }
-- The same hook name and the same (game, items) payload the Gen 1 title
-- menu raises (src/ui/TitleState.lua:openMenu), so one mod's title rows
-- serve both games; only the row shape differs, because Chrome.List reads
@@ -170,7 +171,7 @@ function MainMenu:drawClockBox()
-- minutes; the AM/PM half is drawn by PrintHour itself.
local display = hour % 12
if display == 0 then display = 12 end
local half = hour < 12 and "AM" or "PM"
local half = Strings(hour < 12 and "AM" or "PM")
Chrome.print(("%s:%s %s"):format(
Chrome.number(display, 2), Chrome.number(minute, 2, true), half), 4, 16)
end
@@ -180,15 +181,15 @@ function MainMenu:drawSavePanel()
-- DisplaySaveInfoOnContinue: a box down the right side listing the trainer.
Chrome.textbox(4, 0, 14, 9)
if not summary then
Chrome.print("NO SAVE FILE", 5, 2)
Chrome.print(Strings("NO SAVE FILE"), 5, 2)
return
end
Chrome.print("PLAYER " .. summary.name, 5, 2)
Chrome.print("BADGES", 5, 4)
Chrome.print(Strings("PLAYER %s", summary.name), 5, 2)
Chrome.print(Strings("BADGES"), 5, 4)
Chrome.printRight(tostring(summary.badges), 17, 4)
Chrome.print("POKéDEX", 5, 6)
Chrome.print(Strings("POKéDEX"), 5, 6)
Chrome.printRight(tostring(summary.caught), 17, 6)
Chrome.print("TIME", 5, 8)
Chrome.print(Strings("TIME"), 5, 8)
Chrome.printRight(("%d:%s"):format(
summary.hours, Chrome.number(summary.minutes, 2, true)), 17, 8)
end
+3 -2
View File
@@ -26,6 +26,7 @@ local Font = require("src.render.Font")
local Palettes = require("src.world.gen2.Palettes")
local Phone = require("src.core.gen2.Phone")
local SpriteRenderer = require("src.render.SpriteRenderer")
local Strings = require("src.core.Strings")
local TileSheet = require("src.ui.gen2.TileSheet")
local Pokegear = {}
@@ -1881,7 +1882,7 @@ function Pokegear:drawClock()
self:text(Chrome.number(display, 2), 6, 8)
self:text(":", 8, 8)
self:text(Chrome.number(minute, 2, true), 9, 8)
self:text(hour < 12 and "AM" or "PM", 12, 8)
self:text(Strings(hour < 12 and "AM" or "PM"), 12, 8)
-- The bottom Textbox is part of the card (lb bc, 4, 18 at (0,12)), and
-- PokegearClock_Init prints PokegearPressButtonText straight into it
@@ -2218,7 +2219,7 @@ function Pokegear:drawPlain()
if display == 0 then display = 12 end
Chrome.print(("%s:%s %s"):format(
Chrome.number(display, 2), Chrome.number(minute, 2, true),
hour < 12 and "AM" or "PM"), 5, 9)
Strings(hour < 12 and "AM" or "PM")), 5, 9)
Chrome.print(Clock.daytimeLabel(hour), 5, 11)
elseif id == "radio" then
-- Without the gear sheet there is no dial art, so the frequencies go down
+54 -12
View File
@@ -24,8 +24,10 @@
-- one-line call.
local Chrome = require("src.ui.gen2.Chrome")
local Logger = require("src.core.Logger")
local Save = require("src.core.gen2.Save")
local Sound = require("src.core.Sound")
local Strings = require("src.core.Strings")
local SaveMenu = {}
SaveMenu.__index = SaveMenu
@@ -55,10 +57,50 @@ local TIME_X, TIME_Y = 13, 8
local YESNO_X, YESNO_Y, YESNO_W, YESNO_H = 0, 7, 6, 5
-- AlreadyASaveFileText (AskOverwriteSaveFile, engine/menus/save.asm:47) and
-- SavingDontTurnOffThePower's own line, shared with the PC's CHANGE BOX save.
-- SavingDontTurnOffThePower's own line, shared with the PC's CHANGE BOX save
-- (src/ui/gen2/PcMenu.lua:savePrompt() reads these two tables' lines[1]/
-- lines[2] directly, so their shape is a cross-file contract: keep them
-- plain, untranslated tables).
local OVERWRITE_PROMPT = { "There is already a", "save file. Is it" }
local SAVING_PROMPT = { "SAVING… DON'T TURN", "OFF THE POWER." }
-- Translatable copies of the two prompts above, one \n-joined key each, used
-- only by this screen's own prompt() below. One key per prompt lets a
-- translation write one whole, freely reordered sentence instead of two
-- fragments translated in isolation, and lets a cart whose own text is a
-- single line (German's SAVING prompt) say so directly by simply omitting
-- the "\n" -- the per-line override style used elsewhere requires a
-- non-empty value for every line, so it can't express "this line is blank".
--
-- Written as literals, not `table.concat(OVERWRITE_PROMPT, "\n")`: the
-- translation tooling's string harvester only recognizes a literal inside
-- Strings.source(...), not a computed expression, so a concat call here
-- would quietly never reach a translator. Keep byte-for-byte in sync with
-- OVERWRITE_PROMPT/SAVING_PROMPT above (checked by
-- tests/engine/gen2_save_menu_translation_test.lua).
local OVERWRITE_PROMPT_SOURCE = Strings.source("There is already a\nsave file. Is it")
local SAVING_PROMPT_SOURCE = Strings.source("SAVING… DON'T TURN\nOFF THE POWER.")
-- Splits a translated "line one\nline two" string back into the two-slot
-- table drawPanel's fixed Chrome.print calls expect. No "\n" at all (a
-- single-line message, or German's one-line SAVING prompt) lands whole on
-- the first slot, matching the untranslated code's own { text, "" } shape.
--
-- Only the first "\n" splits, since this box has room for exactly two
-- lines. A third line would otherwise draw as a raw newline byte -- garbage
-- glyph data -- with no other sign anything went wrong, so this warns once
-- per string instead.
local warnedTooManyLines = {}
local function twoLines(text)
local first, second = text:match("^(.-)\n(.*)$")
if second and second:find("\n", 1, true) and not warnedTooManyLines[text] then
warnedTooManyLines[text] = true
Logger.warn("SaveMenu: translation of %q has more than two lines; " ..
"only the first two fit this box", text)
end
return { first or text, second or "" }
end
function SaveMenu:wantsFillScale() return true end
function SaveMenu:drawsWidescreen() return true end
@@ -171,18 +213,18 @@ function SaveMenu:prompt()
if self.phase == "overwrite" then
-- AlreadyASaveFileText when the file is this player's; AnotherSaveFileText
-- when the ID differs. Only the first can happen here.
return OVERWRITE_PROMPT
return twoLines(Strings(OVERWRITE_PROMPT_SOURCE))
end
if self.phase == "saving" then
return SAVING_PROMPT
return twoLines(Strings(SAVING_PROMPT_SOURCE))
end
if self.phase == "done" then
if self.saved then
return { self:playerName() .. " saved", "the game." }
return twoLines(Strings("%s saved\nthe game.", self:playerName()))
end
return { "Could not save.", "" }
return twoLines(Strings("Could not save."))
end
return { "Would you like to", "save the game?" }
return twoLines(Strings("Would you like to\nsave the game?"))
end
function SaveMenu:drawPanel()
@@ -190,10 +232,10 @@ function SaveMenu:drawPanel()
local summary = Save.summary(self.save)
Chrome.box(PANEL_X, PANEL_Y, PANEL_W, PANEL_H)
if summary then
Chrome.print("PLAYER " .. summary.name, LABEL_X, LABEL_Y)
Chrome.print("BADGES", LABEL_X, LABEL_Y + 2)
Chrome.print("POKéDEX", LABEL_X, LABEL_Y + 4)
Chrome.print("TIME", LABEL_X, LABEL_Y + 6)
Chrome.print(Strings("PLAYER %s", summary.name), LABEL_X, LABEL_Y)
Chrome.print(Strings("BADGES"), LABEL_X, LABEL_Y + 2)
Chrome.print(Strings("POKéDEX"), LABEL_X, LABEL_Y + 4)
Chrome.print(Strings("TIME"), LABEL_X, LABEL_Y + 6)
-- PrintNum fills its field from the left, space padded.
Chrome.print(Chrome.number(summary.badges, 2), BADGES_X, BADGES_Y)
Chrome.print(Chrome.number(summary.caught, 3), DEX_X, DEX_Y)
@@ -211,8 +253,8 @@ function SaveMenu:drawPanel()
if self.phase == "confirm" or self.phase == "overwrite" then
Chrome.box(YESNO_X, YESNO_Y, YESNO_W, YESNO_H)
Chrome.print("YES", YESNO_X + 2, YESNO_Y + 1)
Chrome.print("NO", YESNO_X + 2, YESNO_Y + 3)
Chrome.print(Strings("YES"), YESNO_X + 2, YESNO_Y + 1)
Chrome.print(Strings("NO"), YESNO_X + 2, YESNO_Y + 3)
Chrome.cursor(YESNO_X + 1, YESNO_Y + (self.choice == 1 and 1 or 3))
end
love.graphics.setColor(1, 1, 1, 1)