This commit is contained in:
dazedanon 2026-03-15 22:58:15 -05:00
parent 03694b2fdb
commit 6248752366
3 changed files with 97 additions and 31 deletions

View file

@ -79,7 +79,7 @@ LEAVE = False
# FIRSTLINESPEAKERS: Guess speaker from first line.
FIRSTLINESPEAKERS = False
# INLINE401SPEAKERS: Extract speaker from "Name「dialogue」" inline format on 401 lines.
INLINE401SPEAKERS = False
INLINE401SPEAKERS = True
# FACENAME101: Map face name -> speaker.
FACENAME101 = False
# Face name -> speaker mapping for FACENAME101.
@ -1875,12 +1875,24 @@ def searchCodes(page, pbar, jobList, filename):
jaString,
)
# [Speaker] standalone line format (written back by inline re-export)
if len(speakerList) == 0:
inlineFmtMatch = re.match(r"^\[([^\[\]\n]+)\]\s*$", jaString)
if inlineFmtMatch:
speakerList = [inlineFmtMatch.group(1).strip()]
# Inline Japanese quote speakers (e.g. "トルテ「dialogue」" or "エルミナ「first line...")
if len(speakerList) == 0 and INLINE401SPEAKERS:
inlineQuoteDetect = re.match(r"^([^\s「」。、\\\n]{1,20})「", jaString)
if inlineQuoteDetect:
speakerList = [inlineQuoteDetect.group(1).strip()]
# Inline curly-quote speakers (e.g. "Elmina: \u201cdialogue" or "Elmina\u201cdialogue")
if len(speakerList) == 0 and INLINE401SPEAKERS:
inlineCurlyDetect = re.match(r'^([^\s「」。、\\\n\u201c\u201d":\[\]]{1,20})(?:[:]\s*)?[\u201c"]', jaString)
if inlineCurlyDetect:
speakerList = [inlineCurlyDetect.group(1).strip()]
# First Line Speakers
if len(speakerList) == 0 and FIRSTLINESPEAKERS is True:
# Test Speaker
@ -1920,6 +1932,8 @@ def searchCodes(page, pbar, jobList, filename):
sameLineMatch = re.match(r"^\s*【([^】]+)】(.+)", jaString, re.DOTALL)
# Check if speaker+dialogue are on same line via Japanese quote (Name「dialogue)
inlineQuoteMatch = re.match(r"^([^\s「」。、\\\n]{1,20})「(.*)", jaString, re.DOTALL) if INLINE401SPEAKERS else None
# Check if speaker+dialogue are on same line via curly quote (Name: "dialogue or Name"dialogue)
inlineCurlyMatch = re.match(r'^([^\s「」。、\\\n\u201c\u201d":\[\]]{1,20})(?:[:]\s*)?([\u201c"])(.*)', jaString, re.DOTALL) if INLINE401SPEAKERS else None
if sameLineMatch and len(speakerList) == 1:
# Translate speaker
@ -1945,6 +1959,19 @@ def searchCodes(page, pbar, jobList, filename):
if not setData:
nametag = f"[{speaker}]\n" + nametag
# Don't skip to next line - continue with current line
elif inlineCurlyMatch and len(speakerList) == 1:
# Curly/straight-quote format: strip "Name: \u201c" or "Name"" prefix, keep dialogue
response = getSpeaker(speakerList[0])
speaker = response[0]
totalTokens[0] += response[1][0]
totalTokens[1] += response[1][1]
# Remove speaker name, preserve whichever opening quote was matched
openQuote = inlineCurlyMatch.group(2)
jaString = openQuote + inlineCurlyMatch.group(3)
# Format as [Speaker]\n for output
if not setData:
nametag = f"[{speaker}]\n" + nametag
# Don't skip to next line - continue with current line
elif codeList[i + 1]["code"] in [401, 405, -1]:
# Original behavior: speaker on its own line, dialogue on next line
# Single
@ -2205,9 +2232,19 @@ def searchCodes(page, pbar, jobList, filename):
# Handle 401
else:
codeList[j]["parameters"] = [translatedText]
codeList[j]["code"] = code
syncIndex = i + 1
lines = translatedText.split('\n')
if len(lines) > 1:
codeList[j]["parameters"] = [lines[0]]
codeList[j]["code"] = code
for idx, line in enumerate(lines[1:]):
new_item = copy.deepcopy(codeList[j])
new_item["parameters"] = [line]
codeList.insert(j + idx + 1, new_item)
syncIndex = j + len(lines)
else:
codeList[j]["parameters"] = [translatedText]
codeList[j]["code"] = code
syncIndex = i + 1
# Reset
speaker = ""
@ -3311,9 +3348,11 @@ def searchCodes(page, pbar, jobList, filename):
translatedText = list356[0]
# Remove characters that may break scripts
charList = [".", '"']
for char in charList:
translatedText = translatedText.replace(char, "")
# addLog keeps dots and quotes (they're fine in log text)
if "addLog" not in jaString:
charList = [".", '"']
for char in charList:
translatedText = translatedText.replace(char, "")
# Cant have spaces?
translatedText = translatedText.replace(" ", "_")

