Skip to content

Commit 7aec971

Browse files
改造翻译流程
1 parent 8da2cbd commit 7aec971

12 files changed

Lines changed: 6560 additions & 414 deletions

core/engines/wolf.py

Lines changed: 3074 additions & 232 deletions
Large diffs are not rendered by default.

core/tasks/json_creation.py

Lines changed: 114 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import re
44
import json
55
import logging
6+
from core.engines import wolf as wolf_engine
67
from core.utils import file_system, text_processing # 需要文件名清理
8+
from core.utils.engine_detection import detect_game_engine
79

810
log = logging.getLogger(__name__)
911

@@ -28,6 +30,29 @@ def _is_multiline_block_marker(marker):
2830
return marker in MULTILINE_BLOCK_MARKERS or marker.startswith("PluginCommand_")
2931

3032

33+
def _store_extracted_string(
34+
strings, original, translated, marker, speaker_id,
35+
wolf_code=None, wolf_export_schema=None,
36+
):
37+
metadata = strings.get(original)
38+
if metadata is None:
39+
metadata = {
40+
"text_to_translate": translated,
41+
"original_marker": marker,
42+
"speaker_id": speaker_id,
43+
}
44+
strings[original] = metadata
45+
if wolf_code:
46+
codes = metadata.setdefault("wolf_codes", [])
47+
if wolf_code not in codes:
48+
codes.append(wolf_code)
49+
if wolf_export_schema != wolf_engine.WOLF_EXPORT_SCHEMA:
50+
raise ValueError("WOLF 导出版本无效,请重新导出翻译文本")
51+
previous_schema = metadata.setdefault("wolf_export_schema", wolf_export_schema)
52+
if previous_schema != wolf_export_schema:
53+
raise ValueError("同一 WOLF 文本混入了不同版本的导出数据")
54+
55+
3156
def _parse_face_graphic_command_details(command_details_str):
3257
"""
3358
解析 "Select Face Graphic:" 指令的冒号之后的内容。
@@ -73,7 +98,7 @@ def _parse_face_graphic_command_details(command_details_str):
7398
return None
7499

75100

76-
def _extract_strings_from_file(file_path):
101+
def _extract_strings_from_file(file_path, require_wolf_metadata=False):
77102
"""
78103
从单个 StringScripts txt 文件中提取需要翻译的字符串及其元数据。
79104
(此函数逻辑基本保持你提供的已修复版本)
@@ -96,6 +121,10 @@ def _extract_strings_from_file(file_path):
96121
current_speaker_id = DEFAULT_SPEAKER_ID
97122
current_line_number_for_log = 0
98123
current_page_for_log = "Page_Unknown_Init"
124+
pending_wolf_code = None
125+
pending_wolf_export_schema = None
126+
pending_wolf_marker = None
127+
saw_wolf_metadata = False
99128

100129
log.debug(f"开始解析文件: '{os.path.basename(file_path)}', 初始 Speaker ID: '{current_speaker_id}'")
101130

@@ -109,6 +138,24 @@ def _extract_strings_from_file(file_path):
109138
line_content = lines[i]
110139
line_content_stripped = line_content.strip()
111140

141+
if line_content.startswith("@WOLF "):
142+
encoded = line_content[6:].strip()
143+
try:
144+
metadata = wolf_engine._decode_metadata(encoded)
145+
if metadata.get("wolf_export_schema") != wolf_engine.WOLF_EXPORT_SCHEMA:
146+
raise ValueError("WOLF StringScripts 版本已过期,请重新导出翻译文本")
147+
expected_code = wolf_engine._translation_code(metadata)
148+
if metadata.get("wolf_code") != expected_code:
149+
raise ValueError("WOLF StringScripts 结构 Code 无效,请重新导出翻译文本")
150+
pending_wolf_code = expected_code
151+
pending_wolf_export_schema = metadata["wolf_export_schema"]
152+
pending_wolf_marker = metadata.get("marker", "Message")
153+
saw_wolf_metadata = True
154+
except (ValueError, TypeError, json.JSONDecodeError) as error:
155+
raise ValueError(f"无效的 WOLF 条目元数据: {error}") from error
156+
i += 1
157+
continue
158+
112159
# 1. 检查是否是Page分隔符
113160
page_match = RE_PAGE_SEPARATOR.match(line_content_stripped)
114161
if page_match:
@@ -146,6 +193,20 @@ def _extract_strings_from_file(file_path):
146193
marker_match = RE_MARKER_LINE.match(line_content_stripped)
147194
if marker_match:
148195
original_marker = marker_match.group(1)
196+
if require_wolf_metadata and pending_wolf_code is None:
197+
raise ValueError(
198+
"WOLF StringScripts 缺少当前版本元数据,请重新导出翻译文本"
199+
)
200+
if pending_wolf_code and original_marker != pending_wolf_marker:
201+
raise ValueError(
202+
f"WOLF 文本标记与元数据不一致: "
203+
f"{original_marker!r} != {pending_wolf_marker!r}"
204+
)
205+
wolf_code = pending_wolf_code
206+
wolf_export_schema = pending_wolf_export_schema
207+
pending_wolf_code = None
208+
pending_wolf_export_schema = None
209+
pending_wolf_marker = None
149210
# 对话、图文、滚动文本与插件指令参数按完整块处理;其他标记按单行处理。
150211
speaker_id_for_this_entry = current_speaker_id if original_marker == 'Message' else SYSTEM_TEXT_SPEAKER_ID # Choice 文本也可能需要发言人ID,但RPG Maker 2000/2003的Choice通常不直接关联脸图,所以默认为SYSTEM
151212
log.debug(f" [L{current_line_number_for_log}, {current_page_for_log}] 处理标记 '#{original_marker}#'. 使用 Speaker ID: '{speaker_id_for_this_entry}' (基于 current_speaker_id='{current_speaker_id}').")
@@ -162,11 +223,15 @@ def _extract_strings_from_file(file_path):
162223

