mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 15:01:11 +02:00
Translate the PC's CHANGE BOX save flow (PcMenu.lua)
PcMenu.lua's own CHANGE BOX save confirmation stayed in English on every
non-English build: its overwrite/saving/done prompts and its YES/NO choice
were bare literals, invisible to a translation mod's `strings` registry,
even though the overwrite/saving prompts are the exact same two cart
messages Gold's SAVE screen (SaveMenu.lua) already routes through
Strings().
SaveMenu.lua now exports OVERWRITE_PROMPT_SOURCE, SAVING_PROMPT_SOURCE and
twoLines() so PcMenu:savePrompt() can call through them instead of
duplicating the two shared prompts. The now-unused plain
OVERWRITE_PROMPT/SAVING_PROMPT tables (PcMenu's old direct-read contract)
are gone along with them, removing a second copy of the same literal text
that had to be kept in sync by hand. The CHANGE BOX confirm prompt itself
("#MON BOX, data\nwill be saved. OK?") gets its own new Strings.source()
key -- it is not shared with SaveMenu, and the port's own English text is
already a same-shape truncation of the cart's three-line
_ChangeBoxSaveText, confirmed against poke-corpus GoldSilver
en_msg.txt:4897. The done message and YES/NO now go through the exact
same Strings() keys SaveMenu.lua already uses ("%s saved\nthe game.",
"Could not save.", "YES", "NO"), so a translation covering SaveMenu's
screen needs no PcMenu-specific fork for any of those.
Added tests/engine/gen2_pcmenu_changebox_save_translation_test.lua,
mirroring gen2_save_menu_translation_test.lua's technique for PcMenu's own
CHANGE BOX flow, and updated the stale cross-file-contract regression case
in gen2_save_menu_translation_test.lua to match the new shared-export
shape instead of the old plain-table one.
This commit is contained in:
+17
-10
@@ -38,9 +38,12 @@ local MON_HOLDING_MAIL = {
|
||||
Strings.source("Please remove the\nMAIL."),
|
||||
}
|
||||
|
||||
-- _ChangeBoxSaveText (data/text/common_2.asm:1306) is three lines whose `cont`
|
||||
-- has already scrolled by the time YesNoBox goes up over its last two.
|
||||
local CHANGE_BOX_SAVE = { "#MON BOX, data", "will be saved. OK?" }
|
||||
-- _ChangeBoxSaveText (data/text/common_2.asm:1306) is three lines whose first
|
||||
-- `cont` ("When you change a") has already scrolled by the time YesNoBox goes
|
||||
-- up over its last two -- confirmed against poke-corpus GoldSilver
|
||||
-- en_msg.txt:4897. One \n-joined translatable key, same pattern as
|
||||
-- SaveMenu.lua's OVERWRITE_PROMPT_SOURCE/SAVING_PROMPT_SOURCE.
|
||||
local CHANGE_BOX_SAVE_SOURCE = Strings.source("#MON BOX, data\nwill be saved. OK?")
|
||||
|
||||
-- YesNoBox's own `lb bc, SCREEN_WIDTH - 6, 7` (home/menu.asm:382-383).
|
||||
local YESNO_X, YESNO_Y, YESNO_W, YESNO_H = 14, 7, 6, 5
|
||||
@@ -212,16 +215,20 @@ function PcMenu:writeChangeBox()
|
||||
end
|
||||
|
||||
function PcMenu:savePrompt()
|
||||
if self.savePhase == "overwrite" then return SaveMenu.OVERWRITE_PROMPT end
|
||||
if self.savePhase == "saving" then return SaveMenu.SAVING_PROMPT end
|
||||
if self.savePhase == "overwrite" then
|
||||
return SaveMenu.twoLines(Strings(SaveMenu.OVERWRITE_PROMPT_SOURCE))
|
||||
end
|
||||
if self.savePhase == "saving" then
|
||||
return SaveMenu.twoLines(Strings(SaveMenu.SAVING_PROMPT_SOURCE))
|
||||
end
|
||||
if self.savePhase == "done" then
|
||||
if self.saved then
|
||||
local name = (self.save.player and self.save.player.name) or "GOLD"
|
||||
return { name .. " saved", "the game." }
|
||||
return SaveMenu.twoLines(Strings("%s saved\nthe game.", name))
|
||||
end
|
||||
return { "Could not save.", "" }
|
||||
return SaveMenu.twoLines(Strings("Could not save."))
|
||||
end
|
||||
return CHANGE_BOX_SAVE
|
||||
return SaveMenu.twoLines(Strings(CHANGE_BOX_SAVE_SOURCE))
|
||||
end
|
||||
|
||||
function PcMenu:updateChangeBox()
|
||||
@@ -404,8 +411,8 @@ function PcMenu:drawPanel()
|
||||
Chrome.print(lines[2] or "", 1, 16)
|
||||
if self.savePhase == "confirm" or self.savePhase == "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.saveChoice == 1 and 1 or 3))
|
||||
end
|
||||
|
||||
+17
-22
@@ -57,27 +57,21 @@ 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
|
||||
-- (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".
|
||||
-- SavingDontTurnOffThePower's own line -- one \n-joined translatable key
|
||||
-- each, used both by this screen's own prompt() below and, through the
|
||||
-- SOURCE/twoLines() exports at the bottom of this file, by the PC's CHANGE
|
||||
-- BOX save (src/ui/gen2/PcMenu.lua:savePrompt()), which shares these exact
|
||||
-- same two cart messages. 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).
|
||||
-- Written as a literal, not built from a table: 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.
|
||||
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.")
|
||||
|
||||
@@ -279,7 +273,8 @@ end
|
||||
SaveMenu.SFX_SAVE = SFX_SAVE
|
||||
SaveMenu.SAVING_FRAMES = SAVING_FRAMES
|
||||
SaveMenu.SAVED_FRAMES = SAVED_FRAMES
|
||||
SaveMenu.OVERWRITE_PROMPT = OVERWRITE_PROMPT
|
||||
SaveMenu.SAVING_PROMPT = SAVING_PROMPT
|
||||
SaveMenu.OVERWRITE_PROMPT_SOURCE = OVERWRITE_PROMPT_SOURCE
|
||||
SaveMenu.SAVING_PROMPT_SOURCE = SAVING_PROMPT_SOURCE
|
||||
SaveMenu.twoLines = twoLines
|
||||
|
||||
return SaveMenu
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
-- The PC's CHANGE BOX save flow (src/ui/gen2/PcMenu.lua:savePrompt()) used to
|
||||
-- draw its overwrite/saving/done prompts and its YES/NO choice as bare
|
||||
-- literals, invisible to a translation mod's `strings` registry, even though
|
||||
-- the overwrite/saving prompts are the exact same two cart messages Gold's
|
||||
-- SAVE screen (src/ui/gen2/SaveMenu.lua) already routes through Strings().
|
||||
-- Same technique as tests/engine/gen2_save_menu_translation_test.lua: drives
|
||||
-- PcMenu:drawPanel() directly at each save phase with a mod-loaded Strings
|
||||
-- catalog and checks the translated text reaches Font.draw.
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
|
||||
love = require("tests.love_stub")
|
||||
|
||||
require("src.core.Logger").warn = function() end
|
||||
|
||||
local drawn
|
||||
package.loaded["src.render.Font"] = {
|
||||
draw = function(text, x, y)
|
||||
drawn[#drawn + 1] = { text = text, x = x, y = y }
|
||||
end,
|
||||
drawCode = function() end,
|
||||
drawBox = function() end,
|
||||
width = function() return 0 end,
|
||||
}
|
||||
|
||||
local PcMenu = require("src.ui.gen2.PcMenu")
|
||||
local Strings = require("src.core.Strings")
|
||||
|
||||
local function drawnAt(x, y)
|
||||
for _, d in ipairs(drawn) do
|
||||
if d.x == x and d.y == y then return d.text end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Chrome.print multiplies tile coordinates by 8 (src/ui/gen2/Chrome.lua).
|
||||
-- The save-prompt box sits at the same (0,12) origin SaveMenu.lua's does, so
|
||||
-- its two lines print at the same (1,14)/(1,16); PcMenu's own YESNO_X/Y
|
||||
-- (14,7) differ from SaveMenu's (0,7), so YES/NO print at (16,8)/(16,10).
|
||||
local PROMPT1_X, PROMPT1_Y = 1 * 8, 14 * 8
|
||||
local PROMPT2_X, PROMPT2_Y = 1 * 8, 16 * 8
|
||||
local YES_X, YES_Y = 16 * 8, 8 * 8
|
||||
local NO_X, NO_Y = 16 * 8, 10 * 8
|
||||
|
||||
-- One party mon so Boxes.canUsePc doesn't refuse to open the PC at all.
|
||||
local SAVE = { player = { name = "GOLD" }, party = { {} } }
|
||||
|
||||
local function newMenu()
|
||||
return PcMenu.new({}, {
|
||||
save = SAVE,
|
||||
saveExists = false,
|
||||
writer = function() return true end,
|
||||
})
|
||||
end
|
||||
|
||||
-- ---------------------------------------------- vanilla: no mod catalog
|
||||
do
|
||||
local menu = newMenu()
|
||||
menu.picking = true
|
||||
menu.pickIndex = 1
|
||||
menu.savePhase = "confirm"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "#MON BOX, data", "the confirm prompt draws in English with no mod loaded")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "will be saved. OK?", "and its second line")
|
||||
T.eq(drawnAt(YES_X, YES_Y), "YES", "and YES")
|
||||
T.eq(drawnAt(NO_X, NO_Y), "NO", "and NO")
|
||||
|
||||
menu.savePhase = "overwrite"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "There is already a",
|
||||
"the overwrite prompt, the same cart message SaveMenu.lua's SAVE screen shares")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "save file. Is it", "its second line")
|
||||
|
||||
menu.savePhase = "saving"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "SAVING… DON'T TURN", "the saving message")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "OFF THE POWER.", "its second line")
|
||||
|
||||
menu.savePhase, menu.saved = "done", true
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "GOLD saved", "the saved message")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "the game.", "its second line")
|
||||
|
||||
menu.savePhase, menu.saved = "done", false
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "Could not save.", "the failed-save message")
|
||||
end
|
||||
|
||||
-- ------------------------------------------------- a translation mod's turn
|
||||
--
|
||||
-- Same catalog values as gen2_save_menu_translation_test.lua's own
|
||||
-- translated block: the overwrite/saving prompts, the saved/failed messages,
|
||||
-- and YES/NO are the exact same keys both screens read, so one translation
|
||||
-- covers both without a PcMenu-specific fork. Only the CHANGE BOX confirm
|
||||
-- prompt's key is new here.
|
||||
do
|
||||
Strings.load({
|
||||
strings = {
|
||||
["YES"] = "OUI",
|
||||
["NO"] = "NON",
|
||||
["#MON BOX, data\nwill be saved. OK?"] = "Les donnees de la\nBOITE seront sauv.",
|
||||
["There is already a\nsave file. Is it"] = "Un fichier existe\ndeja. Est-ce",
|
||||
["SAVING… DON'T TURN\nOFF THE POWER."] = "SAUVEGARDE...\nN'ETEIGNEZ PAS.",
|
||||
["%s saved\nthe game."] = "%s a sauvegarde\nla partie.",
|
||||
["Could not save."] = "Echec de sauvegarde.",
|
||||
},
|
||||
})
|
||||
|
||||
local menu = newMenu()
|
||||
menu.picking = true
|
||||
menu.pickIndex = 1
|
||||
menu.savePhase = "confirm"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "Les donnees de la", "the confirm prompt")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "BOITE seront sauv.", "its second line")
|
||||
T.eq(drawnAt(YES_X, YES_Y), "OUI", "and YES")
|
||||
T.eq(drawnAt(NO_X, NO_Y), "NON", "and NO")
|
||||
|
||||
menu.savePhase = "overwrite"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "Un fichier existe",
|
||||
"the overwrite prompt, translated with no PcMenu-specific key")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "deja. Est-ce", "its second line")
|
||||
|
||||
menu.savePhase = "saving"
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "SAUVEGARDE...", "the saving message")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "N'ETEIGNEZ PAS.", "its second line")
|
||||
|
||||
menu.savePhase, menu.saved = "done", true
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "GOLD a sauvegarde",
|
||||
"the saved message folds the player name into the mod's own word order")
|
||||
T.eq(drawnAt(PROMPT2_X, PROMPT2_Y), "la partie.", "its second line")
|
||||
|
||||
menu.savePhase, menu.saved = "done", false
|
||||
drawn = {}
|
||||
menu:drawPanel()
|
||||
T.eq(drawnAt(PROMPT1_X, PROMPT1_Y), "Echec de sauvegarde.", "the failed-save message")
|
||||
|
||||
-- Module state is process-global (see tests/gen2_clock_test.lua's own
|
||||
-- note); this suite gets its own process from tests/tier_runner.lua, but
|
||||
-- leaving the catalog loaded past this point would still mistranslate
|
||||
-- every check below it in this file.
|
||||
Strings.load({})
|
||||
T.check(not Strings.active(), "the catalog is unloaded for the checks after this one")
|
||||
end
|
||||
|
||||
T.finish("gen2_pcmenu_changebox_save_translation_test")
|
||||
@@ -147,21 +147,26 @@ do
|
||||
T.check(not Strings.active(), "the catalog is unloaded for the checks after this one")
|
||||
end
|
||||
|
||||
-- src/ui/gen2/PcMenu.lua:savePrompt() returns SaveMenu.OVERWRITE_PROMPT/
|
||||
-- SAVING_PROMPT straight through to its own `lines[1]`/`lines[2]`
|
||||
-- Chrome.print calls (the PC's CHANGE BOX save uses the same two prompts).
|
||||
-- Indexing a plain string with [1]/[2] returns nil, not characters, so this
|
||||
-- shape is a cross-file contract: it caught a real regression during review,
|
||||
-- where routing these through a single Strings.source()-wrapped string (to
|
||||
-- translate SaveMenu's own screen) silently turned them into non-table
|
||||
-- values and left PcMenu's overwrite/saving prompt blank.
|
||||
-- src/ui/gen2/PcMenu.lua:savePrompt() shares SaveMenu's overwrite/saving
|
||||
-- prompts through SaveMenu.OVERWRITE_PROMPT_SOURCE/SAVING_PROMPT_SOURCE and
|
||||
-- SaveMenu.twoLines(), rather than duplicating them -- both exported below,
|
||||
-- both used by PcMenu's own translation test
|
||||
-- (tests/engine/gen2_pcmenu_changebox_save_translation_test.lua). Checked
|
||||
-- here that they stay callable the shape twoLines() expects: a table in,
|
||||
-- one \n-joined string out with the split back on load.
|
||||
do
|
||||
T.eq(type(SaveMenu.OVERWRITE_PROMPT), "table", "OVERWRITE_PROMPT stays a table for PcMenu.lua")
|
||||
T.eq(SaveMenu.OVERWRITE_PROMPT[1], "There is already a", "and its first line stays indexable")
|
||||
T.eq(SaveMenu.OVERWRITE_PROMPT[2], "save file. Is it", "and its second line")
|
||||
T.eq(type(SaveMenu.SAVING_PROMPT), "table", "SAVING_PROMPT stays a table for PcMenu.lua")
|
||||
T.eq(SaveMenu.SAVING_PROMPT[1], "SAVING… DON'T TURN", "and its first line stays indexable")
|
||||
T.eq(SaveMenu.SAVING_PROMPT[2], "OFF THE POWER.", "and its second line")
|
||||
T.eq(SaveMenu.OVERWRITE_PROMPT_SOURCE, "There is already a\nsave file. Is it",
|
||||
"OVERWRITE_PROMPT_SOURCE stays the cart's own \\n-joined text")
|
||||
T.eq(SaveMenu.twoLines(Strings(SaveMenu.OVERWRITE_PROMPT_SOURCE))[1], "There is already a",
|
||||
"and twoLines() splits its untranslated fallback back to the first line")
|
||||
T.eq(SaveMenu.twoLines(Strings(SaveMenu.OVERWRITE_PROMPT_SOURCE))[2], "save file. Is it",
|
||||
"and its second line")
|
||||
T.eq(SaveMenu.SAVING_PROMPT_SOURCE, "SAVING… DON'T TURN\nOFF THE POWER.",
|
||||
"SAVING_PROMPT_SOURCE stays the cart's own \\n-joined text")
|
||||
T.eq(SaveMenu.twoLines(Strings(SaveMenu.SAVING_PROMPT_SOURCE))[1], "SAVING… DON'T TURN",
|
||||
"and twoLines() splits its untranslated fallback back to the first line")
|
||||
T.eq(SaveMenu.twoLines(Strings(SaveMenu.SAVING_PROMPT_SOURCE))[2], "OFF THE POWER.",
|
||||
"and its second line")
|
||||
end
|
||||
|
||||
-- A translation with a THIRD line (a second embedded "\n") has nowhere on
|
||||
|
||||
Reference in New Issue
Block a user