diff --git a/gui/translation_tab.py b/gui/translation_tab.py index 2e7ecd6..a052cd0 100644 --- a/gui/translation_tab.py +++ b/gui/translation_tab.py @@ -1077,8 +1077,8 @@ class TranslationTab(QWidget): ["NScript", [".nscript"], handleOnscripter], ["Wolf RPG", [".txt"], handleWOLF], ["Wolf RPG 2", [".txt"], handleWOLF2], - ["Regex", [".txt"], handleRegex], - ["Text", [".txt"], handleText], + ["Regex", [".txt", ".json", ".script", ".csv"], handleRegex], + ["Text", [".txt", ".srt"], handleText], ["RenPy", [".rpy"], handleRenpy], ["Unity", [".unity"], handleUnity], ["Images", [".png", ".jpg", ".jpeg"], handleImages], diff --git a/modules/csv.py b/modules/csv.py index b4980d7..2f7aac0 100644 --- a/modules/csv.py +++ b/modules/csv.py @@ -39,16 +39,16 @@ FILENAME = None BRACKETNAMES = False # CSV Configuration Settings (configurable via GUI) -CSV_DELIMITER = "," # CSV delimiter character (comma, semicolon, tab) -SOURCE_COLUMN = 0 # Which column has the source text to translate (0-indexed) -TARGET_COLUMN = 1 # Which column to write translations to -SPEAKER_COLUMN = -1 # Which column has speaker names (-1 = none) +CSV_DELIMITER = " " # CSV delimiter character (comma, semicolon, tab) +SOURCE_COLUMN = 2 # Which column has the source text to translate (0-indexed) +TARGET_COLUMN = 3 # Which column to write translations to +SPEAKER_COLUMN = 1 # Which column has speaker names (-1 = none) SKIP_HEADER_ROW = False # Skip the first row (header) USE_TARGET_IF_NOT_EMPTY = False # Use target column text if not empty (T++ style) WRITE_TO_NEXT_COLUMN = False # Write to column after target instead of overwriting PARSE_NAME_TAGS = False # Parse :name[] tags in text PARSE_M_MARKERS = False # Parse \M markers in text -REMOVE_FURIGANA = False # Remove furigana annotations <=> +REMOVE_FURIGANA = True # Remove furigana annotations <=> SKIP_COMMENT_ROWS = False # Skip rows starting with 'comment' # Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex diff --git a/modules/regex.py b/modules/regex.py index 6a978f1..f09c01d 100644 --- a/modules/regex.py +++ b/modules/regex.py @@ -201,17 +201,16 @@ def translateRegex(data, filename, translatedList): speaker = "" global LOCK, ESTIMATE, FILENAME, PBAR, MISMATCH i = 0 + + # Define regex patterns outside loop + lineRegexSpeaker = r'"name":\s?"(.+)",' + choiceRegex = r"\$menu_item.+?,(.*?)," + titleRegex = r"title\s'(.*)'$" + setgamedatatitleRegex = r'\\setgamedatatitle\("(.+?)"\)' + dlgRegex = r'\"text\":\s?\"(.+)\",' + selRegex = r'\\sel\((.+)\)' while i < len(data): - voice = False - lineRegexText = r'●(?:「| |()+(.+?)(?:[」)]|$)+' - lineRegexSpeaker = r"\\text\(\"(.+?)\"\)" - choiceRegex = r"\$menu_item.+?,(.*?)," - titleRegex = r"title\s'(.*)'$" - setgamedatatitleRegex = r'\\setgamedatatitle\("(.+?)"\)' - dlgRegex = r'\\dlg\("(.+?)"\)' - selRegex = r'\\sel\((.+)\)' - speaker = "" # Setgamedatatitle match = re.search(setgamedatatitleRegex, data[i]) @@ -260,23 +259,34 @@ def translateRegex(data, filename, translatedList): tokens[1] += response[1][1] data[i] = data[i].replace(match.group(1), speaker) - # Dlg (Dialogue prompt/choice) + # Dlg (JSON text field) match = re.search(dlgRegex, data[i]) if match: + jaString = match.group(1) + # Pass 1 - Collect for batch translation if not translatedList: - choiceList.append(match.group(1)) + # Add with speaker prefix if available + if speaker: + stringList.append(f"[{speaker}]: {jaString}") + else: + stringList.append(jaString) + # Reset speaker after using it + speaker = "" # Pass 2 - Apply translated text else: - if choiceList: + if stringList: # Grab and Pop - translatedText = choiceList[0] - choiceList.pop(0) + translatedText = stringList[0] + stringList.pop(0) # Set to None if empty list - if len(choiceList) <= 0: - choiceList = None + if len(stringList) <= 0: + stringList = None + + # Remove speaker prefix from translation + translatedText = re.sub(r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText) # Escape Quotes translatedText = translatedText.replace('"', '\\"') @@ -414,11 +424,15 @@ def translateRegex(data, filename, translatedList): if not translatedList: stringListTL = [] choiceListTL = [] + + # Set total progress bar for all items + totalItems = len(stringList) + len(choiceList) + if totalItems > 0: + PBAR.total = totalItems + PBAR.refresh() # String List if stringList: - PBAR.total = len(stringList) - PBAR.refresh() response = translateAI(stringList, "Reply with the English Translation", True) tokens[0] += response[1][0] tokens[1] += response[1][1] diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py index f32b100..63848bc 100644 --- a/modules/rpgmakermvmz.py +++ b/modules/rpgmakermvmz.py @@ -2534,54 +2534,94 @@ def searchCodes(page, pbar, jobList, filename): if "code" in codeList[i] and (codeList[i]["code"] == 355 or codeList[i]["code"] == 655) and CODE355655 is True: jaString = codeList[i]["parameters"][0] + # Patterns: (regex, multiline) - multiline=True means it spans 355 + following 655 codes patterns = { - # "テキスト-": (r"テキスト-(.+)") - # "=": (r'=\s?(.*)",'), - # "var text": (r"var\stext\d+\s=\s\"(.+)\""), - # "logtxt = ": (r"logtxt\s=\s'(.+)'" - # ".setNickname": (r'.setNickname\(\\?"(.+?)\\?"\)' - # "_subject=": r'_subject=(.+?)(?=[_\\"\]])' - # "text =": (r"text\s*=\s*'(.+[^\\])'"), - # "const text": (r'(const\stext\s?=\s?"(.+)";?)'), - # "ex_a_name": (r'ex_a_name\(\d+,"(.+)"\)'), - # "gameVariables.setValue": (r'\$gameVariables\.setValue\(\d+,\s*"([^"]*)"\)'), - # "BattleManager._logWindow.push('addText'": (r"BattleManager._logWindow.push\('addText',\s'(.+)'\)"), - "BattleManager._logWindow.addText": (r"BattleManager._logWindow.addText\('(.+)'\)"), + # "テキスト-": (r"テキスト-(.+)", False), + # "=": (r'=\s?(.*)",', False), + # "var text": (r"var\stext\d+\s=\s\"(.+)\"", False), + # "logtxt = ": (r"logtxt\s=\s'(.+)'", False), + # ".setNickname": (r'.setNickname\(\\?"(.+?)\\?"\)', False), + # "_subject=": (r'_subject=(.+?)(?=[_\\"\]])', False), + # "text =": (r"text\s*=\s*'(.+[^\\])'", False), + # "const text": (r'(const\stext\s?=\s?"(.+)";?)', False), + # "ex_a_name": (r'ex_a_name\(\d+,"(.+)"\)', False), + # "gameVariables.setValue": (r'\$gameVariables\.setValue\(\d+,\s*"([^"]*)"\)', False), + # "BattleManager._logWindow.push('addText'": (r"BattleManager._logWindow.push\('addText',\s'(.+)'\)", False), + # "BattleManager._logWindow.addText": (r"BattleManager._logWindow.addText\('(.+)'\)", False), + # "this.BLogAdd": (r'this\.BLogAdd\([\s,\d]+"(.+)"\);', False), + # "Fuki_Set": (r'Fuki_Set\([\s,\d\w\W]+?"(.+?)",', False), + # "_EventSetting": (r'_EventSetting[\s,\d\w\W]+?"(.+?)";', False), + # "this.Menu_SexTxtSet(": (r'"(.+)"', True), + # "Rn_RsltTxtArr": (r'"(.+)"', True), } - for key, (regex) in patterns.items(): + for key, (regex, multiline) in patterns.items(): if key in jaString: - match = re.search(regex, jaString) - if match: - # Check if the match contains actual text (not just numbers/special chars) - if not re.search(r'[a-zA-Z一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]', match.group(1)): - continue - - # Skip if IGNORETLTEXT is enabled and no Japanese text - if IGNORETLTEXT and not re.search(LANGREGEX, match.group(1)): - continue - - # Pass 1 - if setData: - list355655.append(match.group(1)) - - # Pass 2 - else: - # Grab and Replace - translatedText = list355655[0] - list355655.pop(0) - - # Ensure no quotes in gameVariables - if "gameVariables.setValue" in codeList[i]["parameters"][0]: - translatedText = translatedText.replace('\"', "'") + # Multi-line pattern: spans 355 + subsequent 655 codes + # Each 655 line is translated separately (as a batch) and stays in its own line + if multiline and codeList[i]["code"] == 355: + textLines = [] + textLineIndices = [] + j = i + 1 + + while j < len(codeList) and codeList[j]["code"] == 655: + param = codeList[j]["parameters"][0] if codeList[j]["parameters"] else "" + textMatch = re.search(regex, param) + if textMatch: + text = textMatch.group(1) + if not (IGNORETLTEXT and not re.search(LANGREGEX, text)): + textLines.append(text) + textLineIndices.append(j) + j += 1 + + if textLines: + if setData: + # Store each line separately for batch translation + for text in textLines: + list355655.append(text) + else: + # Apply each translated line back to its corresponding 655 code + for lineIdx in textLineIndices: + if len(list355655) > 0: + translatedText = list355655[0] + list355655.pop(0) + + # Replace quotes with apostrophes to avoid breaking plugin + translatedText = translatedText.replace('\\"', "'") + translatedText = translatedText.replace('"', "'") + + origParam = codeList[lineIdx]["parameters"][0] + origMatch = re.search(regex, origParam) + if origMatch: + codeList[lineIdx]["parameters"][0] = origParam.replace(origMatch.group(1), translatedText) - # Ensure ' has exactly 2 backslashes (don't double-escape if already escaped) - if "BattleManager" in codeList[i]["parameters"][0]: - translatedText = re.sub(r"(?