163224
if message_key_as_original:
164225
text_to_translate_val = text_processing.convert_half_to_full_katakana(message_key_as_original)
165-
strings_with_metadata[message_key_as_original] = {
166-
"text_to_translate": text_to_translate_val,
167-
"original_marker": original_marker,
168-
"speaker_id": speaker_id_for_this_entry if original_marker == 'Message' else SYSTEM_TEXT_SPEAKER_ID
169-
}
226+
_store_extracted_string(
227+
strings_with_metadata,
228+
message_key_as_original,
229+
text_to_translate_val,
230+
original_marker,
231+
speaker_id_for_this_entry if original_marker == 'Message' else SYSTEM_TEXT_SPEAKER_ID,
232+
wolf_code,
233+
wolf_export_schema,
234+
)
170235
log.debug(f" 提取到 '{original_marker}' 块. 原文Key: '{message_key_as_original[:30].replace(chr(10),'/LF/') + ('...' if len(message_key_as_original)>30 else '')}'. Speaker: '{speaker_id_for_this_entry}'")
171236
if message_key_as_original != text_to_translate_val:
172237
log.debug(f" 半角假名已转换.")
@@ -192,11 +257,15 @@ def _extract_strings_from_file(file_path):
192257
choice_line_key = choice_line.strip()
193258
if choice_line_key:
194259
text_to_translate_val = text_processing.convert_half_to_full_katakana(choice_line_key)
195-
strings_with_metadata[choice_line_key] = {
196-
"text_to_translate": text_to_translate_val,
197-
"original_marker": original_marker,
198-
"speaker_id": speaker_id_for_this_entry
199-
}
260+
_store_extracted_string(
261+
strings_with_metadata,
262+
choice_line_key,
263+
text_to_translate_val,
264+
original_marker,
265+
speaker_id_for_this_entry,
266+
wolf_code,
267+
wolf_export_schema,
268+
)
200269
log.debug(f" 提取到 Choice 标记 '{original_marker}'. 原文Key: '{choice_line_key[:30].replace(chr(10),'/LF/') + ('...' if len(choice_line_key)>30 else '')}'. Speaker: '{speaker_id_for_this_entry}' (内容来自 L{i+1})")
201270
if choice_line_key != text_to_translate_val:
202271
log.debug(f" 半角假名已转换.")
@@ -209,11 +278,15 @@ def _extract_strings_from_file(file_path):
209278
if single_line_key:
210279
text_to_translate_val = text_processing.convert_half_to_full_katakana(single_line_key)
211280
# 对于非Message类,speaker_id 固定为 SYSTEM_TEXT_SPEAKER_ID
212-
strings_with_metadata[single_line_key] = {
213-
"text_to_translate": text_to_translate_val,
214-
"original_marker": original_marker,
215-
"speaker_id": SYSTEM_TEXT_SPEAKER_ID
216-
}
281+
_store_extracted_string(
282+
strings_with_metadata,
283+
single_line_key,
284+
text_to_translate_val,
285+
original_marker,
286+
SYSTEM_TEXT_SPEAKER_ID,
287+
wolf_code,
288+
wolf_export_schema,
289+
)
217290
log.debug(f" 提取到单行标记 '{original_marker}'. 原文Key: '{single_line_key[:30].replace(chr(10),'/LF/') + ('...' if len(single_line_key)>30 else '')}'. Speaker: '{SYSTEM_TEXT_SPEAKER_ID}' (内容来自 L{i+1})")
218291
if single_line_key != text_to_translate_val:
219292
log.debug(f" 半角假名已转换.")
@@ -225,15 +298,19 @@ def _extract_strings_from_file(file_path):
225298
else:
226299
i += 1
227300