View file

@ -30,6 +30,7 @@ You will be translating erotic and sexual content. You will receive lines of dia
- **Speech register:** If the entry describes how a character speaks (flustered, blunt, formal, childlike, crude, etc.), mirror that register in their English dialogue. A character described as speaking in a "cute, flustered register" should sound different from one described as "cold and terse".
- **Role & context:** Use the role/personality notes to inform tone — a villain's lines should feel threatening, a comic-relief NPC's lines should feel goofy, etc.
- Japanese omits pronouns constantly. Infer the correct subject and pronoun from context, translation history, and the character list.
- **Preserve third-person self-reference.** Some characters refer to themselves by name instead of using "I" (e.g. ワタシ used as a name, or a character saying their own name). When a character is clearly speaking about themselves in the third person as a stylistic trait, maintain that in English (e.g. "Feris doesn't know" rather than "I don't know").
- Third-person pronouns (彼, 彼女, あいつ, こいつ, そいつ, コイツ) should match the known gender of the person being referenced.
- Translate **コイツ** as "this bastard" (male) or "this bitch" (female) depending on the referenced character's gender.

View file

@ -1375,24 +1375,6 @@ def translateAI(text, history, fullPromptFlag, config, filename=None, pbar=None,
for j in range(len(tItem)):
if tItem[j] and "\ufffd" in str(tItem[j]):
corrupted_map[j] = tItem[j]
if corrupted_map:
clean_indices = [j for j in range(len(tItem)) if j not in corrupted_map]
if not clean_indices:
# All items are corrupted - skip translation entirely
tList[index] = list(tItem)
if pbar is not None:
pbar.update(len(tItem))
history = tItem[-config.maxHistory:]
continue
# Rebuild protected_items and all_replacements for clean items only
protected_items = [protected_items[j] for j in clean_indices]
new_replacements = {}
for new_idx, old_idx in enumerate(clean_indices):
new_replacements[new_idx] = all_replacements.get(old_idx, {})
all_replacements = new_replacements
elif tItem and "\ufffd" in str(tItem):
# Single corrupted string - skip translation entirely
tList[index] = tItem
@ -1400,10 +1382,52 @@ def translateAI(text, history, fullPromptFlag, config, filename=None, pbar=None,
pbar.update(1)
history = tItem
continue
# Build filtered tItem for validation (excludes corrupted items)
if isinstance(tItem, list) and corrupted_map:
clean_tItem = [tItem[j] for j in range(len(tItem)) if j not in corrupted_map]
# Filter out items that have content but no Japanese — they need no translation
# and the AI tends to empty them (e.g. "「………」" -> ""). Apply the same
# cleanup that would happen post-translation and restore them afterwards.
no_japanese_map = {} # original_index -> already-cleaned text
if isinstance(tItem, list):
for j in range(len(tItem)):
if j in corrupted_map:
continue
item_str = str(tItem[j]).strip() if tItem[j] else ""
if item_str and item_str != "Placeholder Text" and not re.search(config.langRegex, item_str):
cleaned = cleanTranslatedText(tItem[j], config.language)
cleaned = cleaned.replace("", '"').replace("", '"').strip()
no_japanese_map[j] = cleaned
# Combine skip sets and rebuild protected_items / all_replacements
skip_indices = set(corrupted_map.keys()) | set(no_japanese_map.keys())
if isinstance(tItem, list) and skip_indices:
clean_indices = [j for j in range(len(tItem)) if j not in skip_indices]
if not clean_indices:
# Every item is either corrupted or untranslatable — reassemble and move on
result = []
for j in range(len(tItem)):
if j in corrupted_map:
result.append(corrupted_map[j])
elif j in no_japanese_map:
result.append(no_japanese_map[j])
else:
result.append(tItem[j])
tList[index] = result
if pbar is not None:
pbar.update(len(tItem))
history = result[-config.maxHistory:]
continue
# Rebuild protected_items and all_replacements for translatable items only
protected_items = [protected_items[j] for j in clean_indices]
new_replacements = {}
for new_idx, old_idx in enumerate(clean_indices):
new_replacements[new_idx] = all_replacements.get(old_idx, {})
all_replacements = new_replacements
# Build filtered tItem for validation (excludes skipped items)
if isinstance(tItem, list) and skip_indices:
clean_tItem = [tItem[j] for j in range(len(tItem)) if j not in skip_indices]
else:
clean_tItem = tItem
@ -1663,13 +1687,15 @@ def translateAI(text, history, fullPromptFlag, config, filename=None, pbar=None,
if j in all_replacements:
final_translations[j] = restore_script_codes(final_translations[j], all_replacements[j])
# Re-insert corrupted originals at their original positions
if corrupted_map:
# Re-insert corrupted / no-japanese originals at their original positions
if corrupted_map or no_japanese_map:
expanded = []
clean_idx = 0
for j in range(len(tItem)):
if j in corrupted_map:
expanded.append(corrupted_map[j])
elif j in no_japanese_map:
expanded.append(no_japanese_map[j])
else:
expanded.append(final_translations[clean_idx])
clean_idx += 1