이 글은 누구를 위한 것인가
- i18n JSON 파일을 수동으로 번역하는 개발팀
- 브랜드 용어가 번역마다 달라져서 일관성이 없는 팀
- DeepL/Google 번역 품질이 부족해 LLM으로 보완하려는 팀
들어가며
"결제하기" 버튼이 어떤 페이지에서는 "Pay Now", 다른 페이지에서는 "Complete Purchase"가 된다면 브랜드 일관성이 깨진다. LLM은 브랜드 가이드라인과 컨텍스트를 이해하고 일관된 번역을 생성한다.
이 글은 bluebutton.kr의 다국어 서비스 운영 을 참고하여 작성했습니다.
1. 번역 파이프라인 설계
[하이브리드 번역 전략]
1단계: 빠른 번역 (DeepL API)
일반 문장, 빠른 처리
비용: $0.00002/문자
품질: 높음 (일반적)
2단계: LLM 후처리 (Claude)
브랜드 용어 교정
문화적 맥락 조정
마케팅 카피 재창작
비용: 높음, 선별 적용
3단계: 품질 검증
역번역으로 의미 보존 확인
용어 일관성 체크
금지 표현 검사
[번역 우선순위]
Critical (즉시): 에러 메시지, 법적 고지
High (1일 내): UI 텍스트, 제품명
Medium (1주): 도움말, FAQ
Low (배치): 블로그, 마케팅 이메일
[지원 언어별 고려사항]
일본어: 존댓말 레벨 (です/だ体)
아랍어: RTL, 숫자 형식
중국어: 간체(대륙) vs 번체(대만/홍콩)
영어: US vs UK 철자
2. 번역 자동화 구현
import anthropic
import json
from dataclasses import dataclass
client = anthropic.Anthropic()
@dataclass
class TranslationConfig:
source_lang: str = "ko"
target_lang: str = "en"
brand_glossary: dict[str, str] = None # 브랜드 용어 사전
tone: str = "professional" # professional, friendly, formal
GLOSSARY_KO_EN = {
"결제하기": "Complete Purchase",
"장바구니": "Cart",
"위시리스트": "Wishlist",
"포인트": "Points",
"쿠폰": "Coupon",
}
async def translate_with_context(
text: str,
config: TranslationConfig,
context: str = "",
) -> str:
"""컨텍스트와 브랜드 용어를 반영한 번역"""
glossary_str = ""
if config.brand_glossary:
glossary_str = "\n".join(
f" {k} → {v}" for k, v in config.brand_glossary.items()
)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"""다음 텍스트를 {config.source_lang}에서 {config.target_lang}로 번역하세요.
원본: {text}
{"맥락: " + context if context else ""}
{"브랜드 용어 사전 (반드시 이 번역 사용):\n" + glossary_str if glossary_str else ""}
톤앤매너: {config.tone}
번역문만 반환하세요. 설명 없이."""
}]
)
return response.content[0].text.strip()
async def translate_i18n_json(
source_file: str,
config: TranslationConfig,
) -> dict:
"""i18n JSON 파일 전체 번역"""
with open(source_file) as f:
source = json.load(f)
async def translate_value(key: str, value: str | dict) -> str | dict:
if isinstance(value, dict):
result = {}
for k, v in value.items():
result[k] = await translate_value(f"{key}.{k}", v)
return result
# 브랜드 용어 직접 치환
if config.brand_glossary and value in config.brand_glossary:
return config.brand_glossary[value]
# 짧은 UI 텍스트는 LLM으로 번역
if len(value) < 200:
return await translate_with_context(
value, config, context=f"UI key: {key}"
)
return await translate_with_context(value, config)
translated = {}
for key, value in source.items():
translated[key] = await translate_value(key, value)
return translated
async def evaluate_translation_quality(
original: str,
translation: str,
source_lang: str,
target_lang: str,
) -> dict:
"""번역 품질 자동 평가"""
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=300,
messages=[{
"role": "user",
"content": f"""번역 품질을 평가하세요.
원본 ({source_lang}): {original}
번역 ({target_lang}): {translation}
JSON으로 평가:
{{
"accuracy": 1-10,
"fluency": 1-10,
"style": 1-10,
"issues": ["문제점1", "문제점2"],
"suggested_correction": "더 나은 번역 (있을 경우)"
}}"""
}]
)
return json.loads(response.content[0].text)
async def batch_translate_strings(
strings: list[str],
config: TranslationConfig,
) -> list[str]:
"""여러 문자열 배치 번역 (토큰 절약)"""
numbered = "\n".join(f"{i+1}. {s}" for i, s in enumerate(strings))
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=3000,
messages=[{
"role": "user",
"content": f"""다음 문자열 목록을 {config.source_lang}에서 {config.target_lang}로 번역하세요.
번호 순서를 유지하고, "1. 번역1\n2. 번역2" 형식으로 반환하세요.
{numbered}"""
}]
)
lines = response.content[0].text.strip().split("\n")
result = []
for line in lines:
if ". " in line:
result.append(line.split(". ", 1)[1].strip())
return result
마무리
번역 자동화의 ROI는 명확하다. i18n JSON 500개 키를 수동 번역하면 2-3일, LLM 파이프라인은 10분이다. 브랜드 용어 사전을 먼저 구축하고, 마케팅 카피 같은 고창의성 텍스트는 LLM이 직접 생성하게 하는 것이 기계 번역보다 낫다.