Some kirikiri updates
This commit is contained in:
parent
88b13fece1
commit
95bf4c0b84
1 changed files with 147 additions and 25 deletions
|
|
@ -105,25 +105,34 @@ def handleKirikiri(filename, estimate):
|
|||
return totalString
|
||||
|
||||
else:
|
||||
# We no longer keep the destination file open during translation because
|
||||
# incremental progress saves (save_progress_lines) need to atomically
|
||||
# replace the file on Windows. Holding an open handle prevents os.replace
|
||||
# from succeeding (WinError 5 Access is denied).
|
||||
try:
|
||||
with open("translated/" + filename, "w", encoding="cp932", errors="ignore") as outFile:
|
||||
start = time.time()
|
||||
translatedData = openFiles(filename)
|
||||
start = time.time()
|
||||
translatedData = openFiles(filename)
|
||||
end = time.time()
|
||||
|
||||
# Print Result
|
||||
end = time.time()
|
||||
if translatedData[0] != []:
|
||||
outFile.writelines(translatedData[0])
|
||||
else:
|
||||
PBAR.write(f"{FILENAME} Failed to write")
|
||||
os.remove(f"translated/{filename}")
|
||||
tqdm.write(getResultString(translatedData, end - start, filename))
|
||||
with LOCK:
|
||||
TOKENS[0] += translatedData[1][0]
|
||||
TOKENS[1] += translatedData[1][1]
|
||||
# Final write safeguard: if for some reason the progress file was
|
||||
# never written (e.g. no translatable lines triggered saves), write it now.
|
||||
try:
|
||||
if translatedData[0]:
|
||||
os.makedirs("translated", exist_ok=True)
|
||||
final_path = os.path.join("translated", filename)
|
||||
# Write directly (small risk window acceptable on final flush)
|
||||
with open(final_path, "w", encoding="cp932", errors="ignore", newline="\n") as f:
|
||||
f.writelines(translatedData[0])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
tqdm.write(getResultString(translatedData, end - start, filename))
|
||||
with LOCK:
|
||||
TOKENS[0] += translatedData[1][0]
|
||||
TOKENS[1] += translatedData[1][1]
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
os.remove(f"translated/{filename}")
|
||||
# Don't blindly remove the file; it may contain partial progress.
|
||||
return "Fail"
|
||||
|
||||
return getResultString(["", TOKENS, None], end - start, "TOTAL")
|
||||
|
|
@ -183,24 +192,57 @@ def parseKiriKiri(readFile, filename):
|
|||
|
||||
|
||||
def save_progress_lines(lines, filename, encoding="cp932"):
|
||||
"""Atomically save current line-based translation progress."""
|
||||
try:
|
||||
if ESTIMATE:
|
||||
return
|
||||
os.makedirs("translated", exist_ok=True)
|
||||
tmp_fd, tmp_path = tempfile.mkstemp(prefix=f"{filename}.", suffix=".tmp", dir="translated")
|
||||
"""Atomically (with retries) save current line-based translation progress.
|
||||
|
||||
Rationale:
|
||||
Windows raises PermissionError if the destination file is open elsewhere.
|
||||
We avoid holding an open handle outside this function and add a small
|
||||
exponential backoff retry loop to handle transient locks (e.g. AV scanners).
|
||||
"""
|
||||
if ESTIMATE:
|
||||
return
|
||||
|
||||
max_attempts = 5
|
||||
backoff = 0.05 # seconds
|
||||
tmp_fd = None
|
||||
tmp_path = None
|
||||
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
os.makedirs("translated", exist_ok=True)
|
||||
|
||||
# Create temp file every attempt (prior one cleaned in finally).
|
||||
tmp_fd, tmp_path = tempfile.mkstemp(prefix=f"{filename}.", suffix=".tmp", dir="translated")
|
||||
with os.fdopen(tmp_fd, "w", encoding=encoding, newline="\n", errors="ignore") as tmp_file:
|
||||
tmp_file.writelines(lines)
|
||||
os.replace(tmp_path, os.path.join("translated", filename))
|
||||
tmp_file.flush()
|
||||
os.fsync(tmp_file.fileno())
|
||||
|
||||
dest_path = os.path.join("translated", filename)
|
||||
try:
|
||||
os.replace(tmp_path, dest_path)
|
||||
except PermissionError as e:
|
||||
# Retry on Windows-specific sharing violation
|
||||
if attempt < max_attempts:
|
||||
time.sleep(backoff)
|
||||
backoff *= 2
|
||||
continue
|
||||
else:
|
||||
raise e
|
||||
# Success, break loop
|
||||
break
|
||||
except Exception:
|
||||
if attempt == max_attempts:
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
# Ensure temp file removed if it still exists
|
||||
if tmp_path and os.path.exists(tmp_path):
|
||||
try:
|
||||
os.remove(tmp_path)
|
||||
except OSError:
|
||||
pass
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
tmp_fd = None
|
||||
tmp_path = None
|
||||
|
||||
|
||||
def translateKiriKiri(data, pbar, filename, jobList):
|
||||
|
|
@ -223,6 +265,7 @@ def translateKiriKiri(data, pbar, filename, jobList):
|
|||
dialogueRegex = r"^\[text\](.*).*\[KeyWait\]|\[\w+\](.*)\[\/\w+\].*\[KeyWait\]"
|
||||
furiganaRegex = r'(\[eruby\sstr="(.*?)"\stext.*?\])'
|
||||
choicesRegex = r"^\s*\[button\d\sclickse=sys_decide.*text='(.*?)'.*"
|
||||
taggedDialogueRegex = r"^\[(?P<tag>[^\s\]/]+)(?:\s[^\]]*)?\](?P<text>.*?)\[/\1\]"
|
||||
|
||||
while i < len(data):
|
||||
speaker = ""
|
||||
|
|
@ -259,6 +302,85 @@ def translateKiriKiri(data, pbar, filename, jobList):
|
|||
data[i] = data[i].replace(jaString, translatedText)
|
||||
save_progress_lines(data, filename)
|
||||
|
||||
# Tagged dialogue lines e.g., [思考 storage="..."]text[/思考]
|
||||
tagged = re.match(taggedDialogueRegex, data[i])
|
||||
if tagged and DIALOGUE:
|
||||
tag_name = tagged.group('tag')
|
||||
jaString = tagged.group('text')
|
||||
|
||||
# Pass 1: enqueue with speaker from closing tag
|
||||
if not setData:
|
||||
# Remove inline wrapping
|
||||
jaString_clean = jaString.replace("[r]", " ")
|
||||
# Remove furigana
|
||||
matchList = re.findall(furiganaRegex, jaString_clean)
|
||||
if matchList:
|
||||
for fm in matchList:
|
||||
jaString_clean = jaString_clean.replace(fm[0], fm[1])
|
||||
|
||||
# Resolve speaker via getSpeaker
|
||||
resolved = getSpeaker(tag_name)
|
||||
tag_speaker = resolved[0]
|
||||
tokens[0] += resolved[1][0]
|
||||
tokens[1] += resolved[1][1]
|
||||
|
||||
if tag_speaker:
|
||||
stringList.append(f"[{tag_speaker}]: {jaString_clean.strip()}")
|
||||
else:
|
||||
stringList.append(jaString_clean.strip())
|
||||
|
||||
# Pass 2: apply translated text back between tags
|
||||
else:
|
||||
if len(stringList) > 0:
|
||||
translatedText = stringList[0]
|
||||
stringList.pop(0)
|
||||
# Remove Speaker label if present
|
||||
translatedText = re.sub(r"\[.*?\]:\s", "", translatedText)
|
||||
# Wrap and convert newlines to [r]
|
||||
translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)
|
||||
translatedText = translatedText.replace("\n", "[r]")
|
||||
# Replace quotes as per convention
|
||||
data[i] = data[i].replace("'", '"')
|
||||
translatedText = translatedText.replace('"', "'")
|
||||
# Replace only inner content
|
||||
data[i] = data[i].replace(jaString, translatedText)
|
||||
save_progress_lines(data, filename)
|
||||
|
||||
# Simple narrative line handling: translate each whitespace-led, non-tag, non-command line independently.
|
||||
# This avoids reflowing or merging blocks, preventing misplaced text.
|
||||
if not re.match(r"^\[", data[i]) and not data[i].lstrip().startswith("@"):
|
||||
if re.match(r"^[ \t\u3000]+", data[i]):
|
||||
# Skip standalone markers like [▼]
|
||||
if data[i].strip() == "[▼]":
|
||||
pass
|
||||
else:
|
||||
# Pass 1
|
||||
if not setData:
|
||||
line_content = data[i].rstrip("\n")
|
||||
# Remove inline wrapping markers and glyph markers
|
||||
line_content = line_content.replace("[r]", " ")
|
||||
line_content = re.sub(r"\[▼\]", "", line_content)
|
||||
# Remove furigana blocks
|
||||
matchList = re.findall(furiganaRegex, line_content)
|
||||
if matchList:
|
||||
for fm in matchList:
|
||||
line_content = line_content.replace(fm[0], fm[1])
|
||||
cleaned = line_content.strip()
|
||||
if cleaned:
|
||||
stringList.append(cleaned)
|
||||
# Pass 2
|
||||
else:
|
||||
if len(stringList) > 0:
|
||||
translatedText = stringList[0]
|
||||
stringList.pop(0)
|
||||
translatedText = re.sub(r"\[.*?\]:\s", "", translatedText)
|
||||
translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)
|
||||
translatedText = translatedText.replace("\n", "[r]")
|
||||
indent_match = re.match(r"^([ \t\u3000]+)", data[i])
|
||||
indent = indent_match.group(1) if indent_match else ""
|
||||
data[i] = f"{indent}{translatedText}\n"
|
||||
save_progress_lines(data, filename)
|
||||
|
||||
# Dialogue
|
||||
match = re.search(dialogueRegex, data[i])
|
||||
if match and DIALOGUE:
|
||||
|
|
|
|||
Loading…
Reference in a new issue