fix(rpgmaker): skip translated text using live values
- Check visible database, system, and event text instead of preserved originals - Keep mixed translated and untranslated batch entries correctly aligned - Prevent skipped dialogue from triggering speaker translation - Add regression coverage across database and map content
This commit is contained in:
parent
d7ab5b6851
commit
5afd2c0fee
3 changed files with 398 additions and 201 deletions
|
|
@ -137,7 +137,7 @@ BRFLAG = False
|
|||
# FIXTEXTWRAP: Rewrap text to WIDTH/NOTEWIDTH.
|
||||
FIXTEXTWRAP = True
|
||||
# IGNORETLTEXT: Skip Translated Text.
|
||||
IGNORETLTEXT = False
|
||||
IGNORETLTEXT = True
|
||||
# PRESERVEORIGINAL: Store Japanese source in _original fields (maps + database) for re-run safety.
|
||||
PRESERVEORIGINAL = True
|
||||
# TLSYSTEMVARIABLES: Translate System Variables. (Optional but sometimes necessary. Can break stuff.)
|
||||
|
|
@ -148,13 +148,13 @@ TLSYSTEMSWITCHES = False
|
|||
JOIN408 = False
|
||||
|
||||
# Dialogue / Scroll / Choices (Main Codes)
|
||||
CODE101 = True
|
||||
CODE401 = True
|
||||
CODE405 = True
|
||||
CODE102 = True
|
||||
CODE101 = False
|
||||
CODE401 = False
|
||||
CODE405 = False
|
||||
CODE102 = False
|
||||
|
||||
# Optional
|
||||
CODE408 = True
|
||||
CODE408 = False
|
||||
|
||||
# Variables
|
||||
CODE122 = False
|
||||
|
|
@ -472,6 +472,21 @@ def _scalar_original(cmd) -> str | None:
|
|||
return None
|
||||
|
||||
|
||||
def _text_needs_translation(current) -> bool:
|
||||
"""Return whether a live value should be translated under the skip setting."""
|
||||
if current is None or not str(current).strip():
|
||||
return False
|
||||
return not IGNORETLTEXT or bool(re.search(LANGREGEX, str(current)))
|
||||
|
||||
|
||||
def _param_current(cmd, index: int) -> str:
|
||||
"""Return the current event-command parameter without consulting _original."""
|
||||
params = cmd.get("parameters") or []
|
||||
if index < len(params) and params[index] is not None:
|
||||
return str(params[index])
|
||||
return ""
|
||||
|
||||
|
||||
def _param_source(cmd, index: int) -> str:
|
||||
"""Prefer scalar _original; else parameters[index] (401/405 dialogue lines)."""
|
||||
orig = _scalar_original(cmd)
|
||||
|
|
@ -514,6 +529,37 @@ def _group_raw_source(codeList, group_start: int, source_parts: list[str]) -> st
|
|||
return "\n".join(source_parts)
|
||||
|
||||
|
||||
def _group_current(codeList, start: int, end: int) -> str:
|
||||
"""Join current visible text for a 401/405/408 group."""
|
||||
parts = []
|
||||
for idx in range(start, end + 1):
|
||||
if idx >= len(codeList):
|
||||
break
|
||||
current = _param_current(codeList[idx], 0)
|
||||
if current.strip():
|
||||
parts.append(current)
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def _text_group_end(codeList, start: int, allowed_codes: tuple[int, ...]) -> int:
|
||||
"""Return the last command joined into a contiguous visible-text group."""
|
||||
end = start
|
||||
while end + 1 < len(codeList):
|
||||
current = codeList[end]
|
||||
following = codeList[end + 1]
|
||||
if following.get("code") not in allowed_codes:
|
||||
break
|
||||
if not current.get("parameters") or not following.get("parameters"):
|
||||
break
|
||||
if re.match(
|
||||
r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])",
|
||||
str(following["parameters"][0]),
|
||||
):
|
||||
break
|
||||
end += 1
|
||||
return end
|
||||
|
||||
|
||||
def _apply_original(cmd, raw_source: str) -> None:
|
||||
"""Set scalar _original only when not already present (re-run safe)."""
|
||||
if not PRESERVEORIGINAL:
|
||||
|
|
@ -539,6 +585,15 @@ def _choice_source(cmd, index: int) -> str:
|
|||
return ""
|
||||
|
||||
|
||||
def _choice_current(cmd, index: int) -> str:
|
||||
"""Return the current visible choice without consulting _original."""
|
||||
params = cmd.get("parameters") or [[]]
|
||||
choices = params[0] if params else []
|
||||
if isinstance(choices, list) and index < len(choices) and choices[index] is not None:
|
||||
return str(choices[index])
|
||||
return ""
|
||||
|
||||
|
||||
def _apply_choice_original(cmd, index: int, raw_source: str) -> None:
|
||||
"""Set _original[index] for code 102 only when that slot is empty."""
|
||||
if not PRESERVEORIGINAL:
|
||||
|
|
@ -579,6 +634,17 @@ def _122_inner_source(cmd) -> str | None:
|
|||
return None
|
||||
|
||||
|
||||
def _122_inner_current(cmd) -> str | None:
|
||||
"""Extract the current quoted value for code 122 without consulting _original."""
|
||||
params = cmd.get("parameters") or []
|
||||
if len(params) <= 4 or not isinstance(params[4], str):
|
||||
return None
|
||||
matched = re.search(r"['\"`](.*)['\"`]", params[4])
|
||||
if matched and matched.group(1).strip():
|
||||
return matched.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def _101_name_source(cmd, is_var: bool) -> str:
|
||||
"""Speaker name field for code 101: _original or parameters[4]/[0]."""
|
||||
orig = _scalar_original(cmd)
|
||||
|
|
@ -592,6 +658,16 @@ def _101_name_source(cmd, is_var: bool) -> str:
|
|||
return ""
|
||||
|
||||
|
||||
def _101_name_current(cmd, is_var: bool) -> str:
|
||||
"""Return the current code-101 name field without consulting _original."""
|
||||
params = cmd.get("parameters") or []
|
||||
if is_var and len(params) > 0 and params[0] is not None:
|
||||
return str(params[0])
|
||||
if not is_var and len(params) > 4 and params[4] is not None:
|
||||
return str(params[4])
|
||||
return ""
|
||||
|
||||
|
||||
def _101_speaker_name(raw_name: str) -> str:
|
||||
"""Return the translatable name inside a code-101 display wrapper.
|
||||
|
||||
|
|
@ -628,6 +704,13 @@ def _entry_field_source(entry, field: str) -> str:
|
|||
return ""
|
||||
|
||||
|
||||
def _entry_field_needs_translation(entry, field: str) -> bool:
|
||||
"""Decide skip status from the current value, not preserved source text."""
|
||||
if not isinstance(entry, dict):
|
||||
return False
|
||||
return _text_needs_translation(entry.get(field))
|
||||
|
||||
|
||||
def _apply_entry_field_original(entry, field: str, raw: str) -> None:
|
||||
"""Set _original[field] only when empty and raw contains Japanese."""
|
||||
if not PRESERVEORIGINAL:
|
||||
|
|
@ -1688,12 +1771,16 @@ def searchNames(data, pbar, context, filename):
|
|||
totalTokens = [0, 0]
|
||||
nameList = []
|
||||
nameSourceList = []
|
||||
nameIndexList = []
|
||||
profileList = []
|
||||
profileSourceList = []
|
||||
profileIndexList = []
|
||||
nicknameList = []
|
||||
nicknameSourceList = []
|
||||
nicknameIndexList = []
|
||||
descriptionList = []
|
||||
descriptionSourceList = []
|
||||
descriptionIndexList = []
|
||||
# For Skills: collect messages across all entries for batch translation
|
||||
messagesList = [] # List of tuples: (entry_idx, message_field, message_text, needs_taro)
|
||||
# Collect name mappings for vocab per run
|
||||
|
|
@ -1828,8 +1915,7 @@ def searchNames(data, pbar, context, filename):
|
|||
msg_field = f"message{msg_num}"
|
||||
if msg_field in entry and entry[msg_field]:
|
||||
msg_text = _entry_field_source(entry, msg_field)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, msg_text):
|
||||
if not _entry_field_needs_translation(entry, msg_field):
|
||||
continue
|
||||
needs_taro = len(msg_text) > 0 and msg_text[0] in ["は", "を", "の", "に", "が"]
|
||||
if needs_taro:
|
||||
|
|
@ -1878,69 +1964,66 @@ def searchNames(data, pbar, context, filename):
|
|||
if context in "Actors":
|
||||
if len(nameList) < BATCHSIZE:
|
||||
name_src = _entry_field_source(data[i], "name")
|
||||
if name_src != "":
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, name_src)):
|
||||
nameList.append(name_src)
|
||||
nameSourceList.append(name_src)
|
||||
if name_src != "" and _entry_field_needs_translation(data[i], "name"):
|
||||
nameList.append(name_src)
|
||||
nameSourceList.append(name_src)
|
||||
nameIndexList.append(i)
|
||||
if "nickname" in data[i]:
|
||||
nick_src = _entry_field_source(data[i], "nickname")
|
||||
if nick_src:
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, nick_src)):
|
||||
nicknameList.append(nick_src)
|
||||
nicknameSourceList.append(nick_src)
|
||||
if nick_src and _entry_field_needs_translation(data[i], "nickname"):
|
||||
nicknameList.append(nick_src)
|
||||
nicknameSourceList.append(nick_src)
|
||||
nicknameIndexList.append(i)
|
||||
if "profile" in data[i]:
|
||||
prof_src = _entry_field_source(data[i], "profile")
|
||||
if prof_src:
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, prof_src)):
|
||||
profileList.append(prof_src.replace("\n", " "))
|
||||
profileSourceList.append(prof_src)
|
||||
if prof_src and _entry_field_needs_translation(data[i], "profile"):
|
||||
profileList.append(prof_src.replace("\n", " "))
|
||||
profileSourceList.append(prof_src)
|
||||
profileIndexList.append(i)
|
||||
i += 1
|
||||
else:
|
||||
batchFull = True
|
||||
if context in ["Armors", "Weapons", "Items"]:
|
||||
if len(nameList) < BATCHSIZE:
|
||||
name_src = _entry_field_source(data[i], "name")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, name_src)):
|
||||
if _entry_field_needs_translation(data[i], "name"):
|
||||
nameList.append(name_src)
|
||||
nameSourceList.append(name_src)
|
||||
nameIndexList.append(i)
|
||||
if "description" in data[i]:
|
||||
desc_src = _entry_field_source(data[i], "description")
|
||||
if desc_src != "":
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, desc_src)):
|
||||
if _entry_field_needs_translation(data[i], "description"):
|
||||
descriptionList.append(desc_src.replace("\n", " "))
|
||||
descriptionSourceList.append(desc_src)
|
||||
descriptionIndexList.append(i)
|
||||
i += 1
|
||||
else:
|
||||
batchFull = True
|
||||
if context in ["Skills"]:
|
||||
if len(nameList) < BATCHSIZE:
|
||||
name_src = _entry_field_source(data[i], "name")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, name_src)):
|
||||
if _entry_field_needs_translation(data[i], "name"):
|
||||
nameList.append(name_src)
|
||||
nameSourceList.append(name_src)
|
||||
nameIndexList.append(i)
|
||||
if "description" in data[i]:
|
||||
desc_src = _entry_field_source(data[i], "description")
|
||||
if desc_src:
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, desc_src)):
|
||||
if _entry_field_needs_translation(data[i], "description"):
|
||||
descriptionList.append(desc_src.replace("\n", " "))
|
||||
descriptionSourceList.append(desc_src)
|
||||
descriptionIndexList.append(i)
|
||||
i += 1
|
||||
else:
|
||||
batchFull = True
|
||||
if context in ["Enemies", "Classes", "MapInfos"]:
|
||||
if len(nameList) < BATCHSIZE:
|
||||
name_src = _entry_field_source(data[i], "name")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, name_src)):
|
||||
if _entry_field_needs_translation(data[i], "name"):
|
||||
nameList.append(name_src)
|
||||
nameSourceList.append(name_src)
|
||||
nameIndexList.append(i)
|
||||
i += 1
|
||||
else:
|
||||
batchFull = True
|
||||
|
|
@ -1952,16 +2035,19 @@ def searchNames(data, pbar, context, filename):
|
|||
# Track tokens for this batch
|
||||
batchTokens = [0, 0]
|
||||
# Name
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None and nameList:
|
||||
pbar.refresh()
|
||||
translatedNameBatch = []
|
||||
if nameList:
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None:
|
||||
pbar.refresh()
|
||||
|
||||
# Nickname
|
||||
translatedNicknameBatch = []
|
||||
if nicknameList:
|
||||
response = translateAI(nicknameList, newContext)
|
||||
translatedNicknameBatch = response[0]
|
||||
|
|
@ -1973,6 +2059,7 @@ def searchNames(data, pbar, context, filename):
|
|||
pbar.refresh()
|
||||
|
||||
# Profile
|
||||
translatedProfileBatch = []
|
||||
if profileList:
|
||||
response = translateAI(profileList, "")
|
||||
translatedProfileBatch = response[0]
|
||||
|
|
@ -1983,43 +2070,42 @@ def searchNames(data, pbar, context, filename):
|
|||
if pbar is not None:
|
||||
pbar.refresh()
|
||||
|
||||
# Set Data
|
||||
if len(nameList) == len(translatedNameBatch):
|
||||
j = k
|
||||
while j < i:
|
||||
# Empty Data
|
||||
if data[j] is None or data[j]["name"] == "":
|
||||
j += 1
|
||||
continue
|
||||
else:
|
||||
# Get Text
|
||||
if data[j]["name"] != "" and nameSourceList:
|
||||
with open("log/translations.txt", "a", encoding="utf-8") as file:
|
||||
file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n')
|
||||
# Actors are excluded from vocab updates
|
||||
raw_name = nameSourceList.pop(0)
|
||||
data[j]["name"] = translatedNameBatch.pop(0)
|
||||
_apply_entry_field_original(data[j], "name", raw_name)
|
||||
if "nickname" in data[j] and data[j]["nickname"] and nicknameSourceList:
|
||||
raw_nick = nicknameSourceList.pop(0)
|
||||
data[j]["nickname"] = translatedNicknameBatch.pop(0)
|
||||
_apply_entry_field_original(data[j], "nickname", raw_nick)
|
||||
if "profile" in data[j] and data[j]["profile"] and profileSourceList:
|
||||
raw_prof = profileSourceList.pop(0)
|
||||
data[j]["profile"] = dazedwrap.wrapText(translatedProfileBatch.pop(0), LISTWIDTH)
|
||||
_apply_entry_field_original(data[j], "profile", raw_prof)
|
||||
actor_lengths_match = (
|
||||
len(nameList) == len(translatedNameBatch)
|
||||
and len(nicknameList) == len(translatedNicknameBatch)
|
||||
and len(profileList) == len(translatedProfileBatch)
|
||||
)
|
||||
if actor_lengths_match:
|
||||
with open("log/translations.txt", "a", encoding="utf-8") as file:
|
||||
for idx, raw_name, translated in zip(
|
||||
nameIndexList, nameSourceList, translatedNameBatch
|
||||
):
|
||||
file.write(f'{data[idx]["name"]} ({translated})\n')
|
||||
data[idx]["name"] = translated
|
||||
_apply_entry_field_original(data[idx], "name", raw_name)
|
||||
for idx, raw_nick, translated in zip(
|
||||
nicknameIndexList, nicknameSourceList, translatedNicknameBatch
|
||||
):
|
||||
data[idx]["nickname"] = translated
|
||||
_apply_entry_field_original(data[idx], "nickname", raw_nick)
|
||||
for idx, raw_prof, translated in zip(
|
||||
profileIndexList, profileSourceList, translatedProfileBatch
|
||||
):
|
||||
data[idx]["profile"] = dazedwrap.wrapText(translated, LISTWIDTH)
|
||||
_apply_entry_field_original(data[idx], "profile", raw_prof)
|
||||
|
||||
# If Batch is empty. Move on.
|
||||
if len(translatedNameBatch) == 0:
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
profileList.clear()
|
||||
profileSourceList.clear()
|
||||
nicknameList.clear()
|
||||
nicknameSourceList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
j += 1
|
||||
j = i
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
nameIndexList.clear()
|
||||
profileList.clear()
|
||||
profileSourceList.clear()
|
||||
profileIndexList.clear()
|
||||
nicknameList.clear()
|
||||
nicknameSourceList.clear()
|
||||
nicknameIndexList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
# Persist after applying this batch only if we actually translated something in this batch
|
||||
checkSave(data, filename, batchTokens)
|
||||
else:
|
||||
|
|
@ -2029,16 +2115,19 @@ def searchNames(data, pbar, context, filename):
|
|||
# Track tokens for this batch
|
||||
batchTokens = [0, 0]
|
||||
# Name
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None and nameList:
|
||||
pbar.refresh()
|
||||
translatedNameBatch = []
|
||||
if nameList:
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None:
|
||||
pbar.refresh()
|
||||
|
||||
# Description
|
||||
translatedDescriptionBatch = []
|
||||
if descriptionList:
|
||||
response = translateAI(
|
||||
descriptionList,
|
||||
|
|
@ -2053,46 +2142,37 @@ def searchNames(data, pbar, context, filename):
|
|||
if pbar is not None:
|
||||
pbar.refresh()
|
||||
|
||||
# Set Data
|
||||
if len(nameList) == len(translatedNameBatch):
|
||||
j = k
|
||||
db_lengths_match = (
|
||||
len(nameList) == len(translatedNameBatch)
|
||||
and len(descriptionList) == len(translatedDescriptionBatch)
|
||||
)
|
||||
if db_lengths_match:
|
||||
with open("log/translations.txt", "a", encoding="utf-8") as file:
|
||||
while j < i:
|
||||
# Empty Data
|
||||
if data[j] is None or data[j]["name"] == "":
|
||||
j += 1
|
||||
continue
|
||||
else:
|
||||
# Get Text
|
||||
raw_name = nameSourceList.pop(0) if nameSourceList else _entry_field_source(data[j], "name")
|
||||
file.write(f"{data[j]['name']} ({translatedNameBatch[0]})\n")
|
||||
if vocab_enabled:
|
||||
try:
|
||||
vocab_pairs.append((raw_name, translatedNameBatch[0]))
|
||||
except Exception:
|
||||
pass
|
||||
data[j]["name"] = translatedNameBatch.pop(0)
|
||||
_apply_entry_field_original(data[j], "name", raw_name)
|
||||
desc_src = _entry_field_source(data[j], "description") if "description" in data[j] else ""
|
||||
if (
|
||||
desc_src
|
||||
and not (IGNORETLTEXT and not re.search(LANGREGEX, desc_src))
|
||||
and descriptionSourceList
|
||||
):
|
||||
raw_desc = descriptionSourceList.pop(0)
|
||||
wrapped = dazedwrap.wrapText(translatedDescriptionBatch.pop(0), LISTWIDTH)
|
||||
data[j]["description"] = wrapped
|
||||
_apply_entry_field_original(data[j], "description", raw_desc)
|
||||
for idx, raw_name, translated in zip(
|
||||
nameIndexList, nameSourceList, translatedNameBatch
|
||||
):
|
||||
file.write(f"{data[idx]['name']} ({translated})\n")
|
||||
if vocab_enabled:
|
||||
vocab_pairs.append((raw_name, translated))
|
||||
data[idx]["name"] = translated
|
||||
_apply_entry_field_original(data[idx], "name", raw_name)
|
||||
for idx, raw_desc, translated in zip(
|
||||
descriptionIndexList,
|
||||
descriptionSourceList,
|
||||
translatedDescriptionBatch,
|
||||
):
|
||||
data[idx]["description"] = dazedwrap.wrapText(translated, LISTWIDTH)
|
||||
_apply_entry_field_original(data[idx], "description", raw_desc)
|
||||
|
||||
# If Batch is empty. Move on.
|
||||
if len(translatedNameBatch) == 0:
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
descriptionList.clear()
|
||||
descriptionSourceList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
j += 1
|
||||
j = i
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
nameIndexList.clear()
|
||||
descriptionList.clear()
|
||||
descriptionSourceList.clear()
|
||||
descriptionIndexList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
# Persist after applying this batch only if we actually translated something in this batch
|
||||
checkSave(data, filename, batchTokens)
|
||||
else:
|
||||
|
|
@ -2100,43 +2180,35 @@ def searchNames(data, pbar, context, filename):
|
|||
if context in ["Enemies", "Classes", "MapInfos"]:
|
||||
# Track tokens for this batch
|
||||
batchTokens = [0, 0]
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None and nameList:
|
||||
pbar.refresh()
|
||||
translatedNameBatch = []
|
||||
if nameList:
|
||||
response = translateAI(nameList, newContext)
|
||||
translatedNameBatch = response[0]
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
batchTokens[0] += response[1][0]
|
||||
batchTokens[1] += response[1][1]
|
||||
if pbar is not None:
|
||||
pbar.refresh()
|
||||
|
||||
# Set Data
|
||||
if len(nameList) == len(translatedNameBatch):
|
||||
j = k
|
||||
while j < i:
|
||||
# Empty Data
|
||||
if data[j] is None or data[j]["name"] == "":
|
||||
j += 1
|
||||
continue
|
||||
else:
|
||||
raw_name = nameSourceList.pop(0) if nameSourceList else _entry_field_source(data[j], "name")
|
||||
with open("log/translations.txt", "a", encoding="utf-8") as file:
|
||||
file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n')
|
||||
# Get Text
|
||||
with open("log/translations.txt", "a", encoding="utf-8") as file:
|
||||
for idx, raw_name, translated in zip(
|
||||
nameIndexList, nameSourceList, translatedNameBatch
|
||||
):
|
||||
file.write(f'{data[idx]["name"]} ({translated})\n')
|
||||
if vocab_enabled:
|
||||
try:
|
||||
vocab_pairs.append((raw_name, translatedNameBatch[0]))
|
||||
except Exception:
|
||||
pass
|
||||
data[j]["name"] = translatedNameBatch.pop(0)
|
||||
_apply_entry_field_original(data[j], "name", raw_name)
|
||||
vocab_pairs.append((raw_name, translated))
|
||||
data[idx]["name"] = translated
|
||||
_apply_entry_field_original(data[idx], "name", raw_name)
|
||||
|
||||
# If Batch is empty. Move on.
|
||||
if len(translatedNameBatch) == 0:
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
j += 1
|
||||
j = i
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
nameIndexList.clear()
|
||||
batchFull = False
|
||||
filling = False
|
||||
# Persist after applying this batch only if we actually translated something in this batch
|
||||
checkSave(data, filename, batchTokens)
|
||||
else:
|
||||
|
|
@ -2147,12 +2219,16 @@ def searchNames(data, pbar, context, filename):
|
|||
MISMATCH.append(nameList)
|
||||
nameList.clear()
|
||||
nameSourceList.clear()
|
||||
nameIndexList.clear()
|
||||
profileList.clear()
|
||||
profileSourceList.clear()
|
||||
profileIndexList.clear()
|
||||
nicknameList.clear()
|
||||
nicknameSourceList.clear()
|
||||
nicknameIndexList.clear()
|
||||
descriptionList.clear()
|
||||
descriptionSourceList.clear()
|
||||
descriptionIndexList.clear()
|
||||
filling = False
|
||||
mismatch = False
|
||||
batchFull = False
|
||||
|
|
@ -2231,6 +2307,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Declare Varss
|
||||
currentGroup = []
|
||||
sourceGroup = []
|
||||
currentSourceGroup = []
|
||||
nametag = ""
|
||||
|
||||
## Event Code: 401 Show Text
|
||||
|
|
@ -2244,6 +2321,10 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
|
||||
# Grab String
|
||||
if len(codeList[i]["parameters"]) > 0:
|
||||
previewEnd = _text_group_end(codeList, i, (401, 405, -1))
|
||||
if not _text_needs_translation(_group_current(codeList, i, previewEnd)):
|
||||
i = previewEnd + 1
|
||||
continue
|
||||
jaString = codeList[i]["parameters"][0]
|
||||
oldjaString = _param_source(codeList[i], 0)
|
||||
speakerWork = oldjaString
|
||||
|
|
@ -2340,6 +2421,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Test Speaker
|
||||
if (
|
||||
len(speakerWork) < 40
|
||||
and i + 1 < len(codeList)
|
||||
and "code" in codeList[i + 1]
|
||||
and codeList[i + 1]["code"] in [401, 405, -1]
|
||||
and len(codeList[i + 1]["parameters"]) > 0
|
||||
|
|
@ -2442,6 +2524,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
currentGroup.append(jaString)
|
||||
anchor_has_orig = _scalar_original(codeList[groupStart]) is not None
|
||||
sourceGroup.append(_param_source(codeList[i], 0))
|
||||
currentSourceGroup.append(_param_current(codeList[i], 0))
|
||||
|
||||
# Join Up 401's into single string
|
||||
if len(codeList) > i + 1:
|
||||
|
|
@ -2455,6 +2538,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
jaString = codeList[i]["parameters"][0]
|
||||
if jaString.strip():
|
||||
currentGroup.append(jaString)
|
||||
currentSourceGroup.append(_param_current(codeList[i], 0))
|
||||
if not anchor_has_orig:
|
||||
sourceGroup.append(_param_source(codeList[i], 0))
|
||||
|
||||
|
|
@ -2465,8 +2549,9 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Format String
|
||||
if len(currentGroup) > 0:
|
||||
rawSource = _group_raw_source(codeList, groupStart, sourceGroup)
|
||||
# Skip empty or IGNORETLTEXT groups; clear speaker since this continue bypasses the normal reset below.
|
||||
if (not rawSource.strip()) or (IGNORETLTEXT and not re.search(LANGREGEX, rawSource)):
|
||||
currentSource = "\n".join(currentSourceGroup)
|
||||
# Skip status is based on the live text, not preserved Japanese source.
|
||||
if not rawSource.strip() or not _text_needs_translation(currentSource):
|
||||
speaker = ""
|
||||
i += 1
|
||||
continue
|
||||
|
|
@ -2576,6 +2661,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
nametag = ""
|
||||
currentGroup = []
|
||||
sourceGroup = []
|
||||
currentSourceGroup = []
|
||||
syncIndex = i + 1
|
||||
|
||||
# Keep textHistory list at length maxHistory
|
||||
|
|
@ -2721,8 +2807,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Set String
|
||||
innerSource = _122_inner_source(codeList[i])
|
||||
if innerSource is not None and innerSource.strip():
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, innerSource):
|
||||
if not _text_needs_translation(_122_inner_current(codeList[i])):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
|
|
@ -3157,7 +3242,8 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Get Speaker
|
||||
rawName = _101_name_source(codeList[i], isVar)
|
||||
sourceName = _101_speaker_name(rawName)
|
||||
if sourceName:
|
||||
currentName = _101_speaker_name(_101_name_current(codeList[i], isVar))
|
||||
if sourceName and _text_needs_translation(currentName):
|
||||
response = getSpeaker(sourceName)
|
||||
totalTokens[0] += response[1][0]
|
||||
totalTokens[1] += response[1][1]
|
||||
|
|
@ -3603,20 +3689,17 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
groupStart408 = i
|
||||
j = i
|
||||
source408Parts = []
|
||||
current408Parts = []
|
||||
rawSource = _param_source(codeList[i], 0)
|
||||
ojaString = rawSource
|
||||
anchor408HasOrig = _scalar_original(codeList[groupStart408]) is not None
|
||||
source408Parts.append(rawSource)
|
||||
current408Parts.append(_param_current(codeList[i], 0))
|
||||
|
||||
if not rawSource.strip():
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, rawSource):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Join Up 408's into single string
|
||||
if len(codeList) > i + 1 and JOIN408 is True:
|
||||
while codeList[i + 1]["code"] in [408] and len(codeList[i]["parameters"]) > 0 and len(codeList[i + 1]["parameters"]) > 0 and not re.match(r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", codeList[i+1]["parameters"][0]):
|
||||
|
|
@ -3627,6 +3710,7 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
j = i
|
||||
|
||||
lineSource = _param_source(codeList[i], 0)
|
||||
current408Parts.append(_param_current(codeList[i], 0))
|
||||
if lineSource.strip() and not anchor408HasOrig:
|
||||
source408Parts.append(lineSource)
|
||||
|
||||
|
|
@ -3634,6 +3718,9 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
break
|
||||
|
||||
rawSource = _group_raw_source(codeList, groupStart408, source408Parts)
|
||||
if not _text_needs_translation("\n".join(current408Parts)):
|
||||
i += 1
|
||||
continue
|
||||
ojaString = rawSource
|
||||
jaString = rawSource.replace("\n", " ")
|
||||
|
||||
|
|
@ -3950,14 +4037,14 @@ def searchCodes(page, pbar, jobList, filename):
|
|||
# Process each string in the parameters list
|
||||
for choice in range(len(codeList[i]["parameters"][0])):
|
||||
rawSource = _choice_source(codeList[i], choice)
|
||||
currentChoice = _choice_current(codeList[i], choice)
|
||||
jaString = rawSource.replace(" 。", ".")
|
||||
|
||||
# Avoid Empty Strings
|
||||
if not jaString.strip():
|
||||
continue
|
||||
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, rawSource):
|
||||
if not _text_needs_translation(currentChoice):
|
||||
continue
|
||||
|
||||
# If and En Statements
|
||||
|
|
@ -4326,16 +4413,14 @@ def searchSS(state, pbar):
|
|||
# Name
|
||||
if "name" in state and state["name"]:
|
||||
name_src = _entry_field_source(state, "name")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, name_src)):
|
||||
if _entry_field_needs_translation(state, "name"):
|
||||
batch_texts.append(name_src)
|
||||
batch_map.append(("name", "name", False, name_src))
|
||||
|
||||
# Description
|
||||
if "description" in state and state["description"]:
|
||||
desc_src = _entry_field_source(state, "description")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, desc_src)):
|
||||
if _entry_field_needs_translation(state, "description"):
|
||||
batch_texts.append(desc_src)
|
||||
batch_map.append(("description", "description", False, desc_src))
|
||||
|
||||
|
|
@ -4343,8 +4428,7 @@ def searchSS(state, pbar):
|
|||
for msg_field in ["message1", "message2", "message3", "message4"]:
|
||||
if msg_field in state and state[msg_field]:
|
||||
msg_text = _entry_field_source(state, msg_field)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, msg_text):
|
||||
if not _entry_field_needs_translation(state, msg_field):
|
||||
continue
|
||||
needs_taro = len(msg_text) > 0 and msg_text[0] in ["は", "を", "の", "に", "が"]
|
||||
if needs_taro:
|
||||
|
|
@ -4478,8 +4562,7 @@ def searchSystem(data, pbar):
|
|||
|
||||
# Title - batch as a single-item list
|
||||
title_src = _system_scalar_source(data, "gameTitle")
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if not (IGNORETLTEXT and not re.search(LANGREGEX, title_src)):
|
||||
if _text_needs_translation(data.get("gameTitle")):
|
||||
response = translateAI(
|
||||
[title_src],
|
||||
ctx("database.game_title"),
|
||||
|
|
@ -4502,8 +4585,7 @@ def searchSystem(data, pbar):
|
|||
for i in range(len(termList)):
|
||||
if termList[i] is not None:
|
||||
src = _system_terms_source(data, term, i)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(termList[i]):
|
||||
continue
|
||||
term_values.append(src)
|
||||
term_indices.append(i)
|
||||
|
|
@ -4528,8 +4610,7 @@ def searchSystem(data, pbar):
|
|||
armor_sources = []
|
||||
for i in range(len(data["armorTypes"])):
|
||||
src = _system_list_source(data, "armorTypes", i)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and (not src or not re.search(LANGREGEX, src)):
|
||||
if not _text_needs_translation(data["armorTypes"][i]):
|
||||
continue
|
||||
armor_values.append(src)
|
||||
armor_indices.append(i)
|
||||
|
|
@ -4555,8 +4636,7 @@ def searchSystem(data, pbar):
|
|||
skill_sources = []
|
||||
for i in range(len(data["skillTypes"])):
|
||||
src = _system_list_source(data, "skillTypes", i)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and (not src or not re.search(LANGREGEX, src)):
|
||||
if not _text_needs_translation(data["skillTypes"][i]):
|
||||
continue
|
||||
skill_values.append(src)
|
||||
skill_indices.append(i)
|
||||
|
|
@ -4582,8 +4662,7 @@ def searchSystem(data, pbar):
|
|||
equip_sources = []
|
||||
for i in range(len(data.get("equipTypes", []) or [])):
|
||||
src = _system_list_source(data, "equipTypes", i)
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and (not src or not re.search(LANGREGEX, src)):
|
||||
if not _text_needs_translation(data["equipTypes"][i]):
|
||||
continue
|
||||
equip_values.append(src)
|
||||
equip_indices.append(i)
|
||||
|
|
@ -4609,10 +4688,7 @@ def searchSystem(data, pbar):
|
|||
element_sources = []
|
||||
for i in range(len(data["elements"])):
|
||||
src = _system_list_source(data, "elements", i)
|
||||
if not src:
|
||||
continue
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(data["elements"][i]):
|
||||
continue
|
||||
element_values.append(src)
|
||||
element_indices.append(i)
|
||||
|
|
@ -4639,10 +4715,7 @@ def searchSystem(data, pbar):
|
|||
weapon_sources = []
|
||||
for i in range(len(data["weaponTypes"])):
|
||||
src = _system_list_source(data, "weaponTypes", i)
|
||||
if not src:
|
||||
continue
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(data["weaponTypes"][i]):
|
||||
continue
|
||||
weapon_values.append(src)
|
||||
weapon_indices.append(i)
|
||||
|
|
@ -4671,8 +4744,7 @@ def searchSystem(data, pbar):
|
|||
for idx, val in enumerate(data["variables"]):
|
||||
src = _system_list_source(data, "variables", idx)
|
||||
if isinstance(val, str) and src.strip():
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(val):
|
||||
continue
|
||||
var_indices.append(idx)
|
||||
var_values.append(src)
|
||||
|
|
@ -4701,8 +4773,7 @@ def searchSystem(data, pbar):
|
|||
for idx, val in enumerate(data["switches"]):
|
||||
src = _system_list_source(data, "switches", idx)
|
||||
if isinstance(val, str) and src.strip():
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(val):
|
||||
continue
|
||||
switch_indices.append(idx)
|
||||
switch_values.append(src)
|
||||
|
|
@ -4732,8 +4803,7 @@ def searchSystem(data, pbar):
|
|||
for key, value in messages.items():
|
||||
src = _system_terms_message_source(data, key)
|
||||
if isinstance(value, str) and src.strip():
|
||||
# Skip if IGNORETLTEXT is enabled and no Japanese text
|
||||
if IGNORETLTEXT and not re.search(LANGREGEX, src):
|
||||
if not _text_needs_translation(value):
|
||||
continue
|
||||
msg_keys.append(key)
|
||||
msg_values.append(src)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,14 @@ def _has_japanese(s: str) -> bool:
|
|||
return bool(re.search(LANGREGEX, s or ""))
|
||||
|
||||
|
||||
def _run_search_names(data, context, filename, *, preserve_original=True):
|
||||
def _run_search_names(
|
||||
data,
|
||||
context,
|
||||
filename,
|
||||
*,
|
||||
preserve_original=True,
|
||||
ignore_tl_text=False,
|
||||
):
|
||||
captured = []
|
||||
|
||||
def translate(text, history, batch=False):
|
||||
|
|
@ -47,9 +54,11 @@ def _run_search_names(data, context, filename, *, preserve_original=True):
|
|||
orig_t = mvmz.translateAI
|
||||
orig_vocab = mvmz.update_vocab_section
|
||||
orig_preserve = mvmz.PRESERVEORIGINAL
|
||||
orig_ignore = mvmz.IGNORETLTEXT
|
||||
mvmz.translateAI = translate
|
||||
mvmz.update_vocab_section = lambda *args, **kwargs: None
|
||||
mvmz.PRESERVEORIGINAL = preserve_original
|
||||
mvmz.IGNORETLTEXT = ignore_tl_text
|
||||
try:
|
||||
data_copy = copy.deepcopy(data)
|
||||
mvmz.searchNames(data_copy, None, context, filename)
|
||||
|
|
@ -58,9 +67,10 @@ def _run_search_names(data, context, filename, *, preserve_original=True):
|
|||
mvmz.translateAI = orig_t
|
||||
mvmz.update_vocab_section = orig_vocab
|
||||
mvmz.PRESERVEORIGINAL = orig_preserve
|
||||
mvmz.IGNORETLTEXT = orig_ignore
|
||||
|
||||
|
||||
def _run_search_ss(state):
|
||||
def _run_search_ss(state, *, ignore_tl_text=False):
|
||||
captured = []
|
||||
|
||||
def translate(text, history, batch=False):
|
||||
|
|
@ -69,8 +79,10 @@ def _run_search_ss(state):
|
|||
|
||||
orig_t = mvmz.translateAI
|
||||
orig_preserve = mvmz.PRESERVEORIGINAL
|
||||
orig_ignore = mvmz.IGNORETLTEXT
|
||||
mvmz.translateAI = translate
|
||||
mvmz.PRESERVEORIGINAL = True
|
||||
mvmz.IGNORETLTEXT = ignore_tl_text
|
||||
try:
|
||||
state_copy = copy.deepcopy(state)
|
||||
mvmz.searchSS(state_copy, None)
|
||||
|
|
@ -78,9 +90,10 @@ def _run_search_ss(state):
|
|||
finally:
|
||||
mvmz.translateAI = orig_t
|
||||
mvmz.PRESERVEORIGINAL = orig_preserve
|
||||
mvmz.IGNORETLTEXT = orig_ignore
|
||||
|
||||
|
||||
def _run_search_system(data):
|
||||
def _run_search_system(data, *, ignore_tl_text=False):
|
||||
captured = []
|
||||
|
||||
def translate(text, history, batch=False):
|
||||
|
|
@ -89,8 +102,10 @@ def _run_search_system(data):
|
|||
|
||||
orig_t = mvmz.translateAI
|
||||
orig_preserve = mvmz.PRESERVEORIGINAL
|
||||
orig_ignore = mvmz.IGNORETLTEXT
|
||||
mvmz.translateAI = translate
|
||||
mvmz.PRESERVEORIGINAL = True
|
||||
mvmz.IGNORETLTEXT = ignore_tl_text
|
||||
try:
|
||||
data_copy = copy.deepcopy(data)
|
||||
mvmz.searchSystem(data_copy, None)
|
||||
|
|
@ -98,6 +113,7 @@ def _run_search_system(data):
|
|||
finally:
|
||||
mvmz.translateAI = orig_t
|
||||
mvmz.PRESERVEORIGINAL = orig_preserve
|
||||
mvmz.IGNORETLTEXT = orig_ignore
|
||||
|
||||
|
||||
def _assert_batches_japanese(captured):
|
||||
|
|
@ -132,6 +148,28 @@ class TestActorsOriginal(unittest.TestCase):
|
|||
self.assertEqual(result2[1]["_original"], orig_snapshot)
|
||||
_assert_batches_japanese(captured2)
|
||||
|
||||
def test_skip_translated_uses_current_fields_not_original(self):
|
||||
data = json.loads((FIXTURES / "Actors_original_fixture.json").read_text(encoding="utf-8"))
|
||||
translated, _ = _run_search_names(data, "Actors", "Actors.json")
|
||||
visible_before = {
|
||||
field: translated[1][field]
|
||||
for field in ("name", "nickname", "profile")
|
||||
}
|
||||
|
||||
result, captured = _run_search_names(
|
||||
translated,
|
||||
"Actors",
|
||||
"Actors.json",
|
||||
ignore_tl_text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(captured, [])
|
||||
self.assertEqual(
|
||||
{field: result[1][field] for field in visible_before},
|
||||
visible_before,
|
||||
)
|
||||
self.assertTrue(_has_japanese(result[1]["_original"]["name"]))
|
||||
|
||||
|
||||
class TestItemsOriginal(unittest.TestCase):
|
||||
def test_first_pass_writes_original(self):
|
||||
|
|
@ -151,6 +189,46 @@ class TestItemsOriginal(unittest.TestCase):
|
|||
self.assertEqual(result2[1]["_original"], orig_snapshot)
|
||||
_assert_batches_japanese(captured2)
|
||||
|
||||
def test_skip_translated_uses_current_fields_not_original(self):
|
||||
data = json.loads((FIXTURES / "Items_original_fixture.json").read_text(encoding="utf-8"))
|
||||
translated, _ = _run_search_names(data, "Items", "Items.json")
|
||||
visible_before = copy.deepcopy(translated)
|
||||
|
||||
result, captured = _run_search_names(
|
||||
translated,
|
||||
"Items",
|
||||
"Items.json",
|
||||
ignore_tl_text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(captured, [])
|
||||
self.assertEqual(result, visible_before)
|
||||
|
||||
def test_skip_translated_keeps_mixed_entry_indices_aligned(self):
|
||||
data = [
|
||||
None,
|
||||
{
|
||||
"name": "Potion",
|
||||
"description": "Restores health.",
|
||||
"note": "",
|
||||
"_original": {"name": "ポーション", "description": "体力を回復する。"},
|
||||
},
|
||||
{"name": "剣", "description": "強い武器。", "note": ""},
|
||||
]
|
||||
|
||||
result, captured = _run_search_names(
|
||||
data,
|
||||
"Items",
|
||||
"Items.json",
|
||||
ignore_tl_text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(result[1]["name"], "Potion")
|
||||
self.assertEqual(result[1]["description"], "Restores health.")
|
||||
self.assertEqual(result[2]["name"], "EN_TRANSLATED")
|
||||
self.assertEqual(result[2]["description"], "EN_TRANSLATED")
|
||||
self.assertEqual(captured, [["剣"], ["強い武器。"]])
|
||||
|
||||
|
||||
class TestSkillsOriginal(unittest.TestCase):
|
||||
def test_first_pass_writes_original(self):
|
||||
|
|
@ -190,6 +268,15 @@ class TestStatesOriginal(unittest.TestCase):
|
|||
self.assertEqual(result2["_original"], orig_snapshot)
|
||||
_assert_batches_japanese(captured2)
|
||||
|
||||
def test_skip_translated_uses_current_fields_not_original(self):
|
||||
data = json.loads((FIXTURES / "States_original_fixture.json").read_text(encoding="utf-8"))
|
||||
translated, _ = _run_search_ss(data[MANIFEST["states"]["entry_index"]])
|
||||
|
||||
result, captured = _run_search_ss(translated, ignore_tl_text=True)
|
||||
|
||||
self.assertEqual(captured, [])
|
||||
self.assertEqual(result, translated)
|
||||
|
||||
|
||||
class TestSystemOriginal(unittest.TestCase):
|
||||
def test_first_pass_writes_original(self):
|
||||
|
|
@ -209,6 +296,15 @@ class TestSystemOriginal(unittest.TestCase):
|
|||
self.assertEqual(result2["_original"], orig_snapshot)
|
||||
_assert_batches_japanese(captured2)
|
||||
|
||||
def test_skip_translated_uses_current_fields_not_original(self):
|
||||
data = json.loads((FIXTURES / "System_original_fixture.json").read_text(encoding="utf-8"))
|
||||
translated, _ = _run_search_system(data)
|
||||
|
||||
result, captured = _run_search_system(translated, ignore_tl_text=True)
|
||||
|
||||
self.assertEqual(captured, [])
|
||||
self.assertEqual(result, translated)
|
||||
|
||||
|
||||
class TestPreserveOriginalDisabled(unittest.TestCase):
|
||||
def test_db_preserve_disabled_skips_original(self):
|
||||
|
|
|
|||
|
|
@ -112,7 +112,13 @@ def _resolve_case_command(page_list, entry, marked_cases=None):
|
|||
return cmd
|
||||
|
||||
|
||||
def _run_search_codes(page, *, preserve_original=True, speaker_fn=None):
|
||||
def _run_search_codes(
|
||||
page,
|
||||
*,
|
||||
preserve_original=True,
|
||||
speaker_fn=None,
|
||||
ignore_tl_text=False,
|
||||
):
|
||||
"""Full Pass 1 -> mock translate -> Pass 2 cycle."""
|
||||
captured = []
|
||||
|
||||
|
|
@ -132,6 +138,7 @@ def _run_search_codes(page, *, preserve_original=True, speaker_fn=None):
|
|||
orig_405 = mvmz.CODE405
|
||||
orig_102 = mvmz.CODE102
|
||||
orig_preserve = mvmz.PRESERVEORIGINAL
|
||||
orig_ignore = mvmz.IGNORETLTEXT
|
||||
mvmz.translateAI = translate
|
||||
mvmz.getSpeaker = speaker
|
||||
mvmz.CODE122 = True
|
||||
|
|
@ -141,6 +148,7 @@ def _run_search_codes(page, *, preserve_original=True, speaker_fn=None):
|
|||
mvmz.CODE405 = True
|
||||
mvmz.CODE102 = True
|
||||
mvmz.PRESERVEORIGINAL = preserve_original
|
||||
mvmz.IGNORETLTEXT = ignore_tl_text
|
||||
try:
|
||||
page_copy = copy.deepcopy(page)
|
||||
mvmz.searchCodes(page_copy, None, [], "TestMap.json")
|
||||
|
|
@ -155,6 +163,7 @@ def _run_search_codes(page, *, preserve_original=True, speaker_fn=None):
|
|||
mvmz.CODE405 = orig_405
|
||||
mvmz.CODE102 = orig_102
|
||||
mvmz.PRESERVEORIGINAL = orig_preserve
|
||||
mvmz.IGNORETLTEXT = orig_ignore
|
||||
|
||||
|
||||
def _find_commands(page, code):
|
||||
|
|
@ -194,6 +203,28 @@ class TestMVMZSourceOriginal(unittest.TestCase):
|
|||
self.assertEqual(c122["_original"], "変数テスト")
|
||||
self.assertIn("EN_TRANSLATED", c122["parameters"][4])
|
||||
|
||||
def test_skip_translated_uses_current_commands_not_original(self):
|
||||
translated, _ = _run_search_codes(
|
||||
_load_map_excerpt(),
|
||||
speaker_fn=lambda _name: ["Alice", [0, 0]],
|
||||
)
|
||||
visible_before = copy.deepcopy(translated)
|
||||
speakers_seen = []
|
||||
|
||||
def speaker(name):
|
||||
speakers_seen.append(name)
|
||||
return _mock_speaker(name)
|
||||
|
||||
result, captured = _run_search_codes(
|
||||
translated,
|
||||
ignore_tl_text=True,
|
||||
speaker_fn=speaker,
|
||||
)
|
||||
|
||||
self.assertEqual(captured, [])
|
||||
self.assertEqual(speakers_seen, [])
|
||||
self.assertEqual(result, visible_before)
|
||||
|
||||
def test_101_display_brackets_do_not_leak_into_401_dialogue(self):
|
||||
speakers_seen = []
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue