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"(?