
API 비용이 생각보다 나간다
RPG Maker 게임 번역하다 보니 API 비용이 보이기 시작했다.
작은 게임 하나: $0.50
중간 크기 게임: $2.00
큰 게임: $5.00+
Prompt Caching으로 90% 절감했는데도 쌓인다.
문제는 중복 번역.
Translation Memory 개선
기존 Translation Memory는 단순했다.
# 기존 (단순 해시)
key = hashlib.md5(f"{original_text}:{language_pair}".encode()).hexdigest()
문제:
- 공백 차이 (
"こんにちは"vs"こんにちは ") → 다른 키 - 줄바꿈 차이 → 다른 키
- 같은 내용인데 두 번 번역
정규화 추가
# core/translation_memory.py
def _normalize_text(self, text):
"""텍스트 정규화"""
# 앞뒤 공백 제거
text = text.strip()
# 연속 공백을 하나로
text = re.sub(r'\s+', ' ', text)
# 줄바꿈 통일
text = text.replace('\r\n', '\n')
return text
def get(self, original_text, language_pair):
normalized = self._normalize_text(original_text)
key = hashlib.md5(f"{normalized}:{language_pair}".encode()).hexdigest()
self.cursor.execute("SELECT translated FROM cache WHERE key=?", (key,))
result = self.cursor.fetchone()
return result[0] if result else None
결과
Before: 캐시 히트율 ~70%
After: 캐시 히트율 ~85%
공백/줄바꿈 차이로 인한 중복 번역 감소.
체감상 10-20% 정도 비용 절감.
공백 하나 때문에 돈 나가는 거 막았다.
배치 번역 (API 호출 줄이기)
기존: 대사 하나씩 API 호출
for line in lines:
translated = api.translate(line) # 500번 호출
문제:
- API 호출 500번
- 네트워크 오버헤드
- 느림
배치 처리
# core/translator.py
def translate_batch(self, texts, batch_size=10):
"""배치 번역 (최대 10개씩)"""
results = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i+batch_size]
# 배치를 하나의 프롬프트로
prompt = "다음 문장들을 번역하세요:\n\n"
for idx, text in enumerate(batch, 1):
prompt += f"{idx}. {text}\n"
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
system=self._get_system_prompt(),
messages=[{"role": "user", "content": prompt}]
)
# 결과 파싱
translated_batch = self._parse_batch_response(response.content[0].text)
results.extend(translated_batch)
return results
성능 비교
Before (개별 호출):
- 500개 대사
- 500번 API 호출
- 소요 시간: ~10분
- 비용: $1.00
After (배치 10개):
- 500개 대사
- 50번 API 호출
- 소요 시간: ~8분
- 비용: $0.90
네트워크 오버헤드는 줄었지만 체감할 만큼은 아니다.
배치 크기를 너무 키우면 파싱 오류 발생. 10개 정도가 안정적.
Mac에서도 쓰고 싶다
윈도우 데스크탑에서 개발했다. 외출할 때는 맥북 들고 다닌다.
맥에서도 쓰고 싶었다.
문제 1: AssetsTools.NET
# Windows만 됨
import clr
from System import Activator
pythonnet은 Windows에서 .NET Framework 사용.
Mac은 .NET Core/Mono. 안 된다.
해결: 플랫폼 감지
# core/unity_extractor.py
import platform
class UnityExtractor:
def __init__(self):
self.system = platform.system()
if self.system == 'Windows':
self._init_assetstools_net()
else:
# Mac/Linux는 UnityPy만 사용
self.use_assetstools = False
def extract(self, bundle_path):
if self.use_assetstools:
return self._extract_with_assetstools(bundle_path)
else:
return self._extract_with_unitypy(bundle_path)
UnityPy는 순수 Python. 크로스플랫폼 지원.
AssetsTools.NET은 Windows 전용 기능으로 분리.
문제 2: 파일 경로
Windows: E:\game\data.bundle
Mac: /Users/name/game/data.bundle
# Before (하드코딩)
backup_path = bundle_path.replace('.bundle', '.bundle.backup')
# After (pathlib)
from pathlib import Path
backup_path = Path(bundle_path).with_suffix('.bundle.backup')
pathlib는 OS 상관없이 작동. 더 이상 경로 문제 없음.
문제 3: GUI 폰트
# Windows
font = QFont("맑은 고딕", 10)
# Mac에는 맑은 고딕 없음
해결: 폰트 폴백
# gui/main_window.py
def _get_default_font(self):
system = platform.system()
if system == 'Windows':
return QFont("맑은 고딕", 10)
elif system == 'Darwin': # Mac
return QFont("Apple SD Gothic Neo", 10)
else: # Linux
return QFont("Noto Sans KR", 10)
OS별 기본 폰트 자동 선택.
실행 스크립트
# run_gui_with_project.sh (Mac/Linux)
#!/bin/bash
# 가상환경 활성화
source venv/bin/activate
# GUI 실행
python -m gui.main_window
# 인자 전달 지원
if [ $# -gt 0 ]; then
python -m gui.main_window "$@"
fi
# run_gui.bat (Windows)
@echo off
venv_win\Scripts\python.exe -m gui.main_window %*
Mac에서 ./run_gui_with_project.sh 실행. 끝.
용어 사전 시스템
게임마다 표현 수위가 다르다.
일부 게임은 폭력적 표현이 많고, 일부는 거의 없다.
API 정책에 걸리는 단어들이 있다.
Error: content_policy_violation
Your request was blocked due to content policy
번역 중단. 게임 번역 못 함.
용어 치환 시스템
API 정책에 걸리는 표현을 완곡하게 바꾸고 번역 후 복원.
# core/translator.py
class UniversalTranslator:
def __init__(self):
self.term_replacements = self._load_replacements()
self.temp_map = {} # 임시 매핑
def _preprocess(self, text):
"""번역 전 용어 치환"""
processed = text
for original, safe in self.term_replacements.items():
if original in processed:
self.temp_map[safe] = original
processed = processed.replace(original, safe)
return processed
def _postprocess(self, translated):
"""번역 후 복원"""
result = translated
for safe, original in self.temp_map.items():
if safe in result:
original_translated = self.tm.get(original, self.language_pair)
if original_translated:
result = result.replace(safe, original_translated)
self.temp_map.clear()
return result
def translate(self, text):
safe_text = self._preprocess(text)
translated = self._api_translate(safe_text)
final = self._postprocess(translated)
return final
작동 원리
원문: "敵を倒す"
전처리: "敵を制圧する"
API 번역: "적을 제압한다"
후처리: "적을 쓰러뜨린다"
API는 완곡한 표현을 보고 번역. 정책 통과.
결과는 원본 의미 유지.
문제 해결
Before:
- content_policy_violation
- 번역 중단
After:
- 정책 통과
- 원본 의미 유지
- 모든 게임 번역 가능
설정 파일 구조화
용어 사전을 규칙별로 분리.
config/
├── rules/
│ ├── general.yaml # 일반 게임
│ ├── violence.yaml # 폭력적 표현
│ └── custom.yaml # 사용자 정의
└── dicts/
├── defaults/ # 기본 사전
│ ├── proper_nouns.json
│ ├── speaker_names.json
│ └── game_terms.json
└── ... # 사용자 사전
사용자가 게임 특성에 맞는 규칙 선택.
# GUI
rule_type = self.rule_combo.currentText()
if rule_type == "폭력적 표현":
translator.load_rules("config/rules/violence.yaml")
else:
translator.load_rules("config/rules/general.yaml")
간단. 명확.
Mac 설정 가이드 작성
맥 사용자를 위한 가이드.
# MAC_SETUP_GUIDE.md
## 필수 요구사항
- Python 3.10+
- Homebrew
## 설치
# Python 설치 (Homebrew)
brew install python@3.10
# 저장소 클론
git clone https://github.com/hasjin/game-translator.git
cd game-translator/gametranslator
# 가상환경
python3 -m venv venv
source venv/bin/activate
# 의존성
pip install -r requirements.txt
# 실행
./run_gui.sh
## 주의사항
- AssetsTools.NET 기능은 Mac에서 비활성화
- UnityPy로만 작동 (대부분 게임 지원)
맥 사용자도 이제 쓸 수 있다.
최종 최적화 성과
API 비용
최적화 기법들:
1. Prompt Caching (Claude 제공)
2. Translation Memory (중복 제거)
3. 텍스트 정규화 (캐시 히트율 개선)
4. 배치 처리 (네트워크 오버헤드 감소)
체감 효과:
- 같은 게임 재번역: 거의 무료 (캐시 히트)
- 신규 게임: Prompt Caching으로 상당히 저렴
- 구체적 수치는 게임마다 천차만별
속도
Translation Memory 히트: 즉시
새로운 문장: ~1-2초/문장 (Claude API 응답 속도)
크로스플랫폼
✅ Windows (완전 지원)
✅ Mac (UnityPy만, 대부분 게임 OK)
✅ Linux (이론상 가능, 미테스트)
코드 변경
~ core/translation_memory.py (정규화 로직)
~ core/translator.py (배치 처리, 용어 치환)
~ core/unity_extractor.py (플랫폼 감지)
~ gui/main_window.py (폰트 폴백)
+ run_gui_with_project.sh (Mac/Linux)
+ MAC_SETUP_GUIDE.md
+ config/rules/violence.yaml
+ config/rules/general.yaml
+ config/rules/custom.yaml
배운 것
- 정규화는 중요하다
공백 하나 차이로 캐시 미스. 정규화로 불필요한 중복 번역 감소. - 배치 처리는 양날의 검
네트워크 호출은 줄지만 파싱 복잡도 증가. 적절한 크기가 중요. - 크로스플랫폼은 처음부터
pathlib, 플랫폼 감지. 나중에 하면 지옥. - API 정책 대응
전처리/후처리로 용어 치환. 정책 통과하면서 의미 유지. - 설정 파일 분리
게임 특성별 규칙 파일. 사용자가 선택. 깔끔.
'개발관련' 카테고리의 다른 글
| Unity 게임 번역기 개발기 #5: RPG 메이커? 그것도 된다 (0) | 2025.10.18 |
|---|---|
| Unity 게임 번역기 개발기 #4: 개발은 디테일이다 (1) | 2025.10.12 |
| Unity 게임 번역기 개발기 #3: 코드가 괴물이 되다 (0) | 2025.10.11 |
| Unity 게임 번역기 개발기 #2: 돈을 쓰기로 했다 (0) | 2025.10.10 |
| Unity 게임 번역기 개발기 #1: 이게 시작이었다 (0) | 2025.10.09 |