From a89f347030b3369204527f3fcc92ba16b7a453d9 Mon Sep 17 00:00:00 2001 From: dazedanon Date: Wed, 18 Mar 2026 09:00:43 -0500 Subject: [PATCH] Handle repeating characters a bit better. --- modules/json.py | 76 +++++++++++++++++++++++++++++++++++++++++++++ util/translation.py | 6 ++-- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/modules/json.py b/modules/json.py index c2d8ae9..ba4d0ba 100644 --- a/modules/json.py +++ b/modules/json.py @@ -168,6 +168,9 @@ def parseJSON(data, filename): # GameSetting format (Nupu_GameSetting.json - fzCardDatas, fzDeckDatas, rentalDecks) elif any(k in data for k in ["fzCardDatas", "fzDeckDatas", "rentalDecks"]): result = translateGameSetting(data, filename) + # Src/Tl dialogue format (numeric-keyed entries with src and tl fields) + elif data and all(isinstance(v, dict) and "src" in v for v in data.values()): + result = translateDialogueSrcTl(data, filename) else: result = translateJSON(data, filename, []) else: @@ -845,6 +848,79 @@ def translateGameSetting(data, filename): return tokens +def translateDialogueSrcTl(data, filename): + """Translate src/tl dialogue JSON format. + + Format: + { + "0": {"type": "dialogue", "src": "Japanese text", "tl": ""}, + "4": {"type": "dialogue", "speaker": "琴音", "src": "「...」", "tl": ""} + } + + Translates 'src' into 'tl'. Uses 'speaker' as context only — not translated. + Skips entries where 'tl' is already populated. + """ + global LOCK, ESTIMATE, FILENAME, PBAR, MISMATCH + tokens = [0, 0] + srcList = [] + keyList = [] # list of (key, entry_type) + + # Collect all entries with translatable content + for key, entry in data.items(): + if not isinstance(entry, dict): + continue + src = entry.get("src", "") + if not src: + continue + tl = entry.get("tl", "") + entry_type = entry.get("type", "") + speaker = entry.get("speaker", "") + + if tl: + # Already has a translation — send tl so the module can decide to skip it + srcList.append(tl) + else: + if not re.search(LANGREGEX, src): + continue + srcClean = src.replace("\r\n", " ").replace("\n", " ").strip() + if speaker: + srcList.append(f"[{speaker}]: {srcClean}") + else: + srcList.append(srcClean) + keyList.append((key, entry_type)) + + if not srcList: + return tokens + + PBAR.total = len(srcList) + PBAR.refresh() + + response = translateAI(srcList, "Reply with the English Translation") + tokens[0] += response[1][0] + tokens[1] += response[1][1] + translatedList = response[0] + + if len(srcList) != len(translatedList): + with LOCK: + if FILENAME not in MISMATCH: + MISMATCH.append(FILENAME) + + for i, (key, entry_type) in enumerate(keyList): + if i >= len(translatedList): + break + translatedText = translatedList[i] + + # Strip speaker prefix if the AI echoed it back + match = re.search(r'^\[.+?\]\s?[|:]\s?', translatedText) + if match: + translatedText = translatedText[match.end():] + + data[key]["tl"] = translatedText + save_progress_json(data, filename) + + return tokens + + def translateJSON(data, filename, translatedList): global LOCK, ESTIMATE, FILENAME, PBAR, MISMATCH if translatedList: diff --git a/util/translation.py b/util/translation.py index e34be9a..fc2e47a 100644 --- a/util/translation.py +++ b/util/translation.py @@ -1405,7 +1405,8 @@ def translateAI(text, history, config, filename=None, pbar=None, lock=None, mism protected_items.append("Placeholder Text") all_replacements[j] = {} else: - protected_text, replacements = protect_script_codes(tItem[j]) + collapsed = re.sub(r'(.)\1{9,}', lambda m: m.group(1) * 10, tItem[j]) + protected_text, replacements = protect_script_codes(collapsed) protected_items.append(protected_text) all_replacements[j] = replacements else: @@ -1413,7 +1414,8 @@ def translateAI(text, history, config, filename=None, pbar=None, lock=None, mism protected_items = "Placeholder Text" all_replacements[0] = {} else: - protected_items, all_replacements[0] = protect_script_codes(tItem) + collapsed = re.sub(r'(.)\1{9,}', lambda m: m.group(1) * 10, tItem) + protected_items, all_replacements[0] = protect_script_codes(collapsed) # Filter out corrupted/mojibake text (U+FFFD) from the batch before API call corrupted_map = {} # original_index -> original_text