diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py index 5cf41c4..e58443e 100644 --- a/modules/rpgmakermvmz.py +++ b/modules/rpgmakermvmz.py @@ -49,7 +49,7 @@ _ACTOR_MAP_CACHE_LOCK = threading.Lock() _VAR_ACTOR_RE = re.compile(r"\\n\[(\d+)\]", re.IGNORECASE) # Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex -LANGREGEX = r"[\u3000\u3002-\u3009\u300C-\u303F\u3040-\u309A\u309C-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF61-\uFF9F]+" +LANGREGEX = r"[\u3000\u3002-\u3009\u300C-\u303F\u3040-\u309A\u309C-\u30FA\u30FC-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF61-\uFF9F]+" # Get pricing configuration based on the model PRICING_CONFIG = getPricingConfig(MODEL) @@ -688,7 +688,37 @@ def parseMap(data, filename): return [data, totalTokens, None] -def translateNote(event, regex, wordwrap=False): +def _normalize_sg_desc(text: str) -> str: + """Normalize SG description text before AI translation. + + Japanese body text is hard-wrapped at screen width using bare \\n. + This collapses those intra-paragraph newlines into spaces so the AI + receives clean prose paragraphs, while preserving: + - \\n\\n paragraph / section breaks + - ◆ / ・ / • / ● header lines (kept on their own line) + """ + HEADER_CHARS = ("◆", "・", "•", "●") + blocks = text.split("\n\n") + normalized_blocks = [] + for block in blocks: + lines = block.split("\n") + result_lines: list[str] = [] + body_buf: list[str] = [] + for line in lines: + stripped = line.strip() + if stripped.startswith(HEADER_CHARS): + if body_buf: + result_lines.append(" ".join(body_buf)) + body_buf = [] + result_lines.append(stripped) + elif stripped: + body_buf.append(stripped) + if body_buf: + result_lines.append(" ".join(body_buf)) + normalized_blocks.append("\n".join(result_lines)) + return "\n\n".join(normalized_blocks) + + # Regex String jaString = event.get("note") or "" if not isinstance(jaString, str): @@ -1340,7 +1370,8 @@ def searchNames(data, pbar, context, filename): # Skip if IGNORETLTEXT is enabled and no Japanese text if IGNORETLTEXT and not re.search(LANGREGEX, match_text): continue - notesBatch.append(match_text) + # Normalize for AI (collapse intra-paragraph \n, keep headers) + notesBatch.append(_normalize_sg_desc(match_text)) notesBatchMap.append((idx, regex, match_text, wordwrap)) else: for m in matches: @@ -1367,7 +1398,10 @@ def searchNames(data, pbar, context, filename): break translated = translatedNotesBatch[note_insert_idx] if wordwrap: - translated = dazedwrap.wrapText(translated, width=NOTEWIDTH) + if regex.startswith(r" str: lines.append(" ".join(current_line)) wrapped_segments.append("\n".join(lines)) # Rejoin with double newlines to preserve hard breaks - return "\n\n".join(wrapped_segments) \ No newline at end of file + return "\n\n".join(wrapped_segments) + + +def wrapSGDesc(text: str, width: int) -> str: + """Structured word-wrap for SG plugin description blocks. + + Unlike wrapText, this function understands the layout of SG info-box + content: + ◆ / ・ / • / ● lines are always kept on their own line as section + headers; the body text immediately following is word-wrapped to + *width*. Paragraph breaks (\\n\\n) are preserved between sections. + """ + HEADER_CHARS = ("◆", "・", "•", "●") + + paragraphs = text.split("\n\n") + wrapped_paragraphs = [] + for para in paragraphs: + lines = para.split("\n") + out_parts = [] + body_buf: list[str] = [] + + def flush_body(): + if not body_buf: + return + words = " ".join(body_buf).split() + cur_line: list[str] = [] + cur_len = 0 + for word in words: + wl = _get_visible_length(word) + if cur_len + wl + len(cur_line) <= width: + cur_line.append(word) + cur_len += wl + else: + if cur_line: + out_parts.append(" ".join(cur_line)) + cur_line = [word] + cur_len = wl + if cur_line: + out_parts.append(" ".join(cur_line)) + body_buf.clear() + + for line in lines: + stripped = line.strip() + if not stripped: + continue + if stripped.startswith(HEADER_CHARS): + flush_body() + # Word-wrap the header line itself — the bullet stays on the + # first wrapped line; continuation lines are plain body lines. + words = stripped.split() + cur_line: list[str] = [] + cur_len = 0 + for word in words: + wl = _get_visible_length(word) + if cur_len + wl + len(cur_line) <= width: + cur_line.append(word) + cur_len += wl + else: + if cur_line: + out_parts.append(" ".join(cur_line)) + cur_line = [word] + cur_len = wl + if cur_line: + out_parts.append(" ".join(cur_line)) + else: + body_buf.append(stripped) + flush_body() + wrapped_paragraphs.append("\n".join(out_parts)) + + return "\n\n".join(wrapped_paragraphs) \ No newline at end of file