스타일, 위치, 가라오케를 깨뜨리지 않고 ASS/SSA 자막을 번역하는 방법
.ass 파일에서 화면에 보이는 대사만 번역한 뒤 타이밍, 태그, 가라오케, 글꼴을 확인하세요. 샘플 파일, 무료 검사기, 9회 로컬 LLM 테스트를 포함합니다.

ASS 또는 SSA 자막 파일은 작은 프로그램입니다. 헤더, 스타일 표, 그리고 마지막 필드에 대사와 서식 코드가 섞여 있는 시간 지정 이벤트 목록으로 구성됩니다. 번역하려면 마지막 필드에서 읽을 수 있는 단어만 바꾸고 나머지는 바이트 단위로 그대로 둔 다음, 어떤 번역 도구도 대신 고쳐 주지 않는 세 가지를 확인해야 합니다. 바로 글꼴, 간격 코드, 가라오케 줄입니다.
14개 이벤트가 있는 샘플을 로컬 모델로 세 가지 프롬프트를 사용해 중국어 간체로 아홉 번 번역하고, 직접 작성한 스크립트로 모든 결과를 검사했습니다. 타이밍, 위치, 색상, 주석, 그림은 아홉 번 모두 그대로 유지되었습니다. 깨진 것은 파일에서 영어를 전제로 한 부분이었습니다. 아홉 번 모두 스타일 글꼴에 중국어 문자가 없어서 파일이 플레이어의 글꼴 대체에 의존하게 되었고, 영어 줄의 하드 스페이스가 중국어에서 눈에 보이는 간격이 되었습니다. 가라오케 줄은 모든 실행에서 타이밍이 잘못된 단어를 가리켰고, 네 번의 실행에서는 일부 음절이 아무것도 강조하지 않았습니다.
빠른 답변: 무엇을 번역하고 무엇을 그대로 둘까
| 파일의 일부 | 샘플에서 가져온 예 | 번역할까? |
|---|---|---|
[Script Info] 헤더 | PlayResX: 1920, WrapStyle: 0 | 아니요. 위치는 이 값들로 계산됩니다. |
[V4+ Styles] | Style: Default,Trebuchet MS,64,... | 아니요. 단, 글꼴 이름은 의도적으로 바꿔야 할 수 있습니다 |
| 이벤트 필드 1–9 | 0,0:00:01.00,0:00:03.20,Default,Jun,0,0,0, | 아니요. 레이어, 타이밍, 스타일, 화자, 여백, 효과입니다. |
| 오버라이드 블록 | {\i1}, {\an8\pos(960,120)}, {\k25} | 아니요 |
| 그림 | {\p1}m 0 0 l 200 0 200 60 0 60{\p0} | 아니요. 텍스트가 아니라 벡터 도형입니다. |
Comment: 이벤트 | Comment: ...,Earlier draft: Try the stacks. | 선택 사항. 주석은 화면에 절대 나타나지 않습니다. |
| 중괄호 메모 | {TL note: "stacks" means the closed shelves} | 선택 사항. 플레이어는 이를 무시합니다. |
| 보이는 대사 | Did you find the second volume? | 예 |
| 줄 코드 | \N, \n, \h | \N은 유지하세요. \n과 \h는 사례별로 판단하세요. |
이 가이드의 나머지 부분에서는 각 행이 왜 중요한지, 그리고 결과를 어떻게 확인하는지 설명합니다.
ASS 한 줄이 구성되는 방식
[Events] 섹션의 모든 줄은 바로 위에 선언된 형식을 따릅니다:
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:03.40,0:00:06.10,Default,Mara,0,0,0,,{\i1}I thought it was{\i0} sold out.
시청자가 읽는 내용은 열 번째 필드인 Text에만 들어 있습니다. libass 프로젝트의 ASS 파일 형식 가이드에 따르면 이 마지막 필드는 "쉼표를 포함할 수 있다"고 명시되어 있습니다. 그래서 모든 쉼표를 기준으로 무심코 분할하면 Well, if it's there, then, we go now. 같은 줄이 깨집니다. 안전한 도구는 처음 아홉 개의 쉼표만 기준으로 분할합니다.
Text 안에서 중괄호로 둘러싸인 부분은 오버라이드 블록입니다. Aegisub의 오버라이드 태그 레퍼런스에는 가장 자주 접하게 될 코드들이 정의되어 있습니다:
\N은 모든 줄바꿈 모드에서 강제 줄바꿈을 합니다.\n은 줄바꿈 모드 2에서만 줄을 바꾸고, 그 외에는 공백으로 처리됩니다.\h는 줄바꿈 없는 공백입니다.\an8은 숫자 키패드 배열을 사용해 정렬을 설정하므로, 8은 상단 중앙입니다.\pos(960,120)은 해당 줄을 스크립트 좌표에 배치합니다.\k25는 가라오케 음절입니다. 지속 시간은 1/100초 단위이므로 25는 0.25초를 의미합니다.\p1이후의 텍스트는 "그리기 명령으로 해석"되므로,m 0 0 l 200 0을 "번역"하는 번역가는 도형을 파괴하게 됩니다.- 중괄호 안의 인식되지 않는 텍스트는 "조용히 무시"되므로, 팬서브 파일에 종종 중괄호로 둘러싼 번역가 노트가 포함되는 이유이기도 합니다.
Comment:로 시작하는 줄은 동일한 필드를 사용하지만, libass 가이드에 따르면 "화면에 아무것도 표시되지 않습니다."
테스트한 내용
우리는 서점을 배경으로 한 14개 이벤트 장면인 sample-source.ass를 작성했으며, 위의 각 기능을 하나씩 예시로 포함했습니다: 이탤릭체, 강제 줄바꿈, 대사 속 쉼표, 중괄호 노트, 주석 줄, 위치가 지정된 간판, 벡터 상자, 페이드, 강제 공백, \r을 사용한 색상 변경, 소프트 줄바꿈, 스크롤 Banner 효과, 6음절 가라오케 줄.
그런 다음 로컬 모델에게 이를 중국어 간체로 번역하도록 세 가지 방식으로 요청했습니다:
- 전체 파일.
.ass파일 전체를 붙여넣고 번역된 파일을 반환하도록 요청합니다. - 텍스트만. 각 대사 줄의
Text필드만 JSON으로 보내고, 태그는 인라인으로 유지하며, 모든{...}블록과 모든\N,\n,\h를 유지하라는 지시를 포함합니다. - 보호됨. 동일하지만, 먼저 모든 중괄호 블록과 줄 코드를 번호가 매겨진 자리표시자로 바꾸고, 나중에 원본을 복원합니다.
두 번째와 세 번째 조건에서는 스크립트가 벡터 드로잉이나 주석 줄을 모델에 전송하지 않으므로, 해당 줄들은 전체 파일 조건에서만 손상될 수 있었습니다.
모델은 Apple M4 Max(64GB 메모리)에서 LM Studio 0.4.8로 실행한 Qwen 3.6 27B(Q4_K_M)였으며, 사고 기능은 끄고 temperature는 0.3으로 설정했습니다. 각 조건을 세 번씩, 총 아홉 번 실행했습니다. 세 번째 실행의 첫 시도는 로컬 서버가 연결을 끊어 중단되었으므로, 서버를 재시작하고 세 번째 라운드를 다시 실행했습니다. 스크립트는 run_llm_ass_test.py이며, 아홉 개의 출력물 모두 편집 없이 함께 게시되었습니다. README에 모든 파일 목록이 있습니다. 로컬 모델을 설정해 본 적이 없다면, 로컬 LLM 번역 가이드에서 설정을 다룹니다.
이것은 하나의 모델, 하나의 언어 쌍, 하나의 샘플로 진행한 소규모 테스트였습니다. 어떤 문제가 발생하는지, 그리고 이를 어떻게 잡아낼 수 있는지를 보여줍니다. 도구나 프롬프트의 순위를 매기는 것은 아닙니다.
결과: 기계적 구조는 살아남았지만, 영어적 가정은 그렇지 못했다
우리는 check_ass_translation.py로 모든 출력을 확인했습니다. 전체 출력은 check-results.txt에 있으며, results.csv에는 스크립트가 감지할 수 없는 문제를 포함하여 실행당 한 행씩 기록되어 있습니다.
| 확인 | 전체 파일 | 텍스트만 | 보호됨 |
|---|---|---|---|
| 타이밍, 스타일 및 필드 1–9 변경 없음 | 3/3 | 3/3 | 3/3 |
| 오버라이드 태그 변경 없음 | 3/3 | 2/3 | 3/3 |
| 그리기 및 주석 줄 그대로 유지 | 3/3 | 3/3 | 3/3 |
| 번역을 표시할 수 있는 스타일 글꼴 | 0/3 | 0/3 | 0/3 |
\h 및 \n이 중국어 텍스트에서 공백으로 남음 | 3/3 | 3/3 | 3/3 |
| 모든 가라오케 태그 유지, 각각 텍스트 포함 | 3/3 | 0/3 | 1/3 |
두 가지 결과가 우리를 놀라게 했다. 전체 파일을 붙여넣은 경우 세 번의 실행 모두에서 구조가 그대로 유지되었지만, 그중 두 번은 헤더의 Title 줄까지 번역했다. 그리고 더 안전해 보이는 텍스트 전용 프롬프트는 유일하게 태그가 깨진 결과를 낳았다. 한 실행에서는 여섯 개의 가라오케 태그가 있는 줄에 대해 {\k25}翻{\k30}页,{\k45}开{\k60}灯을 반환했다. 마지막 두 태그와 1.20초의 하이라이트 타이밍이 사라져서, 하이라이트가 해당 줄보다 훨씬 일찍 끝나게 되었다. 같은 실행에서 Room\h12 is upstairs.는 12\h12号房间在楼上。로 바뀌어 방 번호가 두 번 나왔다.
그러므로 유용한 교훈은 "프롬프트 X를 사용하라"가 아니다. 두려워하는 구조적 손상은 쉽게 감지할 수 있으며, 아홉 번의 실행 모두에서 실제로 발생한 손상은 ASS 번역 도구 페이지에서 다루지 않는 세 곳에 있다는 점이다.
1. 글꼴이 새 언어를 그릴 수 없다
샘플의 스타일은 대사에 Trebuchet MS를, 표지판에 Georgia를 사용하는데, 이는 영어판에서 흔한 일이다. 두 글꼴 모두 한자를 포함하지 않는다. 검사기의 글리프 테스트를 두 글꼴 파일에 적용했을 때, 모든 실행에서 번역된 12개의 텍스트 이벤트가 전부 실패했다:
ERROR event 1: font 'Trebuchet MS' has no glyph for 9 characters, e.g. 了二你到卷吗找第?
ERROR event 7: font 'Georgia' has no glyph for 10 characters, e.g. 书人仅作员工库架闭限
플레이어가 설치된 다른 글꼴로 대체할 수 있으므로 자막이 여전히 나타날 수는 있지만, 릴리스가 모양을 더 이상 제어하지 못하며, 적합한 글꼴이 없는 시스템의 시청자는 빈 사각형을 볼 수 있다. 해결책은 각 스타일의 Fontname 필드를 의도적으로 편집하는 것이다. 한 출력에서 세 스타일 모두를 macOS에 기본 포함된 중국어 글꼴인 Hiragino Sans GB로 변경하고 해당 글꼴에 대해 글리프 테스트를 실행했더니 글리프 오류가 0이 되었다.
시청자가 실제로 가지고 있을 글꼴을 선택하거나 비디오와 함께 제공하라. libass 가이드에 따르면 릴리스는 사용하는 모든 글꼴을 첨부해야 하며, 가능하면 MKV 컨테이너에 첨부하는 것이 좋다. 대상 언어 글꼴은 새로 추가된 것이므로 이제 첨부하는 것도 번역 작업의 일부이다.
2. 간격 코드는 영어 습관을 담고 있다
Room\h12는 "Room"과 "12"를 붙여서 줄바꿈 시 분리되지 않게 한다. 중국어에서는 단어 사이에 공백이 없지만, 모든 실행에서 \h가 어딘가에 남았다: 다섯 번은 12号房间과 在楼上 사이에, 세 번은 숫자 앞에, 한 번은 숫자 두 개 사이에 넣었다. 어느 쪽이든 시청자는 불필요한 공백을 보게 된다. Wait\nhere.도 같은 문제가 있었다. WrapStyle: 0에서는 \n이 공백으로 렌더링되므로 在\n这里等은 세 단어 문장 중간에 간격이 보인다.
여기서 구조는 괜찮지만 언어는 그렇지 않다. 단어 사이에 공백이 없는 대상 언어의 경우 번역된 텍스트에서 \h와 \n을 제거하라. \N은 강제 줄바꿈이 필요한 곳에만 유지하고, 대상 문장의 자연스러운 지점에 배치하라. 검사기가 변경된 \h 및 \n 개수를 오류가 아닌 경고로 보고하는 이유가 여기에 있으며, 중국어, 일본어 또는 한국어 텍스트 내부에 하나가 남아 있으면 경고한다.
3. 가라오케 타이밍은 원래 단어에 속한다
가라오케 줄 {\k25}Turn {\k30}the {\k45}page, {\k60}turn {\k40}the {\k80}light는 각 영어 음절을 노래에 맞춰 타이밍을 잡는다. 번역은 중국어 단어가 같은 박자에 떨어지지 않기 때문에 그 타이밍을 의미 있게 유지할 수 없다. 우리의 실행은 그것이 잘못되는 다양한 방식을 보여준다:
- 네 번의 실행에서 여섯 개의 태그가 모두 유지되었지만, 각각 이전과 다른 단어에 붙었다. 그중 하나는
{\k40}打{\k80}开("열다")로 끝나고 줄에서 "light"를 완전히 삭제했다. - 네 번의 실행에서 음절이 아무것도 강조하지 않았다. 예를 들어
打开{\k40}{\k80}灯(빈 음절에 0.40초 하이라이트). 그중 두 번에서는 0.45초 음절이 쉼표만 강조하기도 했다. - 한 번의 실행에서는 위에서 설명한 대로 두 개의 태그가 사라졌다.
검사기는 제자리에서 번역된 가라오케 줄과 텍스트가 남지 않은 음절을 표시한다. 더 나은 해결책은 편집적이다: 가라오케를 제자리에서 번역하지 마라. 따라 부르기 위해 원래 타이밍 줄을 유지하고, 번역을 별도의 이벤트로 추가하라: 가라오케 이벤트를 복사하고, 자체 스타일(예: 더 작게, 하단에 배치)을 부여하고, \k 태그를 제거하고, 텍스트를 번역하라. 오디오에 맞춰 직접 타이밍을 잡을 준비가 된 경우에만 음절별로 다시 타이밍을 잡아라.
견고한 워크플로
-
먼저 소스를 읽을 수 있게 만들어라. 파일에 깨진 문자가 표시되면 무엇보다 먼저 인코딩을 수정하라. 인코딩 가이드는 SRT 예제를 사용하지만, 동일한 바이트 수준 단계가
.ass파일에도 적용되며 원본 바이트를 안전하게 유지한다. 검사기는 UTF-8과 UTF-16만 읽는다. -
범위를 결정하라. 스타일을 나열하고 어떤 것이 대사인지, 어떤 것이 화면 표지판인지, 어떤 것이 가라오케인지 결정하라. 표지판은 보통 번역이 필요하고, 가라오케는 보통 별도 줄이 필요하며, 주석과 중괄호 메모는 직접 결정하라.
-
Text필드만 번역하라. 일반 텍스트로 취급하지 말고 ASS를 파싱하는 도구를 사용하거나, 세 번째 조건에서처럼 태그를 직접 보호하라. 원본 파일을 출력 옆에 보관하라. -
구조 검사를 실행하라.
python3 check_ass_translation.py source.ass translated.ass이벤트 수, 이벤트 유형, 필드 1–9, 스타일 값, 재정의 블록, 그림, 주석 줄,
\N, 가라오케 음절 및 지속 시간, 비어 있거나 변경되지 않은 채로 돌아온 줄을 비교한다. 번역 품질은 판단하지 않는다. -
폰트는 의도적으로 수정하세요. 이제 대상 언어 텍스트가 들어간 각 스타일의
Fontname을 변경한 다음, 배포할 폰트 파일로 글리프 테스트를 실행하세요:pip install fonttools python3 check_ass_translation.py source.ass translated.ass --font "Noto Sans SC=/path/to/NotoSansSC-Regular.otf"변경된 폰트 이름은 경고로 표시되므로 모든 변경이 의도한 것인지 확인할 수 있습니다.
-
위에서 설명한 대로 간격 코드와 가라오케를 정리하세요.
-
영상과 함께 확인하세요. Aegisub나 플레이어에서 파일을 여세요.
\p박스는 영어 크기를 유지하는 반면 번역된 사인은 훨씬 길거나 짧을 수 있으므로, 사인이 뒤에 그려진 도형에 여전히 맞는지 확인하세요. 화면에 표시되는 시간에 비해 줄이 너무 길지 않은지도 확인하세요. 두 문제 모두 파일 자체에서는 보이지 않습니다.
BookTranslator가 적합한 지점
BookTranslator의 자막 번역기는 SRT, VTT, ASS, SSA 파일을 지원합니다. 제품 페이지에 따르면 타임스탬프와 큐 순서를 유지하고 지원되는 ASS 및 SSA 스타일링을 각 자막 항목에 연결된 상태로 유지하므로, 텍스트를 직접 추출하고 다시 삽입할 필요가 없습니다. 주변 대사를 문맥으로 삼아 대화를 번역하므로 에피소드 전체에 걸친 이름과 반복되는 농담을 처리하는 데 도움이 됩니다.
이 글을 위해 이 샘플을 BookTranslator로 실행하지는 않았습니다. 어떤 도구를 사용하든 결과물에 4~7단계를 실행하세요. 영어 폰트를 유지하는 스타일은 여전히 중국어를 그릴 수 없는 스타일이며, 제자리에서 번역된 가라오케는 여전히 편집 결정이 필요합니다. 나머지 자막 워크플로에 대해서는 AI 자막 번역 가이드, 자막 번역 소프트웨어 비교를 참고하고, 번역 결과를 YouTube에 올릴 예정이라면 YouTube 자막 워크플로를 확인하세요.
대신 SRT로 변환해야 할 때
번역 전에 ASS를 SRT로 변환하면 스타일, 위치, 그림, 가라오케가 제거되며, 이는 위의 문제 대부분도 제거합니다. 목적지에서 일반 자막만 표시하거나 스타일링이 릴리스에 중요하지 않았다면 합리적인 절충입니다. 사인과 가라오케가 작품의 일부인 애니메이션 및 음악 릴리스에는 잘못된 절충입니다. 어떤 형식이 번역하기 더 쉬운지가 아니라 파일이 어디에 표시될지를 기준으로 결정하세요.
테스트 파일 받기
이 테스트의 모든 자료는 자유롭게 재사용할 수 있습니다:
- sample-source.ass: 14개 이벤트로 구성된 영어 샘플;
- check_ass_translation.py: 검사기 (Python 표준 라이브러리;
--font테스트에는 fontTools 필요); - run_llm_ass_test.py: OpenAI 호환 로컬 서버를 위한 세 가지 프롬프트 조건;
- results.csv 및 check-results.txt: 실행별 결과;
- README.txt: 파일 목록과 테스트 설정,
outputs폴더에 편집되지 않은 아홉 개의 출력물 포함.




