diff --git a/modules/csv.py b/modules/csv.py index a1d15ef..bf026ed 100644 --- a/modules/csv.py +++ b/modules/csv.py @@ -656,7 +656,7 @@ def translateGPT(text, history, fullPromptFlag): extractedTranslations = extractTranslation(translatedText, True) if extractedTranslations == None or len(tItem) != len(extractedTranslations): # Mismatch. Try Again - response = translateText(system, user, history, 0.05, format, "gpt-4o") + response = translateText(system, user, history, 0.05, format, MODEL) translatedText = response.choices[0].message.content totalTokens[0] += response.usage.prompt_tokens totalTokens[1] += response.usage.completion_tokens diff --git a/modules/json.py b/modules/json.py index 8903a43..006f9df 100644 --- a/modules/json.py +++ b/modules/json.py @@ -524,7 +524,7 @@ def translateGPT(text, history, fullPromptFlag): extractedTranslations = extractTranslation(translatedText, True) if extractedTranslations == None or len(tItem) != len(extractedTranslations): # Mismatch. Try Again - response = translateText(system, user, history, 0.05, format, "gpt-4o") + response = translateText(system, user, history, 0.05, format, MODEL) translatedText = response.choices[0].message.content totalTokens[0] += response.usage.prompt_tokens totalTokens[1] += response.usage.completion_tokens diff --git a/modules/kirikiri.py b/modules/kirikiri.py index b2286a1..35b3feb 100644 --- a/modules/kirikiri.py +++ b/modules/kirikiri.py @@ -567,7 +567,7 @@ def translateGPT(text, history, fullPromptFlag): extractedTranslations = extractTranslation(translatedText, True) if extractedTranslations == None or len(tItem) != len(extractedTranslations): # Mismatch. Try Again - response = translateText(system, user, history, 0.05, format, "gpt-4o") + response = translateText(system, user, history, 0.05, format, MODEL) translatedText = response.choices[0].message.content totalTokens[0] += response.usage.prompt_tokens totalTokens[1] += response.usage.completion_tokens diff --git a/modules/lune.py b/modules/lune.py index 8b1fcda..acefc9e 100644 --- a/modules/lune.py +++ b/modules/lune.py @@ -1,589 +1,589 @@ -# Libraries -import json -import os -import re -import textwrap -import threading -import time -import traceback -import tiktoken -import openai -from pathlib import Path -from colorama import Fore -from dotenv import load_dotenv -from retry import retry -from tqdm import tqdm - -# Open AI -load_dotenv() -if os.getenv("api").replace(" ", "") != "": - openai.base_url = os.getenv("api") -openai.organization = os.getenv("org") -openai.api_key = os.getenv("key") - -# Globals -MODEL = os.getenv("model") -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")) -NOTEWIDTH = 70 -MAXHISTORY = 10 -ESTIMATE = "" -TOKENS = [0, 0] -NAMESLIST = [] -NAMES = False # Output a list of all the character names found -BRFLAG = False # If the game uses
instead -FIXTEXTWRAP = True # Overwrites textwrap -IGNORETLTEXT = False # Ignores all translated text. -MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) -PBAR = None -FILENAME = None - -# tqdm Globals -BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" -POSITION = 0 -LEAVE = False - -# Pricing - Depends on the model https://openai.com/pricing -# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request -# If you are getting a MISMATCH LENGTH error, lower the batch size. -if "gpt-3.5" in MODEL: - INPUTAPICOST = 0.002 - OUTPUTAPICOST = 0.002 - BATCHSIZE = 10 -elif "gpt-4" in MODEL: - INPUTAPICOST = 0.01 - OUTPUTAPICOST = 0.03 - BATCHSIZE = 50 -else: - INPUTAPICOST = float(os.getenv("input_cost")) - OUTPUTAPICOST = float(os.getenv("output_cost")) - BATCHSIZE = int(os.getenv("batchsize")) - FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) - - -def handleLune(filename, estimate): - global FILENAME, ESTIMATE, totalTokens - ESTIMATE = estimate - FILENAME = filename - - if estimate: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - - return getResultString(["", TOKENS, None], end - start, "TOTAL") - - else: - try: - with open("translated/" + filename, "w", encoding="UTF-8") as outFile: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - json.dump(translatedData[0], outFile, ensure_ascii=False, indent=4) - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - except Exception: - return "Fail" - - return getResultString(["", TOKENS, None], end - start, "TOTAL") - - -def openFiles(filename): - with open("files/" + filename, "r", encoding="UTF-8-sig") as f: - data = json.load(f) - - # Map Files - if ".json" in filename: - translatedData = parseJSON(data, filename) - - else: - raise NameError(filename + " Not Supported") - - return translatedData - - -def getResultString(translatedData, translationTime, filename): - # File Print String - totalTokenstring = ( - Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" - "[Output: " - + str(translatedData[1][1]) - + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) - + "]" - ) - timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" - - if translatedData[2] == None: - # Success - return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET - - else: - # Fail - try: - raise translatedData[2] - except Exception as e: - traceback.print_exc() - errorString = str(e) + Fore.RED - return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET - - -def parseJSON(data, filename): - totalTokens = [0, 0] - totalLines = 0 - totalLines = len(data) - global LOCK - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, total=totalLines, leave=LEAVE) as pbar: - pbar.desc = filename - pbar.total = totalLines - try: - result = translateJSON(data, pbar) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def translateJSON(data, pbar): - global PBAR - PBAR = pbar - textHistory = [] - batch = [] - maxHistory = MAXHISTORY - tokens = [0, 0] - speaker = "None" - insertBool = False - i = 0 - batchStartIndex = 0 - - while i < len(data): - item = data[i] - # Speaker - if "name" in item: - if item["name"] not in [None, "-"]: - response = getSpeaker(item["name"]) - speaker = response[0] - tokens[0] += response[1][0] - tokens[1] += response[1][1] - item["name"] = speaker - else: - speaker = "None" - - # Text - if "message" in item: - for text in [ - "text", - "text2", - "help1", - "help2", - "help3", - "like", - "message", - "me", - ]: - if text in item: - if item[text] != None: - jaString = item[text] - - # Remove any textwrap - if FIXTEXTWRAP == True: - finalJAString = jaString.replace("\n", " ") - - # [Passthrough 1] Pulling From File - if insertBool is False: - # Append to List and Clear Values - batch.append(finalJAString) - speaker = "" - - # Translate Batch if Full - if len(batch) == BATCHSIZE: - # Translate - response = translateGPT(batch, textHistory, True) - tokens[0] += response[1][0] - tokens[1] += response[1][1] - translatedBatch = response[0] - textHistory = translatedBatch[-10:] - - # Set Values - if len(batch) == len(translatedBatch): - i = batchStartIndex - insertBool = True - - # Mismatch - else: - pbar.write(f"Mismatch: {batchStartIndex} - {i}") - MISMATCH.append(batch) - batchStartIndex = i - batch.clear() - - if insertBool is False: - pbar.update(1) - i += 1 - - currentGroup = [] - - # [Passthrough 2] Setting Data - else: - # Get Text - translatedText = translatedBatch[0] - - # Remove added speaker - translatedText = re.sub(r"^.+?:\s", "", translatedText) - - # Textwrap - translatedText = textwrap.fill(translatedText, width=WIDTH) - - # Set Text - item[text] = translatedText - translatedBatch.pop(0) - speaker = "" - currentGroup = [] - i += 1 - - # If Batch is empty. Move on. - if len(translatedBatch) == 0: - insertBool = False - batchStartIndex = i - batch.clear() - else: - i += 1 - pbar.update(1) - - # Translate Batch if not empty and EOF - if len(batch) != 0 and i >= len(data): - # Translate - response = translateGPT(batch, textHistory, True) - tokens[0] += response[1][0] - tokens[1] += response[1][1] - translatedBatch = response[0] - textHistory = translatedBatch[-10:] - - # Set Values - if len(batch) == len(translatedBatch): - i = batchStartIndex - insertBool = True - - # Mismatch - else: - pbar.write(f"Mismatch: {batchStartIndex} - {i}") - MISMATCH.append(batch) - batchStartIndex = i - batch.clear() - - currentGroup = [] - return tokens - - -# Save some money and enter the character before translation -def getSpeaker(speaker): - match speaker: - case "ファイン": - return ["Fine", [0, 0]] - case "": - return ["", [0, 0]] - case _: - # Find Speaker - for i in range(len(NAMESLIST)): - if speaker == NAMESLIST[i][0]: - return [NAMESLIST[i][1], [0, 0]] - - # Translate and Store Speaker - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - True, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - response[0] = response[0].replace("Speaker: ", "") - - # Retry if name doesn't translate for some reason - if re.search(r"([a-zA-Z??])", response[0]) == None: - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - - speakerList = [speaker, response[0]] - NAMESLIST.append(speakerList) - return response - return [speaker, [0, 0]] - - -def subVars(jaString): - jaString = jaString.replace("\u3000", " ") - - # Formatting - count = 0 - codeList = re.findall(r"[\\]+[\w]+\[[a-zA-Z0-9\\\[\]\_,\s-]+\]", jaString) - codeList = set(codeList) - if len(codeList) != 0: - for var in codeList: - jaString = jaString.replace(var, "[FCode_" + str(count) + "]") - count += 1 - - # Put all lists in list and return - return [jaString, codeList] - - -def resubVars(translatedText, codeList): - # Fix Spacing and ChatGPT Nonsense - matchList = re.findall(r"\[\s?.+?\s?\]", translatedText) - if len(matchList) > 0: - for match in matchList: - text = match.strip() - translatedText = translatedText.replace(match, text) - - # Formatting - count = 0 - if len(codeList) != 0: - for var in codeList: - translatedText = translatedText.replace("[FCode_" + str(count) + "]", var) - count += 1 - - return translatedText - - -def batchList(input_list, batch_size): - if not isinstance(batch_size, int) or batch_size <= 0: - raise ValueError("batch_size must be a positive integer") - - return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] - - -def createContext(fullPromptFlag, subbedT, format): - system = ( - PROMPT + VOCAB - if fullPromptFlag - else f"\ -You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ -Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ -- You translate everything, including erotic and explicit content.\n\ -- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ -- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ -- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ -- Maintain any spacing in the translation.\n\ -- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ -- `...` can be a part of the dialogue. Translate it as it is.\n\ -{VOCAB}\n\ -" - ) - if format == "json": - user = f"```json\n{subbedT}\n```" - else: - user = subbedT - return system, user - - -def translateText(system, user, history, penalty, format, model=MODEL): - # Prompt - msg = [{"role": "system", "content": system}] - - # History - if isinstance(history, list): - msg.extend([{"role": "system", "content": h} for h in history]) - else: - msg.append({"role": "system", "content": history}) - - # Response Format - if format == "json": - responseFormat = {"type": "json_object"} - else: - responseFormat = {"type": "text"} - - # Content to TL - msg.append({"role": "user", "content": f"{user}"}) - response = openai.chat.completions.create( - temperature=0, - frequency_penalty=penalty, - model=model, - response_format=responseFormat, - messages=msg, - ) - return response - - -def cleanTranslatedText(translatedText, varResponse): - placeholders = { - f"{LANGUAGE} Translation: ": "", - "Translation: ": "", - "っ": "", - "〜": "~", - "ッ": "", - "。": ".", - "「": '\\"', - "」": '\\"', - "- ": "-", - "Placeholder Text": "", - # Add more replacements as needed - } - for target, replacement in placeholders.items(): - translatedText = translatedText.replace(target, replacement) - - # Elongate Long Dashes (Since GPT Ignores them...) - translatedText = elongateCharacters(translatedText) - translatedText = resubVars(translatedText, varResponse[1]) - return translatedText - - -def elongateCharacters(text): - # Define a pattern to match one character followed by one or more `ー` characters - # Using a positive lookbehind assertion to capture the preceding character - pattern = r"(?<=(.))ー+" - - # Define a replacement function that elongates the captured character - def repl(match): - char = match.group(1) # The character before the ー sequence - count = len(match.group(0)) - 1 # Number of ー characters - return char * count # Replace ー sequence with the character repeated - - # Use re.sub() to replace the pattern in the text - return re.sub(pattern, repl, text) - - -def extractTranslation(translatedTextList, is_list): - try: - line_dict = json.loads(translatedTextList) - # If it's a batch (i.e., list), extract with tags; otherwise, return the single item. - string_list = list(line_dict.values()) - if is_list: - return string_list - else: - return string_list[0] - - except Exception as e: - print(f"extractTranslation Error: {e}") - return None - - -def countTokens(system, user, history): - inputTotalTokens = 0 - outputTotalTokens = 0 - enc = tiktoken.encoding_for_model("gpt-4") - - # Input - if isinstance(history, list): - for line in history: - inputTotalTokens += len(enc.encode(line)) - else: - inputTotalTokens += len(enc.encode(history)) - inputTotalTokens += len(enc.encode(system)) - inputTotalTokens += len(enc.encode(user)) - - # Output - outputTotalTokens += round(len(enc.encode(user)) * 3) - - return [inputTotalTokens, outputTotalTokens] - - -@retry(exceptions=Exception, tries=5, delay=5) -def translateGPT(text, history, fullPromptFlag): - global PBAR, MISMATCH, FILENAME - - mismatch = False - totalTokens = [0, 0] - if isinstance(text, list): - format = "json" - tList = batchList(text, BATCHSIZE) - else: - format = "text" - tList = [text] - - for index, tItem in enumerate(tList): - # Before sending to translation, if we have a list of items, add the formatting - if isinstance(tItem, list): - payload = {f"Line{i+1}": string for i, string in enumerate(tItem)} - payload = json.dumps(payload, indent=4, ensure_ascii=False) - varResponse = subVars(payload) - subbedT = varResponse[0] - else: - varResponse = subVars(tItem) - subbedT = varResponse[0] - - # Things to Check before starting translation - if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", subbedT): - if PBAR is not None: - PBAR.update(len(tItem)) - continue - - # Create Message - system, user = createContext(fullPromptFlag, subbedT, format) - - # Calculate Estimate - if ESTIMATE: - estimate = countTokens(system, user, history) - totalTokens[0] += estimate[0] - totalTokens[1] += estimate[1] - continue - - # Translating - response = translateText(system, user, history, 0.05, format) - translatedText = response.choices[0].message.content - totalTokens[0] += response.usage.prompt_tokens - totalTokens[1] += response.usage.completion_tokens - - # Check Translation - translatedText = cleanTranslatedText(translatedText, varResponse) - if isinstance(tItem, list): - extractedTranslations = extractTranslation(translatedText, True) - if extractedTranslations == None or len(tItem) != len(extractedTranslations): - # Mismatch. Try Again - response = translateText(system, user, history, 0.05, format, "gpt-4o") - translatedText = response.choices[0].message.content - totalTokens[0] += response.usage.prompt_tokens - totalTokens[1] += response.usage.completion_tokens - - # Formatting - translatedText = cleanTranslatedText(translatedText, varResponse) - if isinstance(tItem, list): - extractedTranslations = extractTranslation(translatedText, True) - if extractedTranslations == None or len(tItem) != len(extractedTranslations): - mismatch = True # Just here for breakpoint - - # Set if no mismatch - if mismatch == False: - tList[index] = extractedTranslations - history = extractedTranslations[-10:] # Update history if we have a list - else: - history = text[-10:] - mismatch = False - if FILENAME not in MISMATCH: - MISMATCH.append(FILENAME) - - # Update Loading Bar - with LOCK: - if PBAR is not None: - PBAR.update(len(tItem)) - else: - # Ensure we're passing a single string to extractTranslation - tList[index] = translatedText.replace("Placeholder Text", "") - - # Combine if multilist - if isinstance(tList[0], list): - tList = [t for sublist in tList for t in sublist] - - # Return - if format == "json": - return [tList, totalTokens] - else: - return [tList[0], totalTokens] +# Libraries +import json +import os +import re +import textwrap +import threading +import time +import traceback +import tiktoken +import openai +from pathlib import Path +from colorama import Fore +from dotenv import load_dotenv +from retry import retry +from tqdm import tqdm + +# Open AI +load_dotenv() +if os.getenv("api").replace(" ", "") != "": + openai.base_url = os.getenv("api") +openai.organization = os.getenv("org") +openai.api_key = os.getenv("key") + +# Globals +MODEL = os.getenv("model") +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")) +NOTEWIDTH = 70 +MAXHISTORY = 10 +ESTIMATE = "" +TOKENS = [0, 0] +NAMESLIST = [] +NAMES = False # Output a list of all the character names found +BRFLAG = False # If the game uses
instead +FIXTEXTWRAP = True # Overwrites textwrap +IGNORETLTEXT = False # Ignores all translated text. +MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) +PBAR = None +FILENAME = None + +# tqdm Globals +BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" +POSITION = 0 +LEAVE = False + +# Pricing - Depends on the model https://openai.com/pricing +# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request +# If you are getting a MISMATCH LENGTH error, lower the batch size. +if "gpt-3.5" in MODEL: + INPUTAPICOST = 0.002 + OUTPUTAPICOST = 0.002 + BATCHSIZE = 10 +elif "gpt-4" in MODEL: + INPUTAPICOST = 0.01 + OUTPUTAPICOST = 0.03 + BATCHSIZE = 50 +else: + INPUTAPICOST = float(os.getenv("input_cost")) + OUTPUTAPICOST = float(os.getenv("output_cost")) + BATCHSIZE = int(os.getenv("batchsize")) + FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) + + +def handleLune(filename, estimate): + global FILENAME, ESTIMATE, totalTokens + ESTIMATE = estimate + FILENAME = filename + + if estimate: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + + return getResultString(["", TOKENS, None], end - start, "TOTAL") + + else: + try: + with open("translated/" + filename, "w", encoding="UTF-8") as outFile: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + json.dump(translatedData[0], outFile, ensure_ascii=False, indent=4) + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + except Exception: + return "Fail" + + return getResultString(["", TOKENS, None], end - start, "TOTAL") + + +def openFiles(filename): + with open("files/" + filename, "r", encoding="UTF-8-sig") as f: + data = json.load(f) + + # Map Files + if ".json" in filename: + translatedData = parseJSON(data, filename) + + else: + raise NameError(filename + " Not Supported") + + return translatedData + + +def getResultString(translatedData, translationTime, filename): + # File Print String + totalTokenstring = ( + Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" + "[Output: " + + str(translatedData[1][1]) + + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) + + "]" + ) + timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" + + if translatedData[2] == None: + # Success + return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET + + else: + # Fail + try: + raise translatedData[2] + except Exception as e: + traceback.print_exc() + errorString = str(e) + Fore.RED + return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET + + +def parseJSON(data, filename): + totalTokens = [0, 0] + totalLines = 0 + totalLines = len(data) + global LOCK + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, total=totalLines, leave=LEAVE) as pbar: + pbar.desc = filename + pbar.total = totalLines + try: + result = translateJSON(data, pbar) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def translateJSON(data, pbar): + global PBAR + PBAR = pbar + textHistory = [] + batch = [] + maxHistory = MAXHISTORY + tokens = [0, 0] + speaker = "None" + insertBool = False + i = 0 + batchStartIndex = 0 + + while i < len(data): + item = data[i] + # Speaker + if "name" in item: + if item["name"] not in [None, "-"]: + response = getSpeaker(item["name"]) + speaker = response[0] + tokens[0] += response[1][0] + tokens[1] += response[1][1] + item["name"] = speaker + else: + speaker = "None" + + # Text + if "message" in item: + for text in [ + "text", + "text2", + "help1", + "help2", + "help3", + "like", + "message", + "me", + ]: + if text in item: + if item[text] != None: + jaString = item[text] + + # Remove any textwrap + if FIXTEXTWRAP == True: + finalJAString = jaString.replace("\n", " ") + + # [Passthrough 1] Pulling From File + if insertBool is False: + # Append to List and Clear Values + batch.append(finalJAString) + speaker = "" + + # Translate Batch if Full + if len(batch) == BATCHSIZE: + # Translate + response = translateGPT(batch, textHistory, True) + tokens[0] += response[1][0] + tokens[1] += response[1][1] + translatedBatch = response[0] + textHistory = translatedBatch[-10:] + + # Set Values + if len(batch) == len(translatedBatch): + i = batchStartIndex + insertBool = True + + # Mismatch + else: + pbar.write(f"Mismatch: {batchStartIndex} - {i}") + MISMATCH.append(batch) + batchStartIndex = i + batch.clear() + + if insertBool is False: + pbar.update(1) + i += 1 + + currentGroup = [] + + # [Passthrough 2] Setting Data + else: + # Get Text + translatedText = translatedBatch[0] + + # Remove added speaker + translatedText = re.sub(r"^.+?:\s", "", translatedText) + + # Textwrap + translatedText = textwrap.fill(translatedText, width=WIDTH) + + # Set Text + item[text] = translatedText + translatedBatch.pop(0) + speaker = "" + currentGroup = [] + i += 1 + + # If Batch is empty. Move on. + if len(translatedBatch) == 0: + insertBool = False + batchStartIndex = i + batch.clear() + else: + i += 1 + pbar.update(1) + + # Translate Batch if not empty and EOF + if len(batch) != 0 and i >= len(data): + # Translate + response = translateGPT(batch, textHistory, True) + tokens[0] += response[1][0] + tokens[1] += response[1][1] + translatedBatch = response[0] + textHistory = translatedBatch[-10:] + + # Set Values + if len(batch) == len(translatedBatch): + i = batchStartIndex + insertBool = True + + # Mismatch + else: + pbar.write(f"Mismatch: {batchStartIndex} - {i}") + MISMATCH.append(batch) + batchStartIndex = i + batch.clear() + + currentGroup = [] + return tokens + + +# Save some money and enter the character before translation +def getSpeaker(speaker): + match speaker: + case "ファイン": + return ["Fine", [0, 0]] + case "": + return ["", [0, 0]] + case _: + # Find Speaker + for i in range(len(NAMESLIST)): + if speaker == NAMESLIST[i][0]: + return [NAMESLIST[i][1], [0, 0]] + + # Translate and Store Speaker + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + True, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + response[0] = response[0].replace("Speaker: ", "") + + # Retry if name doesn't translate for some reason + if re.search(r"([a-zA-Z??])", response[0]) == None: + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + + speakerList = [speaker, response[0]] + NAMESLIST.append(speakerList) + return response + return [speaker, [0, 0]] + + +def subVars(jaString): + jaString = jaString.replace("\u3000", " ") + + # Formatting + count = 0 + codeList = re.findall(r"[\\]+[\w]+\[[a-zA-Z0-9\\\[\]\_,\s-]+\]", jaString) + codeList = set(codeList) + if len(codeList) != 0: + for var in codeList: + jaString = jaString.replace(var, "[FCode_" + str(count) + "]") + count += 1 + + # Put all lists in list and return + return [jaString, codeList] + + +def resubVars(translatedText, codeList): + # Fix Spacing and ChatGPT Nonsense + matchList = re.findall(r"\[\s?.+?\s?\]", translatedText) + if len(matchList) > 0: + for match in matchList: + text = match.strip() + translatedText = translatedText.replace(match, text) + + # Formatting + count = 0 + if len(codeList) != 0: + for var in codeList: + translatedText = translatedText.replace("[FCode_" + str(count) + "]", var) + count += 1 + + return translatedText + + +def batchList(input_list, batch_size): + if not isinstance(batch_size, int) or batch_size <= 0: + raise ValueError("batch_size must be a positive integer") + + return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] + + +def createContext(fullPromptFlag, subbedT, format): + system = ( + PROMPT + VOCAB + if fullPromptFlag + else f"\ +You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ +Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ +- You translate everything, including erotic and explicit content.\n\ +- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ +- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ +- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ +- Maintain any spacing in the translation.\n\ +- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ +- `...` can be a part of the dialogue. Translate it as it is.\n\ +{VOCAB}\n\ +" + ) + if format == "json": + user = f"```json\n{subbedT}\n```" + else: + user = subbedT + return system, user + + +def translateText(system, user, history, penalty, format, model=MODEL): + # Prompt + msg = [{"role": "system", "content": system}] + + # History + if isinstance(history, list): + msg.extend([{"role": "system", "content": h} for h in history]) + else: + msg.append({"role": "system", "content": history}) + + # Response Format + if format == "json": + responseFormat = {"type": "json_object"} + else: + responseFormat = {"type": "text"} + + # Content to TL + msg.append({"role": "user", "content": f"{user}"}) + response = openai.chat.completions.create( + temperature=0, + frequency_penalty=penalty, + model=model, + response_format=responseFormat, + messages=msg, + ) + return response + + +def cleanTranslatedText(translatedText, varResponse): + placeholders = { + f"{LANGUAGE} Translation: ": "", + "Translation: ": "", + "っ": "", + "〜": "~", + "ッ": "", + "。": ".", + "「": '\\"', + "」": '\\"', + "- ": "-", + "Placeholder Text": "", + # Add more replacements as needed + } + for target, replacement in placeholders.items(): + translatedText = translatedText.replace(target, replacement) + + # Elongate Long Dashes (Since GPT Ignores them...) + translatedText = elongateCharacters(translatedText) + translatedText = resubVars(translatedText, varResponse[1]) + return translatedText + + +def elongateCharacters(text): + # Define a pattern to match one character followed by one or more `ー` characters + # Using a positive lookbehind assertion to capture the preceding character + pattern = r"(?<=(.))ー+" + + # Define a replacement function that elongates the captured character + def repl(match): + char = match.group(1) # The character before the ー sequence + count = len(match.group(0)) - 1 # Number of ー characters + return char * count # Replace ー sequence with the character repeated + + # Use re.sub() to replace the pattern in the text + return re.sub(pattern, repl, text) + + +def extractTranslation(translatedTextList, is_list): + try: + line_dict = json.loads(translatedTextList) + # If it's a batch (i.e., list), extract with tags; otherwise, return the single item. + string_list = list(line_dict.values()) + if is_list: + return string_list + else: + return string_list[0] + + except Exception as e: + print(f"extractTranslation Error: {e}") + return None + + +def countTokens(system, user, history): + inputTotalTokens = 0 + outputTotalTokens = 0 + enc = tiktoken.encoding_for_model("gpt-4") + + # Input + if isinstance(history, list): + for line in history: + inputTotalTokens += len(enc.encode(line)) + else: + inputTotalTokens += len(enc.encode(history)) + inputTotalTokens += len(enc.encode(system)) + inputTotalTokens += len(enc.encode(user)) + + # Output + outputTotalTokens += round(len(enc.encode(user)) * 3) + + return [inputTotalTokens, outputTotalTokens] + + +@retry(exceptions=Exception, tries=5, delay=5) +def translateGPT(text, history, fullPromptFlag): + global PBAR, MISMATCH, FILENAME + + mismatch = False + totalTokens = [0, 0] + if isinstance(text, list): + format = "json" + tList = batchList(text, BATCHSIZE) + else: + format = "text" + tList = [text] + + for index, tItem in enumerate(tList): + # Before sending to translation, if we have a list of items, add the formatting + if isinstance(tItem, list): + payload = {f"Line{i+1}": string for i, string in enumerate(tItem)} + payload = json.dumps(payload, indent=4, ensure_ascii=False) + varResponse = subVars(payload) + subbedT = varResponse[0] + else: + varResponse = subVars(tItem) + subbedT = varResponse[0] + + # Things to Check before starting translation + if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", subbedT): + if PBAR is not None: + PBAR.update(len(tItem)) + continue + + # Create Message + system, user = createContext(fullPromptFlag, subbedT, format) + + # Calculate Estimate + if ESTIMATE: + estimate = countTokens(system, user, history) + totalTokens[0] += estimate[0] + totalTokens[1] += estimate[1] + continue + + # Translating + response = translateText(system, user, history, 0.05, format) + translatedText = response.choices[0].message.content + totalTokens[0] += response.usage.prompt_tokens + totalTokens[1] += response.usage.completion_tokens + + # Check Translation + translatedText = cleanTranslatedText(translatedText, varResponse) + if isinstance(tItem, list): + extractedTranslations = extractTranslation(translatedText, True) + if extractedTranslations == None or len(tItem) != len(extractedTranslations): + # Mismatch. Try Again + response = translateText(system, user, history, 0.05, format, MODEL) + translatedText = response.choices[0].message.content + totalTokens[0] += response.usage.prompt_tokens + totalTokens[1] += response.usage.completion_tokens + + # Formatting + translatedText = cleanTranslatedText(translatedText, varResponse) + if isinstance(tItem, list): + extractedTranslations = extractTranslation(translatedText, True) + if extractedTranslations == None or len(tItem) != len(extractedTranslations): + mismatch = True # Just here for breakpoint + + # Set if no mismatch + if mismatch == False: + tList[index] = extractedTranslations + history = extractedTranslations[-10:] # Update history if we have a list + else: + history = text[-10:] + mismatch = False + if FILENAME not in MISMATCH: + MISMATCH.append(FILENAME) + + # Update Loading Bar + with LOCK: + if PBAR is not None: + PBAR.update(len(tItem)) + else: + # Ensure we're passing a single string to extractTranslation + tList[index] = translatedText.replace("Placeholder Text", "") + + # Combine if multilist + if isinstance(tList[0], list): + tList = [t for sublist in tList for t in sublist] + + # Return + if format == "json": + return [tList, totalTokens] + else: + return [tList[0], totalTokens] diff --git a/modules/nscript.py b/modules/nscript.py index 0a6a91c..0424b57 100644 --- a/modules/nscript.py +++ b/modules/nscript.py @@ -1,628 +1,628 @@ -# Libraries -import json -import os -import re -import textwrap -import threading -import time -import traceback -import tiktoken -import openai -from pathlib import Path -from colorama import Fore -from dotenv import load_dotenv -from retry import retry -from tqdm import tqdm - -# Open AI -load_dotenv() -if os.getenv("api").replace(" ", "") != "": - openai.base_url = os.getenv("api") -openai.organization = os.getenv("org") -openai.api_key = os.getenv("key") - -# Globals -MODEL = os.getenv("model") -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")) -NOTEWIDTH = 70 -MAXHISTORY = 10 -ESTIMATE = "" -TOKENS = [0, 0] -NAMESLIST = [] -NAMES = False # Output a list of all the character names found -BRFLAG = False # If the game uses
instead -FIXTEXTWRAP = True # Overwrites textwrap -IGNORETLTEXT = False # Ignores all translated text. -MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) - -# tqdm Globals -BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" -POSITION = 0 -LEAVE = False -PBAR = None -FILENAME = None - -# Full Width -ascii_to_wide = dict((i, chr(i + 0xFEE0)) for i in range(0x21, 0x7F)) -ascii_to_wide.update({0x20: "\u3000", 0x2D: "\u2212"}) # space and minus -wide_to_ascii = dict((i, chr(i - 0xFEE0)) for i in range(0xFF01, 0xFF5F)) -wide_to_ascii.update({0x3000: " ", 0x2212: "-"}) # space and minus - -# Pricing - Depends on the model https://openai.com/pricing -# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request -# If you are getting a MISMATCH LENGTH error, lower the batch size. -if "gpt-3.5" in MODEL: - INPUTAPICOST = 0.002 - OUTPUTAPICOST = 0.002 - BATCHSIZE = 10 -elif "gpt-4" in MODEL: - INPUTAPICOST = 0.0025 - OUTPUTAPICOST = 0.01 - BATCHSIZE = 40 -else: - INPUTAPICOST = float(os.getenv("input_cost")) - OUTPUTAPICOST = float(os.getenv("output_cost")) - BATCHSIZE = int(os.getenv("batchsize")) - FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) - - -def handleOnscripter(filename, estimate): - global ESTIMATE, FILENAME - ESTIMATE = estimate - FILENAME = filename - - if ESTIMATE: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - - # Print Total - totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") - - # Print any errors on maps - if len(MISMATCH) > 0: - return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET - else: - return totalString - - else: - try: - with open("translated/" + filename, "w", encoding="cp932", errors="ignore") as outFile: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - outFile.writelines(translatedData[0]) - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - except Exception: - traceback.print_exc() - return "Fail" - - return getResultString(["", TOKENS, None], end - start, "TOTAL") - - -def getResultString(translatedData, translationTime, filename): - # File Print String - totalTokenstring = ( - Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" - "[Output: " - + str(translatedData[1][1]) - + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) - + "]" - ) - timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" - - if translatedData[2] == None: - # Success - return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET - - else: - # Fail - try: - raise translatedData[2] - except Exception as e: - traceback.print_exc() - errorString = str(e) + Fore.RED - return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET - - -def openFiles(filename): - with open("files/" + filename, "r", encoding="cp932") as readFile: - translatedData = parseOnscripter(readFile, filename) - - # Delete lines marked for deletion - finalData = [] - for line in translatedData[0]: - if line != "\\d\n": - finalData.append(line) - translatedData[0] = finalData - - return translatedData - - -def parseOnscripter(readFile, filename): - totalTokens = [0, 0] - - # Read File into data - data = readFile.readlines() - - # Create Progress Bar - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - - try: - result = translateOnscripter(data, pbar, filename, []) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def translateOnscripter(data, pbar, filename, translatedList): - stringList = [] - currentGroup = [] - tokens = [0, 0] - speaker = "" - voice = False - global LOCK, ESTIMATE, PBAR - PBAR = pbar - i = 0 - - # Dialogue - while i < len(data): - # Lines - regex = r"^([\u3000「(【[][^\n]+)" - match = re.search(regex, data[i]) - if match != None and match.group(1) != "": - originalString = match.group(1) - # Pass 1 - if translatedList == []: - # Grab Consecutive Strings - jaString = match.group(1) - while len(data) > i + 1 and re.match(regex, data[i + 1]): - data[i] = "" - i += 1 - jaString = f"{jaString} {data[i]}" - - # Convert from Wide - jaString = jaString.translate(wide_to_ascii) - - # Remove any textwrap and \u3000 and \ - jaString = jaString.replace("\n", "") - jaString = jaString.replace("\u3000", "") - jaString = jaString.replace("\\", "") - jaString = jaString.replace(" >", ")") - jaString = jaString.replace("< ", "(") - - # Remove Furigana - furiMatch = re.findall(r"({(.+?)\/(.+?)})", jaString) - if furiMatch: - for match in furiMatch: - jaString = jaString.replace(match[0], match[2]) - - # Add String - stringList.append(jaString.strip()) - - # Pass 2 - else: - # Get Text - if translatedList: - # Grab and Pop - translatedText = translatedList[0] - translatedList.pop(0) - - # Set to None if empty list - if len(translatedList) <= 0: - translatedList = None - - # Textwrap & Other Text - translatedText = textwrap.fill(translatedText, width=WIDTH) - translatedText = translatedText.replace("\n", "\n\u3000") - - # Split the string into lines - lines = translatedText.split("\n") - - # Add a backslash after every 3rd line - j = 0 - while j < len(lines): - if j == 4: - lines[j - 1] = f"{lines[j-1]}\\" - lines[j] = f"\n{lines[j]}" - j += 1 - - # Join the lines back into a single string - translatedText = "\n".join(lines) - - # Remove Double Spaces - translatedText = translatedText.replace(" ", " ") - - # Convert to Wide - translatedText = translatedText.translate(ascii_to_wide) - - # Fix Formatting - translatedText = fixText(translatedText) - - # Set Data - data[i] = data[i].replace(originalString, f"{translatedText}") - i += 1 - - # Choices - elif "csel" in data[i] and translatedList != []: - choiceList = [] - jaString = data[i] - - choiceList = re.findall(r"\"(.*?)\"", jaString) - if len(choiceList) > 0: - # Translate - response = translateGPT(choiceList, "This will be a dialogue option", True) - translatedTextList = response[0] - tokens[0] += response[1][0] - tokens[1] += response[1][1] - - # Set Data - for j in range(len(translatedTextList)): - # Convert to Wide - translatedText = translatedTextList[j].translate(ascii_to_wide) - - # Set - data[i] = data[i].replace(choiceList[j], translatedText) - i += 1 - - # Nothing relevant. Skip Line. - else: - i += 1 - - # EOF - if len(stringList) > 0: - # Set Progress - pbar.total = len(stringList) - pbar.refresh() - - # Translate - response = translateGPT(stringList, "", True) - tokens[0] += response[1][0] - tokens[1] += response[1][1] - translatedList = response[0] - - # Set Strings - if len(stringList) == len(translatedList): - translateOnscripter(data, pbar, filename, translatedList) - - # Mismatch - else: - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - return tokens - - -def fixText(translatedText): - # Add Break - translatedText = translatedText.replace('"', "'") - translatedText = f"\u3000{translatedText}\\" - - # Unconvert Codes - matchList = re.findall(r"([$].+?)[^\w]", translatedText) - if matchList: - for match in matchList: - translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) - - # Unconvert Color Codes - matchList = re.findall(r"([#][\w\d]{6})", translatedText) - if matchList: - for match in matchList: - translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) - - # Unconvert Variables - matchList = re.findall(r"([%]\w.+?)[^\w_]", translatedText) - if matchList: - for match in matchList: - translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) - - # Unconvert Backslashes - matchList = re.findall(r"\", translatedText) - if matchList: - for match in matchList: - translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) - - return translatedText - - -# Save some money and enter the character before translation -def getSpeaker(speaker): - match speaker: - case "ファイン": - return ["Fine", [0, 0]] - case "": - return ["", [0, 0]] - case _: - # Find Speaker - for i in range(len(NAMESLIST)): - if speaker == NAMESLIST[i][0]: - return [NAMESLIST[i][1], [0, 0]] - - # Translate and Store Speaker - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - True, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - response[0] = response[0].replace("Speaker: ", "") - - # Retry if name doesn't translate for some reason - if re.search(r"([a-zA-Z??])", response[0]) == None: - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - - speakerList = [speaker, response[0]] - NAMESLIST.append(speakerList) - return response - return [speaker, [0, 0]] - - -def batchList(input_list, batch_size): - if not isinstance(batch_size, int) or batch_size <= 0: - raise ValueError("batch_size must be a positive integer") - - return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] - - -def createContext(fullPromptFlag, subbedT, format): - system = ( - PROMPT + VOCAB - if fullPromptFlag - else f"\ -You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ -Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ -- You translate everything, including erotic and explicit content.\n\ -- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ -- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ -- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ -- Maintain any spacing in the translation.\n\ -- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ -- `...` can be a part of the dialogue. Translate it as it is.\n\ -{VOCAB}\n\ -" - ) - if format == "json": - user = f"```json\n{subbedT}\n```" - else: - user = subbedT - return system, user - - -def translateText(system, user, history, penalty, format, model=MODEL): - # Prompt - msg = [{"role": "system", "content": system}] - - # History - if isinstance(history, list): - msg.extend([{"role": "system", "content": h} for h in history]) - else: - msg.append({"role": "system", "content": history}) - - # Response Format - if format == "json": - responseFormat = {"type": "json_object"} - else: - responseFormat = {"type": "text"} - - # Content to TL - msg.append({"role": "user", "content": f"{user}"}) - response = openai.chat.completions.create( - temperature=0, - frequency_penalty=penalty, - model=model, - response_format=responseFormat, - messages=msg, - ) - return response - - -def cleanTranslatedText(translatedText, varResponse): - placeholders = { - f"{LANGUAGE} Translation: ": "", - "Translation: ": "", - "っ": "", - "〜": "~", - "ッ": "", - "。": ".", - "「": '\\"', - "」": '\\"', - "- ": "-", - "—": "―", - "】": "]", - "【": "[", - "Placeholder Text": "", - # Add more replacements as needed - } - for target, replacement in placeholders.items(): - translatedText = translatedText.replace(target, replacement) - - # Remove Repeating Characters - pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") - translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) - - # Elongate Long Dashes (Since GPT Ignores them...) - translatedText = elongateCharacters(translatedText) - return translatedText - - -def elongateCharacters(text): - # Define a pattern to match one character followed by one or more `ー` characters - # Using a positive lookbehind assertion to capture the preceding character - pattern = r"(?<=(.))ー+" - - # Define a replacement function that elongates the captured character - def repl(match): - char = match.group(1) # The character before the ー sequence - count = len(match.group(0)) - 1 # Number of ー characters - return char * count # Replace ー sequence with the character repeated - - # Use re.sub() to replace the pattern in the text - return re.sub(pattern, repl, text) - - -def extractTranslation(translatedTextList, is_list): - try: - translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) - translatedTextList = re.sub(r"(? instead +FIXTEXTWRAP = True # Overwrites textwrap +IGNORETLTEXT = False # Ignores all translated text. +MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) + +# tqdm Globals +BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" +POSITION = 0 +LEAVE = False +PBAR = None +FILENAME = None + +# Full Width +ascii_to_wide = dict((i, chr(i + 0xFEE0)) for i in range(0x21, 0x7F)) +ascii_to_wide.update({0x20: "\u3000", 0x2D: "\u2212"}) # space and minus +wide_to_ascii = dict((i, chr(i - 0xFEE0)) for i in range(0xFF01, 0xFF5F)) +wide_to_ascii.update({0x3000: " ", 0x2212: "-"}) # space and minus + +# Pricing - Depends on the model https://openai.com/pricing +# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request +# If you are getting a MISMATCH LENGTH error, lower the batch size. +if "gpt-3.5" in MODEL: + INPUTAPICOST = 0.002 + OUTPUTAPICOST = 0.002 + BATCHSIZE = 10 +elif "gpt-4" in MODEL: + INPUTAPICOST = 0.0025 + OUTPUTAPICOST = 0.01 + BATCHSIZE = 40 +else: + INPUTAPICOST = float(os.getenv("input_cost")) + OUTPUTAPICOST = float(os.getenv("output_cost")) + BATCHSIZE = int(os.getenv("batchsize")) + FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) + + +def handleOnscripter(filename, estimate): + global ESTIMATE, FILENAME + ESTIMATE = estimate + FILENAME = filename + + if ESTIMATE: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + + # Print Total + totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") + + # Print any errors on maps + if len(MISMATCH) > 0: + return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET + else: + return totalString + + else: + try: + with open("translated/" + filename, "w", encoding="cp932", errors="ignore") as outFile: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + outFile.writelines(translatedData[0]) + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + except Exception: + traceback.print_exc() + return "Fail" + + return getResultString(["", TOKENS, None], end - start, "TOTAL") + + +def getResultString(translatedData, translationTime, filename): + # File Print String + totalTokenstring = ( + Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" + "[Output: " + + str(translatedData[1][1]) + + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) + + "]" + ) + timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" + + if translatedData[2] == None: + # Success + return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET + + else: + # Fail + try: + raise translatedData[2] + except Exception as e: + traceback.print_exc() + errorString = str(e) + Fore.RED + return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET + + +def openFiles(filename): + with open("files/" + filename, "r", encoding="cp932") as readFile: + translatedData = parseOnscripter(readFile, filename) + + # Delete lines marked for deletion + finalData = [] + for line in translatedData[0]: + if line != "\\d\n": + finalData.append(line) + translatedData[0] = finalData + + return translatedData + + +def parseOnscripter(readFile, filename): + totalTokens = [0, 0] + + # Read File into data + data = readFile.readlines() + + # Create Progress Bar + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + + try: + result = translateOnscripter(data, pbar, filename, []) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def translateOnscripter(data, pbar, filename, translatedList): + stringList = [] + currentGroup = [] + tokens = [0, 0] + speaker = "" + voice = False + global LOCK, ESTIMATE, PBAR + PBAR = pbar + i = 0 + + # Dialogue + while i < len(data): + # Lines + regex = r"^([\u3000「(【[][^\n]+)" + match = re.search(regex, data[i]) + if match != None and match.group(1) != "": + originalString = match.group(1) + # Pass 1 + if translatedList == []: + # Grab Consecutive Strings + jaString = match.group(1) + while len(data) > i + 1 and re.match(regex, data[i + 1]): + data[i] = "" + i += 1 + jaString = f"{jaString} {data[i]}" + + # Convert from Wide + jaString = jaString.translate(wide_to_ascii) + + # Remove any textwrap and \u3000 and \ + jaString = jaString.replace("\n", "") + jaString = jaString.replace("\u3000", "") + jaString = jaString.replace("\\", "") + jaString = jaString.replace(" >", ")") + jaString = jaString.replace("< ", "(") + + # Remove Furigana + furiMatch = re.findall(r"({(.+?)\/(.+?)})", jaString) + if furiMatch: + for match in furiMatch: + jaString = jaString.replace(match[0], match[2]) + + # Add String + stringList.append(jaString.strip()) + + # Pass 2 + else: + # Get Text + if translatedList: + # Grab and Pop + translatedText = translatedList[0] + translatedList.pop(0) + + # Set to None if empty list + if len(translatedList) <= 0: + translatedList = None + + # Textwrap & Other Text + translatedText = textwrap.fill(translatedText, width=WIDTH) + translatedText = translatedText.replace("\n", "\n\u3000") + + # Split the string into lines + lines = translatedText.split("\n") + + # Add a backslash after every 3rd line + j = 0 + while j < len(lines): + if j == 4: + lines[j - 1] = f"{lines[j-1]}\\" + lines[j] = f"\n{lines[j]}" + j += 1 + + # Join the lines back into a single string + translatedText = "\n".join(lines) + + # Remove Double Spaces + translatedText = translatedText.replace(" ", " ") + + # Convert to Wide + translatedText = translatedText.translate(ascii_to_wide) + + # Fix Formatting + translatedText = fixText(translatedText) + + # Set Data + data[i] = data[i].replace(originalString, f"{translatedText}") + i += 1 + + # Choices + elif "csel" in data[i] and translatedList != []: + choiceList = [] + jaString = data[i] + + choiceList = re.findall(r"\"(.*?)\"", jaString) + if len(choiceList) > 0: + # Translate + response = translateGPT(choiceList, "This will be a dialogue option", True) + translatedTextList = response[0] + tokens[0] += response[1][0] + tokens[1] += response[1][1] + + # Set Data + for j in range(len(translatedTextList)): + # Convert to Wide + translatedText = translatedTextList[j].translate(ascii_to_wide) + + # Set + data[i] = data[i].replace(choiceList[j], translatedText) + i += 1 + + # Nothing relevant. Skip Line. + else: + i += 1 + + # EOF + if len(stringList) > 0: + # Set Progress + pbar.total = len(stringList) + pbar.refresh() + + # Translate + response = translateGPT(stringList, "", True) + tokens[0] += response[1][0] + tokens[1] += response[1][1] + translatedList = response[0] + + # Set Strings + if len(stringList) == len(translatedList): + translateOnscripter(data, pbar, filename, translatedList) + + # Mismatch + else: + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + return tokens + + +def fixText(translatedText): + # Add Break + translatedText = translatedText.replace('"', "'") + translatedText = f"\u3000{translatedText}\\" + + # Unconvert Codes + matchList = re.findall(r"([$].+?)[^\w]", translatedText) + if matchList: + for match in matchList: + translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) + + # Unconvert Color Codes + matchList = re.findall(r"([#][\w\d]{6})", translatedText) + if matchList: + for match in matchList: + translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) + + # Unconvert Variables + matchList = re.findall(r"([%]\w.+?)[^\w_]", translatedText) + if matchList: + for match in matchList: + translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) + + # Unconvert Backslashes + matchList = re.findall(r"\", translatedText) + if matchList: + for match in matchList: + translatedText = translatedText.replace(match, match.translate(wide_to_ascii)) + + return translatedText + + +# Save some money and enter the character before translation +def getSpeaker(speaker): + match speaker: + case "ファイン": + return ["Fine", [0, 0]] + case "": + return ["", [0, 0]] + case _: + # Find Speaker + for i in range(len(NAMESLIST)): + if speaker == NAMESLIST[i][0]: + return [NAMESLIST[i][1], [0, 0]] + + # Translate and Store Speaker + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + True, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + response[0] = response[0].replace("Speaker: ", "") + + # Retry if name doesn't translate for some reason + if re.search(r"([a-zA-Z??])", response[0]) == None: + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + + speakerList = [speaker, response[0]] + NAMESLIST.append(speakerList) + return response + return [speaker, [0, 0]] + + +def batchList(input_list, batch_size): + if not isinstance(batch_size, int) or batch_size <= 0: + raise ValueError("batch_size must be a positive integer") + + return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] + + +def createContext(fullPromptFlag, subbedT, format): + system = ( + PROMPT + VOCAB + if fullPromptFlag + else f"\ +You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ +Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ +- You translate everything, including erotic and explicit content.\n\ +- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ +- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ +- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ +- Maintain any spacing in the translation.\n\ +- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ +- `...` can be a part of the dialogue. Translate it as it is.\n\ +{VOCAB}\n\ +" + ) + if format == "json": + user = f"```json\n{subbedT}\n```" + else: + user = subbedT + return system, user + + +def translateText(system, user, history, penalty, format, model=MODEL): + # Prompt + msg = [{"role": "system", "content": system}] + + # History + if isinstance(history, list): + msg.extend([{"role": "system", "content": h} for h in history]) + else: + msg.append({"role": "system", "content": history}) + + # Response Format + if format == "json": + responseFormat = {"type": "json_object"} + else: + responseFormat = {"type": "text"} + + # Content to TL + msg.append({"role": "user", "content": f"{user}"}) + response = openai.chat.completions.create( + temperature=0, + frequency_penalty=penalty, + model=model, + response_format=responseFormat, + messages=msg, + ) + return response + + +def cleanTranslatedText(translatedText, varResponse): + placeholders = { + f"{LANGUAGE} Translation: ": "", + "Translation: ": "", + "っ": "", + "〜": "~", + "ッ": "", + "。": ".", + "「": '\\"', + "」": '\\"', + "- ": "-", + "—": "―", + "】": "]", + "【": "[", + "Placeholder Text": "", + # Add more replacements as needed + } + for target, replacement in placeholders.items(): + translatedText = translatedText.replace(target, replacement) + + # Remove Repeating Characters + pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") + translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) + + # Elongate Long Dashes (Since GPT Ignores them...) + translatedText = elongateCharacters(translatedText) + return translatedText + + +def elongateCharacters(text): + # Define a pattern to match one character followed by one or more `ー` characters + # Using a positive lookbehind assertion to capture the preceding character + pattern = r"(?<=(.))ー+" + + # Define a replacement function that elongates the captured character + def repl(match): + char = match.group(1) # The character before the ー sequence + count = len(match.group(0)) - 1 # Number of ー characters + return char * count # Replace ー sequence with the character repeated + + # Use re.sub() to replace the pattern in the text + return re.sub(pattern, repl, text) + + +def extractTranslation(translatedTextList, is_list): + try: + translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) + translatedTextList = re.sub(r"(? instead -FIXTEXTWRAP = True # Overwrites textwrap -IGNORETLTEXT = False # Ignores all translated text. -MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) -FILENAME = None - -# tqdm Globals -BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" -POSITION = 0 -LEAVE = False -PBAR = None - -# Pricing - Depends on the model https://openai.com/pricing -# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request -# If you are getting a MISMATCH LENGTH error, lower the batch size. -if "gpt-3.5" in MODEL: - INPUTAPICOST = 0.002 - OUTPUTAPICOST = 0.002 - BATCHSIZE = 10 -elif "gpt-4" in MODEL: - INPUTAPICOST = 0.0025 - OUTPUTAPICOST = 0.01 - BATCHSIZE = 40 -else: - INPUTAPICOST = float(os.getenv("input_cost")) - OUTPUTAPICOST = float(os.getenv("output_cost")) - BATCHSIZE = int(os.getenv("batchsize")) - FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) - - -def handleRenpy(filename, estimate): - global ESTIMATE - global FILENAME - FILENAME = filename - ESTIMATE = estimate - - if ESTIMATE: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - - # Print Total - totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") - - # Print any errors on maps - if len(MISMATCH) > 0: - return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET - else: - return totalString - - else: - try: - with open("translated/" + filename, "w", encoding="utf8", errors="ignore") as outFile: - start = time.time() - translatedData = openFiles(filename) - - # Print Result - end = time.time() - outFile.writelines(translatedData[0]) - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - except Exception: - traceback.print_exc() - return "Fail" - - return getResultString(["", TOKENS, None], end - start, "TOTAL") - - -def getResultString(translatedData, translationTime, filename): - # File Print String - totalTokenstring = ( - Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" - "[Output: " - + str(translatedData[1][1]) - + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) - + "]" - ) - timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" - - if translatedData[2] == None: - # Success - return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET - - else: - # Fail - try: - raise translatedData[2] - except Exception as e: - traceback.print_exc() - errorString = str(e) + Fore.RED - return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET - - -def openFiles(filename): - with open("files/" + filename, "r", encoding="utf8") as readFile: - translatedData = parseRenpy(readFile, filename) - - # Delete lines marked for deletion - finalData = [] - for line in translatedData[0]: - if line != "\\d\n": - finalData.append(line) - translatedData[0] = finalData - - return translatedData - - -def parseRenpy(readFile, filename): - global PBAR - totalTokens = [0, 0] - - # Read File into data - data = readFile.readlines() - - # Create Progress Bar - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - PBAR = pbar - - try: - result = translateRenpy(data, []) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def translateRenpy(data, translatedList): - stringList = [] - currentGroup = [] - tokens = [0, 0] - speaker = "" - voice = False - global LOCK, ESTIMATE, FILENAME, PBAR - i = 0 - - while i < len(data): - voice = False - speaker = "" - lineRegexNoSpeaker = r'^\s\s\s\s"(.*)"' - lineRegexSpeaker = r'^\s\s\s\s(.+?)\s"(.*)"' - - # Grab Line - match = re.search(lineRegexSpeaker, data[i]) - if match: - response = getSpeaker(match.group(1)) - jaString = match.group(2) - speaker = response[0] - tokens[0] += response[1][0] - tokens[1] += response[1][1] - else: - match = re.search(lineRegexNoSpeaker, data[i]) - if match: - jaString = match.group(1) - - # Valid Line - if match and "voice" not in data[i]: - originalString = jaString - # Pass 1 - if translatedList == []: - # Remove any textwrap - jaString = jaString.replace("\\n", " ") - - # Add String - if speaker: - stringList.append(f"[{speaker}]: {jaString.strip()}") - else: - stringList.append(jaString.strip()) - - # Pass 2 - else: - # Get Text - if translatedList: - # Grab and Pop - translatedText = translatedList[0] - translatedList.pop(0) - - # Set to None if empty list - if len(translatedList) <= 0: - translatedList = None - - # Remove speaker - if speaker != "": - matchSpeakerList = re.findall(r"^\[?(.+?)\]?\s?[|:]\s?", translatedText) - translatedText = re.sub(r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText) - - # Escape Quotes - translatedText = re.sub(r'[\\]*(")', '\\"', translatedText) - translatedText = re.sub(r"[\\]*(')", "\\'", translatedText) - - # Textwrap - translatedText = textwrap.fill(translatedText, width=WIDTH) - translatedText = translatedText.replace("\n", "\\n") - - # Set Data - data[i] = data[i].replace(originalString, translatedText) - i += 1 - else: - i += 1 - - # EOF - if len(stringList) > 0: - # Set Progress - PBAR.total = len(stringList) - PBAR.refresh() - - # Translate - response = translateGPT(stringList, "Reply with the English TL of the NPC Name", True) - tokens[0] += response[1][0] - tokens[1] += response[1][1] - translatedList = response[0] - - # Set Strings - if len(stringList) == len(translatedList): - translateRenpy(data, translatedList) - - # Mismatch - else: - with LOCK: - if FILENAME not in MISMATCH: - MISMATCH.append(FILENAME) - return tokens - - -# Save some money and enter the character before translation -def getSpeaker(speaker): - match speaker: - case "ファイン": - return ["Fine", [0, 0]] - case "": - return ["", [0, 0]] - case _: - # Find Speaker - for i in range(len(NAMESLIST)): - if speaker == NAMESLIST[i][0]: - return [NAMESLIST[i][1], [0, 0]] - - # Translate and Store Speaker - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - True, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - response[0] = response[0].replace("Speaker: ", "") - - # Retry if name doesn't translate for some reason - if re.search(r"([a-zA-Z??])", response[0]) == None: - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - - speakerList = [speaker, response[0]] - NAMESLIST.append(speakerList) - return response - return [speaker, [0, 0]] - - -def batchList(input_list, batch_size): - if not isinstance(batch_size, int) or batch_size <= 0: - raise ValueError("batch_size must be a positive integer") - - return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] - - -def createContext(fullPromptFlag, subbedT, format): - system = ( - PROMPT + VOCAB - if fullPromptFlag - else f"\ -You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ -Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ -- You translate everything, including erotic and explicit content.\n\ -- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ -- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ -- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ -- Maintain any spacing in the translation.\n\ -- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ -- `...` can be a part of the dialogue. Translate it as it is.\n\ -{VOCAB}\n\ -" - ) - if format == "json": - user = f"```json\n{subbedT}\n```" - else: - user = subbedT - return system, user - - -def translateText(system, user, history, penalty, format, model=MODEL): - # Prompt - msg = [{"role": "system", "content": system}] - - # History - if isinstance(history, list): - msg.extend([{"role": "system", "content": h} for h in history]) - else: - msg.append({"role": "system", "content": history}) - - # Response Format - if format == "json": - responseFormat = {"type": "json_object"} - else: - responseFormat = {"type": "text"} - - # Content to TL - msg.append({"role": "user", "content": f"{user}"}) - response = openai.chat.completions.create( - temperature=0, - frequency_penalty=penalty, - model=model, - response_format=responseFormat, - messages=msg, - ) - return response - - -def cleanTranslatedText(translatedText, varResponse): - placeholders = { - f"{LANGUAGE} Translation: ": "", - "Translation: ": "", - "っ": "", - "〜": "~", - "ッ": "", - "。": ".", - "「": '\\"', - "」": '\\"', - "- ": "-", - "—": "―", - "】": "]", - "【": "[", - "Placeholder Text": "", - # Add more replacements as needed - } - for target, replacement in placeholders.items(): - translatedText = translatedText.replace(target, replacement) - - # Remove Repeating Characters - pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") - translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) - - # Elongate Long Dashes (Since GPT Ignores them...) - translatedText = elongateCharacters(translatedText) - return translatedText - - -def elongateCharacters(text): - # Define a pattern to match one character followed by one or more `ー` characters - # Using a positive lookbehind assertion to capture the preceding character - pattern = r"(?<=(.))ー+" - - # Define a replacement function that elongates the captured character - def repl(match): - char = match.group(1) # The character before the ー sequence - count = len(match.group(0)) - 1 # Number of ー characters - return char * count # Replace ー sequence with the character repeated - - # Use re.sub() to replace the pattern in the text - return re.sub(pattern, repl, text) - - -def extractTranslation(translatedTextList, is_list): - try: - translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) - translatedTextList = re.sub(r"(? instead +FIXTEXTWRAP = True # Overwrites textwrap +IGNORETLTEXT = False # Ignores all translated text. +MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) +FILENAME = None + +# tqdm Globals +BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" +POSITION = 0 +LEAVE = False +PBAR = None + +# Pricing - Depends on the model https://openai.com/pricing +# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request +# If you are getting a MISMATCH LENGTH error, lower the batch size. +if "gpt-3.5" in MODEL: + INPUTAPICOST = 0.002 + OUTPUTAPICOST = 0.002 + BATCHSIZE = 10 +elif "gpt-4" in MODEL: + INPUTAPICOST = 0.0025 + OUTPUTAPICOST = 0.01 + BATCHSIZE = 40 +else: + INPUTAPICOST = float(os.getenv("input_cost")) + OUTPUTAPICOST = float(os.getenv("output_cost")) + BATCHSIZE = int(os.getenv("batchsize")) + FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) + + +def handleRenpy(filename, estimate): + global ESTIMATE + global FILENAME + FILENAME = filename + ESTIMATE = estimate + + if ESTIMATE: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + + # Print Total + totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") + + # Print any errors on maps + if len(MISMATCH) > 0: + return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET + else: + return totalString + + else: + try: + with open("translated/" + filename, "w", encoding="utf8", errors="ignore") as outFile: + start = time.time() + translatedData = openFiles(filename) + + # Print Result + end = time.time() + outFile.writelines(translatedData[0]) + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + except Exception: + traceback.print_exc() + return "Fail" + + return getResultString(["", TOKENS, None], end - start, "TOTAL") + + +def getResultString(translatedData, translationTime, filename): + # File Print String + totalTokenstring = ( + Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" + "[Output: " + + str(translatedData[1][1]) + + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) + + "]" + ) + timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" + + if translatedData[2] == None: + # Success + return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET + + else: + # Fail + try: + raise translatedData[2] + except Exception as e: + traceback.print_exc() + errorString = str(e) + Fore.RED + return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET + + +def openFiles(filename): + with open("files/" + filename, "r", encoding="utf8") as readFile: + translatedData = parseRenpy(readFile, filename) + + # Delete lines marked for deletion + finalData = [] + for line in translatedData[0]: + if line != "\\d\n": + finalData.append(line) + translatedData[0] = finalData + + return translatedData + + +def parseRenpy(readFile, filename): + global PBAR + totalTokens = [0, 0] + + # Read File into data + data = readFile.readlines() + + # Create Progress Bar + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + PBAR = pbar + + try: + result = translateRenpy(data, []) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def translateRenpy(data, translatedList): + stringList = [] + currentGroup = [] + tokens = [0, 0] + speaker = "" + voice = False + global LOCK, ESTIMATE, FILENAME, PBAR + i = 0 + + while i < len(data): + voice = False + speaker = "" + lineRegexNoSpeaker = r'^\s\s\s\s"(.*)"' + lineRegexSpeaker = r'^\s\s\s\s(.+?)\s"(.*)"' + + # Grab Line + match = re.search(lineRegexSpeaker, data[i]) + if match: + response = getSpeaker(match.group(1)) + jaString = match.group(2) + speaker = response[0] + tokens[0] += response[1][0] + tokens[1] += response[1][1] + else: + match = re.search(lineRegexNoSpeaker, data[i]) + if match: + jaString = match.group(1) + + # Valid Line + if match and "voice" not in data[i]: + originalString = jaString + # Pass 1 + if translatedList == []: + # Remove any textwrap + jaString = jaString.replace("\\n", " ") + + # Add String + if speaker: + stringList.append(f"[{speaker}]: {jaString.strip()}") + else: + stringList.append(jaString.strip()) + + # Pass 2 + else: + # Get Text + if translatedList: + # Grab and Pop + translatedText = translatedList[0] + translatedList.pop(0) + + # Set to None if empty list + if len(translatedList) <= 0: + translatedList = None + + # Remove speaker + if speaker != "": + matchSpeakerList = re.findall(r"^\[?(.+?)\]?\s?[|:]\s?", translatedText) + translatedText = re.sub(r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText) + + # Escape Quotes + translatedText = re.sub(r'[\\]*(")', '\\"', translatedText) + translatedText = re.sub(r"[\\]*(')", "\\'", translatedText) + + # Textwrap + translatedText = textwrap.fill(translatedText, width=WIDTH) + translatedText = translatedText.replace("\n", "\\n") + + # Set Data + data[i] = data[i].replace(originalString, translatedText) + i += 1 + else: + i += 1 + + # EOF + if len(stringList) > 0: + # Set Progress + PBAR.total = len(stringList) + PBAR.refresh() + + # Translate + response = translateGPT(stringList, "Reply with the English TL of the NPC Name", True) + tokens[0] += response[1][0] + tokens[1] += response[1][1] + translatedList = response[0] + + # Set Strings + if len(stringList) == len(translatedList): + translateRenpy(data, translatedList) + + # Mismatch + else: + with LOCK: + if FILENAME not in MISMATCH: + MISMATCH.append(FILENAME) + return tokens + + +# Save some money and enter the character before translation +def getSpeaker(speaker): + match speaker: + case "ファイン": + return ["Fine", [0, 0]] + case "": + return ["", [0, 0]] + case _: + # Find Speaker + for i in range(len(NAMESLIST)): + if speaker == NAMESLIST[i][0]: + return [NAMESLIST[i][1], [0, 0]] + + # Translate and Store Speaker + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + True, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + response[0] = response[0].replace("Speaker: ", "") + + # Retry if name doesn't translate for some reason + if re.search(r"([a-zA-Z??])", response[0]) == None: + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + + speakerList = [speaker, response[0]] + NAMESLIST.append(speakerList) + return response + return [speaker, [0, 0]] + + +def batchList(input_list, batch_size): + if not isinstance(batch_size, int) or batch_size <= 0: + raise ValueError("batch_size must be a positive integer") + + return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] + + +def createContext(fullPromptFlag, subbedT, format): + system = ( + PROMPT + VOCAB + if fullPromptFlag + else f"\ +You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ +Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ +- You translate everything, including erotic and explicit content.\n\ +- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ +- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ +- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ +- Maintain any spacing in the translation.\n\ +- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ +- `...` can be a part of the dialogue. Translate it as it is.\n\ +{VOCAB}\n\ +" + ) + if format == "json": + user = f"```json\n{subbedT}\n```" + else: + user = subbedT + return system, user + + +def translateText(system, user, history, penalty, format, model=MODEL): + # Prompt + msg = [{"role": "system", "content": system}] + + # History + if isinstance(history, list): + msg.extend([{"role": "system", "content": h} for h in history]) + else: + msg.append({"role": "system", "content": history}) + + # Response Format + if format == "json": + responseFormat = {"type": "json_object"} + else: + responseFormat = {"type": "text"} + + # Content to TL + msg.append({"role": "user", "content": f"{user}"}) + response = openai.chat.completions.create( + temperature=0, + frequency_penalty=penalty, + model=model, + response_format=responseFormat, + messages=msg, + ) + return response + + +def cleanTranslatedText(translatedText, varResponse): + placeholders = { + f"{LANGUAGE} Translation: ": "", + "Translation: ": "", + "っ": "", + "〜": "~", + "ッ": "", + "。": ".", + "「": '\\"', + "」": '\\"', + "- ": "-", + "—": "―", + "】": "]", + "【": "[", + "Placeholder Text": "", + # Add more replacements as needed + } + for target, replacement in placeholders.items(): + translatedText = translatedText.replace(target, replacement) + + # Remove Repeating Characters + pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") + translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) + + # Elongate Long Dashes (Since GPT Ignores them...) + translatedText = elongateCharacters(translatedText) + return translatedText + + +def elongateCharacters(text): + # Define a pattern to match one character followed by one or more `ー` characters + # Using a positive lookbehind assertion to capture the preceding character + pattern = r"(?<=(.))ー+" + + # Define a replacement function that elongates the captured character + def repl(match): + char = match.group(1) # The character before the ー sequence + count = len(match.group(0)) - 1 # Number of ー characters + return char * count # Replace ー sequence with the character repeated + + # Use re.sub() to replace the pattern in the text + return re.sub(pattern, repl, text) + + +def extractTranslation(translatedTextList, is_list): + try: + translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) + translatedTextList = re.sub(r"(? instead -FIXTEXTWRAP = True # Overwrites textwrap -IGNORETLTEXT = False # Ignores all translated text. -MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) -BRACKETNAMES = False -PBAR = None -FILENAME = None - -# Pricing - Depends on the model https://openai.com/pricing -# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request -# If you are getting a MISMATCH LENGTH error, lower the batch size. -if "gpt-3.5" in MODEL: - INPUTAPICOST = 0.002 - OUTPUTAPICOST = 0.002 - BATCHSIZE = 10 - FREQUENCY_PENALTY = 0.2 -elif "gpt-4" in MODEL: - INPUTAPICOST = 0.0025 - OUTPUTAPICOST = 0.01 - BATCHSIZE = 20 - FREQUENCY_PENALTY = 0.1 -else: - INPUTAPICOST = float(os.getenv("input_cost")) - OUTPUTAPICOST = float(os.getenv("output_cost")) - BATCHSIZE = int(os.getenv("batchsize")) - FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) - -# tqdm Globals -BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" -POSITION = 0 -LEAVE = False - -# Dialogue / Scroll / Choices (Main Codes) -CODE401 = True -CODE405 = False -CODE102 = False - -# Optional -CODE101 = False # Turn this one when names exist in 101 -CODE408 = False # Warning, translates comments and can inflate costs. - -# Variables -CODE122 = False - -# Other -CODE355655 = False -CODE357 = False -CODE657 = False -CODE356 = False -CODE320 = False -CODE324 = False -CODE111 = False -CODE108 = False - - -def handleACE(filename, estimate): - global ESTIMATE, TOKENS, FILENAME - ESTIMATE = estimate - FILENAME = filename - - # Translate - start = time.time() - translatedData = openFiles(filename) - - # Translate - if not estimate: - try: - with open("translated/" + filename, "w", encoding="utf-8") as outFile: - yaml = YAML(pure=True) - yaml.width = 4096 - yaml.default_style = "'" - yaml.dump(translatedData[0], outFile) - except Exception: - traceback.print_exc() - return "Fail" - - # Print File - end = time.time() - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - - # Print Total - totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") - - # Print any errors on maps - if len(MISMATCH) > 0: - return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET - else: - return totalString - - -def openFiles(filename): - yaml = YAML(pure=True) # Need a yaml instance per thread. - yaml.width = 4096 - yaml.default_style = "'" - - with open("files/" + filename, "r", encoding="UTF-8") as f: - # Map Files - if "Map" in filename and "MapInfos" not in filename: - data = yaml.load(f) - translatedData = parseMap(data, filename) - - # CommonEvents Files - elif "CommonEvents" in filename: - data = yaml.load(f) - translatedData = parseCommonEvents(data, filename) - - # Actor File - elif "Actors" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Actors") - - # Armor File - elif "Armors" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Armors") - - # Weapons File - elif "Weapons" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Weapons") - - # Classes File - elif "Classes" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Classes") - - # Enemies File - elif "Enemies" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Enemies") - - # Items File - elif "Items" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Items") - - # MapInfo File - elif "MapInfos" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "MapInfos") - - # Skills File - elif "Skills" in filename: - data = yaml.load(f) - translatedData = parseNames(data, filename, "Skills") - - # Troops File - elif "Troops" in filename: - data = yaml.load(f) - translatedData = parseTroops(data, filename) - - # States File - elif "States" in filename: - data = yaml.load(f) - translatedData = parseSS(data, filename) - - # System File - elif "System" in filename: - data = yaml.load(f) - translatedData = parseSystem(data, filename) - - # Scenario File - elif "Scenario" in filename: - data = yaml.load(f) - translatedData = parseScenario(data, filename) - - else: - raise NameError(filename + " Not Supported") - - return translatedData - - -def getResultString(translatedData, translationTime, filename): - # File Print String - totalTokenstring = ( - Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" - "[Output: " - + str(translatedData[1][1]) - + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) - + "]" - ) - timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" - - if translatedData[2] is None: - # Success - return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET - else: - # Fail - try: - raise translatedData[2] - except Exception as e: - traceback.print_exc() - errorString = str(e) + Fore.RED - return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET - - -def parseMap(data, filename): - totalTokens = [0, 0] - totalLines = 0 - events = data["events"] - global LOCK - - # Translate displayName for Map files - if "Map" in filename: - response = translateGPT( - data["display_name"], - "Reply with only the " + LANGUAGE + " translation of the RPG location name", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - data["display_name"] = response[0].replace('"', "") - - # Thread for each page in file - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - with ThreadPoolExecutor(max_workers=THREADS) as executor: - for key in events: - if key is not None: - futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in events[key]["pages"] if page is not None] - for future in as_completed(futures): - try: - totalTokensFuture = future.result() - totalTokens[0] += totalTokensFuture[0] - totalTokens[1] += totalTokensFuture[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def translateNote(event, regex): - # Regex String - jaString = event["note"] - match = re.findall(regex, jaString, re.DOTALL) - if match: - tokens = [0, 0] - i = 0 - while i < len(match): - initialJAString = match[i] - # Remove any textwrap - modifiedJAString = initialJAString.replace("\n", " ") - - # Translate - response = translateGPT( - modifiedJAString, - "Reply with only the " + LANGUAGE + " translation.", - False, - ) - translatedText = response[0] - tokens[0] += response[1][0] - tokens[1] += response[1][1] - - # Textwrap - translatedText = textwrap.fill(translatedText, width=NOTEWIDTH) - translatedText = translatedText.replace('"', "") - jaString = jaString.replace(initialJAString, translatedText) - event["note"] = jaString - i += 1 - return tokens - return [0, 0] - - -# For notes that can't have spaces. -def translateNoteOmitSpace(event, regex): - # Regex that only matches text inside LB. - jaString = event["note"] - - match = re.findall(regex, jaString, re.DOTALL) - if match: - oldJAString = match[0] - # Remove any textwrap - jaString = re.sub(r"\n", " ", oldJAString) - - # Translate - response = translateGPT( - jaString, - "Reply with the " + LANGUAGE + " translation of the location name.", - False, - ) - translatedText = response[0] - - translatedText = translatedText.replace('"', "") - translatedText = translatedText.replace(" ", "_") - event["note"] = event["note"].replace(oldJAString, translatedText) - return response[1] - return [0, 0] - - -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"]) - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - with ThreadPoolExecutor(max_workers=THREADS) as executor: - futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in data if page is not None] - for future in as_completed(futures): - try: - totalTokensFuture = future.result() - totalTokens[0] += totalTokensFuture[0] - totalTokens[1] += totalTokensFuture[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -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"]: - totalLines += len(page["list"]) + 1 # The +1 is because each page has a name. - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - for troop in data: - if troop is not None: - with ThreadPoolExecutor(max_workers=THREADS) as executor: - futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in troop["pages"] if page is not None] - for future in as_completed(futures): - try: - totalTokensFuture = future.result() - totalTokens[0] += totalTokensFuture[0] - totalTokens[1] += totalTokensFuture[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def parseNames(data, filename, context): - totalTokens = [0, 0] - totalLines = 0 - totalLines += len(data) - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - try: - result = searchNames(data, pbar, context) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def parseSS(data, filename): - totalTokens = [0, 0] - totalLines = 0 - totalLines += len(data) - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - for ss in data: - if ss is not None: - try: - result = searchSS(ss, pbar) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def parseSystem(data, filename): - totalTokens = [0, 0] - totalLines = 0 - - # Calculate Total Lines - for term in data["terms"]: - termList = data["terms"][term] - totalLines += len(termList) - totalLines += len(data["game_title"]) - totalLines += len(data["variables"]) - totalLines += len(data["weapon_types"]) - totalLines += len(data["armor_types"]) - totalLines += len(data["skill_types"]) - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - try: - result = searchSystem(data, pbar) - totalTokens[0] += result[0] - totalTokens[1] += result[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -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]) - - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - with ThreadPoolExecutor(max_workers=THREADS) as executor: - futures = [executor.submit(searchCodes, page[1], pbar, [], filename) for page in data.items() if page[1] is not None] - for future in as_completed(futures): - try: - totalTokensFuture = future.result() - totalTokens[0] += totalTokensFuture[0] - totalTokens[1] += totalTokensFuture[1] - except Exception as e: - traceback.print_exc() - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def searchNames(data, pbar, context): - totalTokens = [0, 0] - nameList = [] - profileList = [] - nicknameList = [] - descriptionList = [] - noteList = [] - i = 0 # Counter - j = 0 # Counter 2 - filling = False - mismatch = False - batchFull = False - - # Set the context of what we are translating - if "Actors" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the NPC name" - if "Armors" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the RPG equipment name" - if "Classes" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the RPG class name" - if "MapInfos" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the location name" - if "Enemies" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the enemy NPC name" - if "Weapons" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the RPG weapon name" - if "Items" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the RPG item name" - if "Skills" in context: - newContext = "Reply with only the " + LANGUAGE + " translation of the RPG skill name" - - # Names - with open("translations.txt", "a", encoding="utf-8") as file: - file.write(f"\n#{context}\n") - while i < len(data) or filling == True: - if i < len(data): - # Empty Data - if "MapInfos" in FILENAME and i == 0: - i += 1 - if "MapInfos" in FILENAME and j == 0: - j += 1 - if data[i] is None or data[i]["name"] == "": - i += 1 - - continue - - # Filling up Batch - filling = True - if context in "Actors": - if len(nameList) < BATCHSIZE: - if data[i]["name"] != "": - nameList.append(data[i]["name"]) - if data[i]["nickname"] != "": - nicknameList.append(data[i]["nickname"]) - if data[i]["description"] != "": - profileList.append(data[i]["description"].replace("\n", " ")) - - # Notes - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "PE拡張" in data[i]["note"]: - tokensResponse = translateNote(data[i], r"") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - i += 1 - else: - batchFull = True - if context in ["Armors", "Weapons", "Items"]: - if len(nameList) < BATCHSIZE: - nameList.append(data[i]["name"]) - if "description" in data[i] and data[i]["description"] != "": - descriptionList.append(data[i]["description"].replace("\n", " ")) - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "Switch Shop Description" in data[i]["note"]: - tokensResponse = translateNote(data[i], r"\n(.*)\n") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - - i += 1 - else: - batchFull = True - if context in ["Skills"]: - if len(nameList) < BATCHSIZE: - nameList.append(data[i]["name"]) - if "description" in data[i] and data[i]["description"] != "": - descriptionList.append(data[i]["description"].replace("\n", " ")) - - # Messages - number = 1 - while number < 5: - if f"message{number}" in data[i]: - if len(data[i][f"message{number}"]) > 0 and data[i][f"message{number}"][0] in ["は", "を", "の", "に", "が"]: - msgResponse = translateGPT( - "Taro" + data[i][f"message{number}"], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - False, - ) - data[i][f"message{number}"] = msgResponse[0].replace("Taro", "") - totalTokens[0] += msgResponse[1][0] - totalTokens[1] += msgResponse[1][1] - number += 1 - - else: - msgResponse = translateGPT( - data[i][f"message{number}"], - "reply with only the gender neutral " + LANGUAGE + " translation", - False, - ) - data[i][f"message{number}"] = msgResponse[0] - totalTokens[0] += msgResponse[1][0] - totalTokens[1] += msgResponse[1][1] - number += 1 - else: - number += 1 - - i += 1 - else: - batchFull = True - if context in ["Enemies", "Classes", "MapInfos"]: - if len(nameList) < BATCHSIZE: - nameList.append(data[i]["name"]) - - # Notes - if "note" in data[i]: - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - if "") - totalTokens[0] += tokensResponse[0] - totalTokens[1] += tokensResponse[1] - i += 1 - else: - batchFull = True - - # Batch Full - if batchFull == True or i >= len(data): - k = j # Original Index - if context in "Actors": - # Name - response = translateGPT(nameList, newContext, True) - translatedNameBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Nickname - response = translateGPT(nicknameList, newContext, True) - translatedNicknameBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Profile - response = translateGPT(profileList, "", True) - translatedProfileBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # 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"] != "": - with open("translations.txt", "a", encoding="utf-8") as file: - file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n') - data[j]["name"] = translatedNameBatch[0] - translatedNameBatch.pop(0) - if data[j]["nickname"] != "": - data[j]["nickname"] = translatedNicknameBatch[0] - translatedNicknameBatch.pop(0) - if data[j]["profile"] != "": - data[j]["profile"] = textwrap.fill(translatedProfileBatch[0], LISTWIDTH) - translatedProfileBatch.pop(0) - - # If Batch is empty. Move on. - if len(translatedNameBatch) == 0: - nameList.clear() - filling = False - j += 1 - else: - mismatch = True - - if context in ["Armors", "Weapons", "Items", "Skills"]: - # Name - response = translateGPT(nameList, newContext, True) - translatedNameBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Description - response = translateGPT( - descriptionList, - f"Reply with only the {LANGUAGE} translation of the text.", - True, - ) - translatedDescriptionBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - if len(nameList) == len(translatedNameBatch): - j = k - with open("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 - file.write(f'{data[j]['name']} ({translatedNameBatch[0]})\n') - data[j]["name"] = translatedNameBatch[0] - translatedNameBatch.pop(0) - if "description" in data[j] and data[j]["description"] != "": - data[j]["description"] = textwrap.fill(translatedDescriptionBatch[0], LISTWIDTH) - translatedDescriptionBatch.pop(0) - - # If Batch is empty. Move on. - if len(translatedNameBatch) == 0: - nameList.clear() - descriptionList.clear() - batchFull = False - filling = False - j += 1 - else: - mismatch = True - if context in ["Enemies", "Classes", "MapInfos"]: - response = translateGPT(nameList, newContext, True) - translatedNameBatch = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # 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: - with open("translations.txt", "a", encoding="utf-8") as file: - file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n') - # Get Text - data[j]["name"] = translatedNameBatch[0] - translatedNameBatch.pop(0) - - # If Batch is empty. Move on. - if len(translatedNameBatch) == 0: - nameList.clear() - batchFull = False - filling = False - j += 1 - else: - mismatch = True - - # Mismatch - if mismatch == True: - MISMATCH.append(nameList) - nameList.clear() - profileList.clear() - descriptionList.clear() - filling = False - mismatch = False - - i += 1 - - return totalTokens - - -def searchCodes(page, pbar, jobList, filename): - if len(jobList) > 0: - list401 = jobList[0] - list122 = jobList[1] - list355655 = jobList[2] - list108 = jobList[3] - list356 = jobList[4] - list357 = jobList[5] - setData = True - else: - list401 = [] - list122 = [] - list355655 = [] - list108 = [] - list356 = [] - list357 = [] - setData = False - textHistory = [] - match = [] - totalTokens = [0, 0] - translatedText = "" - speaker = "" - speakerID = None - syncIndex = 0 - CLFlag = False - maxHistory = MAXHISTORY - VNameValue = None - speakerWindow = FIRSTLINESPEAKERS - global LOCK - global NAMESLIST - global MISMATCH - global PBAR - with LOCK: - PBAR = pbar - - # Begin Parsing File - try: - # Normal Format - if "list" in page: - codeList = page["list"] - - # Special Format (Scenario) - else: - codeList = page - - # Iterate through page - i = 0 - while i < len(codeList): - with LOCK: - # syncIndex will keep i in sync when it gets modified - if syncIndex > i: - i = syncIndex - if len(codeList) <= i: - break - - # Declare Varss - currentGroup = [] - nametag = "" - - ## Event Code: 401 Show Text - if "c" in codeList[i] and codeList[i]["c"] in [401, 405, -1] and (CODE401 or CODE405): - # Save Code and starting index (j) - code = codeList[i]["c"] - j = i - endtag = "" - - # Grab String - if len(codeList[i]["p"]) > 0: - jaString = codeList[i]["p"][0] - oldjaString = jaString - else: - codeList[i]["c"] = -1 - i += 1 - continue - - # # For Retarded Devs - # retardRegex = r'([\\]+[nN]\[[\\]+V\[\d*?\]\])' - # match = re.search(retardRegex, jaString) - # if match: - # if VNameValue == 1: - # jaString = re.sub(retardRegex, 'リッカ', jaString) - # if VNameValue == 2: - # jaString = re.sub(retardRegex, 'ミミ', jaString) - # if VNameValue == 3: - # jaString = re.sub(retardRegex, 'ヒトミ', jaString) - # if VNameValue == 4: - # jaString = re.sub(retardRegex, 'Taro', jaString) - # if VNameValue == 5: - # jaString = re.sub(retardRegex, '富士見', jaString) - - # Speaker Check - speakerList = [] - - # m and z Codes - match = re.search(r"(.*?)[\\]+m\[\d+?\][\\]+z\[\d+?\]", jaString) - if match: - speakerList.append(match.group(1)) - if "\\c" in speakerList[0]: - speakerList = re.findall( - r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[[\d]\]\\?\\?$", - speakerList[0], - ) - - # Brackets - if len(speakerList) == 0: - speakerList = re.findall(r"^【(.*?)】$", jaString) - - # Colors - if len(speakerList) == 0: - speakerList = re.findall( - r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[[\d]\]\\?\\?$", - jaString, - ) - - # Full Width Space - if len(speakerList) == 0: - speakerList = re.findall(r"^[  ](.*)", jaString) - - # First Line Speakers - if len(speakerList) == 0 and FIRSTLINESPEAKERS is True: - # Remove any RPGMaker Code at start - ffMatch = re.search( - r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", - jaString, - ) - if ffMatch != None: - jaString = jaString.replace(ffMatch.group(0), "") - nametag += ffMatch.group(0) - - # Test Speaker - if ( - len(jaString) < 40 - and "c" in codeList[i + 1] - and codeList[i + 1]["c"] in [401, 405, -1] - and len(codeList[i + 1]["p"]) > 0 - and len(codeList[i + 1]["p"][0]) > 0 - ): - if codeList[i + 1]["p"] != "" and codeList[i + 1]["p"][0].strip()[0] in [ - "「", - '"', - "(", - "(", - "*", - "[", - ]: - speakerList = re.findall(r".+", jaString) - - if len(speakerList) != 0 and codeList[i + 1]["c"] in [401, 405, -1]: - # Get Speaker - response = getSpeaker(speakerList[0]) - speaker = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - 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 401's in a row. - currentGroup.append(jaString) - - # Join Up 401's into single string - if len(codeList) > i + 1: - while codeList[i + 1]["c"] in [401, 405, -1]: - if setData == True: - codeList[i]["p"] = [] - codeList[i]["c"] = -1 - i += 1 - j = i - - # Only add if not empty - if len(codeList[i]["p"]) > 0: - jaString = codeList[i]["p"][0] - currentGroup.append(jaString) - - # Make sure not the end of the list. - if len(codeList) <= i + 1: - break - - # Format String - if len(currentGroup) > 0: - finalJAString = "\n".join(currentGroup) - oldjaString = finalJAString - - # Check if Empty - if finalJAString == "": - i += 1 - continue - - # Set Back - if setData == True: - codeList[i]["p"] = [finalJAString] - - ### \\n - nCase = None - regex = r"([\\]+[kKnN][wWcCrRrEe]?[\[<](.*?)[>\]])" - match = re.search(regex, finalJAString) - - # Set Name - if match: - nametag = match.group(1) - speaker = match.group(2) - - # Translate Speaker - response = getSpeaker(speaker) - 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(speaker, tledSpeaker) - speaker = tledSpeaker - - # Bracket Names - if BRACKETNAMES is True and len(matchList) != 0: - if matchList[0][0] != "": - match0 = matchList[0][0] - match1 = matchList[0][1] - else: - match0 = matchList[0][2] - match1 = matchList[0][3] - - # Translate Speaker - speakerID = j - response = getSpeaker(match1) - speaker = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Nametag and Remove from Final String - fullSpeaker = match0.replace(match1, speaker) - finalJAString = finalJAString.replace(match0, "") - - # Set next item as dialogue - if codeList[j + 1]["c"] == 401 or codeList[j + 1]["c"] == -1: - # Set name var to top of list - codeList[j]["p"] = [fullSpeaker] - codeList[j]["c"] = code - j += 1 - codeList[j]["p"] = [finalJAString] - codeList[j]["c"] = code - else: - # Set nametag in string - codeList[j]["p"] = [fullSpeaker + finalJAString] - codeList[j]["c"] = code - - # Remove Extra Stuff bad for translation. - finalJAString = finalJAString.replace("゙", "") - finalJAString = finalJAString.replace("―", "-") - finalJAString = finalJAString.replace("…", "...") - finalJAString = finalJAString.replace("。", ".") - finalJAString = re.sub(r"(\.{3}\.+)", "...", finalJAString) - finalJAString = finalJAString.replace(" ", "") - finalJAString = finalJAString.replace("「", '"') - finalJAString = finalJAString.replace("」", '"') - - ### Remove format codes - # Furigana - rcodeMatch = re.findall(r"([\\]+[r][b]?\[.*?,(.*?)\])", finalJAString) - if len(rcodeMatch) > 0: - for match in rcodeMatch: - finalJAString = finalJAString.replace(match[0], match[1]) - - # Formatting - formatMatch = re.findall(r"[\\]+[!><.|#^{}]", finalJAString) - if len(formatMatch) > 0: - for match in formatMatch: - finalJAString = finalJAString.replace(match, "") - - # Remove any RPGMaker Code at start - ffMatch = re.search( - r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", - finalJAString, - ) - if ffMatch != None: - finalJAString = finalJAString.replace(ffMatch.group(0), "") - nametag += ffMatch.group(0) - - # Remove _ABL Codes - ffMatch = re.search(r"^(_ABL).*", finalJAString) - if ffMatch != None: - finalJAString = finalJAString.replace(ffMatch.group(1), "") - nametag += ffMatch.group(1) - - # Center Lines - if "\\CL" in finalJAString or "\\ac" in finalJAString: - finalJAString = finalJAString.replace("\\CL ", "") - finalJAString = finalJAString.replace("\\CL", "") - finalJAString = finalJAString.replace("\\ac ", "") - finalJAString = finalJAString.replace("\\ac", "") - CLFlag = True - - # 1st Passthrough (Grabbing Data) - if setData == False: - if finalJAString != "": - if speaker == "" and finalJAString != "": - list401.append(finalJAString) - elif finalJAString != "": - list401.append(f"[{speaker}]: {finalJAString}") - else: - list401.append(speaker) - speaker = "" - match = [] - nametag = "" - currentGroup = [] - syncIndex = i + 1 - - # Keep textHistory list at length maxHistory - textHistory.append('"' + finalJAString + '"') - if len(textHistory) > maxHistory: - textHistory.pop(0) - - # 2nd Passthrough (Setting Data) - else: - # Grab Translated String - if len(list401) > 0: - translatedText = list401[0] - - # Remove speaker - if speaker != "": - matchSpeakerList = re.findall(r"^\[?(.+?)\]?\s?[|:]\s?", translatedText) - if len(matchSpeakerList) > 0: - newSpeaker = matchSpeakerList[0] - nametag = nametag.replace(speaker, newSpeaker) - translatedText = re.sub(r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText) - - # Fix '- ' - translatedText = translatedText.replace("- ", "-") - - # Textwrap - if FIXTEXTWRAP is True: - finalJAString = re.sub(r"\n", " ", finalJAString) - finalJAString = finalJAString.replace("
", " ") - - if FIXTEXTWRAP is True and "_ABL" in nametag: - translatedText = textwrap.fill(translatedText, width=100) - elif FIXTEXTWRAP is True: - translatedText = textwrap.fill(translatedText, width=WIDTH) - - # BR Flag - if BRFLAG is True: - translatedText = translatedText.replace("\n", "
") - - ### Add Var Strings - # CL Flag - if CLFlag: - translatedText = "\\ac " + translatedText - translatedText = translatedText.replace("\n", "\n\\ac ") - translatedText = re.sub(r"[\\]+?ac\s+", r"\\ac ", translatedText) - CLFlag = False - - # Nametag - if nCase == 0: - translatedText = translatedText + nametag - else: - translatedText = nametag + translatedText - nametag = "" - - # Endtag - if endtag != "": - translatedText = translatedText + endtag - endtag = "" - - # Set Data - if speakerID != None: - codeList[speakerID]["p"] = [fullSpeaker] - codeList[j]["p"] = [translatedText] - codeList[j]["c"] = code - speaker = "" - match = [] - currentGroup = [] - syncIndex = i + 1 - list401.pop(0) - - ## Event Code: 122 [Set Variables] - if "c" in codeList[i] and codeList[i]["c"] == 122 and CODE122 is True: - # This is going to be the var being set. (IMPORTANT) - if codeList[i]["p"][0] not in list(range(155, 165)): - i += 1 - continue - - jaString = codeList[i]["p"][4] - - # # For Retarded Devs - # VNameValue = jaString - # i += 1 - # continue - - # Definitely don't want to mess with files - # if 'gameV' in jaString or '_' in jaString: - # i += 1 - # continue - - # Validate String - if not isinstance(jaString, str): - i += 1 - continue - - # Set String - matchedText = None - if len(re.findall(r"([\'\"])", jaString)) == 2: - matchedText = re.search(r"[\'\"\`](.*)[\'\"\`]", jaString) - # else: - # matchedText = re.search(r'(.*)', jaString) - - # Last Check - if matchedText != None: - # Remove Textwrap - finalJAString = matchedText.group(1).replace("\\n", " ") - - # Pass 1 - if setData == False: - if finalJAString != "": - list122.append(finalJAString) - - # Pass 2 - else: - if len(list122) > 0: - # Grab and Replace - translatedText = list122[0] - translatedText = jaString.replace(jaString, translatedText) - - # Remove characters that may break scripts - charList = ['"', "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Textwrap - translatedText = textwrap.fill(translatedText, width=WIDTH) - translatedText = translatedText.replace("\n", "\\n") - - # Set - codeList[i]["p"][4] = jaString.replace(finalJAString, translatedText) - list122.pop(0) - - ## Event Code: 357 [Picture Text] [Optional] - if "c" in codeList[i] and codeList[i]["c"] == 357 and CODE357 is True: - headerString = codeList[i]["p"][0] - - if headerString == "LL_GalgeChoiceWindow": - ### Message Text First - jaString = codeList[i]["p"][3]["messageText"] - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3]["messageText"] = translatedText - - ### Choices - jaString = codeList[i]["p"][3]["choices"] - matchList = re.findall(r'"label[\\]*":[\\]*"(.*?)[\\]', jaString) - if matchList != None: - # Translate - question = codeList[i]["p"][3]["messageText"] - response = translateGPT( - matchList, - f"Previous text for context: {question}\n\nThis will be a dialogue option", - True, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - translatedText = jaString - - # Replace Strings - for j in range(len(matchList)): - translatedText = translatedText.replace(matchList[j], response[0][j]) - - # Set Data - codeList[i]["p"][3]["choices"] = translatedText - - if "SoR_GabWindow" in headerString: - argVar = "arg1" - ### Message Text First - if argVar in codeList[i]["p"][3]: - jaString = codeList[i]["p"][3][argVar] - - # If there isn't any Japanese in the text just skip - if not re.search( - r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", - jaString, - ): - i += 1 - continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3][argVar] = translatedText - pbar.update(1) - - if "TorigoyaMZ_NotifyMessage" in headerString: - argVar = "message" - ### Message Text First - if argVar in codeList[i]["p"][3]: - jaString = codeList[i]["p"][3][argVar] - - # If there isn't any Japanese in the text just skip - if not re.search( - r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", - jaString, - ): - i += 1 - continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3][argVar] = translatedText - pbar.update(1) - - if "_TMLogWindowMZ" in headerString: - argVar = "text" - ### Message Text First - if argVar in codeList[i]["p"][3]: - jaString = codeList[i]["p"][3][argVar] - - # If there isn't any Japanese in the text just skip - # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): - # i += 1 - # continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3][argVar] = translatedText - pbar.update(1) - - if "DestinationWindow" in headerString: - argVar = "destination" - ### Message Text First - if argVar in codeList[i]["p"][3]: - jaString = codeList[i]["p"][3][argVar] - - # If there isn't any Japanese in the text just skip - # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): - # i += 1 - # continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3][argVar] = translatedText - pbar.update(1) - - if "MNKR_CommonPopupCoreMZ" in headerString: - argVar = "text" - ### Message Text First - if argVar in codeList[i]["p"][3]: - jaString = codeList[i]["p"][3][argVar] - - # If there isn't any Japanese in the text just skip - # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): - # i += 1 - # continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Set - translatedText = textwrap.fill(translatedText, width=WIDTH) - codeList[i]["p"][3][argVar] = translatedText - pbar.update(1) - - if "TextPicture" in headerString or "BalloonInBattle" in headerString: - argVar = "text" - ### Message Text First - if argVar in codeList[i]["p"][3]: - acExist = False - jaString = codeList[i]["p"][3][argVar] - - # Check ac - if "\\ac" in jaString: - acExist = True - else: - acExist = False - - # If there isn't any Japanese in the text just skip - # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): - # i += 1 - # continue - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - if acExist: - jaString = jaString.replace("\\ac ", " ") - jaString = jaString.replace("\\ac", "") - - # Pass 1 - if setData == False: - list357.append(jaString) - - # Pass 2 - else: - if len(list357) > 0: - # Grab and Replace - translatedText = list357[0] - translatedText = jaString.replace(jaString, translatedText) - - # Remove characters that may break scripts - charList = ['"', "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Textwrap - translatedText = textwrap.fill(translatedText, WIDTH) - translatedText = translatedText.replace("- ", "-") - - # Center Text - if acExist: - translatedText = f'\\ac {translatedText.replace('\n', '\n\\ac ')}' - - # Set - codeList[i]["p"][3][argVar] = translatedText - # codeList[i]["p"][3]['fontSize'] = "18" - list357.pop(0) - - ## Event Code: 657 [Picture Text] [Optional] - if "c" in codeList[i] and codeList[i]["c"] == 657 and CODE657 is True: - if "text" in codeList[i]["p"][0]: - jaString = codeList[i]["p"][0] - if not isinstance(jaString, str): - i += 1 - continue - - # Definitely don't want to mess with files - if "_" in jaString: - i += 1 - continue - - # If there isn't any Japanese in the text just skip - if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): - i += 1 - continue - - # Remove outside text - startString = re.search(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", jaString) - jaString = re.sub(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", "", jaString) - endString = re.search(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。!?\\]+$", jaString) - jaString = re.sub(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。!?\\]+$", "", jaString) - if startString is None: - startString = "" - else: - startString = startString.group() - if endString is None: - endString = "" - else: - endString = endString.group() - - # Remove any textwrap - jaString = re.sub(r"\n", " ", jaString) - - # Translate - response = translateGPT(jaString, "", True) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - translatedText = response[0] - - # Remove characters that may break scripts - charList = [".", '"', "'"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Textwrap - translatedText = textwrap.fill(translatedText, width=WIDTH) - translatedText = startString + translatedText + endString - - # Set Data - codeList[i]["p"][0] = translatedText - - ## Event Code: 101 [Name] [Optional] - if "c" in codeList[i] and codeList[i]["c"] == 101 and CODE101 is True: - # Check Window Type (Certain games switch between 1st line speakers and none) - if FIRSTLINESPEAKERS: - if codeList[i]["p"][2] == 0: - speakerWindow = True - else: - speakerWindow = False - - else: - isVar = False - - # Grab String - jaString = "" - if len(codeList[i]["p"]) > 4: - jaString = codeList[i]["p"][4] - # Check for Var - elif len(codeList[i]["p"]) > 0: - jaString = codeList[i]["p"][0] - isVar = True - if not isinstance(jaString, str): - i += 1 - continue - - # Force Speaker using var - if "\\ap[1左]" in jaString.lower() or "\\ap[1右]" in jaString.lower(): - speaker = "Cecily" - i += 1 - continue - elif "\\ap[2左]" in jaString.lower() or "\\ap[2右]" in jaString.lower(): - speaker = "Amelia" - i += 1 - continue - elif "\\ap[3左]" in jaString.lower() or "\\ap[3右]" in jaString.lower(): - speaker = "Henry" - i += 1 - continue - elif "\\ap[4左]" in jaString.lower() or "\\ap[4右]" in jaString.lower(): - speaker = "Oswald" - i += 1 - continue - elif "\\ap" in jaString: - speaker = re.search(r"[\\]+AP\[(.*?)\]", jaString).group(1) - i += 1 - continue - - # Get Speaker - if "\\" not in jaString: - response = getSpeaker(jaString) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - speaker = response[0] - - # Validate Speaker is not empty - if len(speaker) > 0: - if isVar == False: - codeList[i]["p"][4] = speaker - i += 1 - continue - else: - codeList[i]["p"][0] = speaker - isVar = False - i += 1 - continue - else: - speaker = "" - - ## Event Code: 355 or 655 Scripts [Optional] - if "c" in codeList[i] and (codeList[i]["c"] == 355 or codeList[i]["c"] == 655) and CODE355655 is True: - jaString = codeList[i]["p"][0] - regex = r"BattleManager\._logWindow.addText\('(.*)'" - - # Var Text - match = re.search(regex, jaString) - if re.search(regex, jaString): - finalJAString = match.group(1) - # Pass 1 - if setData is False: - list355655.append(finalJAString) - - # Pass 2 - else: - # Grab and Replace - translatedText = list355655[0] - translatedText = translatedText.replace("'", "\\'") - - # Set - codeList[i]["p"][0] = codeList[i]["p"][0].replace(finalJAString, translatedText) - list355655.pop(0) - - ## Event Code: 408 (Script) - if "c" in codeList[i] and (codeList[i]["c"] == 408) and CODE408 is True: - jaString = codeList[i]["p"][0] - - # If there isn't any Japanese in the text just skip - if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): - i += 1 - continue - - if "secretText" in jaString: - regex = r"secretText:\s?(.+)" - elif "title" in jaString: - regex = r"title:\s?(.+)" - else: - regex = r"(.+)" - - # Need to remove outside code and put it back later - matchList = re.findall(regex, jaString) - - for match in matchList: - # Remove Textwrap - match = match.replace("\n", " ") - response = translateGPT( - match, - "Reply with the " + LANGUAGE + " translation of the achievement title.", - False, - ) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Replace - translatedText = jaString.replace(match, translatedText) - - # Remove characters that may break scripts - charList = [".", '"', "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Textwrap - translatedText = textwrap.fill(translatedText, width=WIDTH) - - # Set Data - codeList[i]["p"][0] = translatedText - - ## Event Code: 108 (Script) - if "c" in codeList[i] and (codeList[i]["c"] == 108) and CODE108 is True: - jaString = codeList[i]["p"][0] - - # If there isn't any Japanese in the text just skip - if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): - i += 1 - continue - - # Translate - if "info:" in jaString: - regex = r"info:(.*)" - elif "ActiveMessage:" in jaString: - regex = r"" - elif "event_text" in jaString: - regex = r"event_text\s*:\s*(.*)" - else: - i += 1 - continue - - # Need to remove outside code and put it back later - match = re.search(regex, jaString) - if match: - # Pass 1 - if setData is False: - list108.append(match.group(1)) - - # Pass 2 - else: - # Grab and Replace - translatedText = list108[0] - list108.pop(0) - - # Remove characters that may break scripts - charList = [".", '"'] - for char in charList: - translatedText = translatedText.replace(char, "") - translatedText = translatedText.replace('"', '"') - translatedText = translatedText.replace(" ", "_") - translatedText = jaString.replace(match.group(1), translatedText) - - # Set Data - codeList[i]["p"][0] = translatedText - - ## Event Code: 356 - if "c" in codeList[i] and codeList[i]["c"] == 356 and CODE356 is True: - jaString = codeList[i]["p"][0] - oldjaString = jaString - - # Grab Speaker - if "Tachie showName" in jaString: - matchList = re.findall(r"Tachie showName (.+)", jaString) - if len(matchList) > 0: - # Translate - response = translateGPT( - matchList[0], - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Text - speaker = translatedText - speaker = speaker.replace(" ", " ") - codeList[i]["p"][0] = jaString.replace(matchList[0], speaker) - i += 1 - continue - - # Want to translate this script - if "D_TEXT " in jaString: - regex = r"D_TEXT\s(.*?)\s.+" - elif "ShowInfo" in jaString: - regex = r"ShowInfo\s(.*)" - elif "PushGab" in jaString: - regex = r"PushGab\s(.*)" - elif "addLog" in jaString: - regex = r"addLog\s(.*)" - elif "DW_" in jaString: - regex = r"DW_.*?\s(.*)" - elif "CommonPopup" in jaString: - regex = r"CommonPopup\sadd\stext:(.*?)[\\]+}" - else: - regex = r"" - - # Remove any textwrap - jaString = re.sub(r"\n", "_", jaString) - - # Capture Arguments and text - textMatch = re.search(regex, jaString) - if textMatch and textMatch.group(0) != "": - text = textMatch.group(1) - - # Pass 1 - if setData == False: - text = text.replace("_", " ") - list356.append(text) - - # Pass 2 - else: - if len(list356) > 0: - # Grab - translatedText = list356[0] - - # Remove characters that may break scripts - charList = [".", '"'] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Cant have spaces? - translatedText = translatedText.replace(" ", "_") - translatedText = translatedText.replace("__", "_") - - # Put Args Back - translatedText = jaString.replace(text, translatedText) - - # Set Data - codeList[i]["p"][0] = translatedText - list356.pop(0) - - if "namePop" in jaString: - matchList = re.findall(r"namePop\s\d+\s(.+?)\s.+", jaString) - if len(matchList) > 0: - # Translate - text = matchList[0] - response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - translatedText = jaString.replace(text, translatedText) - codeList[i]["p"][0] = translatedText - - if "LL_InfoPopupWIndowMV" in jaString: - matchList = re.findall(r"LL_InfoPopupWIndowMV\sshowWindow\s(.+?) .+", jaString) - if len(matchList) > 0: - # Translate - text = matchList[0] - response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - translatedText = translatedText.replace(" ", "_") - translatedText = jaString.replace(text, translatedText) - codeList[i]["p"][0] = translatedText - - if "OriginMenuStatus SetParam" in jaString: - matchList = re.findall(r"OriginMenuStatus\sSetParam\sparam[\d]\s(.*)", jaString) - if len(matchList) > 0: - # Translate - text = matchList[0] - response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - translatedText = translatedText.replace(" ", "_") - translatedText = jaString.replace(text, translatedText) - codeList[i]["p"][0] = translatedText - - # LL_GalgeChoiceWindowMV Message - if "LL_GalgeChoiceWindowMV setMessageText" in jaString: - ### Message Text First - match = re.search(r"LL_GalgeChoiceWindowMV setMessageText (.+)", jaString) - if match: - jaString = match.group(1) - - # Remove any textwrap & TL - jaString = re.sub(r"\n", " ", jaString) - response = translateGPT(jaString, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap & Replace Whitespace - translatedText = textwrap.fill(translatedText, width=WIDTH) - translatedText = translatedText.replace(" ", "_") - - # Replace and Set - translatedText = match.group(0).replace(match.group(1), translatedText) - codeList[i]["p"][0] = translatedText - - # LL_GalgeChoiceWindowMV Choices - if "LL_GalgeChoiceWindowMV setChoices": - match = re.search(r"LL_GalgeChoiceWindowMV setChoices (.+)", jaString) - if match: - jaString = match.group(1) - choiceList = jaString.split(",") - - # Translate - question = translatedText - response = translateGPT( - choiceList, - f"Previous text for context: {question}\n\nThis will be a dialogue option", - True, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - choiceListTL = response[0] - translatedText = match.group(0) - - # Replace Strings - for j in range(len(choiceListTL)): - choiceListTL[j] = choiceListTL[j].replace(" ", "_") - translatedText = translatedText.replace(choiceList[j], choiceListTL[j]) - - # Set Data - codeList[i]["p"][0] = translatedText - - ### Event Code: 102 Show Choice - if "c" in codeList[i] and codeList[i]["c"] == 102 and CODE102 is True: - choiceList = [] - varList = [] - for choice in range(len(codeList[i]["p"][0])): - jaString = codeList[i]["p"][0][choice] - jaString = jaString.replace(" 。", ".") - - # Avoid Empty Strings - if jaString == "": - i += 1 - continue - - # If and En Statements - ifVar = "" - ifList = re.findall(r"([ei][nf]\(.+?\)\)?\)?)", jaString) - if len(ifList) != 0: - for var in ifList: - jaString = jaString.replace(var, "") - ifVar += var - varList.append(ifVar) - - # Append to List - choiceList.append(jaString) - - # Translate - if len(textHistory) > 0: - response = translateGPT( - choiceList, - "This will be a dialogue option.\nPrevious text for context: " + str(textHistory), - True, - ) - translatedTextList = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - else: - response = translateGPT(choiceList, "This will be a dialogue option", True) - translatedTextList = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(translatedTextList) == len(choiceList): - for choice in range(len(codeList[i]["p"][0])): - translatedText = translatedTextList[choice] - - # Set Data - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if translatedText != "": - translatedText = varList[choice] + translatedText[0].upper() + translatedText[1:] - else: - translatedText = varList[choice] + translatedText - codeList[i]["p"][0][choice] = translatedText - else: - if filename not in MISMATCH: - MISMATCH.append(filename) - - ### Event Code: 111 Script - if "c" in codeList[i] and codeList[i]["c"] == 111 and CODE111 is True: - for j in range(len(codeList[i]["p"])): - jaString = codeList[i]["p"][j] - - # Check if String - if not isinstance(jaString, str): - i += 1 - continue - - # Only TL the Game Variable - if "$gameVariables" not in jaString: - i += 1 - continue - - # This is going to be the var being set. (IMPORTANT) - if "1045" not in jaString: - i += 1 - continue - - # Need to remove outside code and put it back later - matchList = re.findall(r"'(.*?)'", jaString) - - for match in matchList: - response = translateGPT(match, "", False) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Remove characters that may break scripts - charList = [".", '"', "'", "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - jaString = jaString.replace(match, translatedText) - - # Set Data - translatedText = jaString - codeList[i]["p"][j] = translatedText - - ### Event Code: 320 Set Variable - if "c" in codeList[i] and codeList[i]["c"] == 320 and CODE320 is True: - jaString = codeList[i]["p"][1] - if not isinstance(jaString, str): - i += 1 - continue - - # Definitely don't want to mess with files - if "■" in jaString or "_" in jaString: - i += 1 - continue - - # If there isn't any Japanese in the text just skip - if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): - i += 1 - continue - - # Translate - getSpeaker(jaString) - - # Remove characters that may break scripts - charList = [".", '"', "'", "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Set Data - codeList[i]["p"][1] = translatedText - - # Iterate - else: - i += 1 - - # EOF - list401TL = [] - list122TL = [] - list356TL = [] - list357TL = [] - list355655TL = [] - list108TL = [] - setData = False - PBAR = pbar - - # 401 - if len(list401) > 0: - response = translateGPT(list401, "", True) - list401TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list401TL) != len(list401): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 122 - if len(list122) > 0: - response = translateGPT(list122, "Keep you translation as brief as possible", True) - list122TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list122TL) != len(list122): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 355/655 - if len(list355655) > 0: - response = translateGPT(list355655, textHistory, True) - list355655TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list355655TL) != len(list355655): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 108 - if len(list108) > 0: - response = translateGPT(list108, textHistory, True) - list108TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list108TL) != len(list108): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 356 - if len(list356) > 0: - response = translateGPT(list356, textHistory, True) - list356TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list356TL) != len(list356): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 357 - if len(list357) > 0: - response = translateGPT(list357, textHistory, True) - list357TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list357TL) != len(list357): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # Start Pass 2 - if setData: - searchCodes( - page, - pbar, - [list401TL, list122TL, list355655TL, list108TL, list356TL, list357TL], - filename, - ) - - # Delete all -1 codes - codeListFinal = [] - for i in range(len(codeList)): - if "c" in codeList[i] and codeList[i]["c"] != -1: - codeListFinal.append(codeList[i]) - - # Normal Format - if "list" in page: - page["list"] = codeListFinal - - # Special Format (Scenario) - else: - page = codeListFinal - - except IndexError as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + oldjaString) from None - except Exception as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + oldjaString) from None - - return totalTokens - - -def searchSS(state, pbar): - totalTokens = [0, 0] - - # Name - nameResponse = ( - translateGPT( - state["name"], - "Reply with only the " + LANGUAGE + " translation of the RPG Skill name.", - False, - ) - if "name" in state - else "" - ) - - # Description - descriptionResponse = ( - translateGPT( - state["description"], - "Reply with only the " + LANGUAGE + " translation of the description.", - False, - ) - if "description" in state - else "" - ) - - # Messages - message1Response = "" - message4Response = "" - message2Response = "" - message3Response = "" - - if "message1" in state: - if len(state["message1"]) > 0 and state["message1"][0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - message1Response = translateGPT( - "Taro" + state["message1"], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example,\ -Translate 'Taroを倒した!' as 'Taro was defeated!'", - False, - ) - else: - message1Response = translateGPT( - state["message1"], - "reply with only the gender neutral " + LANGUAGE + " translation", - False, - ) - - if "message2" in state: - if len(state["message2"]) > 0 and state["message2"][0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - message2Response = translateGPT( - "Taro" + state["message2"], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example,\ -Translate 'Taroを倒した!' as 'Taro was defeated!'", - False, - ) - else: - message2Response = translateGPT( - state["message2"], - "reply with only the gender neutral " + LANGUAGE + " translation", - False, - ) - - if "message3" in state: - if len(state["message3"]) > 0 and state["message3"][0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - message3Response = translateGPT( - "Taro" + state["message3"], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example,\ -Translate 'Taroを倒した!' as 'Taro was defeated!'", - False, - ) - else: - message3Response = translateGPT( - state["message3"], - "reply with only the gender neutral " + LANGUAGE + " translation", - False, - ) - - if "message4" in state: - if len(state["message4"]) > 0 and state["message4"][0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - message4Response = translateGPT( - "Taro" + state["message4"], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example,\ -Translate 'Taroを倒した!' as 'Taro was defeated!'", - False, - ) - else: - message4Response = translateGPT( - state["message4"], - "reply with only the gender neutral " + LANGUAGE + " translation", - False, - ) - - # Translate State Notes - if "help" in state["note"]: - noteResponse = translateNote(state, r"]*)>") - totalTokens[0] += noteResponse[0] - totalTokens[1] += noteResponse[1] - if "STATE_HELP" in state["note"]: - noteResponse = translateNote(state, r"\n(.*)\n") - totalTokens[0] += noteResponse[0] - totalTokens[1] += noteResponse[1] - - # Count totalTokens - totalTokens[0] += nameResponse[1][0] if nameResponse != "" else 0 - totalTokens[1] += nameResponse[1][1] if nameResponse != "" else 0 - totalTokens[0] += descriptionResponse[1][0] if descriptionResponse != "" else 0 - totalTokens[1] += descriptionResponse[1][1] if descriptionResponse != "" else 0 - totalTokens[0] += message1Response[1][0] if message1Response != "" else 0 - totalTokens[1] += message1Response[1][1] if message1Response != "" else 0 - totalTokens[0] += message2Response[1][0] if message2Response != "" else 0 - totalTokens[1] += message2Response[1][1] if message2Response != "" else 0 - totalTokens[0] += message3Response[1][0] if message3Response != "" else 0 - totalTokens[1] += message3Response[1][1] if message3Response != "" else 0 - totalTokens[0] += message4Response[1][0] if message4Response != "" else 0 - totalTokens[1] += message4Response[1][1] if message4Response != "" else 0 - - # Set Data - if "name" in state: - state["name"] = nameResponse[0].replace('"', "") - if "description" in state: - # Textwrap - translatedText = descriptionResponse[0] - translatedText = textwrap.fill(translatedText, width=LISTWIDTH) - state["description"] = translatedText.replace('"', "") - if "message1" in state: - state["message1"] = message1Response[0].replace('"', "").replace("Taro", "") - if "message2" in state: - state["message2"] = message2Response[0].replace('"', "").replace("Taro", "") - if "message3" in state: - state["message3"] = message3Response[0].replace('"', "").replace("Taro", "") - if "message4" in state: - state["message4"] = message4Response[0].replace('"', "").replace("Taro", "") - - return totalTokens - - -def searchSystem(data, pbar): - totalTokens = [0, 0] - context = "Reply with only the " + LANGUAGE + ' translation of the UI textbox."' - - # Title - response = translateGPT( - data["game_title"], - " Reply with the " + LANGUAGE + " translation of the game title name", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - data["game_title"] = response[0].strip(".") - - # Terms - for term in data["terms"]: - if term != "messages": - termList = data["terms"][term] - for i in range(len(termList)): # Last item is a messages object - if termList[i] is not None: - response = translateGPT(termList[i], context, False) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - termList[i] = response[0].replace('"', "").strip() - - # Armor Types - for i in range(len(data["armor_types"])): - response = translateGPT( - data["armor_types"][i], - "Reply with only the " + LANGUAGE + " translation of the armor type", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - data["armor_types"][i] = response[0].replace('"', "").strip() - - # Skill Types - for i in range(len(data["skill_types"])): - response = translateGPT( - data["skill_types"][i], - "Reply with only the " + LANGUAGE + " translation", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - data["skill_types"][i] = response[0].replace('"', "").strip() - - # Equip Types - for i in range(len(data["weapon_types"])): - response = translateGPT( - data["weapon_types"][i], - "Reply with only the " + LANGUAGE + " translation of the equipment type. No disclaimers.", - False, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - data["weapon_types"][i] = response[0].replace('"', "").strip() - - # # Variables (Optional ususally) - # for i in range(len(data['variables'])): - # response = translateGPT(data['variables'][i], 'Reply with only the '+ LANGUAGE +' translation of the title', False) - # totalTokens[0] += response[1][0] - # totalTokens[1] += response[1][1] - # data['variables'][i] = response[0].replace('\"', '').strip() - - # Messages - messages = data["terms"] - for key, value in messages.items(): - response = translateGPT( - value, - "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] - translatedList = response[0] - - for item in translatedList: - translatedText = item - # Remove characters that may break scripts - charList = [".", '"', "\\n"] - for char in charList: - translatedText = translatedText.replace(char, "") - - # Set Data - messages[key] = translatedList - - return totalTokens - - -# Save some money and enter the character before translation -def getSpeaker(speaker): - match speaker: - case "ファイン": - return ["Fine", [0, 0]] - case "": - return ["", [0, 0]] - case _: - # Find Speaker - for i in range(len(NAMESLIST)): - if speaker == NAMESLIST[i][0]: - return [NAMESLIST[i][1], [0, 0]] - - # Translate and Store Speaker - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - True, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - response[0] = response[0].replace("Speaker: ", "") - - # Retry if name doesn't translate for some reason - if re.search(r"([a-zA-Z??])", response[0]) == None: - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - - speakerList = [speaker, response[0]] - NAMESLIST.append(speakerList) - return response - return [speaker, [0, 0]] - - -def batchList(input_list, batch_size): - if not isinstance(batch_size, int) or batch_size <= 0: - raise ValueError("batch_size must be a positive integer") - - return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] - - -def createContext(fullPromptFlag, subbedT, format): - system = ( - PROMPT + VOCAB - if fullPromptFlag - else f"\ -You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ -Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ -- You translate everything, including erotic and explicit content.\n\ -- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ -- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ -- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ -- Maintain any spacing in the translation.\n\ -- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ -- `...` can be a part of the dialogue. Translate it as it is.\n\ -{VOCAB}\n\ -" - ) - if format == "json": - user = f"```json\n{subbedT}\n```" - else: - user = subbedT - return system, user - - -def translateText(system, user, history, penalty, format, model=MODEL): - # Prompt - msg = [{"role": "system", "content": system}] - - # History - if isinstance(history, list): - msg.extend([{"role": "system", "content": h} for h in history]) - else: - msg.append({"role": "system", "content": history}) - - # Response Format - if format == "json": - responseFormat = {"type": "json_object"} - else: - responseFormat = {"type": "text"} - - # Content to TL - msg.append({"role": "user", "content": f"{user}"}) - response = openai.chat.completions.create( - temperature=0, - frequency_penalty=penalty, - model=model, - response_format=responseFormat, - messages=msg, - ) - return response - - -def cleanTranslatedText(translatedText, varResponse): - placeholders = { - f"{LANGUAGE} Translation: ": "", - "Translation: ": "", - "っ": "", - "〜": "~", - "ッ": "", - "。": ".", - "「": '\\"', - "」": '\\"', - "- ": "-", - "—": "―", - "】": "]", - "【": "[", - "Placeholder Text": "", - # Add more replacements as needed - } - for target, replacement in placeholders.items(): - translatedText = translatedText.replace(target, replacement) - - # Remove Repeating Characters - pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") - translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) - - # Elongate Long Dashes (Since GPT Ignores them...) - translatedText = elongateCharacters(translatedText) - return translatedText - - -def elongateCharacters(text): - # Define a pattern to match one character followed by one or more `ー` characters - # Using a positive lookbehind assertion to capture the preceding character - pattern = r"(?<=(.))ー+" - - # Define a replacement function that elongates the captured character - def repl(match): - char = match.group(1) # The character before the ー sequence - count = len(match.group(0)) - 1 # Number of ー characters - return char * count # Replace ー sequence with the character repeated - - # Use re.sub() to replace the pattern in the text - return re.sub(pattern, repl, text) - - -def extractTranslation(translatedTextList, is_list): - try: - translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) - translatedTextList = re.sub(r"(? instead +FIXTEXTWRAP = True # Overwrites textwrap +IGNORETLTEXT = False # Ignores all translated text. +MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) +BRACKETNAMES = False +PBAR = None +FILENAME = None + +# Pricing - Depends on the model https://openai.com/pricing +# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request +# If you are getting a MISMATCH LENGTH error, lower the batch size. +if "gpt-3.5" in MODEL: + INPUTAPICOST = 0.002 + OUTPUTAPICOST = 0.002 + BATCHSIZE = 10 + FREQUENCY_PENALTY = 0.2 +elif "gpt-4" in MODEL: + INPUTAPICOST = 0.0025 + OUTPUTAPICOST = 0.01 + BATCHSIZE = 20 + FREQUENCY_PENALTY = 0.1 +else: + INPUTAPICOST = float(os.getenv("input_cost")) + OUTPUTAPICOST = float(os.getenv("output_cost")) + BATCHSIZE = int(os.getenv("batchsize")) + FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) + +# tqdm Globals +BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" +POSITION = 0 +LEAVE = False + +# Dialogue / Scroll / Choices (Main Codes) +CODE401 = True +CODE405 = False +CODE102 = False + +# Optional +CODE101 = False # Turn this one when names exist in 101 +CODE408 = False # Warning, translates comments and can inflate costs. + +# Variables +CODE122 = False + +# Other +CODE355655 = False +CODE357 = False +CODE657 = False +CODE356 = False +CODE320 = False +CODE324 = False +CODE111 = False +CODE108 = False + + +def handleACE(filename, estimate): + global ESTIMATE, TOKENS, FILENAME + ESTIMATE = estimate + FILENAME = filename + + # Translate + start = time.time() + translatedData = openFiles(filename) + + # Translate + if not estimate: + try: + with open("translated/" + filename, "w", encoding="utf-8") as outFile: + yaml = YAML(pure=True) + yaml.width = 4096 + yaml.default_style = "'" + yaml.dump(translatedData[0], outFile) + except Exception: + traceback.print_exc() + return "Fail" + + # Print File + end = time.time() + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + + # Print Total + totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") + + # Print any errors on maps + if len(MISMATCH) > 0: + return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET + else: + return totalString + + +def openFiles(filename): + yaml = YAML(pure=True) # Need a yaml instance per thread. + yaml.width = 4096 + yaml.default_style = "'" + + with open("files/" + filename, "r", encoding="UTF-8") as f: + # Map Files + if "Map" in filename and "MapInfos" not in filename: + data = yaml.load(f) + translatedData = parseMap(data, filename) + + # CommonEvents Files + elif "CommonEvents" in filename: + data = yaml.load(f) + translatedData = parseCommonEvents(data, filename) + + # Actor File + elif "Actors" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Actors") + + # Armor File + elif "Armors" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Armors") + + # Weapons File + elif "Weapons" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Weapons") + + # Classes File + elif "Classes" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Classes") + + # Enemies File + elif "Enemies" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Enemies") + + # Items File + elif "Items" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Items") + + # MapInfo File + elif "MapInfos" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "MapInfos") + + # Skills File + elif "Skills" in filename: + data = yaml.load(f) + translatedData = parseNames(data, filename, "Skills") + + # Troops File + elif "Troops" in filename: + data = yaml.load(f) + translatedData = parseTroops(data, filename) + + # States File + elif "States" in filename: + data = yaml.load(f) + translatedData = parseSS(data, filename) + + # System File + elif "System" in filename: + data = yaml.load(f) + translatedData = parseSystem(data, filename) + + # Scenario File + elif "Scenario" in filename: + data = yaml.load(f) + translatedData = parseScenario(data, filename) + + else: + raise NameError(filename + " Not Supported") + + return translatedData + + +def getResultString(translatedData, translationTime, filename): + # File Print String + totalTokenstring = ( + Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" + "[Output: " + + str(translatedData[1][1]) + + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) + + "]" + ) + timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" + + if translatedData[2] is None: + # Success + return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET + else: + # Fail + try: + raise translatedData[2] + except Exception as e: + traceback.print_exc() + errorString = str(e) + Fore.RED + return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET + + +def parseMap(data, filename): + totalTokens = [0, 0] + totalLines = 0 + events = data["events"] + global LOCK + + # Translate displayName for Map files + if "Map" in filename: + response = translateGPT( + data["display_name"], + "Reply with only the " + LANGUAGE + " translation of the RPG location name", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + data["display_name"] = response[0].replace('"', "") + + # Thread for each page in file + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + with ThreadPoolExecutor(max_workers=THREADS) as executor: + for key in events: + if key is not None: + futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in events[key]["pages"] if page is not None] + for future in as_completed(futures): + try: + totalTokensFuture = future.result() + totalTokens[0] += totalTokensFuture[0] + totalTokens[1] += totalTokensFuture[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def translateNote(event, regex): + # Regex String + jaString = event["note"] + match = re.findall(regex, jaString, re.DOTALL) + if match: + tokens = [0, 0] + i = 0 + while i < len(match): + initialJAString = match[i] + # Remove any textwrap + modifiedJAString = initialJAString.replace("\n", " ") + + # Translate + response = translateGPT( + modifiedJAString, + "Reply with only the " + LANGUAGE + " translation.", + False, + ) + translatedText = response[0] + tokens[0] += response[1][0] + tokens[1] += response[1][1] + + # Textwrap + translatedText = textwrap.fill(translatedText, width=NOTEWIDTH) + translatedText = translatedText.replace('"', "") + jaString = jaString.replace(initialJAString, translatedText) + event["note"] = jaString + i += 1 + return tokens + return [0, 0] + + +# For notes that can't have spaces. +def translateNoteOmitSpace(event, regex): + # Regex that only matches text inside LB. + jaString = event["note"] + + match = re.findall(regex, jaString, re.DOTALL) + if match: + oldJAString = match[0] + # Remove any textwrap + jaString = re.sub(r"\n", " ", oldJAString) + + # Translate + response = translateGPT( + jaString, + "Reply with the " + LANGUAGE + " translation of the location name.", + False, + ) + translatedText = response[0] + + translatedText = translatedText.replace('"', "") + translatedText = translatedText.replace(" ", "_") + event["note"] = event["note"].replace(oldJAString, translatedText) + return response[1] + return [0, 0] + + +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"]) + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + with ThreadPoolExecutor(max_workers=THREADS) as executor: + futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in data if page is not None] + for future in as_completed(futures): + try: + totalTokensFuture = future.result() + totalTokens[0] += totalTokensFuture[0] + totalTokens[1] += totalTokensFuture[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +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"]: + totalLines += len(page["list"]) + 1 # The +1 is because each page has a name. + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + for troop in data: + if troop is not None: + with ThreadPoolExecutor(max_workers=THREADS) as executor: + futures = [executor.submit(searchCodes, page, pbar, [], filename) for page in troop["pages"] if page is not None] + for future in as_completed(futures): + try: + totalTokensFuture = future.result() + totalTokens[0] += totalTokensFuture[0] + totalTokens[1] += totalTokensFuture[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def parseNames(data, filename, context): + totalTokens = [0, 0] + totalLines = 0 + totalLines += len(data) + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + try: + result = searchNames(data, pbar, context) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def parseSS(data, filename): + totalTokens = [0, 0] + totalLines = 0 + totalLines += len(data) + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + for ss in data: + if ss is not None: + try: + result = searchSS(ss, pbar) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def parseSystem(data, filename): + totalTokens = [0, 0] + totalLines = 0 + + # Calculate Total Lines + for term in data["terms"]: + termList = data["terms"][term] + totalLines += len(termList) + totalLines += len(data["game_title"]) + totalLines += len(data["variables"]) + totalLines += len(data["weapon_types"]) + totalLines += len(data["armor_types"]) + totalLines += len(data["skill_types"]) + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + try: + result = searchSystem(data, pbar) + totalTokens[0] += result[0] + totalTokens[1] += result[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +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]) + + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + with ThreadPoolExecutor(max_workers=THREADS) as executor: + futures = [executor.submit(searchCodes, page[1], pbar, [], filename) for page in data.items() if page[1] is not None] + for future in as_completed(futures): + try: + totalTokensFuture = future.result() + totalTokens[0] += totalTokensFuture[0] + totalTokens[1] += totalTokensFuture[1] + except Exception as e: + traceback.print_exc() + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def searchNames(data, pbar, context): + totalTokens = [0, 0] + nameList = [] + profileList = [] + nicknameList = [] + descriptionList = [] + noteList = [] + i = 0 # Counter + j = 0 # Counter 2 + filling = False + mismatch = False + batchFull = False + + # Set the context of what we are translating + if "Actors" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the NPC name" + if "Armors" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the RPG equipment name" + if "Classes" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the RPG class name" + if "MapInfos" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the location name" + if "Enemies" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the enemy NPC name" + if "Weapons" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the RPG weapon name" + if "Items" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the RPG item name" + if "Skills" in context: + newContext = "Reply with only the " + LANGUAGE + " translation of the RPG skill name" + + # Names + with open("translations.txt", "a", encoding="utf-8") as file: + file.write(f"\n#{context}\n") + while i < len(data) or filling == True: + if i < len(data): + # Empty Data + if "MapInfos" in FILENAME and i == 0: + i += 1 + if "MapInfos" in FILENAME and j == 0: + j += 1 + if data[i] is None or data[i]["name"] == "": + i += 1 + + continue + + # Filling up Batch + filling = True + if context in "Actors": + if len(nameList) < BATCHSIZE: + if data[i]["name"] != "": + nameList.append(data[i]["name"]) + if data[i]["nickname"] != "": + nicknameList.append(data[i]["nickname"]) + if data[i]["description"] != "": + profileList.append(data[i]["description"].replace("\n", " ")) + + # Notes + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "PE拡張" in data[i]["note"]: + tokensResponse = translateNote(data[i], r"") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + i += 1 + else: + batchFull = True + if context in ["Armors", "Weapons", "Items"]: + if len(nameList) < BATCHSIZE: + nameList.append(data[i]["name"]) + if "description" in data[i] and data[i]["description"] != "": + descriptionList.append(data[i]["description"].replace("\n", " ")) + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "Switch Shop Description" in data[i]["note"]: + tokensResponse = translateNote(data[i], r"\n(.*)\n") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + + i += 1 + else: + batchFull = True + if context in ["Skills"]: + if len(nameList) < BATCHSIZE: + nameList.append(data[i]["name"]) + if "description" in data[i] and data[i]["description"] != "": + descriptionList.append(data[i]["description"].replace("\n", " ")) + + # Messages + number = 1 + while number < 5: + if f"message{number}" in data[i]: + if len(data[i][f"message{number}"]) > 0 and data[i][f"message{number}"][0] in ["は", "を", "の", "に", "が"]: + msgResponse = translateGPT( + "Taro" + data[i][f"message{number}"], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + False, + ) + data[i][f"message{number}"] = msgResponse[0].replace("Taro", "") + totalTokens[0] += msgResponse[1][0] + totalTokens[1] += msgResponse[1][1] + number += 1 + + else: + msgResponse = translateGPT( + data[i][f"message{number}"], + "reply with only the gender neutral " + LANGUAGE + " translation", + False, + ) + data[i][f"message{number}"] = msgResponse[0] + totalTokens[0] += msgResponse[1][0] + totalTokens[1] += msgResponse[1][1] + number += 1 + else: + number += 1 + + i += 1 + else: + batchFull = True + if context in ["Enemies", "Classes", "MapInfos"]: + if len(nameList) < BATCHSIZE: + nameList.append(data[i]["name"]) + + # Notes + if "note" in data[i]: + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + if "") + totalTokens[0] += tokensResponse[0] + totalTokens[1] += tokensResponse[1] + i += 1 + else: + batchFull = True + + # Batch Full + if batchFull == True or i >= len(data): + k = j # Original Index + if context in "Actors": + # Name + response = translateGPT(nameList, newContext, True) + translatedNameBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Nickname + response = translateGPT(nicknameList, newContext, True) + translatedNicknameBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Profile + response = translateGPT(profileList, "", True) + translatedProfileBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # 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"] != "": + with open("translations.txt", "a", encoding="utf-8") as file: + file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n') + data[j]["name"] = translatedNameBatch[0] + translatedNameBatch.pop(0) + if data[j]["nickname"] != "": + data[j]["nickname"] = translatedNicknameBatch[0] + translatedNicknameBatch.pop(0) + if data[j]["profile"] != "": + data[j]["profile"] = textwrap.fill(translatedProfileBatch[0], LISTWIDTH) + translatedProfileBatch.pop(0) + + # If Batch is empty. Move on. + if len(translatedNameBatch) == 0: + nameList.clear() + filling = False + j += 1 + else: + mismatch = True + + if context in ["Armors", "Weapons", "Items", "Skills"]: + # Name + response = translateGPT(nameList, newContext, True) + translatedNameBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Description + response = translateGPT( + descriptionList, + f"Reply with only the {LANGUAGE} translation of the text.", + True, + ) + translatedDescriptionBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + if len(nameList) == len(translatedNameBatch): + j = k + with open("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 + file.write(f'{data[j]['name']} ({translatedNameBatch[0]})\n') + data[j]["name"] = translatedNameBatch[0] + translatedNameBatch.pop(0) + if "description" in data[j] and data[j]["description"] != "": + data[j]["description"] = textwrap.fill(translatedDescriptionBatch[0], LISTWIDTH) + translatedDescriptionBatch.pop(0) + + # If Batch is empty. Move on. + if len(translatedNameBatch) == 0: + nameList.clear() + descriptionList.clear() + batchFull = False + filling = False + j += 1 + else: + mismatch = True + if context in ["Enemies", "Classes", "MapInfos"]: + response = translateGPT(nameList, newContext, True) + translatedNameBatch = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # 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: + with open("translations.txt", "a", encoding="utf-8") as file: + file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n') + # Get Text + data[j]["name"] = translatedNameBatch[0] + translatedNameBatch.pop(0) + + # If Batch is empty. Move on. + if len(translatedNameBatch) == 0: + nameList.clear() + batchFull = False + filling = False + j += 1 + else: + mismatch = True + + # Mismatch + if mismatch == True: + MISMATCH.append(nameList) + nameList.clear() + profileList.clear() + descriptionList.clear() + filling = False + mismatch = False + + i += 1 + + return totalTokens + + +def searchCodes(page, pbar, jobList, filename): + if len(jobList) > 0: + list401 = jobList[0] + list122 = jobList[1] + list355655 = jobList[2] + list108 = jobList[3] + list356 = jobList[4] + list357 = jobList[5] + setData = True + else: + list401 = [] + list122 = [] + list355655 = [] + list108 = [] + list356 = [] + list357 = [] + setData = False + textHistory = [] + match = [] + totalTokens = [0, 0] + translatedText = "" + speaker = "" + speakerID = None + syncIndex = 0 + CLFlag = False + maxHistory = MAXHISTORY + VNameValue = None + speakerWindow = FIRSTLINESPEAKERS + global LOCK + global NAMESLIST + global MISMATCH + global PBAR + with LOCK: + PBAR = pbar + + # Begin Parsing File + try: + # Normal Format + if "list" in page: + codeList = page["list"] + + # Special Format (Scenario) + else: + codeList = page + + # Iterate through page + i = 0 + while i < len(codeList): + with LOCK: + # syncIndex will keep i in sync when it gets modified + if syncIndex > i: + i = syncIndex + if len(codeList) <= i: + break + + # Declare Varss + currentGroup = [] + nametag = "" + + ## Event Code: 401 Show Text + if "c" in codeList[i] and codeList[i]["c"] in [401, 405, -1] and (CODE401 or CODE405): + # Save Code and starting index (j) + code = codeList[i]["c"] + j = i + endtag = "" + + # Grab String + if len(codeList[i]["p"]) > 0: + jaString = codeList[i]["p"][0] + oldjaString = jaString + else: + codeList[i]["c"] = -1 + i += 1 + continue + + # # For Retarded Devs + # retardRegex = r'([\\]+[nN]\[[\\]+V\[\d*?\]\])' + # match = re.search(retardRegex, jaString) + # if match: + # if VNameValue == 1: + # jaString = re.sub(retardRegex, 'リッカ', jaString) + # if VNameValue == 2: + # jaString = re.sub(retardRegex, 'ミミ', jaString) + # if VNameValue == 3: + # jaString = re.sub(retardRegex, 'ヒトミ', jaString) + # if VNameValue == 4: + # jaString = re.sub(retardRegex, 'Taro', jaString) + # if VNameValue == 5: + # jaString = re.sub(retardRegex, '富士見', jaString) + + # Speaker Check + speakerList = [] + + # m and z Codes + match = re.search(r"(.*?)[\\]+m\[\d+?\][\\]+z\[\d+?\]", jaString) + if match: + speakerList.append(match.group(1)) + if "\\c" in speakerList[0]: + speakerList = re.findall( + r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[[\d]\]\\?\\?$", + speakerList[0], + ) + + # Brackets + if len(speakerList) == 0: + speakerList = re.findall(r"^【(.*?)】$", jaString) + + # Colors + if len(speakerList) == 0: + speakerList = re.findall( + r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[[\d]\]\\?\\?$", + jaString, + ) + + # Full Width Space + if len(speakerList) == 0: + speakerList = re.findall(r"^[  ](.*)", jaString) + + # First Line Speakers + if len(speakerList) == 0 and FIRSTLINESPEAKERS is True: + # Remove any RPGMaker Code at start + ffMatch = re.search( + r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", + jaString, + ) + if ffMatch != None: + jaString = jaString.replace(ffMatch.group(0), "") + nametag += ffMatch.group(0) + + # Test Speaker + if ( + len(jaString) < 40 + and "c" in codeList[i + 1] + and codeList[i + 1]["c"] in [401, 405, -1] + and len(codeList[i + 1]["p"]) > 0 + and len(codeList[i + 1]["p"][0]) > 0 + ): + if codeList[i + 1]["p"] != "" and codeList[i + 1]["p"][0].strip()[0] in [ + "「", + '"', + "(", + "(", + "*", + "[", + ]: + speakerList = re.findall(r".+", jaString) + + if len(speakerList) != 0 and codeList[i + 1]["c"] in [401, 405, -1]: + # Get Speaker + response = getSpeaker(speakerList[0]) + speaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + 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 401's in a row. + currentGroup.append(jaString) + + # Join Up 401's into single string + if len(codeList) > i + 1: + while codeList[i + 1]["c"] in [401, 405, -1]: + if setData == True: + codeList[i]["p"] = [] + codeList[i]["c"] = -1 + i += 1 + j = i + + # Only add if not empty + if len(codeList[i]["p"]) > 0: + jaString = codeList[i]["p"][0] + currentGroup.append(jaString) + + # Make sure not the end of the list. + if len(codeList) <= i + 1: + break + + # Format String + if len(currentGroup) > 0: + finalJAString = "\n".join(currentGroup) + oldjaString = finalJAString + + # Check if Empty + if finalJAString == "": + i += 1 + continue + + # Set Back + if setData == True: + codeList[i]["p"] = [finalJAString] + + ### \\n + nCase = None + regex = r"([\\]+[kKnN][wWcCrRrEe]?[\[<](.*?)[>\]])" + match = re.search(regex, finalJAString) + + # Set Name + if match: + nametag = match.group(1) + speaker = match.group(2) + + # Translate Speaker + response = getSpeaker(speaker) + 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(speaker, tledSpeaker) + speaker = tledSpeaker + + # Bracket Names + if BRACKETNAMES is True and len(matchList) != 0: + if matchList[0][0] != "": + match0 = matchList[0][0] + match1 = matchList[0][1] + else: + match0 = matchList[0][2] + match1 = matchList[0][3] + + # Translate Speaker + speakerID = j + response = getSpeaker(match1) + speaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Nametag and Remove from Final String + fullSpeaker = match0.replace(match1, speaker) + finalJAString = finalJAString.replace(match0, "") + + # Set next item as dialogue + if codeList[j + 1]["c"] == 401 or codeList[j + 1]["c"] == -1: + # Set name var to top of list + codeList[j]["p"] = [fullSpeaker] + codeList[j]["c"] = code + j += 1 + codeList[j]["p"] = [finalJAString] + codeList[j]["c"] = code + else: + # Set nametag in string + codeList[j]["p"] = [fullSpeaker + finalJAString] + codeList[j]["c"] = code + + # Remove Extra Stuff bad for translation. + finalJAString = finalJAString.replace("゙", "") + finalJAString = finalJAString.replace("―", "-") + finalJAString = finalJAString.replace("…", "...") + finalJAString = finalJAString.replace("。", ".") + finalJAString = re.sub(r"(\.{3}\.+)", "...", finalJAString) + finalJAString = finalJAString.replace(" ", "") + finalJAString = finalJAString.replace("「", '"') + finalJAString = finalJAString.replace("」", '"') + + ### Remove format codes + # Furigana + rcodeMatch = re.findall(r"([\\]+[r][b]?\[.*?,(.*?)\])", finalJAString) + if len(rcodeMatch) > 0: + for match in rcodeMatch: + finalJAString = finalJAString.replace(match[0], match[1]) + + # Formatting + formatMatch = re.findall(r"[\\]+[!><.|#^{}]", finalJAString) + if len(formatMatch) > 0: + for match in formatMatch: + finalJAString = finalJAString.replace(match, "") + + # Remove any RPGMaker Code at start + ffMatch = re.search( + r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", + finalJAString, + ) + if ffMatch != None: + finalJAString = finalJAString.replace(ffMatch.group(0), "") + nametag += ffMatch.group(0) + + # Remove _ABL Codes + ffMatch = re.search(r"^(_ABL).*", finalJAString) + if ffMatch != None: + finalJAString = finalJAString.replace(ffMatch.group(1), "") + nametag += ffMatch.group(1) + + # Center Lines + if "\\CL" in finalJAString or "\\ac" in finalJAString: + finalJAString = finalJAString.replace("\\CL ", "") + finalJAString = finalJAString.replace("\\CL", "") + finalJAString = finalJAString.replace("\\ac ", "") + finalJAString = finalJAString.replace("\\ac", "") + CLFlag = True + + # 1st Passthrough (Grabbing Data) + if setData == False: + if finalJAString != "": + if speaker == "" and finalJAString != "": + list401.append(finalJAString) + elif finalJAString != "": + list401.append(f"[{speaker}]: {finalJAString}") + else: + list401.append(speaker) + speaker = "" + match = [] + nametag = "" + currentGroup = [] + syncIndex = i + 1 + + # Keep textHistory list at length maxHistory + textHistory.append('"' + finalJAString + '"') + if len(textHistory) > maxHistory: + textHistory.pop(0) + + # 2nd Passthrough (Setting Data) + else: + # Grab Translated String + if len(list401) > 0: + translatedText = list401[0] + + # Remove speaker + if speaker != "": + matchSpeakerList = re.findall(r"^\[?(.+?)\]?\s?[|:]\s?", translatedText) + if len(matchSpeakerList) > 0: + newSpeaker = matchSpeakerList[0] + nametag = nametag.replace(speaker, newSpeaker) + translatedText = re.sub(r"^\[?(.+?)\]?\s?[|:]\s?", "", translatedText) + + # Fix '- ' + translatedText = translatedText.replace("- ", "-") + + # Textwrap + if FIXTEXTWRAP is True: + finalJAString = re.sub(r"\n", " ", finalJAString) + finalJAString = finalJAString.replace("
", " ") + + if FIXTEXTWRAP is True and "_ABL" in nametag: + translatedText = textwrap.fill(translatedText, width=100) + elif FIXTEXTWRAP is True: + translatedText = textwrap.fill(translatedText, width=WIDTH) + + # BR Flag + if BRFLAG is True: + translatedText = translatedText.replace("\n", "
") + + ### Add Var Strings + # CL Flag + if CLFlag: + translatedText = "\\ac " + translatedText + translatedText = translatedText.replace("\n", "\n\\ac ") + translatedText = re.sub(r"[\\]+?ac\s+", r"\\ac ", translatedText) + CLFlag = False + + # Nametag + if nCase == 0: + translatedText = translatedText + nametag + else: + translatedText = nametag + translatedText + nametag = "" + + # Endtag + if endtag != "": + translatedText = translatedText + endtag + endtag = "" + + # Set Data + if speakerID != None: + codeList[speakerID]["p"] = [fullSpeaker] + codeList[j]["p"] = [translatedText] + codeList[j]["c"] = code + speaker = "" + match = [] + currentGroup = [] + syncIndex = i + 1 + list401.pop(0) + + ## Event Code: 122 [Set Variables] + if "c" in codeList[i] and codeList[i]["c"] == 122 and CODE122 is True: + # This is going to be the var being set. (IMPORTANT) + if codeList[i]["p"][0] not in list(range(155, 165)): + i += 1 + continue + + jaString = codeList[i]["p"][4] + + # # For Retarded Devs + # VNameValue = jaString + # i += 1 + # continue + + # Definitely don't want to mess with files + # if 'gameV' in jaString or '_' in jaString: + # i += 1 + # continue + + # Validate String + if not isinstance(jaString, str): + i += 1 + continue + + # Set String + matchedText = None + if len(re.findall(r"([\'\"])", jaString)) == 2: + matchedText = re.search(r"[\'\"\`](.*)[\'\"\`]", jaString) + # else: + # matchedText = re.search(r'(.*)', jaString) + + # Last Check + if matchedText != None: + # Remove Textwrap + finalJAString = matchedText.group(1).replace("\\n", " ") + + # Pass 1 + if setData == False: + if finalJAString != "": + list122.append(finalJAString) + + # Pass 2 + else: + if len(list122) > 0: + # Grab and Replace + translatedText = list122[0] + translatedText = jaString.replace(jaString, translatedText) + + # Remove characters that may break scripts + charList = ['"', "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Textwrap + translatedText = textwrap.fill(translatedText, width=WIDTH) + translatedText = translatedText.replace("\n", "\\n") + + # Set + codeList[i]["p"][4] = jaString.replace(finalJAString, translatedText) + list122.pop(0) + + ## Event Code: 357 [Picture Text] [Optional] + if "c" in codeList[i] and codeList[i]["c"] == 357 and CODE357 is True: + headerString = codeList[i]["p"][0] + + if headerString == "LL_GalgeChoiceWindow": + ### Message Text First + jaString = codeList[i]["p"][3]["messageText"] + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3]["messageText"] = translatedText + + ### Choices + jaString = codeList[i]["p"][3]["choices"] + matchList = re.findall(r'"label[\\]*":[\\]*"(.*?)[\\]', jaString) + if matchList != None: + # Translate + question = codeList[i]["p"][3]["messageText"] + response = translateGPT( + matchList, + f"Previous text for context: {question}\n\nThis will be a dialogue option", + True, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + translatedText = jaString + + # Replace Strings + for j in range(len(matchList)): + translatedText = translatedText.replace(matchList[j], response[0][j]) + + # Set Data + codeList[i]["p"][3]["choices"] = translatedText + + if "SoR_GabWindow" in headerString: + argVar = "arg1" + ### Message Text First + if argVar in codeList[i]["p"][3]: + jaString = codeList[i]["p"][3][argVar] + + # If there isn't any Japanese in the text just skip + if not re.search( + r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", + jaString, + ): + i += 1 + continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3][argVar] = translatedText + pbar.update(1) + + if "TorigoyaMZ_NotifyMessage" in headerString: + argVar = "message" + ### Message Text First + if argVar in codeList[i]["p"][3]: + jaString = codeList[i]["p"][3][argVar] + + # If there isn't any Japanese in the text just skip + if not re.search( + r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", + jaString, + ): + i += 1 + continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3][argVar] = translatedText + pbar.update(1) + + if "_TMLogWindowMZ" in headerString: + argVar = "text" + ### Message Text First + if argVar in codeList[i]["p"][3]: + jaString = codeList[i]["p"][3][argVar] + + # If there isn't any Japanese in the text just skip + # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): + # i += 1 + # continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3][argVar] = translatedText + pbar.update(1) + + if "DestinationWindow" in headerString: + argVar = "destination" + ### Message Text First + if argVar in codeList[i]["p"][3]: + jaString = codeList[i]["p"][3][argVar] + + # If there isn't any Japanese in the text just skip + # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): + # i += 1 + # continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3][argVar] = translatedText + pbar.update(1) + + if "MNKR_CommonPopupCoreMZ" in headerString: + argVar = "text" + ### Message Text First + if argVar in codeList[i]["p"][3]: + jaString = codeList[i]["p"][3][argVar] + + # If there isn't any Japanese in the text just skip + # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): + # i += 1 + # continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Set + translatedText = textwrap.fill(translatedText, width=WIDTH) + codeList[i]["p"][3][argVar] = translatedText + pbar.update(1) + + if "TextPicture" in headerString or "BalloonInBattle" in headerString: + argVar = "text" + ### Message Text First + if argVar in codeList[i]["p"][3]: + acExist = False + jaString = codeList[i]["p"][3][argVar] + + # Check ac + if "\\ac" in jaString: + acExist = True + else: + acExist = False + + # If there isn't any Japanese in the text just skip + # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString): + # i += 1 + # continue + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + if acExist: + jaString = jaString.replace("\\ac ", " ") + jaString = jaString.replace("\\ac", "") + + # Pass 1 + if setData == False: + list357.append(jaString) + + # Pass 2 + else: + if len(list357) > 0: + # Grab and Replace + translatedText = list357[0] + translatedText = jaString.replace(jaString, translatedText) + + # Remove characters that may break scripts + charList = ['"', "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Textwrap + translatedText = textwrap.fill(translatedText, WIDTH) + translatedText = translatedText.replace("- ", "-") + + # Center Text + if acExist: + translatedText = f'\\ac {translatedText.replace('\n', '\n\\ac ')}' + + # Set + codeList[i]["p"][3][argVar] = translatedText + # codeList[i]["p"][3]['fontSize'] = "18" + list357.pop(0) + + ## Event Code: 657 [Picture Text] [Optional] + if "c" in codeList[i] and codeList[i]["c"] == 657 and CODE657 is True: + if "text" in codeList[i]["p"][0]: + jaString = codeList[i]["p"][0] + if not isinstance(jaString, str): + i += 1 + continue + + # Definitely don't want to mess with files + if "_" in jaString: + i += 1 + continue + + # If there isn't any Japanese in the text just skip + if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): + i += 1 + continue + + # Remove outside text + startString = re.search(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", jaString) + jaString = re.sub(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", "", jaString) + endString = re.search(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。!?\\]+$", jaString) + jaString = re.sub(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。!?\\]+$", "", jaString) + if startString is None: + startString = "" + else: + startString = startString.group() + if endString is None: + endString = "" + else: + endString = endString.group() + + # Remove any textwrap + jaString = re.sub(r"\n", " ", jaString) + + # Translate + response = translateGPT(jaString, "", True) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + translatedText = response[0] + + # Remove characters that may break scripts + charList = [".", '"', "'"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Textwrap + translatedText = textwrap.fill(translatedText, width=WIDTH) + translatedText = startString + translatedText + endString + + # Set Data + codeList[i]["p"][0] = translatedText + + ## Event Code: 101 [Name] [Optional] + if "c" in codeList[i] and codeList[i]["c"] == 101 and CODE101 is True: + # Check Window Type (Certain games switch between 1st line speakers and none) + if FIRSTLINESPEAKERS: + if codeList[i]["p"][2] == 0: + speakerWindow = True + else: + speakerWindow = False + + else: + isVar = False + + # Grab String + jaString = "" + if len(codeList[i]["p"]) > 4: + jaString = codeList[i]["p"][4] + # Check for Var + elif len(codeList[i]["p"]) > 0: + jaString = codeList[i]["p"][0] + isVar = True + if not isinstance(jaString, str): + i += 1 + continue + + # Force Speaker using var + if "\\ap[1左]" in jaString.lower() or "\\ap[1右]" in jaString.lower(): + speaker = "Cecily" + i += 1 + continue + elif "\\ap[2左]" in jaString.lower() or "\\ap[2右]" in jaString.lower(): + speaker = "Amelia" + i += 1 + continue + elif "\\ap[3左]" in jaString.lower() or "\\ap[3右]" in jaString.lower(): + speaker = "Henry" + i += 1 + continue + elif "\\ap[4左]" in jaString.lower() or "\\ap[4右]" in jaString.lower(): + speaker = "Oswald" + i += 1 + continue + elif "\\ap" in jaString: + speaker = re.search(r"[\\]+AP\[(.*?)\]", jaString).group(1) + i += 1 + continue + + # Get Speaker + if "\\" not in jaString: + response = getSpeaker(jaString) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + speaker = response[0] + + # Validate Speaker is not empty + if len(speaker) > 0: + if isVar == False: + codeList[i]["p"][4] = speaker + i += 1 + continue + else: + codeList[i]["p"][0] = speaker + isVar = False + i += 1 + continue + else: + speaker = "" + + ## Event Code: 355 or 655 Scripts [Optional] + if "c" in codeList[i] and (codeList[i]["c"] == 355 or codeList[i]["c"] == 655) and CODE355655 is True: + jaString = codeList[i]["p"][0] + regex = r"BattleManager\._logWindow.addText\('(.*)'" + + # Var Text + match = re.search(regex, jaString) + if re.search(regex, jaString): + finalJAString = match.group(1) + # Pass 1 + if setData is False: + list355655.append(finalJAString) + + # Pass 2 + else: + # Grab and Replace + translatedText = list355655[0] + translatedText = translatedText.replace("'", "\\'") + + # Set + codeList[i]["p"][0] = codeList[i]["p"][0].replace(finalJAString, translatedText) + list355655.pop(0) + + ## Event Code: 408 (Script) + if "c" in codeList[i] and (codeList[i]["c"] == 408) and CODE408 is True: + jaString = codeList[i]["p"][0] + + # If there isn't any Japanese in the text just skip + if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): + i += 1 + continue + + if "secretText" in jaString: + regex = r"secretText:\s?(.+)" + elif "title" in jaString: + regex = r"title:\s?(.+)" + else: + regex = r"(.+)" + + # Need to remove outside code and put it back later + matchList = re.findall(regex, jaString) + + for match in matchList: + # Remove Textwrap + match = match.replace("\n", " ") + response = translateGPT( + match, + "Reply with the " + LANGUAGE + " translation of the achievement title.", + False, + ) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Replace + translatedText = jaString.replace(match, translatedText) + + # Remove characters that may break scripts + charList = [".", '"', "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Textwrap + translatedText = textwrap.fill(translatedText, width=WIDTH) + + # Set Data + codeList[i]["p"][0] = translatedText + + ## Event Code: 108 (Script) + if "c" in codeList[i] and (codeList[i]["c"] == 108) and CODE108 is True: + jaString = codeList[i]["p"][0] + + # If there isn't any Japanese in the text just skip + if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): + i += 1 + continue + + # Translate + if "info:" in jaString: + regex = r"info:(.*)" + elif "ActiveMessage:" in jaString: + regex = r"" + elif "event_text" in jaString: + regex = r"event_text\s*:\s*(.*)" + else: + i += 1 + continue + + # Need to remove outside code and put it back later + match = re.search(regex, jaString) + if match: + # Pass 1 + if setData is False: + list108.append(match.group(1)) + + # Pass 2 + else: + # Grab and Replace + translatedText = list108[0] + list108.pop(0) + + # Remove characters that may break scripts + charList = [".", '"'] + for char in charList: + translatedText = translatedText.replace(char, "") + translatedText = translatedText.replace('"', '"') + translatedText = translatedText.replace(" ", "_") + translatedText = jaString.replace(match.group(1), translatedText) + + # Set Data + codeList[i]["p"][0] = translatedText + + ## Event Code: 356 + if "c" in codeList[i] and codeList[i]["c"] == 356 and CODE356 is True: + jaString = codeList[i]["p"][0] + oldjaString = jaString + + # Grab Speaker + if "Tachie showName" in jaString: + matchList = re.findall(r"Tachie showName (.+)", jaString) + if len(matchList) > 0: + # Translate + response = translateGPT( + matchList[0], + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Text + speaker = translatedText + speaker = speaker.replace(" ", " ") + codeList[i]["p"][0] = jaString.replace(matchList[0], speaker) + i += 1 + continue + + # Want to translate this script + if "D_TEXT " in jaString: + regex = r"D_TEXT\s(.*?)\s.+" + elif "ShowInfo" in jaString: + regex = r"ShowInfo\s(.*)" + elif "PushGab" in jaString: + regex = r"PushGab\s(.*)" + elif "addLog" in jaString: + regex = r"addLog\s(.*)" + elif "DW_" in jaString: + regex = r"DW_.*?\s(.*)" + elif "CommonPopup" in jaString: + regex = r"CommonPopup\sadd\stext:(.*?)[\\]+}" + else: + regex = r"" + + # Remove any textwrap + jaString = re.sub(r"\n", "_", jaString) + + # Capture Arguments and text + textMatch = re.search(regex, jaString) + if textMatch and textMatch.group(0) != "": + text = textMatch.group(1) + + # Pass 1 + if setData == False: + text = text.replace("_", " ") + list356.append(text) + + # Pass 2 + else: + if len(list356) > 0: + # Grab + translatedText = list356[0] + + # Remove characters that may break scripts + charList = [".", '"'] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Cant have spaces? + translatedText = translatedText.replace(" ", "_") + translatedText = translatedText.replace("__", "_") + + # Put Args Back + translatedText = jaString.replace(text, translatedText) + + # Set Data + codeList[i]["p"][0] = translatedText + list356.pop(0) + + if "namePop" in jaString: + matchList = re.findall(r"namePop\s\d+\s(.+?)\s.+", jaString) + if len(matchList) > 0: + # Translate + text = matchList[0] + response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + translatedText = jaString.replace(text, translatedText) + codeList[i]["p"][0] = translatedText + + if "LL_InfoPopupWIndowMV" in jaString: + matchList = re.findall(r"LL_InfoPopupWIndowMV\sshowWindow\s(.+?) .+", jaString) + if len(matchList) > 0: + # Translate + text = matchList[0] + response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + translatedText = translatedText.replace(" ", "_") + translatedText = jaString.replace(text, translatedText) + codeList[i]["p"][0] = translatedText + + if "OriginMenuStatus SetParam" in jaString: + matchList = re.findall(r"OriginMenuStatus\sSetParam\sparam[\d]\s(.*)", jaString) + if len(matchList) > 0: + # Translate + text = matchList[0] + response = translateGPT(text, "Reply with the " + LANGUAGE + " Translation", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + translatedText = translatedText.replace(" ", "_") + translatedText = jaString.replace(text, translatedText) + codeList[i]["p"][0] = translatedText + + # LL_GalgeChoiceWindowMV Message + if "LL_GalgeChoiceWindowMV setMessageText" in jaString: + ### Message Text First + match = re.search(r"LL_GalgeChoiceWindowMV setMessageText (.+)", jaString) + if match: + jaString = match.group(1) + + # Remove any textwrap & TL + jaString = re.sub(r"\n", " ", jaString) + response = translateGPT(jaString, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap & Replace Whitespace + translatedText = textwrap.fill(translatedText, width=WIDTH) + translatedText = translatedText.replace(" ", "_") + + # Replace and Set + translatedText = match.group(0).replace(match.group(1), translatedText) + codeList[i]["p"][0] = translatedText + + # LL_GalgeChoiceWindowMV Choices + if "LL_GalgeChoiceWindowMV setChoices": + match = re.search(r"LL_GalgeChoiceWindowMV setChoices (.+)", jaString) + if match: + jaString = match.group(1) + choiceList = jaString.split(",") + + # Translate + question = translatedText + response = translateGPT( + choiceList, + f"Previous text for context: {question}\n\nThis will be a dialogue option", + True, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + choiceListTL = response[0] + translatedText = match.group(0) + + # Replace Strings + for j in range(len(choiceListTL)): + choiceListTL[j] = choiceListTL[j].replace(" ", "_") + translatedText = translatedText.replace(choiceList[j], choiceListTL[j]) + + # Set Data + codeList[i]["p"][0] = translatedText + + ### Event Code: 102 Show Choice + if "c" in codeList[i] and codeList[i]["c"] == 102 and CODE102 is True: + choiceList = [] + varList = [] + for choice in range(len(codeList[i]["p"][0])): + jaString = codeList[i]["p"][0][choice] + jaString = jaString.replace(" 。", ".") + + # Avoid Empty Strings + if jaString == "": + i += 1 + continue + + # If and En Statements + ifVar = "" + ifList = re.findall(r"([ei][nf]\(.+?\)\)?\)?)", jaString) + if len(ifList) != 0: + for var in ifList: + jaString = jaString.replace(var, "") + ifVar += var + varList.append(ifVar) + + # Append to List + choiceList.append(jaString) + + # Translate + if len(textHistory) > 0: + response = translateGPT( + choiceList, + "This will be a dialogue option.\nPrevious text for context: " + str(textHistory), + True, + ) + translatedTextList = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + else: + response = translateGPT(choiceList, "This will be a dialogue option", True) + translatedTextList = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(translatedTextList) == len(choiceList): + for choice in range(len(codeList[i]["p"][0])): + translatedText = translatedTextList[choice] + + # Set Data + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if translatedText != "": + translatedText = varList[choice] + translatedText[0].upper() + translatedText[1:] + else: + translatedText = varList[choice] + translatedText + codeList[i]["p"][0][choice] = translatedText + else: + if filename not in MISMATCH: + MISMATCH.append(filename) + + ### Event Code: 111 Script + if "c" in codeList[i] and codeList[i]["c"] == 111 and CODE111 is True: + for j in range(len(codeList[i]["p"])): + jaString = codeList[i]["p"][j] + + # Check if String + if not isinstance(jaString, str): + i += 1 + continue + + # Only TL the Game Variable + if "$gameVariables" not in jaString: + i += 1 + continue + + # This is going to be the var being set. (IMPORTANT) + if "1045" not in jaString: + i += 1 + continue + + # Need to remove outside code and put it back later + matchList = re.findall(r"'(.*?)'", jaString) + + for match in matchList: + response = translateGPT(match, "", False) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Remove characters that may break scripts + charList = [".", '"', "'", "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + jaString = jaString.replace(match, translatedText) + + # Set Data + translatedText = jaString + codeList[i]["p"][j] = translatedText + + ### Event Code: 320 Set Variable + if "c" in codeList[i] and codeList[i]["c"] == 320 and CODE320 is True: + jaString = codeList[i]["p"][1] + if not isinstance(jaString, str): + i += 1 + continue + + # Definitely don't want to mess with files + if "■" in jaString or "_" in jaString: + i += 1 + continue + + # If there isn't any Japanese in the text just skip + if not re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9\uFF61-\uFF9F]+", jaString): + i += 1 + continue + + # Translate + getSpeaker(jaString) + + # Remove characters that may break scripts + charList = [".", '"', "'", "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Set Data + codeList[i]["p"][1] = translatedText + + # Iterate + else: + i += 1 + + # EOF + list401TL = [] + list122TL = [] + list356TL = [] + list357TL = [] + list355655TL = [] + list108TL = [] + setData = False + PBAR = pbar + + # 401 + if len(list401) > 0: + response = translateGPT(list401, "", True) + list401TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list401TL) != len(list401): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 122 + if len(list122) > 0: + response = translateGPT(list122, "Keep you translation as brief as possible", True) + list122TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list122TL) != len(list122): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 355/655 + if len(list355655) > 0: + response = translateGPT(list355655, textHistory, True) + list355655TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list355655TL) != len(list355655): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 108 + if len(list108) > 0: + response = translateGPT(list108, textHistory, True) + list108TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list108TL) != len(list108): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 356 + if len(list356) > 0: + response = translateGPT(list356, textHistory, True) + list356TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list356TL) != len(list356): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 357 + if len(list357) > 0: + response = translateGPT(list357, textHistory, True) + list357TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list357TL) != len(list357): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # Start Pass 2 + if setData: + searchCodes( + page, + pbar, + [list401TL, list122TL, list355655TL, list108TL, list356TL, list357TL], + filename, + ) + + # Delete all -1 codes + codeListFinal = [] + for i in range(len(codeList)): + if "c" in codeList[i] and codeList[i]["c"] != -1: + codeListFinal.append(codeList[i]) + + # Normal Format + if "list" in page: + page["list"] = codeListFinal + + # Special Format (Scenario) + else: + page = codeListFinal + + except IndexError as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + oldjaString) from None + except Exception as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + oldjaString) from None + + return totalTokens + + +def searchSS(state, pbar): + totalTokens = [0, 0] + + # Name + nameResponse = ( + translateGPT( + state["name"], + "Reply with only the " + LANGUAGE + " translation of the RPG Skill name.", + False, + ) + if "name" in state + else "" + ) + + # Description + descriptionResponse = ( + translateGPT( + state["description"], + "Reply with only the " + LANGUAGE + " translation of the description.", + False, + ) + if "description" in state + else "" + ) + + # Messages + message1Response = "" + message4Response = "" + message2Response = "" + message3Response = "" + + if "message1" in state: + if len(state["message1"]) > 0 and state["message1"][0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + message1Response = translateGPT( + "Taro" + state["message1"], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example,\ +Translate 'Taroを倒した!' as 'Taro was defeated!'", + False, + ) + else: + message1Response = translateGPT( + state["message1"], + "reply with only the gender neutral " + LANGUAGE + " translation", + False, + ) + + if "message2" in state: + if len(state["message2"]) > 0 and state["message2"][0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + message2Response = translateGPT( + "Taro" + state["message2"], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example,\ +Translate 'Taroを倒した!' as 'Taro was defeated!'", + False, + ) + else: + message2Response = translateGPT( + state["message2"], + "reply with only the gender neutral " + LANGUAGE + " translation", + False, + ) + + if "message3" in state: + if len(state["message3"]) > 0 and state["message3"][0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + message3Response = translateGPT( + "Taro" + state["message3"], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example,\ +Translate 'Taroを倒した!' as 'Taro was defeated!'", + False, + ) + else: + message3Response = translateGPT( + state["message3"], + "reply with only the gender neutral " + LANGUAGE + " translation", + False, + ) + + if "message4" in state: + if len(state["message4"]) > 0 and state["message4"][0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + message4Response = translateGPT( + "Taro" + state["message4"], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example,\ +Translate 'Taroを倒した!' as 'Taro was defeated!'", + False, + ) + else: + message4Response = translateGPT( + state["message4"], + "reply with only the gender neutral " + LANGUAGE + " translation", + False, + ) + + # Translate State Notes + if "help" in state["note"]: + noteResponse = translateNote(state, r"]*)>") + totalTokens[0] += noteResponse[0] + totalTokens[1] += noteResponse[1] + if "STATE_HELP" in state["note"]: + noteResponse = translateNote(state, r"\n(.*)\n") + totalTokens[0] += noteResponse[0] + totalTokens[1] += noteResponse[1] + + # Count totalTokens + totalTokens[0] += nameResponse[1][0] if nameResponse != "" else 0 + totalTokens[1] += nameResponse[1][1] if nameResponse != "" else 0 + totalTokens[0] += descriptionResponse[1][0] if descriptionResponse != "" else 0 + totalTokens[1] += descriptionResponse[1][1] if descriptionResponse != "" else 0 + totalTokens[0] += message1Response[1][0] if message1Response != "" else 0 + totalTokens[1] += message1Response[1][1] if message1Response != "" else 0 + totalTokens[0] += message2Response[1][0] if message2Response != "" else 0 + totalTokens[1] += message2Response[1][1] if message2Response != "" else 0 + totalTokens[0] += message3Response[1][0] if message3Response != "" else 0 + totalTokens[1] += message3Response[1][1] if message3Response != "" else 0 + totalTokens[0] += message4Response[1][0] if message4Response != "" else 0 + totalTokens[1] += message4Response[1][1] if message4Response != "" else 0 + + # Set Data + if "name" in state: + state["name"] = nameResponse[0].replace('"', "") + if "description" in state: + # Textwrap + translatedText = descriptionResponse[0] + translatedText = textwrap.fill(translatedText, width=LISTWIDTH) + state["description"] = translatedText.replace('"', "") + if "message1" in state: + state["message1"] = message1Response[0].replace('"', "").replace("Taro", "") + if "message2" in state: + state["message2"] = message2Response[0].replace('"', "").replace("Taro", "") + if "message3" in state: + state["message3"] = message3Response[0].replace('"', "").replace("Taro", "") + if "message4" in state: + state["message4"] = message4Response[0].replace('"', "").replace("Taro", "") + + return totalTokens + + +def searchSystem(data, pbar): + totalTokens = [0, 0] + context = "Reply with only the " + LANGUAGE + ' translation of the UI textbox."' + + # Title + response = translateGPT( + data["game_title"], + " Reply with the " + LANGUAGE + " translation of the game title name", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + data["game_title"] = response[0].strip(".") + + # Terms + for term in data["terms"]: + if term != "messages": + termList = data["terms"][term] + for i in range(len(termList)): # Last item is a messages object + if termList[i] is not None: + response = translateGPT(termList[i], context, False) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + termList[i] = response[0].replace('"', "").strip() + + # Armor Types + for i in range(len(data["armor_types"])): + response = translateGPT( + data["armor_types"][i], + "Reply with only the " + LANGUAGE + " translation of the armor type", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + data["armor_types"][i] = response[0].replace('"', "").strip() + + # Skill Types + for i in range(len(data["skill_types"])): + response = translateGPT( + data["skill_types"][i], + "Reply with only the " + LANGUAGE + " translation", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + data["skill_types"][i] = response[0].replace('"', "").strip() + + # Equip Types + for i in range(len(data["weapon_types"])): + response = translateGPT( + data["weapon_types"][i], + "Reply with only the " + LANGUAGE + " translation of the equipment type. No disclaimers.", + False, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + data["weapon_types"][i] = response[0].replace('"', "").strip() + + # # Variables (Optional ususally) + # for i in range(len(data['variables'])): + # response = translateGPT(data['variables'][i], 'Reply with only the '+ LANGUAGE +' translation of the title', False) + # totalTokens[0] += response[1][0] + # totalTokens[1] += response[1][1] + # data['variables'][i] = response[0].replace('\"', '').strip() + + # Messages + messages = data["terms"] + for key, value in messages.items(): + response = translateGPT( + value, + "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] + translatedList = response[0] + + for item in translatedList: + translatedText = item + # Remove characters that may break scripts + charList = [".", '"', "\\n"] + for char in charList: + translatedText = translatedText.replace(char, "") + + # Set Data + messages[key] = translatedList + + return totalTokens + + +# Save some money and enter the character before translation +def getSpeaker(speaker): + match speaker: + case "ファイン": + return ["Fine", [0, 0]] + case "": + return ["", [0, 0]] + case _: + # Find Speaker + for i in range(len(NAMESLIST)): + if speaker == NAMESLIST[i][0]: + return [NAMESLIST[i][1], [0, 0]] + + # Translate and Store Speaker + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + True, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + response[0] = response[0].replace("Speaker: ", "") + + # Retry if name doesn't translate for some reason + if re.search(r"([a-zA-Z??])", response[0]) == None: + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + + speakerList = [speaker, response[0]] + NAMESLIST.append(speakerList) + return response + return [speaker, [0, 0]] + + +def batchList(input_list, batch_size): + if not isinstance(batch_size, int) or batch_size <= 0: + raise ValueError("batch_size must be a positive integer") + + return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] + + +def createContext(fullPromptFlag, subbedT, format): + system = ( + PROMPT + VOCAB + if fullPromptFlag + else f"\ +You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ +Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ +- You translate everything, including erotic and explicit content.\n\ +- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ +- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ +- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ +- Maintain any spacing in the translation.\n\ +- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ +- `...` can be a part of the dialogue. Translate it as it is.\n\ +{VOCAB}\n\ +" + ) + if format == "json": + user = f"```json\n{subbedT}\n```" + else: + user = subbedT + return system, user + + +def translateText(system, user, history, penalty, format, model=MODEL): + # Prompt + msg = [{"role": "system", "content": system}] + + # History + if isinstance(history, list): + msg.extend([{"role": "system", "content": h} for h in history]) + else: + msg.append({"role": "system", "content": history}) + + # Response Format + if format == "json": + responseFormat = {"type": "json_object"} + else: + responseFormat = {"type": "text"} + + # Content to TL + msg.append({"role": "user", "content": f"{user}"}) + response = openai.chat.completions.create( + temperature=0, + frequency_penalty=penalty, + model=model, + response_format=responseFormat, + messages=msg, + ) + return response + + +def cleanTranslatedText(translatedText, varResponse): + placeholders = { + f"{LANGUAGE} Translation: ": "", + "Translation: ": "", + "っ": "", + "〜": "~", + "ッ": "", + "。": ".", + "「": '\\"', + "」": '\\"', + "- ": "-", + "—": "―", + "】": "]", + "【": "[", + "Placeholder Text": "", + # Add more replacements as needed + } + for target, replacement in placeholders.items(): + translatedText = translatedText.replace(target, replacement) + + # Remove Repeating Characters + pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") + translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) + + # Elongate Long Dashes (Since GPT Ignores them...) + translatedText = elongateCharacters(translatedText) + return translatedText + + +def elongateCharacters(text): + # Define a pattern to match one character followed by one or more `ー` characters + # Using a positive lookbehind assertion to capture the preceding character + pattern = r"(?<=(.))ー+" + + # Define a replacement function that elongates the captured character + def repl(match): + char = match.group(1) # The character before the ー sequence + count = len(match.group(0)) - 1 # Number of ー characters + return char * count # Replace ー sequence with the character repeated + + # Use re.sub() to replace the pattern in the text + return re.sub(pattern, repl, text) + + +def extractTranslation(translatedTextList, is_list): + try: + translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) + translatedTextList = re.sub(r"(? instead -FIXTEXTWRAP = True # Overwrites textwrap -IGNORETLTEXT = False # Ignores all translated text. -MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) -FILENAME = None -BRACKETNAMES = False - -# Pricing - Depends on the model https://openai.com/pricing -# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request -# If you are getting a MISMATCH LENGTH error, lower the batch size. -if "gpt-3.5" in MODEL: - INPUTAPICOST = 0.002 - OUTPUTAPICOST = 0.002 - BATCHSIZE = 10 - FREQUENCY_PENALTY = 0.2 -elif "gpt-4" in MODEL: - INPUTAPICOST = 0.0025 - OUTPUTAPICOST = 0.01 - BATCHSIZE = 20 - FREQUENCY_PENALTY = 0.1 -else: - INPUTAPICOST = float(os.getenv("input_cost")) - OUTPUTAPICOST = float(os.getenv("output_cost")) - BATCHSIZE = int(os.getenv("batchsize")) - FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) - -# tqdm Globals -BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" -POSITION = 0 -LEAVE = False -PBAR = None -FILENAME = None - -# Dialogue / Scroll -CODE101 = False -CODE102 = False - -# Set String (Fragile but necessary) -CODE122 = False -CODE150 = True - -# Other -CODE210 = False -CODE300 = False -CODE250 = False - -# Database -SCENARIOFLAG = False -OPTIONSFLAG = False -NPCFLAG = False -DBNAMEFLAG = False -ITEMFLAG = True -STATEFLAG = False -ENEMYFLAG = False -ARMORFLAG = True -WEAPONFLAG = True -SKILLFLAG = True - - -def handleWOLF(filename, estimate): - global ESTIMATE, TOKENS, FILENAME - ESTIMATE = estimate - FILENAME = filename - - # Translate - start = time.time() - translatedData = openFiles(filename) - - # Translate - if not estimate: - try: - with open("translated/" + filename, "w", encoding="utf-8") as outFile: - json.dump(translatedData[0], outFile, ensure_ascii=False, indent=4) - except Exception: - traceback.print_exc() - return "Fail" - - # Print File - end = time.time() - tqdm.write(getResultString(translatedData, end - start, filename)) - with LOCK: - TOKENS[0] += translatedData[1][0] - TOKENS[1] += translatedData[1][1] - - # Print Total - totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") - - # Print any errors on maps - if len(MISMATCH) > 0: - return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET - else: - return totalString - - -def openFiles(filename): - with open("files/" + filename, "r", encoding="utf-8-sig") as f: - data = json.load(f) - - # Map Files - if "'events':" in str(data): - if len(data["events"]) > 0: - translatedData = parseMap(data, filename) - else: - return [data, [0, 0], None] - - # Map Files - elif "'types':" in str(data): - translatedData = parseDB(data, filename) - - # Other Files - elif "'commands':" in str(data): - translatedData = parseOther(data, filename) - - else: - raise NameError(filename + " Not Supported") - - return translatedData - - -def getResultString(translatedData, translationTime, filename): - # File Print String - totalTokenstring = ( - Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" - "[Output: " - + str(translatedData[1][1]) - + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) - + "]" - ) - timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" - - if translatedData[2] is None: - # Success - return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET - else: - # Fail - try: - raise translatedData[2] - except Exception as e: - traceback.print_exc() - errorString = str(e) + Fore.RED - return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET - - -def parseOther(data, filename): - totalTokens = [0, 0] - totalLines = 0 - events = data["commands"] - global LOCK - - # Thread for each page in file - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - pbar.total = totalLines - translationData = searchCodes(events, pbar, [], filename) - try: - totalTokens[0] += translationData[0] - totalTokens[1] += translationData[1] - except Exception as e: - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def parseDB(data, filename): - totalTokens = [0, 0] - totalLines = 0 - events = data["types"] - global LOCK - - # Thread for each page in file - with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: - pbar.desc = filename - pbar.total = totalLines - translationData = searchDB(events, pbar, [], filename) - try: - totalTokens[0] += translationData[0] - totalTokens[1] += translationData[1] - except Exception as e: - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def parseMap(data, filename): - totalTokens = [0, 0] - totalLines = 0 - events = data["events"] - global LOCK - - # Get total for progress bar - for event in events: - if event is not None: - for page in event["pages"]: - totalLines += len(page["list"]) - - # Thread for each page in file - with tqdm(bar_format=BAR_FORMAT, position=POSITION, total=totalLines, leave=LEAVE) as pbar: - pbar.desc = filename - pbar.total = totalLines - with ThreadPoolExecutor(max_workers=THREADS) as executor: - for event in events: - if event is not None: - futures = [executor.submit(searchCodes, page["list"], pbar, None, filename) for page in event["pages"] if page is not None] - for future in as_completed(futures): - try: - totalTokensFuture = future.result() - totalTokens[0] += totalTokensFuture[0] - totalTokens[1] += totalTokensFuture[1] - except Exception as e: - return [data, totalTokens, e] - return [data, totalTokens, None] - - -def searchCodes(events, pbar, jobList, filename): - # Lists - if jobList: - stringList = jobList[0] - list210 = jobList[1] - list300 = jobList[2] - setData = True - else: - stringList = [] - list210 = [] - list300 = [] - setData = False - - # Other - codeList = events - textHistory = [] - totalTokens = [0, 0] - translatedText = "" - speaker = "" - nametag = "" - initialJAString = "" - global LOCK, NAMESLIST, MISMATCH, PBAR, FILENAME - FILENAME = filename - PBAR = pbar - - # Calculate Total Length - code_flags = {102: CODE102, 122: CODE122, 300: CODE300, 250: CODE250} - totalList = 0 - for code_item in codeList: - if code_flags.get(code_item["code"], False): - totalList += 1 - pbar.total = totalList - pbar.refresh() - - # Begin Parsing File - try: - # Iterate through events - i = 0 - while i < len(codeList): - ### Event Code: 101 Message - if codeList[i]["code"] == 101 and CODE101 == True: - # Grab String - jaString = codeList[i]["stringArgs"][0] - initialJAString = jaString - - # Grab Speaker - if ":\n" in jaString: - nameList = re.findall(r"(.*):\n", jaString) - if nameList is not None: - # TL Speaker - response = getSpeaker(nameList[0]) - speaker = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set nametag and remove from string - nametag = f"{speaker}:\n" - jaString = jaString.replace(f"{nameList[0]}:\n", "") - - # Remove Textwrap - jaString = jaString.replace("\n", " ") - - # 1st Pass (Save Text to List) - if not setData: - if speaker == "": - stringList.append(jaString) - else: - stringList.append(f"[{speaker}]: {jaString}") - - # 2nd Pass (Set Text) - else: - # Grab Translated String - translatedText = stringList[0] - - # Remove speaker - matchSpeakerList = re.findall(r"^(\[.+?\]\s?[|:]\s?)\s?", translatedText) - if len(matchSpeakerList) > 0: - translatedText = translatedText.replace(matchSpeakerList[0], "") - - # Textwrap - if FIXTEXTWRAP is True: - translatedText = textwrap.fill(translatedText, width=WIDTH) - - # Add back Nametag - translatedText = nametag + translatedText - nametag = "" - - # Set Data - codeList[i]["stringArgs"][0] = translatedText - - # Reset Data and Pop Item - speaker = "" - stringList.pop(0) - - ### Event Code: 102 Choices - if codeList[i]["code"] == 102 and CODE102 == True: - # Grab Choice List - choiceList = [] - jaChoiceList = codeList[i]["stringArgs"] - - # Filter Empty - for j in range(len(jaChoiceList)): - if jaChoiceList[j]: - choiceList.append(jaChoiceList[j]) - - # Translate - response = translateGPT( - choiceList, - f"Reply with the {LANGUAGE} translation of the dialogue choice", - True, - ) - translatedChoiceList = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Validate and Set Data - if len(translatedChoiceList) == len(choiceList): - for j in range(len(jaChoiceList)): - if jaChoiceList[j]: - codeList[i]["stringArgs"][j] = translatedChoiceList[0] - translatedChoiceList.pop(0) - - ### Event Code: 210 Common Event - if codeList[i]["code"] == 210 and CODE210 == True: - # Speaker Event - if "stringArgs" in codeList[i] and codeList[i]["intArgs"][0] == None and len(codeList[i]["stringArgs"]) == 2: - response = getSpeaker(codeList[i]["stringArgs"][1]) - totalTokens[1] += response[1][0] - totalTokens[1] += response[1][1] - - # Set Data - codeList[i]["stringArgs"][1] = response[0] - - # Logs - elif "stringArgs" in codeList[i] and codeList[i]["intArgs"][0] == 500220 and len(codeList[i]["stringArgs"]) == 2: - # Grab String - jaString = codeList[i]["stringArgs"][1] - initialJAString = jaString - - # Remove Textwrap - jaString = jaString.replace("\r", "") - jaString = jaString.replace("\n", " ") - - # 1st Pass (Save Text to List) - if not setData: - list210.append(jaString) - - # 2nd Pass (Set Text) - else: - # Grab Translated String - translatedText = list210[0] - - # Textwrap - if FIXTEXTWRAP is True: - translatedText = textwrap.fill(translatedText, width=WIDTH) - - # Set Data - codeList[i]["stringArgs"][1] = translatedText - - # Pop Item - list210.pop(0) - - ### Event Code: 122 SetString - if codeList[i]["code"] == 122 and CODE122 == True: - if "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 0: - # Grab String - jaString = re.search(r"^\n?(.*)\n?$", codeList[i]["stringArgs"][0]) - if jaString: - jaString = jaString.group(1) - else: - jaString = codeList[i]["stringArgs"][0] - - # Translate Conversations - if ":Nothing" in jaString: - # Separate into list - list122 = jaString.split("\n\n") - - # Remove Textwrap - # for j in range(len(list122)): - # list122[j] = list122[j].replace("\n", " ") - - # Translate - response = translateGPT( - list122, - f"Reply with the {LANGUAGE} translation of the text", - True, - ) - list122TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Validate and Set Data - if len(list122) == len(list122TL): - # Adjust Speaker and Add Textwrap - for j in range(len(list122TL)): - list122TL[j] = textwrap.fill(list122TL[j], WIDTH) - list122TL[j] = re.sub(r"^\[?(.+?)\]?:", r"\1:", list122TL[j]) - list122TL[j] = list122TL[j].replace(":", ":\n") - list122TL[j] = list122TL[j].replace(":\n ", ":\n") - - # Join back into single string - list122TL = "\n\n".join(list122TL) - - # Set String - codeList[i]["stringArgs"][0] = list122TL - - # Translate Other Strings [Specific Files Only] - else: - if ( - not re.search(r"\.[\w]+$", jaString) - and jaString != "" - and "_" not in jaString - and '",' not in jaString - and "/" not in jaString - ): - # Japanese Text Only - if re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", jaString): - # Translate - response = translateGPT( - jaString, - f"Reply with the {LANGUAGE} translation of the text", - True, - ) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Set String - codeList[i]["stringArgs"][0] = codeList[i]["stringArgs"][0].replace(jaString, translatedText) - - ### Event Code: 122 SetString - if codeList[i]["code"] == 150 and CODE150 == True: - if "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 0: - # Grab String - jaString = re.search(r"^\n?(.*)\n?$", codeList[i]["stringArgs"][0]) - if jaString: - jaString = jaString.group(1) - else: - jaString = codeList[i]["stringArgs"][0] - - # Remove Textwrap - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - - # Translate Other Strings [Specific Files Only] - if ( - not re.search(r"\.[\w]+$", jaString) - and jaString != "" - and "_" not in jaString - and '",' not in jaString - and "/" not in jaString - ): - # Japanese Text Only - if re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", jaString): - # Translate - response = translateGPT( - jaString, - f"Reply with the {LANGUAGE} translation of the text", - True, - ) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Textwrap - translatedText = textwrap.fill(translatedText, WIDTH) - - # Set String - codeList[i]["stringArgs"][0] = translatedText - - ### Event Code: 300 Common Events - if codeList[i]["code"] == 300 and CODE300 == True and "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 1: - # Choices - if codeList[i]["stringArgs"][0] == "[共]汎用ウィンドウ生成" or codeList[i]["stringArgs"][0] == "[共]選択生成": - # Grab String - choiceList = codeList[i]["stringArgs"][1].split("\r\n") - - # # Translate Question - # question = codeList[i]['stringArgs'][2] - # response = translateGPT(question, "", True) - # translatedText = response[0] - # totalTokens[0] += response[1][0] - # totalTokens[1] += response[1][1] - - # # Translate Question - # codeList[i]['stringArgs'][2] = translatedText - - # Translate Choices - response = translateGPT(choiceList, translatedText, True) - choiceListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Replace Commas - for j in range(len(choiceListTL)): - choiceListTL[j] = choiceListTL[j].replace(", ", "、") - - # Convert to String and Set - translatedText = "\r\n".join(choiceListTL) - codeList[i]["stringArgs"][1] = translatedText - - # Dialogue - elif codeList[i]["stringArgs"][0] == "○【戦闘】テキスト表示": - jaString = codeList[i]["stringArgs"][1] - - # Pass 1 - if not setData: - # Remove Textwrap - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - - # Append - list300.append(jaString) - - # Pass 2 - else: - # Add Textwrap and Font - translatedText = textwrap.fill(list300[0], WIDTH) - list300.pop(0) - - # Write to File - codeList[i]["stringArgs"][1] = translatedText - - ### Event Code: 250 DB Read/Writes - if codeList[i]["code"] == 250 and CODE250 == True: - foundTerm = False - - # Validate size - if len(codeList[i]["stringArgs"]) == 4: - if codeList[i]["stringArgs"][1] == "┣所持アイテム個数" and codeList[i]["stringArgs"][2] != "": - # Grab String - jaString = codeList[i]["stringArgs"][2] - - # Catch Vars that may break the TL - varString = "" - matchList = re.findall(r"^[\\_]+[\w]+\[[a-zA-Z0-9\\\[\]\_,\s-]+\]", jaString) - if len(matchList) != 0: - varString = matchList[0] - jaString = jaString.replace(matchList[0], "") - - # Check if term already translated - for j in range(len(TERMSLIST)): - if jaString == TERMSLIST[j][0]: - translatedText = TERMSLIST[j][1] - foundTerm = True - - # Translate - if foundTerm == False: - response = translateGPT( - jaString, - f"Reply with the {LANGUAGE} translation of the text.", - True, - ) - translatedText = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - TERMSLIST.append([jaString, translatedText]) - - # Add back Potential Variables in String - translatedText = varString + translatedText - - # Set Data - codeList[i]["stringArgs"][2] = translatedText - - ### Iterate - i += 1 - - # EOF - stringListTL = [] - list210TL = [] - list300TL = [] - setData = False - - # String List - if len(stringList) > 0: - pbar.total = len(stringList) - pbar.refresh() - response = translateGPT(stringList, textHistory, True) - stringListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(stringListTL) != len(stringList): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 210 List - if len(list210) > 0: - pbar.total = len(list210) - pbar.refresh() - response = translateGPT(list210, textHistory, True) - list210TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list210TL) != len(list210): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # 300 List - if len(list300) > 0: - pbar.total = len(list300) - pbar.refresh() - response = translateGPT(list300, textHistory, True) - list300TL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - if len(list300TL) != len(list300): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - setData = True - - # Pass 2 - if setData: - stringList = [] - searchCodes(events, pbar, [stringListTL, list210TL, list300TL], filename) - else: - # Set Data - events = codeList - - except IndexError as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + initialJAString) from None - except Exception as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + initialJAString) from None - - return totalTokens - - -def formatDramon(jaString): - imageRegex = r"(\r?\n?_[a-zA-Z_\d/.]+\r?\n?)|(@-?\d?\r\n)|(@-?\d?)([^\r]\d?-?\d?[^\r]+?)(\r\n|$)|(_PDC)|(>\r\n)|(^[#])|(\r\n@$)|(/)|(_SS_)|(\r\n[@/]\s?-?\d?\r\n)" - - jaString = jaString.replace("\u3000", " ") - jaString = jaString.replace("#", "") - jaString = re.sub(r"([^\r])\n", r"\1\r\n", jaString) - - # Grab and Split - jaStringList = re.split(imageRegex, jaString) - - # Clean List - cleanedList = [x for x in jaStringList if x is not None and x != "" and x != "\r\n" and x != "_SS_"] - - # Iterate Through List - j = 0 - translatedText = "" - while j < len(cleanedList): - if ( - ("@" in cleanedList[j] or "/" in cleanedList[j]) - and j < len(cleanedList) - 1 - and re.search(r"([@/]-?\d?\r\n)", cleanedList[j]) is None - and ".ogg" not in cleanedList[j] - ): - # Setup @ - if j > 0 and "@" not in cleanedList[j - 1] and "/" not in cleanedList[j - 1] and "_" not in cleanedList[j - 1]: - cleanedList[j - 1] = cleanedList[j - 1] + cleanedList[j + 1] - else: - cleanedList.insert(j, cleanedList[j + 1]) - j += 1 - cleanedList[j] = f"\r\n{cleanedList[j]}\r\n" - cleanedList.pop(j + 1) - j += 1 - - return cleanedList - - -# Database -def searchDB(events, pbar, jobList, filename): - # Set Lists - if len(jobList) > 0: - scenarioList = jobList[0] - npcList = jobList[1] - itemList = jobList[2] - stateList = jobList[3] - armorList = jobList[4] - enemyList = jobList[5] - weaponsList = jobList[6] - skillList = jobList[7] - optionsList = jobList[8] - dbNameList = jobList[9] - setData = True - else: - scenarioList = [[], [], []] - npcList = [[], [], [], []] - itemList = [[], [], [], []] - armorList = [[], []] - enemyList = [[], []] - weaponsList = [[], [], [], []] - skillList = [[], [], [], [], []] - stateList = [[], [], [], [], [], [], [], []] - optionsList = [[], [], [], []] - dbNameList = [[]] - setData = False - - # Vars/Globals - totalTokens = [0, 0] - initialJAString = "" - tableList = events - font = "" - global LOCK - global NAMESLIST - global MISMATCH - - # Begin Parsing File - try: - for table in tableList: - # Grab Armors - if table["name"] == "主人公ステータス" and NPCFLAG == True: - for npc in table["data"]: - dataList = npc["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "キャラ名" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - npcList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": npcList[0][0]}) - npcList[0].pop(0) - - # Description - if "肩書き" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - npcList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = npcList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Set Data - dataList[j].update({"value": translatedText}) - npcList[1].pop(0) - - # Grab Scenarios - if table["name"] == "MGP_参加者" and SCENARIOFLAG == True: - for hScenario in table["data"]: - dataList = hScenario["data"] - - # Parse - # Pass 1 (Grab Data) - if setData == False: - if dataList[1].get("value") != "": - scenarioList[0].append(dataList[1].get("value")) - if dataList[44].get("value") != "": - scenarioList[1].append(dataList[44].get("value")) - if dataList[45].get("value") != "": - scenarioList[2].append(dataList[45].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[1].get("value") != "": - dataList[1].update({"value": scenarioList[0][0]}) - scenarioList[0].pop(0) - if dataList[44].get("value") != "": - dataList[44].update({"value": scenarioList[1][0]}) - scenarioList[1].pop(0) - if dataList[45].get("value") != "": - dataList[45].update({"value": scenarioList[2][0]}) - scenarioList[2].pop(0) - - # Grab Options - if table["name"] == "選択肢説明" and OPTIONSFLAG == True: - for option in table["data"]: - dataList = option["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "表示名" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - optionsList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": choiceList[0][0]}) - optionsList[0].pop(0) - - # Description - if "選択肢1" in dataList[j].get("name"): - if setData == False: - if dataList[j].get("value") != "": - # Grab Choices - optionsList[1] = dataList[j].get("value").split("\r\n") - - # Translate - response = translateGPT( - optionsList[1], - "Reply with the English translation of the dialogue choices", - True, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - translatedText = "\r\n".split(response[0]) - - # Set Data - dataList[j].update({"value": translatedText}) - # Description - if "選択肢1" in dataList[j].get("name"): - if setData == False: - if dataList[j].get("value") != "": - # Grab Choices - optionsList[1] = dataList[j].get("value").split("\r\n") - - # Translate - response = translateGPT( - optionsList[1], - "Reply with the English translation of the dialogue choices", - True, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - translatedText = "\r\n".split(response[0]) - - # Set Data - dataList[j].update({"value": translatedText}) - # Description - if "選択肢1" in dataList[j].get("name"): - if setData == False: - if dataList[j].get("value") != "": - # Grab Choices - optionsList[1] = dataList[j].get("value").split("\r\n") - - # Translate - response = translateGPT( - optionsList[1], - "Reply with the English translation of the dialogue choices", - True, - ) - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - translatedText = "\r\n".split(response[0]) - - # Set Data - dataList[j].update({"value": translatedText}) - - # Grab DB Names - if table["name"] == "マップ設定" and DBNAMEFLAG == True: - font = None - dbName = table["data"] - for j in range(len(dbName)): - # Pass 1 (Grab Data) - if setData == False: - if dbName[j].get("name") != "": - dbNameList[0].append(dbName[j].get("name")) - - # Pass 2 (Set Data) - else: - if dbName[j].get("name") != "": - dbName[j].update({"name": dbNameList[0][0]}) - dbNameList[0].pop(0) - - # Grab Items - if table["name"] == "Item" and ITEMFLAG == True: - # Write Category - if setData: - with open("translations.txt", "a", encoding="utf-8") as file: - file.write(f"\n#Items\n") - - # Begin Translation - for item in table["data"]: - dataList = item["data"] - - # Parse - for j in range(len(dataList)): - font = None - # Name - if "アイテム名" in dataList[j].get("name"): - jaString = dataList[j].get("value") - if jaString != "": - # Pass 1 (Grab Data) - if setData == False: - if jaString != "": - itemList[0].append(jaString) - - # Pass 2 (Set Data) - else: - # Write to TL File - with open("translations.txt", "a", encoding="utf-8") as file: - file.write(f"{jaString} ({itemList[0][0]})\n") - - dataList[j].update({"value": itemList[0][0]}) - itemList[0].pop(0) - - # Description - if "説明文[2行まで可]" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap & Font - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - itemList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = itemList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - - # Font - if font: - translatedText = f"{font}{translatedText}" - - # Set Data - dataList[j].update({"value": translatedText}) - itemList[1].pop(0) - # Description - if "-------------------------" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - itemList[2].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = itemList[2][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - - # Font - if font: - translatedText = f"{font}{translatedText}" - - # Set Data - dataList[j].update({"value": translatedText}) - itemList[2].pop(0) - # Description - if "使用時文章[戦](人名~" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - itemList[3].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = itemList[3][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - - # Font - if font: - translatedText = f"{font}{translatedText}" - - # Set Data - dataList[j].update({"value": translatedText}) - itemList[3].pop(0) - - # Grab Armors - if table["name"] == "防具" and ARMORFLAG == True: - for armor in table["data"]: - dataList = armor["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "防具の名前" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - armorList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": armorList[0][0]}) - armorList[0].pop(0) - - # Description - if "防具の説明" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - armorList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = armorList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - - # Set Data - dataList[j].update({"value": translatedText}) - armorList[1].pop(0) - - # Grab Enemies - if table["name"] == "敵キャラ個体データ" and ENEMYFLAG == True: - for enemy in table["data"]: - dataList = enemy["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "敵キャラ名" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - enemyList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": enemyList[0][0]}) - enemyList[0].pop(0) - - # Description - if "NULL" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - enemyList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = enemyList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Set Data - dataList[j].update({"value": translatedText}) - enemyList[1].pop(0) - - # Grab Weapons - if table["name"] == "武器" and WEAPONFLAG == True: - for weapon in table["data"]: - dataList = weapon["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "武器の名前" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - weaponsList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": weaponsList[0][0]}) - weaponsList[0].pop(0) - - # Description - if "武器の説明" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - weaponsList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = weaponsList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - - # Set Data - dataList[j].update({"value": translatedText}) - weaponsList[1].pop(0) - - # Grab Skills - if table["name"] == "技能" and SKILLFLAG == True: - for skill in table["data"]: - dataList = skill["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "技能の名前" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - skillList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": skillList[0][0]}) - skillList[0].pop(0) - - # Description - if "説明" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - skillList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = skillList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Set Data - dataList[j].update({"value": translatedText}) - skillList[1].pop(0) - - # Log - if "使用時文章[移動](人名~" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Skill Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - skillList[2].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = skillList[2][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - skillList[2].pop(0) - - # Log - if "使用時文章[戦闘](人名~" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Skill Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - skillList[3].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = skillList[3][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - skillList[3].pop(0) - - # Log - if "失敗時文章[(対象)~]" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Skill Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - skillList[4].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = skillList[4][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - skillList[4].pop(0) - - # Grab States - if table["name"] == "状態設定" and STATEFLAG == True: - for state in table["data"]: - dataList = state["data"] - - # Parse - for j in range(len(dataList)): - # Name - if "状態名" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - stateList[0].append(dataList[j].get("value")) - - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - dataList[j].update({"value": stateList[0][0]}) - stateList[0].pop(0) - - # Description - if "表示名" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # Append Data - stateList[1].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[1][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[1].pop(0) - - # Log - if "発生時の文章[(人名)~]" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[2].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[2][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[2].pop(0) - - # Log - if "行動制限時文章(空欄:ナシ" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[3].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[3][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[3].pop(0) - - # Log - if "回復時の文章[(人名)~]" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[4].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[4][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[4].pop(0) - # Log - if "┣ カウンター発動文[対象~" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[5].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[5][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[5].pop(0) - - # Log - if "尻もち 行動不能 持続3ターン" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[6].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[6][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[6].pop(0) - - # Log - if "状態異常の説明" in dataList[j].get("name"): - # Pass 1 (Grab Data) - if setData == False: - if dataList[j].get("value") != "": - # Remove Textwrap - jaString = dataList[j].get("value") - jaString = jaString.replace("\n", " ") - jaString = jaString.replace("\r", "") - jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) - - # State Action - if jaString[0] in [ - "は", - "を", - "の", - "に", - "が", - ]: - jaString = f"Taro{jaString}" - - # Append Data - stateList[7].append(jaString) - # Pass 2 (Set Data) - else: - if dataList[j].get("value") != "": - # Textwrap - translatedText = stateList[7][0] - translatedText = textwrap.fill(translatedText, LISTWIDTH) - translatedText = font + translatedText - - # Remove Taro - translatedText = re.sub(r"\bTaro\b", "", translatedText) - - # Set Data - dataList[j].update({"value": translatedText}) - stateList[7].pop(0) - - # Translation - scenarioListTL = [[], [], []] - npcListTL = [[], [], [], []] - itemListTL = [[], [], [], []] - stateListTL = [[], [], [], [], [], [], [], []] - armorListTL = [[], []] - enemyListTL = [[], []] - weaponsListTL = [[], [], []] - skillListTL = [[], [], [], [], []] - optionsListTL = [[], [], [], []] - dbNameListTL = [[]] - - translate = False - - # NPCs - if len(npcList[0]) > 0: - # Progress Bar - total = 0 - for itemArray in npcList: - total += len(itemArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - npcList[0], - "Reply with only the " + LANGUAGE + " translation of the RPG enemy name", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT(npcList[1], "Reply with only the " + LANGUAGE + " translation", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 2 - response = translateGPT(npcList[2], "Reply with only the " + LANGUAGE + " translation", True) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 3 - response = translateGPT(npcList[3], "Reply with only the " + LANGUAGE + " translation", True) - descListTL3 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if ( - len(nameListTL) != len(npcList[0]) - or len(descListTL1) != len(npcList[1]) - or len(descListTL2) != len(npcList[2]) - or len(descListTL3) != len(npcList[3]) - ): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - npcListTL = [nameListTL, descListTL1, descListTL2, descListTL3] - translate = True - - # SCENARIO - if len(scenarioList[0]) > 0: - # Progress Bar - total = 0 - for scenarioArray in scenarioList: - total += len(scenarioArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - scenarioList[0], - "Reply with only the " + LANGUAGE + " translation", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT( - scenarioList[1], - "reply with only the gender neutral " + LANGUAGE + " translation of the NPC name", - True, - ) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 2 - response = translateGPT( - scenarioList[2], - "reply with only the gender neutral " + LANGUAGE + " translation of the NPC name", - True, - ) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(scenarioList[0]) or len(descListTL1) != len(scenarioList[1]) or len(descListTL2) != len(scenarioList[2]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - scenarioListTL = [nameListTL, descListTL1, descListTL2] - translate = True - - # ITEMS - if len(itemList[0]) > 0 or len(itemList[1]) > 0: - # Progress Bar - total = 0 - for itemArray in itemList: - total += len(itemArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT(itemList[0], "Reply with only the " + LANGUAGE + " translation", True) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT(itemList[1], "Reply with only the " + LANGUAGE + " translation", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 2 - response = translateGPT(itemList[2], "Reply with only the " + LANGUAGE + " translation", True) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 3 - response = translateGPT(itemList[3], "Reply with only the " + LANGUAGE + " translation", True) - descListTL3 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if ( - len(nameListTL) != len(itemList[0]) - or len(descListTL1) != len(itemList[1]) - or len(descListTL2) != len(itemList[2]) - or len(descListTL3) != len(itemList[3]) - ): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - itemListTL = [nameListTL, descListTL1, descListTL2, descListTL3] - translate = True - - # Armor - if len(armorList[0]) > 0: - # Progress Bar - total = 0 - for armorArray in armorList: - total += len(armorArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - armorList[0], - "Reply with only the " + LANGUAGE + " translation of the NPC name", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT(armorList[1], "Reply with only the " + LANGUAGE + " translation", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(armorList[0]) or len(descListTL1) != len(armorList[1]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - armorListTL = [nameListTL, descListTL1] - translate = True - - # Enemies - if len(enemyList[0]) > 0: - # Progress Bar - total = 0 - for enemyArray in enemyList: - total += len(enemyArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - enemyList[0], - "Reply with only the " + LANGUAGE + " translation of the enemy NPC name", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT(enemyList[1], "Reply with only the " + LANGUAGE + " translation", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(enemyList[0]) or len(descListTL1) != len(enemyList[1]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - enemyListTL = [nameListTL, descListTL1] - translate = True - - # Weapons - if len(weaponsList[0]) > 0: - # Progress Bar - total = 0 - for weaponsArray in weaponsList: - total += len(weaponsArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - weaponsList[0], - "Reply with only the " + LANGUAGE + " translation of the RPG weapon name", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 1 - response = translateGPT(weaponsList[1], "", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - # Desc 2 - response = translateGPT(weaponsList[2], "", True) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(weaponsList[0]) or len(descListTL1) != len(weaponsList[1]) or len(descListTL2) != len(weaponsList[2]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - weaponsListTL = [nameListTL, descListTL1, descListTL2] - translate = True - - # Skills - if len(skillList[0]) > 0: - # Progress Bar - total = 0 - for skillArray in skillList: - total += len(skillArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - skillList[0], - "Reply with only the " + LANGUAGE + " translation of the RPG skill name", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Desc - response = translateGPT( - skillList[1], - "Reply with only the " + LANGUAGE + " translation of the RPG skill description", - True, - ) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 1 - response = translateGPT( - skillList[2], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 2 - response = translateGPT( - skillList[3], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL3 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 3 - response = translateGPT( - skillList[4], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL4 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if ( - len(nameListTL) != len(skillList[0]) - or len(descListTL1) != len(skillList[1]) - or len(descListTL2) != len(skillList[2]) - or len(descListTL3) != len(skillList[3]) - or len(descListTL4) != len(skillList[4]) - ): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - skillListTL = [nameListTL, descListTL1, descListTL2, descListTL3, descListTL4] - translate = True - - # State - for list in stateList: - if len(list) > 0: - # Progress Bar - total = 0 - for stateArray in stateList: - total += len(stateArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT( - stateList[0], - f"Reply with the {LANGUAGE} translation of the status effect.", - True, - ) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Desc 1 - response = translateGPT(stateList[1], "", True) - descListTL1 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 1 - response = translateGPT( - stateList[2], - f"Reply with the {LANGUAGE} translation of the status effect.", - True, - ) - descListTL2 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 2 - response = translateGPT( - stateList[3], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL3 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 3 - response = translateGPT( - stateList[4], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL4 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 4 - response = translateGPT( - stateList[5], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL5 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 1 - response = translateGPT( - stateList[6], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL6 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Log 1 - response = translateGPT( - stateList[7], - "reply with only the gender neutral " - + LANGUAGE - + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", - True, - ) - descListTL7 = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if ( - len(nameListTL) != len(stateList[0]) - or len(descListTL1) != len(stateList[1]) - or len(descListTL2) != len(stateList[2]) - or len(descListTL3) != len(stateList[3]) - or len(descListTL4) != len(stateList[4]) - or len(descListTL5) != len(stateList[5]) - or len(descListTL6) != len(stateList[6]) - ): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - stateListTL = [ - nameListTL, - descListTL1, - descListTL2, - descListTL3, - descListTL4, - descListTL5, - descListTL6, - descListTL7, - ] - translate = True - - # OPTIONS - if len(optionsList[0]) > 0 or len(optionsList[1]) > 0: - # Progress Bar - total = 0 - for optionsArray in optionsList: - total += len(optionsArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT(itemList[0], "Reply with only the " + LANGUAGE + " translation", True) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(optionsList[0]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - optionsListTL = [nameListTL] - translate = True - - # DB Names - if len(dbNameList[0]) > 0: - # Progress Bar - total = 0 - for dbNameArray in dbNameList: - total += len(dbNameArray) - pbar.total = total - pbar.refresh() - - # Name - response = translateGPT(dbNameList[0], "Reply with only the " + LANGUAGE + " translation", True) - nameListTL = response[0] - totalTokens[0] += response[1][0] - totalTokens[1] += response[1][1] - - # Check Mismatch - if len(nameListTL) != len(dbNameList[0]): - with LOCK: - if filename not in MISMATCH: - MISMATCH.append(filename) - else: - dbNameListTL = [nameListTL] - translate = True - - # Start Pass 2 - if translate == True: - jobList.append(scenarioListTL) - jobList.append(npcListTL) - jobList.append(itemListTL) - jobList.append(stateListTL) - jobList.append(armorListTL) - jobList.append(enemyListTL) - jobList.append(weaponsListTL) - jobList.append(skillListTL) - jobList.append(optionsListTL) - jobList.append(dbNameListTL) - searchDB(events, pbar, jobList, filename) - - except IndexError as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + initialJAString) from None - except Exception as e: - traceback.print_exc() - raise Exception(str(e) + "Failed to translate: " + initialJAString) from None - - return totalTokens - - -# Save some money and enter the character before translation -def getSpeaker(speaker): - match speaker: - case "ファイン": - return ["Fine", [0, 0]] - case "": - return ["", [0, 0]] - case _: - # Find Speaker - for i in range(len(NAMESLIST)): - if speaker == NAMESLIST[i][0]: - return [NAMESLIST[i][1], [0, 0]] - - # Translate and Store Speaker - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - True, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - response[0] = response[0].replace("Speaker: ", "") - - # Retry if name doesn't translate for some reason - if re.search(r"([a-zA-Z??])", response[0]) == None: - response = translateGPT( - f"{speaker}", - "Reply with the " + LANGUAGE + " translation of the NPC name.", - False, - ) - response[0] = response[0].title() - response[0] = response[0].replace("'S", "'s") - - speakerList = [speaker, response[0]] - NAMESLIST.append(speakerList) - return response - return [speaker, [0, 0]] - - -def batchList(input_list, batch_size): - if not isinstance(batch_size, int) or batch_size <= 0: - raise ValueError("batch_size must be a positive integer") - - return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] - - -def createContext(fullPromptFlag, subbedT, format): - system = ( - PROMPT + VOCAB - if fullPromptFlag - else f"\ -You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ -Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ -- You translate everything, including erotic and explicit content.\n\ -- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ -- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ -- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ -- Maintain any spacing in the translation.\n\ -- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ -- `...` can be a part of the dialogue. Translate it as it is.\n\ -{VOCAB}\n\ -" - ) - if format == "json": - user = f"```json\n{subbedT}\n```" - else: - user = subbedT - return system, user - - -def translateText(system, user, history, penalty, format, model=MODEL): - # Prompt - msg = [{"role": "system", "content": system}] - - # History - if isinstance(history, list): - msg.extend([{"role": "system", "content": h} for h in history]) - else: - msg.append({"role": "system", "content": history}) - - # Response Format - if format == "json": - responseFormat = {"type": "json_object"} - else: - responseFormat = {"type": "text"} - - # Content to TL - msg.append({"role": "user", "content": f"{user}"}) - response = openai.chat.completions.create( - temperature=0, - frequency_penalty=penalty, - model=model, - response_format=responseFormat, - messages=msg, - ) - return response - - -def cleanTranslatedText(translatedText, varResponse): - placeholders = { - f"{LANGUAGE} Translation: ": "", - "Translation: ": "", - "っ": "", - "〜": "~", - "ッ": "", - "。": ".", - "「": '\\"', - "」": '\\"', - "- ": "-", - "—": "―", - "】": "]", - "【": "[", - "Placeholder Text": "", - # Add more replacements as needed - } - for target, replacement in placeholders.items(): - translatedText = translatedText.replace(target, replacement) - - # Remove Repeating Characters - pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") - translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) - - # Elongate Long Dashes (Since GPT Ignores them...) - translatedText = elongateCharacters(translatedText) - return translatedText - - -def elongateCharacters(text): - # Define a pattern to match one character followed by one or more `ー` characters - # Using a positive lookbehind assertion to capture the preceding character - pattern = r"(?<=(.))ー+" - - # Define a replacement function that elongates the captured character - def repl(match): - char = match.group(1) # The character before the ー sequence - count = len(match.group(0)) - 1 # Number of ー characters - return char * count # Replace ー sequence with the character repeated - - # Use re.sub() to replace the pattern in the text - return re.sub(pattern, repl, text) - - -def extractTranslation(translatedTextList, is_list): - try: - translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) - translatedTextList = re.sub(r"(? instead +FIXTEXTWRAP = True # Overwrites textwrap +IGNORETLTEXT = False # Ignores all translated text. +MISMATCH = [] # Lists files that throw a mismatch error (Length of GPT list response is wrong) +FILENAME = None +BRACKETNAMES = False + +# Pricing - Depends on the model https://openai.com/pricing +# Batch Size - GPT 3.5 Struggles past 15 lines per request. GPT4 struggles past 50 lines per request +# If you are getting a MISMATCH LENGTH error, lower the batch size. +if "gpt-3.5" in MODEL: + INPUTAPICOST = 0.002 + OUTPUTAPICOST = 0.002 + BATCHSIZE = 10 + FREQUENCY_PENALTY = 0.2 +elif "gpt-4" in MODEL: + INPUTAPICOST = 0.0025 + OUTPUTAPICOST = 0.01 + BATCHSIZE = 20 + FREQUENCY_PENALTY = 0.1 +else: + INPUTAPICOST = float(os.getenv("input_cost")) + OUTPUTAPICOST = float(os.getenv("output_cost")) + BATCHSIZE = int(os.getenv("batchsize")) + FREQUENCY_PENALTY = float(os.getenv("frequency_penalty")) + +# tqdm Globals +BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}" +POSITION = 0 +LEAVE = False +PBAR = None +FILENAME = None + +# Dialogue / Scroll +CODE101 = False +CODE102 = False + +# Set String (Fragile but necessary) +CODE122 = False +CODE150 = True + +# Other +CODE210 = False +CODE300 = False +CODE250 = False + +# Database +SCENARIOFLAG = False +OPTIONSFLAG = False +NPCFLAG = False +DBNAMEFLAG = False +ITEMFLAG = True +STATEFLAG = False +ENEMYFLAG = False +ARMORFLAG = True +WEAPONFLAG = True +SKILLFLAG = True + + +def handleWOLF(filename, estimate): + global ESTIMATE, TOKENS, FILENAME + ESTIMATE = estimate + FILENAME = filename + + # Translate + start = time.time() + translatedData = openFiles(filename) + + # Translate + if not estimate: + try: + with open("translated/" + filename, "w", encoding="utf-8") as outFile: + json.dump(translatedData[0], outFile, ensure_ascii=False, indent=4) + except Exception: + traceback.print_exc() + return "Fail" + + # Print File + end = time.time() + tqdm.write(getResultString(translatedData, end - start, filename)) + with LOCK: + TOKENS[0] += translatedData[1][0] + TOKENS[1] += translatedData[1][1] + + # Print Total + totalString = getResultString(["", TOKENS, None], end - start, "TOTAL") + + # Print any errors on maps + if len(MISMATCH) > 0: + return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET + else: + return totalString + + +def openFiles(filename): + with open("files/" + filename, "r", encoding="utf-8-sig") as f: + data = json.load(f) + + # Map Files + if "'events':" in str(data): + if len(data["events"]) > 0: + translatedData = parseMap(data, filename) + else: + return [data, [0, 0], None] + + # Map Files + elif "'types':" in str(data): + translatedData = parseDB(data, filename) + + # Other Files + elif "'commands':" in str(data): + translatedData = parseOther(data, filename) + + else: + raise NameError(filename + " Not Supported") + + return translatedData + + +def getResultString(translatedData, translationTime, filename): + # File Print String + totalTokenstring = ( + Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]" + "[Output: " + + str(translatedData[1][1]) + + "]" "[Cost: ${:,.4f}".format((translatedData[1][0] * 0.001 * INPUTAPICOST) + (translatedData[1][1] * 0.001 * OUTPUTAPICOST)) + + "]" + ) + timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]" + + if translatedData[2] is None: + # Success + return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET + else: + # Fail + try: + raise translatedData[2] + except Exception as e: + traceback.print_exc() + errorString = str(e) + Fore.RED + return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET + + +def parseOther(data, filename): + totalTokens = [0, 0] + totalLines = 0 + events = data["commands"] + global LOCK + + # Thread for each page in file + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + pbar.total = totalLines + translationData = searchCodes(events, pbar, [], filename) + try: + totalTokens[0] += translationData[0] + totalTokens[1] += translationData[1] + except Exception as e: + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def parseDB(data, filename): + totalTokens = [0, 0] + totalLines = 0 + events = data["types"] + global LOCK + + # Thread for each page in file + with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar: + pbar.desc = filename + pbar.total = totalLines + translationData = searchDB(events, pbar, [], filename) + try: + totalTokens[0] += translationData[0] + totalTokens[1] += translationData[1] + except Exception as e: + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def parseMap(data, filename): + totalTokens = [0, 0] + totalLines = 0 + events = data["events"] + global LOCK + + # Get total for progress bar + for event in events: + if event is not None: + for page in event["pages"]: + totalLines += len(page["list"]) + + # Thread for each page in file + with tqdm(bar_format=BAR_FORMAT, position=POSITION, total=totalLines, leave=LEAVE) as pbar: + pbar.desc = filename + pbar.total = totalLines + with ThreadPoolExecutor(max_workers=THREADS) as executor: + for event in events: + if event is not None: + futures = [executor.submit(searchCodes, page["list"], pbar, None, filename) for page in event["pages"] if page is not None] + for future in as_completed(futures): + try: + totalTokensFuture = future.result() + totalTokens[0] += totalTokensFuture[0] + totalTokens[1] += totalTokensFuture[1] + except Exception as e: + return [data, totalTokens, e] + return [data, totalTokens, None] + + +def searchCodes(events, pbar, jobList, filename): + # Lists + if jobList: + stringList = jobList[0] + list210 = jobList[1] + list300 = jobList[2] + setData = True + else: + stringList = [] + list210 = [] + list300 = [] + setData = False + + # Other + codeList = events + textHistory = [] + totalTokens = [0, 0] + translatedText = "" + speaker = "" + nametag = "" + initialJAString = "" + global LOCK, NAMESLIST, MISMATCH, PBAR, FILENAME + FILENAME = filename + PBAR = pbar + + # Calculate Total Length + code_flags = {102: CODE102, 122: CODE122, 300: CODE300, 250: CODE250} + totalList = 0 + for code_item in codeList: + if code_flags.get(code_item["code"], False): + totalList += 1 + pbar.total = totalList + pbar.refresh() + + # Begin Parsing File + try: + # Iterate through events + i = 0 + while i < len(codeList): + ### Event Code: 101 Message + if codeList[i]["code"] == 101 and CODE101 == True: + # Grab String + jaString = codeList[i]["stringArgs"][0] + initialJAString = jaString + + # Grab Speaker + if ":\n" in jaString: + nameList = re.findall(r"(.*):\n", jaString) + if nameList is not None: + # TL Speaker + response = getSpeaker(nameList[0]) + speaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set nametag and remove from string + nametag = f"{speaker}:\n" + jaString = jaString.replace(f"{nameList[0]}:\n", "") + + # Remove Textwrap + jaString = jaString.replace("\n", " ") + + # 1st Pass (Save Text to List) + if not setData: + if speaker == "": + stringList.append(jaString) + else: + stringList.append(f"[{speaker}]: {jaString}") + + # 2nd Pass (Set Text) + else: + # Grab Translated String + translatedText = stringList[0] + + # Remove speaker + matchSpeakerList = re.findall(r"^(\[.+?\]\s?[|:]\s?)\s?", translatedText) + if len(matchSpeakerList) > 0: + translatedText = translatedText.replace(matchSpeakerList[0], "") + + # Textwrap + if FIXTEXTWRAP is True: + translatedText = textwrap.fill(translatedText, width=WIDTH) + + # Add back Nametag + translatedText = nametag + translatedText + nametag = "" + + # Set Data + codeList[i]["stringArgs"][0] = translatedText + + # Reset Data and Pop Item + speaker = "" + stringList.pop(0) + + ### Event Code: 102 Choices + if codeList[i]["code"] == 102 and CODE102 == True: + # Grab Choice List + choiceList = [] + jaChoiceList = codeList[i]["stringArgs"] + + # Filter Empty + for j in range(len(jaChoiceList)): + if jaChoiceList[j]: + choiceList.append(jaChoiceList[j]) + + # Translate + response = translateGPT( + choiceList, + f"Reply with the {LANGUAGE} translation of the dialogue choice", + True, + ) + translatedChoiceList = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Validate and Set Data + if len(translatedChoiceList) == len(choiceList): + for j in range(len(jaChoiceList)): + if jaChoiceList[j]: + codeList[i]["stringArgs"][j] = translatedChoiceList[0] + translatedChoiceList.pop(0) + + ### Event Code: 210 Common Event + if codeList[i]["code"] == 210 and CODE210 == True: + # Speaker Event + if "stringArgs" in codeList[i] and codeList[i]["intArgs"][0] == None and len(codeList[i]["stringArgs"]) == 2: + response = getSpeaker(codeList[i]["stringArgs"][1]) + totalTokens[1] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Data + codeList[i]["stringArgs"][1] = response[0] + + # Logs + elif "stringArgs" in codeList[i] and codeList[i]["intArgs"][0] == 500220 and len(codeList[i]["stringArgs"]) == 2: + # Grab String + jaString = codeList[i]["stringArgs"][1] + initialJAString = jaString + + # Remove Textwrap + jaString = jaString.replace("\r", "") + jaString = jaString.replace("\n", " ") + + # 1st Pass (Save Text to List) + if not setData: + list210.append(jaString) + + # 2nd Pass (Set Text) + else: + # Grab Translated String + translatedText = list210[0] + + # Textwrap + if FIXTEXTWRAP is True: + translatedText = textwrap.fill(translatedText, width=WIDTH) + + # Set Data + codeList[i]["stringArgs"][1] = translatedText + + # Pop Item + list210.pop(0) + + ### Event Code: 122 SetString + if codeList[i]["code"] == 122 and CODE122 == True: + if "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 0: + # Grab String + jaString = re.search(r"^\n?(.*)\n?$", codeList[i]["stringArgs"][0]) + if jaString: + jaString = jaString.group(1) + else: + jaString = codeList[i]["stringArgs"][0] + + # Translate Conversations + if ":Nothing" in jaString: + # Separate into list + list122 = jaString.split("\n\n") + + # Remove Textwrap + # for j in range(len(list122)): + # list122[j] = list122[j].replace("\n", " ") + + # Translate + response = translateGPT( + list122, + f"Reply with the {LANGUAGE} translation of the text", + True, + ) + list122TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Validate and Set Data + if len(list122) == len(list122TL): + # Adjust Speaker and Add Textwrap + for j in range(len(list122TL)): + list122TL[j] = textwrap.fill(list122TL[j], WIDTH) + list122TL[j] = re.sub(r"^\[?(.+?)\]?:", r"\1:", list122TL[j]) + list122TL[j] = list122TL[j].replace(":", ":\n") + list122TL[j] = list122TL[j].replace(":\n ", ":\n") + + # Join back into single string + list122TL = "\n\n".join(list122TL) + + # Set String + codeList[i]["stringArgs"][0] = list122TL + + # Translate Other Strings [Specific Files Only] + else: + if ( + not re.search(r"\.[\w]+$", jaString) + and jaString != "" + and "_" not in jaString + and '",' not in jaString + and "/" not in jaString + ): + # Japanese Text Only + if re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", jaString): + # Translate + response = translateGPT( + jaString, + f"Reply with the {LANGUAGE} translation of the text", + True, + ) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set String + codeList[i]["stringArgs"][0] = codeList[i]["stringArgs"][0].replace(jaString, translatedText) + + ### Event Code: 122 SetString + if codeList[i]["code"] == 150 and CODE150 == True: + if "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 0: + # Grab String + jaString = re.search(r"^\n?(.*)\n?$", codeList[i]["stringArgs"][0]) + if jaString: + jaString = jaString.group(1) + else: + jaString = codeList[i]["stringArgs"][0] + + # Remove Textwrap + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + + # Translate Other Strings [Specific Files Only] + if ( + not re.search(r"\.[\w]+$", jaString) + and jaString != "" + and "_" not in jaString + and '",' not in jaString + and "/" not in jaString + ): + # Japanese Text Only + if re.search(r"[一-龠ぁ-ゔァ-ヴーa-zA-Z0-9]+", jaString): + # Translate + response = translateGPT( + jaString, + f"Reply with the {LANGUAGE} translation of the text", + True, + ) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Textwrap + translatedText = textwrap.fill(translatedText, WIDTH) + + # Set String + codeList[i]["stringArgs"][0] = translatedText + + ### Event Code: 300 Common Events + if codeList[i]["code"] == 300 and CODE300 == True and "stringArgs" in codeList[i] and len(codeList[i]["stringArgs"]) > 1: + # Choices + if codeList[i]["stringArgs"][0] == "[共]汎用ウィンドウ生成" or codeList[i]["stringArgs"][0] == "[共]選択生成": + # Grab String + choiceList = codeList[i]["stringArgs"][1].split("\r\n") + + # # Translate Question + # question = codeList[i]['stringArgs'][2] + # response = translateGPT(question, "", True) + # translatedText = response[0] + # totalTokens[0] += response[1][0] + # totalTokens[1] += response[1][1] + + # # Translate Question + # codeList[i]['stringArgs'][2] = translatedText + + # Translate Choices + response = translateGPT(choiceList, translatedText, True) + choiceListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Replace Commas + for j in range(len(choiceListTL)): + choiceListTL[j] = choiceListTL[j].replace(", ", "、") + + # Convert to String and Set + translatedText = "\r\n".join(choiceListTL) + codeList[i]["stringArgs"][1] = translatedText + + # Dialogue + elif codeList[i]["stringArgs"][0] == "○【戦闘】テキスト表示": + jaString = codeList[i]["stringArgs"][1] + + # Pass 1 + if not setData: + # Remove Textwrap + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + + # Append + list300.append(jaString) + + # Pass 2 + else: + # Add Textwrap and Font + translatedText = textwrap.fill(list300[0], WIDTH) + list300.pop(0) + + # Write to File + codeList[i]["stringArgs"][1] = translatedText + + ### Event Code: 250 DB Read/Writes + if codeList[i]["code"] == 250 and CODE250 == True: + foundTerm = False + + # Validate size + if len(codeList[i]["stringArgs"]) == 4: + if codeList[i]["stringArgs"][1] == "┣所持アイテム個数" and codeList[i]["stringArgs"][2] != "": + # Grab String + jaString = codeList[i]["stringArgs"][2] + + # Catch Vars that may break the TL + varString = "" + matchList = re.findall(r"^[\\_]+[\w]+\[[a-zA-Z0-9\\\[\]\_,\s-]+\]", jaString) + if len(matchList) != 0: + varString = matchList[0] + jaString = jaString.replace(matchList[0], "") + + # Check if term already translated + for j in range(len(TERMSLIST)): + if jaString == TERMSLIST[j][0]: + translatedText = TERMSLIST[j][1] + foundTerm = True + + # Translate + if foundTerm == False: + response = translateGPT( + jaString, + f"Reply with the {LANGUAGE} translation of the text.", + True, + ) + translatedText = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + TERMSLIST.append([jaString, translatedText]) + + # Add back Potential Variables in String + translatedText = varString + translatedText + + # Set Data + codeList[i]["stringArgs"][2] = translatedText + + ### Iterate + i += 1 + + # EOF + stringListTL = [] + list210TL = [] + list300TL = [] + setData = False + + # String List + if len(stringList) > 0: + pbar.total = len(stringList) + pbar.refresh() + response = translateGPT(stringList, textHistory, True) + stringListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(stringListTL) != len(stringList): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 210 List + if len(list210) > 0: + pbar.total = len(list210) + pbar.refresh() + response = translateGPT(list210, textHistory, True) + list210TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list210TL) != len(list210): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # 300 List + if len(list300) > 0: + pbar.total = len(list300) + pbar.refresh() + response = translateGPT(list300, textHistory, True) + list300TL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if len(list300TL) != len(list300): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + setData = True + + # Pass 2 + if setData: + stringList = [] + searchCodes(events, pbar, [stringListTL, list210TL, list300TL], filename) + else: + # Set Data + events = codeList + + except IndexError as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + initialJAString) from None + except Exception as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + initialJAString) from None + + return totalTokens + + +def formatDramon(jaString): + imageRegex = r"(\r?\n?_[a-zA-Z_\d/.]+\r?\n?)|(@-?\d?\r\n)|(@-?\d?)([^\r]\d?-?\d?[^\r]+?)(\r\n|$)|(_PDC)|(>\r\n)|(^[#])|(\r\n@$)|(/)|(_SS_)|(\r\n[@/]\s?-?\d?\r\n)" + + jaString = jaString.replace("\u3000", " ") + jaString = jaString.replace("#", "") + jaString = re.sub(r"([^\r])\n", r"\1\r\n", jaString) + + # Grab and Split + jaStringList = re.split(imageRegex, jaString) + + # Clean List + cleanedList = [x for x in jaStringList if x is not None and x != "" and x != "\r\n" and x != "_SS_"] + + # Iterate Through List + j = 0 + translatedText = "" + while j < len(cleanedList): + if ( + ("@" in cleanedList[j] or "/" in cleanedList[j]) + and j < len(cleanedList) - 1 + and re.search(r"([@/]-?\d?\r\n)", cleanedList[j]) is None + and ".ogg" not in cleanedList[j] + ): + # Setup @ + if j > 0 and "@" not in cleanedList[j - 1] and "/" not in cleanedList[j - 1] and "_" not in cleanedList[j - 1]: + cleanedList[j - 1] = cleanedList[j - 1] + cleanedList[j + 1] + else: + cleanedList.insert(j, cleanedList[j + 1]) + j += 1 + cleanedList[j] = f"\r\n{cleanedList[j]}\r\n" + cleanedList.pop(j + 1) + j += 1 + + return cleanedList + + +# Database +def searchDB(events, pbar, jobList, filename): + # Set Lists + if len(jobList) > 0: + scenarioList = jobList[0] + npcList = jobList[1] + itemList = jobList[2] + stateList = jobList[3] + armorList = jobList[4] + enemyList = jobList[5] + weaponsList = jobList[6] + skillList = jobList[7] + optionsList = jobList[8] + dbNameList = jobList[9] + setData = True + else: + scenarioList = [[], [], []] + npcList = [[], [], [], []] + itemList = [[], [], [], []] + armorList = [[], []] + enemyList = [[], []] + weaponsList = [[], [], [], []] + skillList = [[], [], [], [], []] + stateList = [[], [], [], [], [], [], [], []] + optionsList = [[], [], [], []] + dbNameList = [[]] + setData = False + + # Vars/Globals + totalTokens = [0, 0] + initialJAString = "" + tableList = events + font = "" + global LOCK + global NAMESLIST + global MISMATCH + + # Begin Parsing File + try: + for table in tableList: + # Grab Armors + if table["name"] == "主人公ステータス" and NPCFLAG == True: + for npc in table["data"]: + dataList = npc["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "キャラ名" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + npcList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": npcList[0][0]}) + npcList[0].pop(0) + + # Description + if "肩書き" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + npcList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = npcList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Set Data + dataList[j].update({"value": translatedText}) + npcList[1].pop(0) + + # Grab Scenarios + if table["name"] == "MGP_参加者" and SCENARIOFLAG == True: + for hScenario in table["data"]: + dataList = hScenario["data"] + + # Parse + # Pass 1 (Grab Data) + if setData == False: + if dataList[1].get("value") != "": + scenarioList[0].append(dataList[1].get("value")) + if dataList[44].get("value") != "": + scenarioList[1].append(dataList[44].get("value")) + if dataList[45].get("value") != "": + scenarioList[2].append(dataList[45].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[1].get("value") != "": + dataList[1].update({"value": scenarioList[0][0]}) + scenarioList[0].pop(0) + if dataList[44].get("value") != "": + dataList[44].update({"value": scenarioList[1][0]}) + scenarioList[1].pop(0) + if dataList[45].get("value") != "": + dataList[45].update({"value": scenarioList[2][0]}) + scenarioList[2].pop(0) + + # Grab Options + if table["name"] == "選択肢説明" and OPTIONSFLAG == True: + for option in table["data"]: + dataList = option["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "表示名" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + optionsList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": choiceList[0][0]}) + optionsList[0].pop(0) + + # Description + if "選択肢1" in dataList[j].get("name"): + if setData == False: + if dataList[j].get("value") != "": + # Grab Choices + optionsList[1] = dataList[j].get("value").split("\r\n") + + # Translate + response = translateGPT( + optionsList[1], + "Reply with the English translation of the dialogue choices", + True, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + translatedText = "\r\n".split(response[0]) + + # Set Data + dataList[j].update({"value": translatedText}) + # Description + if "選択肢1" in dataList[j].get("name"): + if setData == False: + if dataList[j].get("value") != "": + # Grab Choices + optionsList[1] = dataList[j].get("value").split("\r\n") + + # Translate + response = translateGPT( + optionsList[1], + "Reply with the English translation of the dialogue choices", + True, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + translatedText = "\r\n".split(response[0]) + + # Set Data + dataList[j].update({"value": translatedText}) + # Description + if "選択肢1" in dataList[j].get("name"): + if setData == False: + if dataList[j].get("value") != "": + # Grab Choices + optionsList[1] = dataList[j].get("value").split("\r\n") + + # Translate + response = translateGPT( + optionsList[1], + "Reply with the English translation of the dialogue choices", + True, + ) + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + translatedText = "\r\n".split(response[0]) + + # Set Data + dataList[j].update({"value": translatedText}) + + # Grab DB Names + if table["name"] == "マップ設定" and DBNAMEFLAG == True: + font = None + dbName = table["data"] + for j in range(len(dbName)): + # Pass 1 (Grab Data) + if setData == False: + if dbName[j].get("name") != "": + dbNameList[0].append(dbName[j].get("name")) + + # Pass 2 (Set Data) + else: + if dbName[j].get("name") != "": + dbName[j].update({"name": dbNameList[0][0]}) + dbNameList[0].pop(0) + + # Grab Items + if table["name"] == "Item" and ITEMFLAG == True: + # Write Category + if setData: + with open("translations.txt", "a", encoding="utf-8") as file: + file.write(f"\n#Items\n") + + # Begin Translation + for item in table["data"]: + dataList = item["data"] + + # Parse + for j in range(len(dataList)): + font = None + # Name + if "アイテム名" in dataList[j].get("name"): + jaString = dataList[j].get("value") + if jaString != "": + # Pass 1 (Grab Data) + if setData == False: + if jaString != "": + itemList[0].append(jaString) + + # Pass 2 (Set Data) + else: + # Write to TL File + with open("translations.txt", "a", encoding="utf-8") as file: + file.write(f"{jaString} ({itemList[0][0]})\n") + + dataList[j].update({"value": itemList[0][0]}) + itemList[0].pop(0) + + # Description + if "説明文[2行まで可]" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap & Font + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + itemList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = itemList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + + # Font + if font: + translatedText = f"{font}{translatedText}" + + # Set Data + dataList[j].update({"value": translatedText}) + itemList[1].pop(0) + # Description + if "-------------------------" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + itemList[2].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = itemList[2][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + + # Font + if font: + translatedText = f"{font}{translatedText}" + + # Set Data + dataList[j].update({"value": translatedText}) + itemList[2].pop(0) + # Description + if "使用時文章[戦](人名~" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + itemList[3].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = itemList[3][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + + # Font + if font: + translatedText = f"{font}{translatedText}" + + # Set Data + dataList[j].update({"value": translatedText}) + itemList[3].pop(0) + + # Grab Armors + if table["name"] == "防具" and ARMORFLAG == True: + for armor in table["data"]: + dataList = armor["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "防具の名前" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + armorList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": armorList[0][0]}) + armorList[0].pop(0) + + # Description + if "防具の説明" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + armorList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = armorList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + + # Set Data + dataList[j].update({"value": translatedText}) + armorList[1].pop(0) + + # Grab Enemies + if table["name"] == "敵キャラ個体データ" and ENEMYFLAG == True: + for enemy in table["data"]: + dataList = enemy["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "敵キャラ名" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + enemyList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": enemyList[0][0]}) + enemyList[0].pop(0) + + # Description + if "NULL" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + enemyList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = enemyList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Set Data + dataList[j].update({"value": translatedText}) + enemyList[1].pop(0) + + # Grab Weapons + if table["name"] == "武器" and WEAPONFLAG == True: + for weapon in table["data"]: + dataList = weapon["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "武器の名前" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + weaponsList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": weaponsList[0][0]}) + weaponsList[0].pop(0) + + # Description + if "武器の説明" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + weaponsList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = weaponsList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + + # Set Data + dataList[j].update({"value": translatedText}) + weaponsList[1].pop(0) + + # Grab Skills + if table["name"] == "技能" and SKILLFLAG == True: + for skill in table["data"]: + dataList = skill["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "技能の名前" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + skillList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": skillList[0][0]}) + skillList[0].pop(0) + + # Description + if "説明" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + skillList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = skillList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Set Data + dataList[j].update({"value": translatedText}) + skillList[1].pop(0) + + # Log + if "使用時文章[移動](人名~" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Skill Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + skillList[2].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = skillList[2][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + skillList[2].pop(0) + + # Log + if "使用時文章[戦闘](人名~" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Skill Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + skillList[3].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = skillList[3][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + skillList[3].pop(0) + + # Log + if "失敗時文章[(対象)~]" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Skill Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + skillList[4].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = skillList[4][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + skillList[4].pop(0) + + # Grab States + if table["name"] == "状態設定" and STATEFLAG == True: + for state in table["data"]: + dataList = state["data"] + + # Parse + for j in range(len(dataList)): + # Name + if "状態名" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + stateList[0].append(dataList[j].get("value")) + + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + dataList[j].update({"value": stateList[0][0]}) + stateList[0].pop(0) + + # Description + if "表示名" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # Append Data + stateList[1].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[1][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[1].pop(0) + + # Log + if "発生時の文章[(人名)~]" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[2].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[2][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[2].pop(0) + + # Log + if "行動制限時文章(空欄:ナシ" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[3].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[3][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[3].pop(0) + + # Log + if "回復時の文章[(人名)~]" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[4].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[4][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[4].pop(0) + # Log + if "┣ カウンター発動文[対象~" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[5].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[5][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[5].pop(0) + + # Log + if "尻もち 行動不能 持続3ターン" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[6].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[6][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[6].pop(0) + + # Log + if "状態異常の説明" in dataList[j].get("name"): + # Pass 1 (Grab Data) + if setData == False: + if dataList[j].get("value") != "": + # Remove Textwrap + jaString = dataList[j].get("value") + jaString = jaString.replace("\n", " ") + jaString = jaString.replace("\r", "") + jaString = re.sub(r"[\\]+f\[\d+\]", "", jaString) + + # State Action + if jaString[0] in [ + "は", + "を", + "の", + "に", + "が", + ]: + jaString = f"Taro{jaString}" + + # Append Data + stateList[7].append(jaString) + # Pass 2 (Set Data) + else: + if dataList[j].get("value") != "": + # Textwrap + translatedText = stateList[7][0] + translatedText = textwrap.fill(translatedText, LISTWIDTH) + translatedText = font + translatedText + + # Remove Taro + translatedText = re.sub(r"\bTaro\b", "", translatedText) + + # Set Data + dataList[j].update({"value": translatedText}) + stateList[7].pop(0) + + # Translation + scenarioListTL = [[], [], []] + npcListTL = [[], [], [], []] + itemListTL = [[], [], [], []] + stateListTL = [[], [], [], [], [], [], [], []] + armorListTL = [[], []] + enemyListTL = [[], []] + weaponsListTL = [[], [], []] + skillListTL = [[], [], [], [], []] + optionsListTL = [[], [], [], []] + dbNameListTL = [[]] + + translate = False + + # NPCs + if len(npcList[0]) > 0: + # Progress Bar + total = 0 + for itemArray in npcList: + total += len(itemArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + npcList[0], + "Reply with only the " + LANGUAGE + " translation of the RPG enemy name", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT(npcList[1], "Reply with only the " + LANGUAGE + " translation", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 2 + response = translateGPT(npcList[2], "Reply with only the " + LANGUAGE + " translation", True) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 3 + response = translateGPT(npcList[3], "Reply with only the " + LANGUAGE + " translation", True) + descListTL3 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if ( + len(nameListTL) != len(npcList[0]) + or len(descListTL1) != len(npcList[1]) + or len(descListTL2) != len(npcList[2]) + or len(descListTL3) != len(npcList[3]) + ): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + npcListTL = [nameListTL, descListTL1, descListTL2, descListTL3] + translate = True + + # SCENARIO + if len(scenarioList[0]) > 0: + # Progress Bar + total = 0 + for scenarioArray in scenarioList: + total += len(scenarioArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + scenarioList[0], + "Reply with only the " + LANGUAGE + " translation", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT( + scenarioList[1], + "reply with only the gender neutral " + LANGUAGE + " translation of the NPC name", + True, + ) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 2 + response = translateGPT( + scenarioList[2], + "reply with only the gender neutral " + LANGUAGE + " translation of the NPC name", + True, + ) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(scenarioList[0]) or len(descListTL1) != len(scenarioList[1]) or len(descListTL2) != len(scenarioList[2]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + scenarioListTL = [nameListTL, descListTL1, descListTL2] + translate = True + + # ITEMS + if len(itemList[0]) > 0 or len(itemList[1]) > 0: + # Progress Bar + total = 0 + for itemArray in itemList: + total += len(itemArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT(itemList[0], "Reply with only the " + LANGUAGE + " translation", True) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT(itemList[1], "Reply with only the " + LANGUAGE + " translation", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 2 + response = translateGPT(itemList[2], "Reply with only the " + LANGUAGE + " translation", True) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 3 + response = translateGPT(itemList[3], "Reply with only the " + LANGUAGE + " translation", True) + descListTL3 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if ( + len(nameListTL) != len(itemList[0]) + or len(descListTL1) != len(itemList[1]) + or len(descListTL2) != len(itemList[2]) + or len(descListTL3) != len(itemList[3]) + ): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + itemListTL = [nameListTL, descListTL1, descListTL2, descListTL3] + translate = True + + # Armor + if len(armorList[0]) > 0: + # Progress Bar + total = 0 + for armorArray in armorList: + total += len(armorArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + armorList[0], + "Reply with only the " + LANGUAGE + " translation of the NPC name", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT(armorList[1], "Reply with only the " + LANGUAGE + " translation", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(armorList[0]) or len(descListTL1) != len(armorList[1]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + armorListTL = [nameListTL, descListTL1] + translate = True + + # Enemies + if len(enemyList[0]) > 0: + # Progress Bar + total = 0 + for enemyArray in enemyList: + total += len(enemyArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + enemyList[0], + "Reply with only the " + LANGUAGE + " translation of the enemy NPC name", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT(enemyList[1], "Reply with only the " + LANGUAGE + " translation", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(enemyList[0]) or len(descListTL1) != len(enemyList[1]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + enemyListTL = [nameListTL, descListTL1] + translate = True + + # Weapons + if len(weaponsList[0]) > 0: + # Progress Bar + total = 0 + for weaponsArray in weaponsList: + total += len(weaponsArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + weaponsList[0], + "Reply with only the " + LANGUAGE + " translation of the RPG weapon name", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 1 + response = translateGPT(weaponsList[1], "", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + # Desc 2 + response = translateGPT(weaponsList[2], "", True) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(weaponsList[0]) or len(descListTL1) != len(weaponsList[1]) or len(descListTL2) != len(weaponsList[2]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + weaponsListTL = [nameListTL, descListTL1, descListTL2] + translate = True + + # Skills + if len(skillList[0]) > 0: + # Progress Bar + total = 0 + for skillArray in skillList: + total += len(skillArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + skillList[0], + "Reply with only the " + LANGUAGE + " translation of the RPG skill name", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Desc + response = translateGPT( + skillList[1], + "Reply with only the " + LANGUAGE + " translation of the RPG skill description", + True, + ) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 1 + response = translateGPT( + skillList[2], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 2 + response = translateGPT( + skillList[3], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL3 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 3 + response = translateGPT( + skillList[4], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL4 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if ( + len(nameListTL) != len(skillList[0]) + or len(descListTL1) != len(skillList[1]) + or len(descListTL2) != len(skillList[2]) + or len(descListTL3) != len(skillList[3]) + or len(descListTL4) != len(skillList[4]) + ): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + skillListTL = [nameListTL, descListTL1, descListTL2, descListTL3, descListTL4] + translate = True + + # State + for list in stateList: + if len(list) > 0: + # Progress Bar + total = 0 + for stateArray in stateList: + total += len(stateArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT( + stateList[0], + f"Reply with the {LANGUAGE} translation of the status effect.", + True, + ) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Desc 1 + response = translateGPT(stateList[1], "", True) + descListTL1 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 1 + response = translateGPT( + stateList[2], + f"Reply with the {LANGUAGE} translation of the status effect.", + True, + ) + descListTL2 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 2 + response = translateGPT( + stateList[3], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL3 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 3 + response = translateGPT( + stateList[4], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL4 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 4 + response = translateGPT( + stateList[5], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL5 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 1 + response = translateGPT( + stateList[6], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL6 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Log 1 + response = translateGPT( + stateList[7], + "reply with only the gender neutral " + + LANGUAGE + + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した!' as 'Taro was defeated!'", + True, + ) + descListTL7 = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if ( + len(nameListTL) != len(stateList[0]) + or len(descListTL1) != len(stateList[1]) + or len(descListTL2) != len(stateList[2]) + or len(descListTL3) != len(stateList[3]) + or len(descListTL4) != len(stateList[4]) + or len(descListTL5) != len(stateList[5]) + or len(descListTL6) != len(stateList[6]) + ): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + stateListTL = [ + nameListTL, + descListTL1, + descListTL2, + descListTL3, + descListTL4, + descListTL5, + descListTL6, + descListTL7, + ] + translate = True + + # OPTIONS + if len(optionsList[0]) > 0 or len(optionsList[1]) > 0: + # Progress Bar + total = 0 + for optionsArray in optionsList: + total += len(optionsArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT(itemList[0], "Reply with only the " + LANGUAGE + " translation", True) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(optionsList[0]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + optionsListTL = [nameListTL] + translate = True + + # DB Names + if len(dbNameList[0]) > 0: + # Progress Bar + total = 0 + for dbNameArray in dbNameList: + total += len(dbNameArray) + pbar.total = total + pbar.refresh() + + # Name + response = translateGPT(dbNameList[0], "Reply with only the " + LANGUAGE + " translation", True) + nameListTL = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Check Mismatch + if len(nameListTL) != len(dbNameList[0]): + with LOCK: + if filename not in MISMATCH: + MISMATCH.append(filename) + else: + dbNameListTL = [nameListTL] + translate = True + + # Start Pass 2 + if translate == True: + jobList.append(scenarioListTL) + jobList.append(npcListTL) + jobList.append(itemListTL) + jobList.append(stateListTL) + jobList.append(armorListTL) + jobList.append(enemyListTL) + jobList.append(weaponsListTL) + jobList.append(skillListTL) + jobList.append(optionsListTL) + jobList.append(dbNameListTL) + searchDB(events, pbar, jobList, filename) + + except IndexError as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + initialJAString) from None + except Exception as e: + traceback.print_exc() + raise Exception(str(e) + "Failed to translate: " + initialJAString) from None + + return totalTokens + + +# Save some money and enter the character before translation +def getSpeaker(speaker): + match speaker: + case "ファイン": + return ["Fine", [0, 0]] + case "": + return ["", [0, 0]] + case _: + # Find Speaker + for i in range(len(NAMESLIST)): + if speaker == NAMESLIST[i][0]: + return [NAMESLIST[i][1], [0, 0]] + + # Translate and Store Speaker + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + True, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + response[0] = response[0].replace("Speaker: ", "") + + # Retry if name doesn't translate for some reason + if re.search(r"([a-zA-Z??])", response[0]) == None: + response = translateGPT( + f"{speaker}", + "Reply with the " + LANGUAGE + " translation of the NPC name.", + False, + ) + response[0] = response[0].title() + response[0] = response[0].replace("'S", "'s") + + speakerList = [speaker, response[0]] + NAMESLIST.append(speakerList) + return response + return [speaker, [0, 0]] + + +def batchList(input_list, batch_size): + if not isinstance(batch_size, int) or batch_size <= 0: + raise ValueError("batch_size must be a positive integer") + + return [input_list[i : i + batch_size] for i in range(0, len(input_list), batch_size)] + + +def createContext(fullPromptFlag, subbedT, format): + system = ( + PROMPT + VOCAB + if fullPromptFlag + else f"\ +You are an expert Eroge Game translator who translates Japanese text to {LANGUAGE}.\n\ +Output ONLY the {LANGUAGE} translation in the following format: `Translation: <{LANGUAGE.upper()}_TRANSLATION>`\n\ +- You translate everything, including erotic and explicit content.\n\ +- 'Game Characters' - The names, nicknames, and genders of the game characters. Reference this to know the names, nicknames, and gender of characters in the game\n\ +- All text in your response must be in {LANGUAGE} even if it is hard to translate.\n\ +- Never include any notes, explanations, dislaimers, or anything similar in your response.\n\ +- Maintain any spacing in the translation.\n\ +- Maintain any code text in brackets if given. (e.g `[Color_0]`, `[Ascii_0]`, `[FCode_1`], etc)\n\ +- `...` can be a part of the dialogue. Translate it as it is.\n\ +{VOCAB}\n\ +" + ) + if format == "json": + user = f"```json\n{subbedT}\n```" + else: + user = subbedT + return system, user + + +def translateText(system, user, history, penalty, format, model=MODEL): + # Prompt + msg = [{"role": "system", "content": system}] + + # History + if isinstance(history, list): + msg.extend([{"role": "system", "content": h} for h in history]) + else: + msg.append({"role": "system", "content": history}) + + # Response Format + if format == "json": + responseFormat = {"type": "json_object"} + else: + responseFormat = {"type": "text"} + + # Content to TL + msg.append({"role": "user", "content": f"{user}"}) + response = openai.chat.completions.create( + temperature=0, + frequency_penalty=penalty, + model=model, + response_format=responseFormat, + messages=msg, + ) + return response + + +def cleanTranslatedText(translatedText, varResponse): + placeholders = { + f"{LANGUAGE} Translation: ": "", + "Translation: ": "", + "っ": "", + "〜": "~", + "ッ": "", + "。": ".", + "「": '\\"', + "」": '\\"', + "- ": "-", + "—": "―", + "】": "]", + "【": "[", + "Placeholder Text": "", + # Add more replacements as needed + } + for target, replacement in placeholders.items(): + translatedText = translatedText.replace(target, replacement) + + # Remove Repeating Characters + pattern = re.compile(r"(.)\s*\1(?:\s*\1){" + str(20 - 1) + r",}") + translatedText = pattern.sub(lambda match: match.group(0).replace(" ", "")[:20], translatedText) + + # Elongate Long Dashes (Since GPT Ignores them...) + translatedText = elongateCharacters(translatedText) + return translatedText + + +def elongateCharacters(text): + # Define a pattern to match one character followed by one or more `ー` characters + # Using a positive lookbehind assertion to capture the preceding character + pattern = r"(?<=(.))ー+" + + # Define a replacement function that elongates the captured character + def repl(match): + char = match.group(1) # The character before the ー sequence + count = len(match.group(0)) - 1 # Number of ー characters + return char * count # Replace ー sequence with the character repeated + + # Use re.sub() to replace the pattern in the text + return re.sub(pattern, repl, text) + + +def extractTranslation(translatedTextList, is_list): + try: + translatedTextList = re.sub(r'\\"+\"([^,\n}])', r'\\"\1', translatedTextList) + translatedTextList = re.sub(r"(?