코드import os import re import tkinter as tk from tkinter import filedialog, messagebox, scrolledtext, ttk import threading import urllib.parse import requests import time
def google_translate(text, src='ja', dest='ko'): if not text.strip(): return text if not re.search(r'[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]', text): return text
try: url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl={src}&tl={dest}&dt=t&q={urllib.parse.quote(text)}" headers = {'User-Agent': 'Mozilla/5.0'} res = requests.get(url, headers=headers, timeout=5) res.raise_for_status() parts = res.json()[0] result = "".join([p[0] for p in parts if p and p[0]]).strip() # 마침표 보정 로직 if not text.strip().endswith('.') and result.endswith('.'): result = result[:-1].strip() return result if result else text except Exception: return text
class AdvancedTranslator: def __init__(self): self.root = tk.Tk() self.root.title("하위 폴더 통합 번역기 v8.0 (큰따옴표 지원)") self.root.geometry("800x650")
config_frame = tk.Frame(self.root) config_frame.pack(fill="x", padx=10, pady=5)
# 구분자를 세미콜론(;)으로 사용하고 기본값에 큰따옴표(") 추가 tk.Label(config_frame, text="번역 제외 문자 (';'로 구분):").grid(row=0, column=0, sticky="w") self.exclude_entry = tk.Entry(config_frame, width=50) self.exclude_entry.insert(0, '「; 」; <; >; "; ---; \\n; \\i; ,; [; ]; (; ); :; …; !; \\; :; (; ); ,') self.exclude_entry.grid(row=0, column=1, padx=5, sticky="w")
self.btn = tk.Button(self.root, text="폴더 선택 (하위 폴더 포함 번역 시작)", command=self.start_thread, height=2, bg="#4CAF50", fg="white", font=("맑은 고딕", 10, "bold")) self.btn.pack(fill="x", padx=10, pady=5)
self.notebook = ttk.Notebook(self.root) self.notebook.pack(fill="both", expand=True, padx=10, pady=10)
self.log_area = scrolledtext.ScrolledText(self.notebook, bg="#1e1e1e", fg="#ffffff", font=("맑은 고딕", 12)) self.monitor_area = scrolledtext.ScrolledText(self.notebook, bg="#2d2d2d", fg="#a6e22e", font=("맑은 고딕", 12)) self.notebook.add(self.log_area, text=" 전체 진행 로그 ") self.notebook.add(self.monitor_area, text=" 실시간 번역 비교 ")
self.jp_pattern = re.compile(r'[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]')
def start_thread(self): path = filedialog.askdirectory() if not path: return self.btn.config(state='disabled') threading.Thread(target=self.process, args=(path,), daemon=True).start()
def split_and_translate(self, text, exclude_chars): if not exclude_chars: return google_translate(text)
# 특수 문자 및 큰따옴표를 안전하게 정규식 패턴으로 변환 pattern = '|'.join(re.escape(char) for char in exclude_chars) tokens = re.split(f'({pattern})', text) translated_tokens = [] for token in tokens: if not token: continue # 빈 문자열 건너뜀 if token in exclude_chars: translated_tokens.append(token) elif self.jp_pattern.search(token): # 실제 번역 수행 (일본어 -> 한국어) translated_tokens.append(google_translate(token, src='ja', dest='ko')) time.sleep(0.15) else: translated_tokens.append(token) return "".join(translated_tokens)
def process(self, root_path): raw_excludes = self.exclude_entry.get().split(";") exclude_chars = [x.strip() for x in raw_excludes if x.strip()] all_files = [] for root, dirs, files in os.walk(root_path): for file in files: if file.lower().endswith(('.txt', '.json', '.ini', '.csv')): all_files.append(os.path.join(root, file))
parent_dir = os.path.dirname(root_path) folder_name = os.path.basename(root_path) out_root_dir = os.path.join(parent_dir, f"{folder_name}_Translated") self.log_insert(f"🚀 총 {len(all_files)}개의 파일을 찾았습니다.\n") self.log_insert(f"🏠 저장 위치: {out_root_dir}\n{'-'*50}\n")
for i, file_path in enumerate(all_files): rel_path = os.path.relpath(file_path, root_path) save_path = os.path.join(out_root_dir, rel_path) os.makedirs(os.path.dirname(save_path), exist_ok=True) self.log_insert(f"📄 [{i+1}/{len(all_files)}] {rel_path} 번역 중...\n") content_lines = [] for enc in ['shift_jis', 'utf-8-sig', 'cp932', 'utf-8', 'euc-kr']: try: with open(file_path, 'r', encoding=enc) as f: content_lines = f.readlines() break except: continue
translated_lines = [] for line in content_lines: if self.jp_pattern.search(line): origin = line.rstrip('\n\r') translated = self.split_and_translate(origin, exclude_chars) self.monitor_insert(f"파일: {rel_path}\n원문: {origin}\n번역: {translated}\n{'-'*20}\n") translated_lines.append(translated + '\n') else: translated_lines.append(line)
with open(save_path, 'w', encoding='utf-8-sig') as f: f.writelines(translated_lines)
self.btn.config(state='normal') messagebox.showinfo("완료", "큰따옴표를 포함한 모든 파일 번역이 완료되었습니다.")
def log_insert(self, msg): self.log_area.insert(tk.END, msg) self.log_area.see(tk.END)
def monitor_insert(self, msg): self.monitor_area.insert(tk.END, msg) self.monitor_area.see(tk.END)
if __name__ == "__main__": app = AdvancedTranslator() app.root.mainloop() |