From 384d0930eb34a9c5e18b54fb63dfc622c14b99a9 Mon Sep 17 00:00:00 2001 From: no Date: Thu, 30 Oct 2025 22:58:02 +0200 Subject: [PATCH 1/5] Ace module, injecting fixes + option for speaker processing for code 408 --- gui/rpgmaker_tab.py | 19 ++++ modules/rpgmakerace.py | 232 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 232 insertions(+), 19 deletions(-) diff --git a/gui/rpgmaker_tab.py b/gui/rpgmaker_tab.py index 6db1ab2..0eb5d02 100644 --- a/gui/rpgmaker_tab.py +++ b/gui/rpgmaker_tab.py @@ -256,6 +256,13 @@ class RPGMakerTab(QWidget): self.join408_cb.setToolTip("Join 408 codes into a single string") left_column.addWidget(self.join408_cb) + self.speakers408_cb = QCheckBox("Process Speakers in 408") + self.speakers408_cb.setToolTip("Apply speaker detection and processing to code 408 (same as code 401)") + left_column.addWidget(self.speakers408_cb) + # Only show SPEAKERS408 for ACE engine (rpgmakerace.py) + if self.engine != "ACE": + self.speakers408_cb.hide() + left_column.addSpacing(15) # Main Codes @@ -379,6 +386,8 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.stateChanged.disconnect() self.ignoretltext_cb.stateChanged.disconnect() self.join408_cb.stateChanged.disconnect() + if self.engine == "ACE": + self.speakers408_cb.stateChanged.disconnect() # Main Codes self.code401_cb.stateChanged.disconnect() @@ -415,6 +424,8 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.ignoretltext_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.join408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + if self.engine == "ACE": + self.speakers408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) # Main Codes self.code401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) @@ -473,6 +484,10 @@ class RPGMakerTab(QWidget): "CODE111": self.code111_cb.isChecked(), "CODE108": self.code108_cb.isChecked(), } + + # Only include SPEAKERS408 for ACE engine + if self.engine == "ACE": + config["SPEAKERS408"] = self.speakers408_cb.isChecked() return config @@ -485,6 +500,10 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True)) self.ignoretltext_cb.setChecked(config.get("IGNORETLTEXT", False)) self.join408_cb.setChecked(config.get("JOIN408", False)) + + # Only set SPEAKERS408 for ACE engine + if self.engine == "ACE": + self.speakers408_cb.setChecked(config.get("SPEAKERS408", False)) # Main Codes self.code401_cb.setChecked(config.get("CODE401", True)) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index c9eaeb2..a6e0281 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -74,7 +74,7 @@ LEAVE = False # Config (Default) # FIRSTLINESPEAKERS: Guess speaker from first line. -FIRSTLINESPEAKERS = False +FIRSTLINESPEAKERS = True # FACENAME101: Map face name -> speaker. FACENAME101 = False # BRFLAG: Newlines ->
. @@ -86,7 +86,9 @@ IGNORETLTEXT = False # TLSYSTEMVARIABLES: Translate System Variables. (Optional but sometimes necessary. Can break stuff.) TLSYSTEMVARIABLES = False # Join 408 codes into a single string like 401. -JOIN408 = False +JOIN408 = True +# SPEAKERS408: Process speakers in code 408 the same way as code 401. +SPEAKERS408 = False # Dialogue / Scroll / Choices (Main Codes) CODE101 = True @@ -95,7 +97,7 @@ CODE405 = True CODE102 = True # Optional -CODE408 = False +CODE408 = True # Variables CODE122 = False @@ -1360,16 +1362,18 @@ def searchCodes(page, pbar, jobList, filename): # Brackets (support multiple names like 【A】【B】) # This now properly detects speakers after control codes are removed if len(speakerList) == 0: + # Strip \sp, \l, \r control codes to check for bracketed speakers + testString = re.sub(r"^(?:[\\]+[splrSPLR]\[[^\]]*\]\s*)+", "", jaString) + # Check if the line contains bracketed names 【name】 - # After control codes are stripped, check for pattern - startsWithBracket = re.match(r"^\s*【", jaString) is not None + startsWithBracket = re.match(r"^\s*【", testString) is not None endsWithBracket = re.search( r"(】\s*$|】\s*(?:[\\]+[A-Za-z]+(?:\[(?:[^\[\]]|\[[^\]]*\])*\])+\s*)$)", - jaString, + testString, ) is not None if startsWithBracket and endsWithBracket: - candidates = re.findall(r"【(.*?)】", jaString) + candidates = re.findall(r"【(.*?)】", testString) if candidates: candidates = [c.strip() for c in candidates] if candidates: @@ -2122,10 +2126,144 @@ def searchCodes(page, pbar, jobList, filename): jaString = codeList[i]["p"][0] match = re.search(r"(.+)", jaString) if match: - # Remove Textwrap - jaString = codeList[i]["p"][0] - ojaString = jaString - jaString = jaString.replace("\n", " ") + # Save starting index + j = i + nametag = "" + + # Speaker Check (if SPEAKERS408 is enabled) + if SPEAKERS408: + speakerList = [] + + # Remove any RPGMaker Code at start + ffMatch = re.search( + r"^((?:[\\]+[^cCnNiIkKvVrRlL]+\[[\d\w]+\])+)", + jaString, + ) + if ffMatch != None: + jaString = jaString.replace(ffMatch.group(0), "") + nametag += ffMatch.group(0) + + # m and z Codes + matchSpeaker = re.search(r"(.*?)[\\]+m\[\d+?\][\\]+z\[\d+?\]", jaString) + if matchSpeaker: + speakerList.append(matchSpeaker.group(1)) + if "\\c" in speakerList[0]: + speakerList = re.findall( + r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + speakerList[0], + ) + + # Brackets (support multiple names like 【A】【B】) + if len(speakerList) == 0: + # Strip \sp, \l, \r control codes to check for bracketed speakers + testString = re.sub(r"^(?:[\\]+[splrSPLR]\[[^\]]*\]\s*)+", "", jaString) + + # Check if the line contains bracketed names 【name】 + startsWithBracket = re.match(r"^\s*【", testString) is not None + endsWithBracket = re.search( + r"(】\s*$|】\s*(?:[\\]+[A-Za-z]+(?:\[(?:[^\[\]]|\[[^\]]*\])*\])+\s*)$)", + testString, + ) is not None + + if startsWithBracket and endsWithBracket: + candidates = re.findall(r"【(.*?)】", testString) + if candidates: + candidates = [c.strip() for c in candidates] + if candidates: + speakerList = candidates + + # Colors + if len(speakerList) == 0: + speakerList = re.findall( + r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + jaString, + ) + + # Colons + if len(speakerList) == 0: + speakerList = re.findall( + r"(.+):$", + jaString, + ) + + # First Line Speakers + if len(speakerList) == 0 and FIRSTLINESPEAKERS is True: + # Test Speaker + if ( + len(jaString) < 40 + and len(codeList) > i + 1 + and "c" in codeList[i + 1] + and codeList[i + 1]["c"] in [408, -1] + and len(codeList[i + 1]["p"]) > 0 + and len(codeList[i + 1]["p"][0]) > 0 + ): + nextString = codeList[i + 1]["p"][0].strip() + + # Remove any RPGMaker Code at start + ffMatchNS = re.search( + r"^((?:[\\]+[^cCnNiIkKvVSsrRlL{}]+?\[[\d\w\W]+?\]?\])+)", + nextString, + ) + if ffMatchNS != None: + nextString = nextString.replace(ffMatchNS.group(1), "") + + # Remove other format codes + formatMatch = re.search(r"(^[\\]+[\W]+?)", nextString) + if formatMatch != None: + nextString = nextString.replace(formatMatch.group(1), "") + + # If next line starts with dialogue marker, current line is likely a speaker + if nextString and nextString[0] in [ + "「", + '"', + "(", + "(", + "*", + "[", + ]: + speakerList = re.findall(r".+", jaString) + + # Replace Speaker + if len(speakerList) != 0 and len(codeList) > i + 1 and codeList[i + 1]["c"] in [408, -1]: + # Single + if len(speakerList) == 1: + response = getSpeaker(speakerList[0]) + speaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Multiple (Brackets) + elif len(speakerList) > 1: + jaStringUpdated = jaString + for idx, sp in enumerate(speakerList): + response = getSpeaker(sp) + tled = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if not setData: + pattern = r"【\s*" + re.escape(sp) + r"\s*】" + jaStringUpdated = re.sub(pattern, lambda m: f"【{tled}】", jaStringUpdated) + # Back-compat: set 'speaker' to the first translated name + if idx == 0: + speaker = tled + + # Set Data + if not setData and len(speakerList) > 1: + codeList[i]["p"][0] = nametag + jaStringUpdated + elif not setData and len(speakerList) == 1: + codeList[i]["p"][0] = nametag + jaString.replace(speakerList[0], speaker) + nametag = "" + + # Iterate to next string + i += 1 + j = i + while codeList[i]["c"] in [-1]: + i += 1 + j = i + jaString = codeList[i]["p"][0] + + # Using this to keep track of 408's in a row. + currentGroup.append(jaString) # Join Up 408's into single string if len(codeList) > i + 1 and JOIN408 is True: @@ -2144,22 +2282,78 @@ def searchCodes(page, pbar, jobList, filename): if len(codeList) <= i + 1: break + # Format String + if len(currentGroup) > 0: + finalJAString = "\n".join(currentGroup) + + # Set Back + if not setData: + codeList[j]["p"] = [finalJAString] + + # Process speaker name tag if SPEAKERS408 enabled + if SPEAKERS408: + ### \\n + regex = r"([\\]+[kKnN][wWcCrRrEe]?[\[<](?:[\\]*\w\[\d+\])?(.*?)(?:[\\]*\w\[\d+\])?[>])" + matchSpeaker = re.search(regex, finalJAString) + + # Set Name + if matchSpeaker: + nametag = matchSpeaker.group(1) + speakerName = matchSpeaker.group(2) + + # Translate Speaker + response = getSpeaker(speakerName) + tledSpeaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Nametag and Remove from Final String + finalJAString = finalJAString.replace(nametag, "") + nametag = nametag.replace(speakerName, tledSpeaker) + speaker = tledSpeaker + # Pass 1 if setData: # Remove Textwrap - jaString = jaString.replace("\n", " ") - list408.append(jaString) + finalJAString = finalJAString.replace("\n", " ") + + # Append with or without speaker + if SPEAKERS408 and speaker != "" and finalJAString != "": + list408.append(f"[{speaker}]: {finalJAString}") + else: + list408.append(finalJAString) + + speaker = "" + currentGroup = [] + syncIndex = i + 1 # Pass 2 else: - translatedText = list408[0] - list408.pop(0) + if len(list408) > 0: + translatedText = list408[0] + list408.pop(0) - # Textwrap - translatedText = dazedwrap.wrapText(translatedText, width=WIDTH) + # Remove speaker if SPEAKERS408 enabled + if SPEAKERS408: + matchSpeaker = re.search(r'(^\[.+?\]\s?[|:]\s?)', translatedText) + if matchSpeaker: + translatedText = translatedText.replace(matchSpeaker.group(1), "") - # Set Data - codeList[i]["p"][0] = codeList[i]["p"][0].replace(ojaString, translatedText) + # Textwrap + translatedText = dazedwrap.wrapText(translatedText, width=WIDTH) + + # Add Nametag Back In if SPEAKERS408 enabled + if SPEAKERS408 and nametag: + translatedText = nametag + translatedText + nametag = "" + + # Set Data + codeList[j]["p"][0] = translatedText + + # Reset + speaker = "" + currentGroup = [] + syncIndex = i + 1 ## Event Code: 108 (Script) if "c" in codeList[i] and (codeList[i]["c"] == 108) and CODE108 is True: From 5b6771d88c9393fe1b91b446a715e308446ac29d Mon Sep 17 00:00:00 2001 From: no Date: Sat, 1 Nov 2025 00:09:44 +0200 Subject: [PATCH 2/5] more accurate progress bar for mvmz + ace --- modules/rpgmakerace.py | 581 +++++++++++++++++++++++++++++++-------- modules/rpgmakermvmz.py | 589 ++++++++++++++++++++++++++++++++-------- 2 files changed, 942 insertions(+), 228 deletions(-) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index a6e0281..4d54670 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -37,6 +37,7 @@ PBAR = None FILENAME = None TIMETOTAL = 0 # Total Time Taken for all translations VOCAB_LOCK = threading.Lock() +PREFLIGHT_COUNT_MODE = False # When True, translateAI wrapper only counts units and never calls API # Speakers NAMESLIST = [] @@ -447,10 +448,101 @@ def update_vocab_section(category: str, pairs: list[tuple[str, str]]): def parseMap(data, filename): totalTokens = [0, 0] - totalLines = 0 events = data["events"] global LOCK + # --- Preflight: estimate exact progress total using the same translation batching --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_map_units(d, fname) -> int: + dcopy = copy.deepcopy(d) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE + saved_preflight = PREFLIGHT_COUNT_MODE + global PBAR + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + # Count display name TL (1 unit if present) + if "Map" in fname and isinstance(dcopy.get("display_name", None), str): + try: + translateAI( + dcopy["display_name"], + "Reply with only the " + LANGUAGE + " translation of the RPG location name", + False, + ) + except Exception: + pass + + # Notes and pages + evts = dcopy.get("events", {}) or {} + for evt in evts.values(): + if not evt: + continue + note_val = evt.get("note") or "" + if not isinstance(note_val, str): + note_val = str(note_val) if note_val is not None else "" + + # name translation + if "" in note_val: + name_val = evt.get("name") or "" + if isinstance(name_val, str) and name_val: + try: + translateAI( + name_val, + "Reply with only the " + LANGUAGE + " translation of the RPG location name", + False, + ) + except Exception: + pass + + # + if "", False) + except Exception: + pass + + # , , handled before page processing in real run + if ".*") + except Exception: + pass + if ".*") + except Exception: + pass + + for page in (evt.get("pages", []) or []): + try: + searchCodes(page, bar, [], fname) + except Exception: + pass + + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_preflight + PBAR = saved_pbar + # Translate display_name for Map files if "Map" in filename: response = translateAI( @@ -462,30 +554,17 @@ def parseMap(data, filename): totalTokens[1] += response[1][1] data["display_name"] = response[0].replace('"', "") - # Get total for progress bar (sum of all command list lengths across pages) - for event in events.values(): - if event and isinstance(event, dict): - note_val = event.get("note") or "" - if not isinstance(note_val, str): - note_val = str(note_val) if note_val is not None else "" - if "" in note_val: - # Translate event name when flagged with - name_val = event.get("name") or "" - if isinstance(name_val, str) and name_val: - response = translateAI( - name_val, - "Reply with only the " + LANGUAGE + " translation of the RPG location name", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - event["name"] = response[0].replace('"', "") - if "", False) - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - for page in event["pages"]: - totalLines += len(page["list"]) + totalLines = _estimate_map_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + # Fallback to naive count so a bar still renders + totalLines = 0 + for evt in events.values(): + if evt: + for page in (evt.get("pages", []) or []): + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR # Process each page synchronously with progress updates @@ -609,13 +688,52 @@ def translateNoteOmitSpace(event, regex): def parseCommonEvents(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for page in data: - if page is not None: - totalLines += len(page["list"]) + # --- Preflight: estimate exact progress total using same batching --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(pages, fname) -> int: + dcopy = copy.deepcopy(pages) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for page in dcopy: + if page is not None: + try: + searchCodes(page, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + # Fallback to naive command count + totalLines = 0 + for page in data: + if page is not None: + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -637,15 +755,55 @@ def parseCommonEvents(data, filename): def parseTroops(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for troop in data: - if troop is not None: - for page in troop["pages"]: - # Progress measured by number of commands in each page's list - totalLines += len(page["list"]) + # --- Preflight total using same code paths --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(troops, fname) -> int: + tcopy = copy.deepcopy(troops) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for troop in tcopy: + if troop is None: + continue + for page in (troop.get("pages", []) or []): + if page is not None: + try: + searchCodes(page, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + totalLines = 0 + for troop in data: + if troop is not None: + for page in troop.get("pages", []) or []: + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -670,49 +828,138 @@ def parseTroops(data, filename): def parseNames(data, filename, context): totalTokens = [0, 0] - # Precompute total work units for progress bar - def count_work_units(data, context): - total = 0 + # --- Preflight: custom estimator that mirrors searchNames increments (incl. notes/messages) --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + def _estimate_names_units(entries, ctx, fname) -> int: + ecopy = copy.deepcopy(entries) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + # Counts + name_cnt = 0 + desc_cnt = 0 + profile_cnt = 0 + nickname_cnt = 0 + msg_cnt = 0 + notes_cnt = 0 + + note_regexes = [ + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", True), + (r"", False), + (r"\n(.*)\n", False), + (r"", False), + (r"WATs:(.+?)>", False), + (r"ADTs?:(.+?)>", False), + (r"", False), + (r"", False), + (r"]+)", True), + (r"]+)", True), + (r"]+)", True), + (r"", True), + (r"", True), + (r"", False), + (r"<拡張説明:(.+?)>", False), + (r"\n(.+?)\n<", False), + (r"text:(.+)>", False), + ] + + for entry in ecopy: + if not entry: + continue + nm = entry.get("name") or "" + ds = entry.get("description") or "" + nn = entry.get("nickname") or "" + pf = entry.get("profile") or "" + if ctx == "Actors": + if nm: name_cnt += 1 + if nn: nickname_cnt += 1 + if pf: profile_cnt += 1 + elif ctx in ["Armors", "Weapons", "Items"]: + if nm: name_cnt += 1 + if ds: desc_cnt += 1 + elif ctx == "Skills": + if nm: name_cnt += 1 + if ds: desc_cnt += 1 + for k in range(1,5): + if entry.get(f"message{k}"): msg_cnt += 1 + elif ctx in ["Enemies", "Classes", "MapInfos"]: + if nm: name_cnt += 1 + + # Notes counting + note = entry.get("note") or "" + if isinstance(note, str) and note: + for regex, _ww in note_regexes: + try: + matches = re.findall(regex, note, re.DOTALL) + except Exception: + matches = [] + if regex.startswith(r" int: + scopy = copy.deepcopy(states) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for ss in scopy: + if ss is not None: + try: + searchSS(ss, bar) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + total_units = _estimate_units(data, filename) + if not isinstance(total_units, int) or total_units <= 0: + total_units = 0 + for st in data: if not st: continue - if st.get("name"): - total += 1 - if st.get("description"): - total += 1 - for n in range(1, 5): - if st.get(f"message{n}"): - total += 1 - return total - - total_units = count_work_units(data) + if st.get("name"): total_units += 1 + if st.get("description"): total_units += 1 + for n in range(1,5): + if st.get(f"message{n}"): total_units += 1 global PBAR with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -772,27 +1047,54 @@ def parseSS(data, filename): def parseSystem(data, filename): totalTokens = [0, 0] - # Precompute total units for Ace schema - def count_work_units(sys): - total = 0 - # Terms: sum list lengths of each term list - for term in sys.get("terms", {}): - termList = sys["terms"][term] - if isinstance(termList, list): - total += len(termList) - # game_title might be a string; count as 1 if non-empty - gt = sys.get("game_title") - if isinstance(gt, str) and gt: - total += 1 - # variables is a list - total += len(sys.get("variables", []) or []) - total += len(sys.get("weapon_types", []) or []) - total += len(sys.get("armor_types", []) or []) - total += len(sys.get("skill_types", []) or []) - total += len(sys.get("equip_types", []) or []) - return total + # --- Preflight: call searchSystem on deep copy to count increments --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass - total_units = count_work_units(data) + def _estimate_units(sysobj, fname) -> int: + scopy = copy.deepcopy(sysobj) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + try: + searchSystem(scopy, bar) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + total_units = _estimate_units(data, filename) + if not isinstance(total_units, int) or total_units <= 0: + # Fallback: rough count + total_units = 0 + for term in data.get("terms", {}) or {}: + termList = data["terms"][term] + if isinstance(termList, list): + total_units += len(termList) + gt = data.get("game_title") + if isinstance(gt, str) and gt: + total_units += 1 + total_units += len(data.get("variables", []) or []) + total_units += len(data.get("weapon_types", []) or []) + total_units += len(data.get("armor_types", []) or []) + total_units += len(data.get("skill_types", []) or []) + total_units += len(data.get("equip_types", []) or []) global PBAR with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -812,12 +1114,50 @@ def parseSystem(data, filename): def parseScenario(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for page in data.items(): - totalLines += len(page[1]) + # --- Preflight: run searchCodes on each page list --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(scenario, fname) -> int: + scopy = copy.deepcopy(scenario) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for key, lst in scopy.items(): + if lst is not None: + try: + searchCodes(lst, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + totalLines = 0 + for _, lst in data.items(): + try: + totalLines += len(lst or []) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -3394,6 +3734,21 @@ def translateAI(text, history, fullPromptFlag): # Return original text unmodified with zero tokens return [text, [0, 0]] + # Preflight count mode: don't hit API; just simulate progress units + if 'PREFLIGHT_COUNT_MODE' in globals() and PREFLIGHT_COUNT_MODE: + try: + n = len(text) if isinstance(text, list) else 1 + except Exception: + n = 1 + if PBAR is not None: + try: + with LOCK: + PBAR.update(n) + except Exception: + pass + # Return original payload and zero tokens so totals aren't affected + return [text, [0, 0]] + return sharedtranslateAI( text=text, history=history, diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py index 3951b20..1b26e46 100644 --- a/modules/rpgmakermvmz.py +++ b/modules/rpgmakermvmz.py @@ -35,6 +35,7 @@ PBAR = None FILENAME = None TIMETOTAL = 0 # Total Time Taken for all translations VOCAB_LOCK = threading.Lock() +PREFLIGHT_COUNT_MODE = False # When True, translateAI wrapper only counts units and never calls API # Speakers NAMESLIST = [] @@ -434,10 +435,107 @@ def update_vocab_section(category: str, pairs: list[tuple[str, str]]): def parseMap(data, filename): totalTokens = [0, 0] - totalLines = 0 events = data["events"] global LOCK + # --- Preflight: estimate exact progress total using the same translation batching --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + # Silent during preflight + pass + def refresh(self): + pass + + def _estimate_map_units(d, fname) -> int: + # Use a deep copy to avoid mutating real data during estimation + dcopy = copy.deepcopy(d) + bar = _CountingBar() + # Temporarily enable preflight-count mode and route progress updates to our counter + global PREFLIGHT_COUNT_MODE + saved_preflight = PREFLIGHT_COUNT_MODE + PREFLIGHT_COUNT_MODE = True + global PBAR + saved_pbar = PBAR + PBAR = bar + try: + # Count display name TL (1 unit if present) + if "Map" in fname and isinstance(dcopy.get("displayName", None), str): + try: + translateAI( + dcopy["displayName"], + "Reply with only the " + LANGUAGE + " translation of the RPG location name", + False, + ) + except Exception: + pass + + # Notes and pages + evts = dcopy.get("events", []) or [] + for evt in evts: + if not evt: + continue + note_val = evt.get("note") or "" + if not isinstance(note_val, str): + note_val = str(note_val) if note_val is not None else "" + + # name translation + if "" in note_val: + name_val = evt.get("name") or "" + if isinstance(name_val, str) and name_val: + try: + translateAI( + name_val, + "Reply with only the " + LANGUAGE + " translation of the RPG location name", + False, + ) + except Exception: + pass + + # + if "", False) + except Exception: + pass + + # , , handled before page processing in real run + if ".*") + except Exception: + pass + if ".*") + except Exception: + pass + + # Other note tags handled during main loop + # We'll just invoke the normal search pass which will batch and call translateAI + for page in (evt.get("pages", []) or []): + try: + searchCodes(page, bar, [], fname) + except Exception: + # Ignore counting errors to avoid blocking + pass + + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_preflight + PBAR = saved_pbar + # Translate displayName for Map files if "Map" in filename: response = translateAI( @@ -449,30 +547,18 @@ def parseMap(data, filename): totalTokens[1] += response[1][1] data["displayName"] = response[0].replace('"', "") - # Get total for progress bar (sum of all command list lengths across pages) - for event in events: - if event: - note_val = event.get("note") or "" - if not isinstance(note_val, str): - note_val = str(note_val) if note_val is not None else "" - if "" in note_val: - # Translate event name when flagged with - name_val = event.get("name") or "" - if isinstance(name_val, str) and name_val: - response = translateAI( - name_val, - "Reply with only the " + LANGUAGE + " translation of the RPG location name", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - event["name"] = response[0].replace('"', "") - if "", False) - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - for page in event["pages"]: - totalLines += len(page["list"]) + # Compute accurate total using preflight (includes speakers, choices, groups, and notes) + totalLines = _estimate_map_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + # Fallback to naive count so a bar still renders + totalLines = 0 + for event in events: + if event: + for page in event.get("pages", []) or []: + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR # Process each page synchronously with progress updates @@ -596,13 +682,52 @@ def translateNoteOmitSpace(event, regex): def parseCommonEvents(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for page in data: - if page is not None: - totalLines += len(page["list"]) + # --- Preflight: estimate exact progress total using same batching --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(pages, fname) -> int: + dcopy = copy.deepcopy(pages) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for page in dcopy: + if page is not None: + try: + searchCodes(page, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + # Fallback to naive command count + totalLines = 0 + for page in data: + if page is not None: + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -624,15 +749,55 @@ def parseCommonEvents(data, filename): def parseTroops(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for troop in data: - if troop is not None: - for page in troop["pages"]: - # Progress measured by number of commands in each page's list - totalLines += len(page["list"]) + # --- Preflight total using same code paths --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(troops, fname) -> int: + tcopy = copy.deepcopy(troops) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for troop in tcopy: + if troop is None: + continue + for page in (troop.get("pages", []) or []): + if page is not None: + try: + searchCodes(page, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + totalLines = 0 + for troop in data: + if troop is not None: + for page in troop.get("pages", []) or []: + try: + totalLines += len(page.get("list", [])) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -657,49 +822,138 @@ def parseTroops(data, filename): def parseNames(data, filename, context): totalTokens = [0, 0] - # Precompute total work units for progress bar - def count_work_units(data, context): - total = 0 + # --- Preflight: custom estimator that mirrors searchNames increments (incl. notes/messages) --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + def _estimate_names_units(entries, ctx, fname) -> int: + ecopy = copy.deepcopy(entries) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + # Count names/descriptions/profile/nickname + name_cnt = 0 + desc_cnt = 0 + profile_cnt = 0 + nickname_cnt = 0 + msg_cnt = 0 + notes_cnt = 0 + + note_regexes = [ + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", False), + (r"", True), + (r"", False), + (r"\n(.*)\n", False), + (r"", False), + (r"WATs:(.+?)>", False), + (r"ADTs?:(.+?)>", False), + (r"", False), + (r"", False), + (r"]+)", True), + (r"]+)", True), + (r"]+)", True), + (r"", True), + (r"", True), + (r"", False), + (r"<拡張説明:(.+?)>", False), + (r"\n(.+?)\n<", False), + (r"text:(.+)>", False), + ] + + for entry in ecopy: + if not entry: + continue + nm = entry.get("name") or "" + ds = entry.get("description") or "" + nn = entry.get("nickname") or "" + pf = entry.get("profile") or "" + if ctx == "Actors": + if nm: name_cnt += 1 + if nn: nickname_cnt += 1 + if pf: profile_cnt += 1 + elif ctx in ["Armors", "Weapons", "Items"]: + if nm: name_cnt += 1 + if ds: desc_cnt += 1 + elif ctx == "Skills": + if nm: name_cnt += 1 + if ds: desc_cnt += 1 + for k in range(1,5): + if entry.get(f"message{k}"): msg_cnt += 1 + elif ctx in ["Enemies", "Classes", "MapInfos"]: + if nm: name_cnt += 1 + + # Notes counting + note = entry.get("note") or "" + if isinstance(note, str) and note: + for regex, _ww in note_regexes: + try: + matches = re.findall(regex, note, re.DOTALL) + except Exception: + matches = [] + if regex.startswith(r" int: + scopy = copy.deepcopy(states) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for ss in scopy: + if ss is not None: + try: + searchSS(ss, bar) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + total_units = _estimate_units(data, filename) + if not isinstance(total_units, int) or total_units <= 0: + total_units = 0 + for st in data: if not st: continue - if st.get("name"): - total += 1 - if st.get("description"): - total += 1 - for n in range(1, 5): - if st.get(f"message{n}"): - total += 1 - return total - - total_units = count_work_units(data) + if st.get("name"): total_units += 1 + if st.get("description"): total_units += 1 + for n in range(1,5): + if st.get(f"message{n}"): total_units += 1 global PBAR with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -759,29 +1041,53 @@ def parseSS(data, filename): def parseSystem(data, filename): totalTokens = [0, 0] - # Precompute total units across system sections (exclude notes; count strings) - def count_work_units(sys): - total = 0 - # Title - if sys.get("gameTitle"): - total += 1 - # Terms (excluding 'messages' object) - terms = sys.get("terms", {}) - for term_key, term_list in terms.items(): - if term_key == "messages": - continue - if isinstance(term_list, list): - total += sum(1 for x in term_list if x is not None) - # Armor, Skill, Equip types - total += len(sys.get("armorTypes", []) or []) - total += len(sys.get("skillTypes", []) or []) - total += len(sys.get("equipTypes", []) or []) - # Messages - messages = terms.get("messages", {}) or {} - total += len(messages) - return total + # --- Preflight: call searchSystem on deep copy to count increments --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass - total_units = count_work_units(data) + def _estimate_units(sysobj, fname) -> int: + scopy = copy.deepcopy(sysobj) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + try: + searchSystem(scopy, bar) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + total_units = _estimate_units(data, filename) + if not isinstance(total_units, int) or total_units <= 0: + # Fallback: rough count of strings + total_units = 0 + if data.get("gameTitle"): total_units += 1 + terms = data.get("terms", {}) or {} + for k,v in terms.items(): + if k == "messages": + continue + if isinstance(v, list): + total_units += sum(1 for x in v if x is not None) + total_units += len(data.get("armorTypes", []) or []) + total_units += len(data.get("skillTypes", []) or []) + total_units += len(data.get("equipTypes", []) or []) + total_units += len((terms.get("messages", {}) or {})) global PBAR with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -801,12 +1107,50 @@ def parseSystem(data, filename): def parseScenario(data, filename): totalTokens = [0, 0] - totalLines = 0 global LOCK - # Get total for progress bar - for page in data.items(): - totalLines += len(page[1]) + # --- Preflight: run searchCodes on each page list --- + class _CountingBar: + def __init__(self): + self.n = 0 + def update(self, n=1): + try: + self.n += int(n) if n is not None else 1 + except Exception: + self.n += 1 + def write(self, *args, **kwargs): + pass + def refresh(self): + pass + + def _estimate_units(scenario, fname) -> int: + scopy = copy.deepcopy(scenario) + bar = _CountingBar() + global PREFLIGHT_COUNT_MODE, PBAR + saved_flag = PREFLIGHT_COUNT_MODE + saved_pbar = PBAR + PREFLIGHT_COUNT_MODE = True + PBAR = bar + try: + for key, lst in scopy.items(): + if lst is not None: + try: + searchCodes(lst, bar, [], fname) + except Exception: + pass + return getattr(bar, "n", 0) or 0 + finally: + PREFLIGHT_COUNT_MODE = saved_flag + PBAR = saved_pbar + + totalLines = _estimate_units(data, filename) + if not isinstance(totalLines, int) or totalLines <= 0: + totalLines = 0 + for _, lst in data.items(): + try: + totalLines += len(lst or []) + except Exception: + pass global PBAR with tqdm(total=totalLines, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE, desc=filename) as pbar: @@ -3187,6 +3531,21 @@ def translateAI(text, history, fullPromptFlag): # Return original text unmodified with zero tokens return [text, [0, 0]] + # Preflight count mode: don't hit API; just simulate progress units + if 'PREFLIGHT_COUNT_MODE' in globals() and PREFLIGHT_COUNT_MODE: + try: + n = len(text) if isinstance(text, list) else 1 + except Exception: + n = 1 + if PBAR is not None: + try: + with LOCK: + PBAR.update(n) + except Exception: + pass + # Return original payload and zero tokens so totals aren't affected + return [text, [0, 0]] + return sharedtranslateAI( text=text, history=history, From 1a6980496bfb2a7608332c281714ebafc4b89698 Mon Sep 17 00:00:00 2001 From: no Date: Tue, 4 Nov 2025 17:32:11 +0200 Subject: [PATCH 3/5] speakers reinjection fix --- modules/rpgmakerace.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index 4d54670..8ebd543 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -46,6 +46,12 @@ _speakerCache = {} _speakerCacheLock = threading.Lock() SPEAKER_COLLECTED = [] # Original speaker names collected during parse mode (untranslated) +def clearSpeakerCache(): + """Clear the speaker cache between passes to ensure fresh translations""" + global _speakerCache + with _speakerCacheLock: + _speakerCache.clear() + # Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex LANGREGEX = r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+" @@ -89,7 +95,7 @@ TLSYSTEMVARIABLES = False # Join 408 codes into a single string like 401. JOIN408 = True # SPEAKERS408: Process speakers in code 408 the same way as code 401. -SPEAKERS408 = False +SPEAKERS408 = True # Dialogue / Scroll / Choices (Main Codes) CODE101 = True @@ -1695,7 +1701,7 @@ def searchCodes(page, pbar, jobList, filename): speakerList.append(match.group(1)) if "\\c" in speakerList[0]: speakerList = re.findall( - r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + r"^[\\]+[cC]\[[^\]]+\]【(.+?)】[\\]+[cC]\[[^\]]+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", speakerList[0], ) @@ -1719,10 +1725,11 @@ def searchCodes(page, pbar, jobList, filename): if candidates: speakerList = candidates - # Colors + # Colors (strict): require a bracketed name inside color codes to count as a speaker + # This prevents arbitrary color-wrapped sentences from being misclassified as speakers. if len(speakerList) == 0: speakerList = re.findall( - r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + r"^[\\]+[cC]\[[^\]]+\]【(.+?)】[\\]+[cC]\[[^\]]+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", jaString, ) @@ -2489,7 +2496,7 @@ def searchCodes(page, pbar, jobList, filename): speakerList.append(matchSpeaker.group(1)) if "\\c" in speakerList[0]: speakerList = re.findall( - r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + r"^[\\]+[cC]\[[^\]]+\]【(.+?)】[\\]+[cC]\[[^\]]+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", speakerList[0], ) @@ -2512,10 +2519,10 @@ def searchCodes(page, pbar, jobList, filename): if candidates: speakerList = candidates - # Colors + # Colors (strict): require a bracketed name inside color codes to count as a speaker if len(speakerList) == 0: speakerList = re.findall( - r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + r"^[\\]+[cC]\[[^\]]+\]【(.+?)】[\\]+[cC]\[[^\]]+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", jaString, ) @@ -3278,6 +3285,7 @@ def searchCodes(page, pbar, jobList, filename): # Start Pass 2 if setData: + clearSpeakerCache() # Clear cache to ensure fresh speaker translations in Pass 2 searchCodes( page, pbar, From 9db676760c01fc24651cde8a2ee2d94746410983 Mon Sep 17 00:00:00 2001 From: no Date: Fri, 7 Nov 2025 22:40:10 +0200 Subject: [PATCH 4/5] system.yaml fixes --- modules/rpgmakerace.py | 202 +++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 98 deletions(-) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index 8ebd543..de34eed 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -3504,53 +3504,56 @@ def searchSystem(data, pbar): if pbar is not None: pbar.refresh() - # Armor Types - batch translate all - armor_values = [data["armor_types"][i] for i in range(len(data["armor_types"]))] - if armor_values: - response = translateAI( - armor_values, - "Reply with only the " + LANGUAGE + " translation of the armor type", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - tl_list = response[0] - for i in range(min(len(tl_list), len(data["armor_types"]))): - data["armor_types"][i] = tl_list[i].replace('"', "").strip() - if pbar is not None: - pbar.refresh() + # Armor Types - batch translate all (check if exists) + if "armor_types" in data and data["armor_types"]: + armor_values = [data["armor_types"][i] for i in range(len(data["armor_types"]))] + if armor_values: + response = translateAI( + armor_values, + "Reply with only the " + LANGUAGE + " translation of the armor type", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + tl_list = response[0] + for i in range(min(len(tl_list), len(data["armor_types"]))): + data["armor_types"][i] = tl_list[i].replace('"', "").strip() + if pbar is not None: + pbar.refresh() - # Skill Types - batch translate all - skill_values = [data["skill_types"][i] for i in range(len(data["skill_types"]))] - if skill_values: - response = translateAI( - skill_values, - "Reply with only the " + LANGUAGE + " translation", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - tl_list = response[0] - for i in range(min(len(tl_list), len(data["skill_types"]))): - data["skill_types"][i] = tl_list[i].replace('"', "").strip() - if pbar is not None: - pbar.refresh() + # Skill Types - batch translate all (check if exists) + if "skill_types" in data and data["skill_types"]: + skill_values = [data["skill_types"][i] for i in range(len(data["skill_types"]))] + if skill_values: + response = translateAI( + skill_values, + "Reply with only the " + LANGUAGE + " translation", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + tl_list = response[0] + for i in range(min(len(tl_list), len(data["skill_types"]))): + data["skill_types"][i] = tl_list[i].replace('"', "").strip() + if pbar is not None: + pbar.refresh() - # Equip Types - batch translate all - equip_values = [data["equip_types"][i] for i in range(len(data["equip_types"]))] - if equip_values: - response = translateAI( - equip_values, - "Reply with only the " + LANGUAGE + " translation of the equipment type. No disclaimers.", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - tl_list = response[0] - for i in range(min(len(tl_list), len(data["equip_types"]))): - data["equip_types"][i] = tl_list[i].replace('"', "").strip() - if pbar is not None: - pbar.refresh() + # Equip Types - batch translate all (check if exists) + if "equip_types" in data and data["equip_types"]: + equip_values = [data["equip_types"][i] for i in range(len(data["equip_types"]))] + if equip_values: + response = translateAI( + equip_values, + "Reply with only the " + LANGUAGE + " translation of the equipment type. No disclaimers.", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + tl_list = response[0] + for i in range(min(len(tl_list), len(data["equip_types"]))): + data["equip_types"][i] = tl_list[i].replace('"', "").strip() + if pbar is not None: + pbar.refresh() # Elements - batch translate all (skip empty) element_values = [] @@ -3575,26 +3578,28 @@ def searchSystem(data, pbar): pbar.refresh() # Weapon Types - batch translate all (skip empty) - weapon_values = [] - weapon_indices = [] - for i in range(len(data["weaponTypes"])): - if data["weaponTypes"][i]: # Skip empty strings - weapon_values.append(data["weaponTypes"][i]) - weapon_indices.append(i) - - if weapon_values: - response = translateAI( - weapon_values, - "Reply with only the " + LANGUAGE + " translation of the weapon type", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - tl_list = response[0] - for n, idx in enumerate(weapon_indices[: len(tl_list)]): - data["weaponTypes"][idx] = tl_list[n].replace('"', "").strip() - if pbar is not None: - pbar.refresh() + weapon_key = "weaponTypes" if "weaponTypes" in data else "weapon_types" + if weapon_key in data and data[weapon_key]: + weapon_values = [] + weapon_indices = [] + for i in range(len(data[weapon_key])): + if data[weapon_key][i]: # Skip empty strings + weapon_values.append(data[weapon_key][i]) + weapon_indices.append(i) + + if weapon_values: + response = translateAI( + weapon_values, + "Reply with only the " + LANGUAGE + " translation of the weapon type", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + tl_list = response[0] + for n, idx in enumerate(weapon_indices[: len(tl_list)]): + data[weapon_key][idx] = tl_list[n].replace('"', "").strip() + if pbar is not None: + pbar.refresh() # Variables (Optional usually) — batch translate to reduce calls if TLSYSTEMVARIABLES and "variables" in data and isinstance(data["variables"], list): @@ -3619,40 +3624,41 @@ def searchSystem(data, pbar): if pbar is not None: pbar.refresh() - # Messages — batch translate to reduce calls - messages = data["terms"]["messages"] - if messages: - msg_keys = [] - msg_values = [] - for key, value in messages.items(): - if isinstance(value, str) and value.strip(): - msg_keys.append(key) - msg_values.append(value) - - if msg_values: - response = translateAI( - msg_values, - "Reply with only the " - + LANGUAGE - + ' translation of the battle text.\nTranslate "常時ダッシュ" as "Always Dash"\nTranslate "次の%1まで" as Next %1.', - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - tl_list = response[0] + # Messages — batch translate to reduce calls (check if exists) + if "messages" in data["terms"]: + messages = data["terms"]["messages"] + if messages: + msg_keys = [] + msg_values = [] + for key, value in messages.items(): + if isinstance(value, str) and value.strip(): + msg_keys.append(key) + msg_values.append(value) - # Remove characters that may break scripts - charList = [".", '"', "\\n"] - - # Assign back translations to corresponding keys - for n, key in enumerate(msg_keys[: len(tl_list)]): - translatedText = tl_list[n] - for char in charList: - translatedText = translatedText.replace(char, "") - messages[key] = translatedText - - if pbar is not None: - pbar.refresh() + if msg_values: + response = translateAI( + msg_values, + "Reply with only the " + + LANGUAGE + + ' translation of the battle text.\nTranslate "常時ダッシュ" as "Always Dash"\nTranslate "次の%1まで" as Next %1.', + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + tl_list = response[0] + + # Remove characters that may break scripts + charList = [".", '"', "\\n"] + + # Assign back translations to corresponding keys + for n, key in enumerate(msg_keys[: len(tl_list)]): + translatedText = tl_list[n] + for char in charList: + translatedText = translatedText.replace(char, "") + messages[key] = translatedText + + if pbar is not None: + pbar.refresh() return totalTokens From db9cd2c3827e41dde89b2c842b2af1905917a76a Mon Sep 17 00:00:00 2001 From: xorxorrax Date: Sun, 16 Nov 2025 18:04:43 +0000 Subject: [PATCH 5/5] Remove unused THREADS environment variable from multiple modules --- .env.example | 7 ++----- modules/csv.py | 1 - modules/images.py | 1 - modules/json.py | 1 - modules/kirikiri.py | 1 - modules/lune.py | 1 - modules/nscript.py | 1 - modules/regex.py | 1 - modules/renpy.py | 1 - modules/rpgmakerace.py | 1 - modules/rpgmakermvmz.py | 1 - modules/rpgmakerplugin.py | 1 - modules/srpg.py | 1 - modules/text.py | 1 - modules/tyrano.py | 1 - modules/unity.py | 1 - modules/wolf.py | 1 - modules/wolf2.py | 1 - 18 files changed, 2 insertions(+), 22 deletions(-) diff --git a/.env.example b/.env.example index ce4081d..faf43b7 100644 --- a/.env.example +++ b/.env.example @@ -34,16 +34,13 @@ timeout="120" #The number of files to translate at the same time, 1 recommended for free or self hosted API or gpt-4 fileThreads="1" -#The number of threads per file, 1 recommended for free or self hosted API or gpt-4 -threads="1" - #The wordwrap of dialogue text width="60" -#The wordwap of items and help text +#The wordwrap of items and help text listWidth="100" -#The wordwap of items and help text +#The wordwrap of items and help text noteWidth="75" # Custom input API cost - default value for gpt-3.5, replace with your actual input API cost - depends on the model, see https://openai.com/pricing diff --git a/modules/csv.py b/modules/csv.py index a87820d..cd4a9ca 100644 --- a/modules/csv.py +++ b/modules/csv.py @@ -22,7 +22,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/images.py b/modules/images.py index cc502fa..bc35abf 100644 --- a/modules/images.py +++ b/modules/images.py @@ -20,7 +20,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() PBAR = None WIDTH = int(os.getenv("width")) diff --git a/modules/json.py b/modules/json.py index d5c51d4..1b6352d 100644 --- a/modules/json.py +++ b/modules/json.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/kirikiri.py b/modules/kirikiri.py index bc3682e..2090aa5 100644 --- a/modules/kirikiri.py +++ b/modules/kirikiri.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/lune.py b/modules/lune.py index b23d8b3..e422e14 100644 --- a/modules/lune.py +++ b/modules/lune.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/nscript.py b/modules/nscript.py index 6fde949..b66802b 100644 --- a/modules/nscript.py +++ b/modules/nscript.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/regex.py b/modules/regex.py index 8dfd475..105de20 100644 --- a/modules/regex.py +++ b/modules/regex.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/renpy.py b/modules/renpy.py index 1041ed3..d861ef3 100644 --- a/modules/renpy.py +++ b/modules/renpy.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index c9eaeb2..713ec93 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -23,7 +23,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() THREAD_CTX = threading.local() WIDTH = int(os.getenv("width")) diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py index bfd3609..6a390d8 100644 --- a/modules/rpgmakermvmz.py +++ b/modules/rpgmakermvmz.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() THREAD_CTX = threading.local() WIDTH = int(os.getenv("width")) diff --git a/modules/rpgmakerplugin.py b/modules/rpgmakerplugin.py index 164f5af..a2ce10a 100644 --- a/modules/rpgmakerplugin.py +++ b/modules/rpgmakerplugin.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/srpg.py b/modules/srpg.py index 075c8c1..af8b127 100644 --- a/modules/srpg.py +++ b/modules/srpg.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() VOCAB_LOCK = threading.Lock() # Dedicated lock for vocab.txt updates WIDTH = int(os.getenv("width")) diff --git a/modules/text.py b/modules/text.py index 60c1a8a..da8169a 100644 --- a/modules/text.py +++ b/modules/text.py @@ -21,7 +21,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/tyrano.py b/modules/tyrano.py index cbd7b76..f0b18ea 100644 --- a/modules/tyrano.py +++ b/modules/tyrano.py @@ -23,7 +23,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/unity.py b/modules/unity.py index 62a2893..e5f0213 100644 --- a/modules/unity.py +++ b/modules/unity.py @@ -23,7 +23,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/wolf.py b/modules/wolf.py index d10a9a5..8e1ed80 100644 --- a/modules/wolf.py +++ b/modules/wolf.py @@ -23,7 +23,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth")) diff --git a/modules/wolf2.py b/modules/wolf2.py index 1c2c99f..b0e0ee5 100644 --- a/modules/wolf2.py +++ b/modules/wolf2.py @@ -24,7 +24,6 @@ TIMEOUT = int(os.getenv("timeout")) LANGUAGE = os.getenv("language").capitalize() PROMPT = Path("prompt.txt").read_text(encoding="utf-8") VOCAB = Path("vocab.txt").read_text(encoding="utf-8") -THREADS = int(os.getenv("threads")) LOCK = threading.Lock() WIDTH = int(os.getenv("width")) LISTWIDTH = int(os.getenv("listWidth"))