301+
if pending_wolf_code is not None:
302+
raise ValueError("WOLF 条目元数据后缺少文本标记")
303+
if require_wolf_metadata and not saw_wolf_metadata:
304+
raise ValueError("WOLF StringScripts 不含当前版本元数据,请重新导出翻译文本")
228305
log.debug(f"完成文件解析: '{os.path.basename(file_path)}'. 共提取 {len(strings_with_metadata)} 条数据.")
229306
return strings_with_metadata
230307

231308
except FileNotFoundError:
232309
log.error(f"读取文件失败: {file_path} 未找到。")
233-
return {}
310+
raise
234311
except Exception as e:
235312
log.exception(f"处理文件 '{os.path.basename(file_path)}' (行 ~{current_line_number_for_log}) 时发生严重错误: {e}")
236-
return {}
313+
raise
237314

238315

239316
# --- 主任务函数 ---
@@ -251,6 +328,8 @@ def run_create_json(game_path, works_dir, message_queue):
251328
message_queue.put(("status", "创建 JSON 失败"))
252329
message_queue.put(("done", None))
253330
return
331+
detected_game = detect_game_engine(game_path)
332+
is_wolf_game = bool(detected_game and detected_game.engine == "wolf")
254333

255334
game_folder_name = text_processing.sanitize_filename(os.path.basename(game_path))
256335
if not game_folder_name: game_folder_name = "UntitledGame"
@@ -261,6 +340,9 @@ def run_create_json(game_path, works_dir, message_queue):
261340
raise OSError(f"无法创建或访问游戏特定工作目录: {work_game_dir}")
262341
if not file_system.ensure_dir_exists(untranslated_dir):
263342
raise OSError(f"无法创建或访问 untranslated 目录: {untranslated_dir}")
343+
json_path = os.path.join(untranslated_dir, "translation.json")
344+
if os.path.exists(json_path) and not file_system.safe_remove(json_path):
345+
raise OSError(f"无法废弃旧版未翻译 JSON: {json_path}")
264346

265347
message_queue.put(("log", ("normal", f"将在以下目录创建 JSON: {untranslated_dir}")))
266348

@@ -282,7 +364,13 @@ def run_create_json(game_path, works_dir, message_queue):
282364
message_queue.put(("log", ("debug", f"正在解析文件: {file_key_in_json}")))
283365

284366
# 调用 _extract_strings_from_file 获取该文件的所有文本和元数据
285-
data_from_single_file = _extract_strings_from_file(file_path)
367+
data_from_single_file = _extract_strings_from_file(
368+
file_path,
369+
require_wolf_metadata=(
370+
is_wolf_game
371+
or bool(relative_parts and relative_parts[0].casefold() == "wolf")
372+
),
373+
)
286374

287375
if data_from_single_file: # 只有当文件中有数据时才添加
288376
file_organized_data[file_key_in_json] = data_from_single_file
@@ -297,17 +385,21 @@ def run_create_json(game_path, works_dir, message_queue):
297385
if not file_organized_data: # 如果没有任何文件包含可翻译内容
298386
message_queue.put(("log", ("warning", "未从任何文件中提取到文本条目。生成的 JSON 文件将为空对象。")))
299387

300-
json_filename = "translation.json" # 输出文件名保持不变
301-
json_path = os.path.join(untranslated_dir, json_filename)
302388
message_queue.put(("log", ("normal", f"正在将按文件组织的文本及元数据写入 JSON 文件: {json_path}")))
303389

390+
temporary_json_path = f"{json_path}.tmp"
304391
try:
305-
with open(json_path, 'w', encoding='utf-8') as json_file:
392+
with open(temporary_json_path, 'w', encoding='utf-8') as json_file:
306393
json.dump(file_organized_data, json_file, ensure_ascii=False, indent=4) # 写入新的组织结构
394+
json_file.flush()
395+
os.fsync(json_file.fileno())
396+
os.replace(temporary_json_path, json_path)
307397
message_queue.put(("success", f"按文件组织的未翻译 JSON 文件创建成功: {json_path}"))
308398
message_queue.put(("status", "创建 JSON 文件完成"))
309399
message_queue.put(("done", None))
310400
except Exception as write_err:
401+
if os.path.exists(temporary_json_path):
402+
file_system.safe_remove(temporary_json_path)
311403
log.exception(f"写入 JSON 文件失败: {json_path} - {write_err}")
312404
message_queue.put(("error", f"写入 JSON 文件失败: {write_err}"))
313405
message_queue.put(("status", "创建 JSON 失败"))

0 commit comments

Comments
 (0)