코드, 링크, 프런트 매터를 깨뜨리지 않고 마크다운 파일을 번역하는 방법
마크다운 또는 MDX 파일에서 번역해야 할 부분과 그대로 두어야 할 부분, 그리고 결과를 확인하는 방법. 샘플 파일, 무료 검사기, 9회 로컬 LLM 테스트 포함.

마크다운 파일을 번역하려면 산문은 번역하고 기계적인 부분은 그대로 두어야 합니다. 프런트 매터 키, 코드, URL, 링크 레이블, 컴포넌트 태그, 각주 레이블은 바이트 단위로 동일하게 반환되어야 합니다. 그런 다음 신중한 번역에서도 여전히 깨지는 세 가지를 확인하세요. 제목에 대한 링크, 코드 주석, 그리고 중국어, 일본어, 한국어에서 굵은 텍스트와 숫자 범위 주변의 띄어쓰기입니다.
우리는 73줄짜리 MDX 페이지를 로컬 모델로 간체 중국어로 아홉 번 번역했으며, 세 가지 다른 프롬프트를 사용하고 모든 결과를 스크립트로 확인했습니다. 사람들이 보통 걱정하는 부분은 아홉 번의 실행 모두에서 살아남았습니다. 코드, 인라인 코드, URL, 표, JSX 컴포넌트, 작업 목록이 그렇습니다. 다른 세 가지 문제가 발생했습니다. 제목에 대한 링크는 아홉 번의 실행 모두에서 깨졌습니다. 코드 주석은 전체 파일을 보낸 여섯 번의 실행 모두에서 번역되었으며, 프롬프트에서 코드를 건드리지 말라고 한 세 번의 실행도 포함됩니다. 그리고 구문을 자리 표시자 뒤에 숨긴 프롬프트는 가장 안전한 옵션처럼 들리지만, 세 번의 실행 모두에서 프런트 매터를 깨뜨렸습니다.
빠른 답변: 무엇을 번역하고 무엇을 그대로 둘까
| 파일의 일부 | 샘플에서 가져온 예시 | 번역할까요? |
|---|---|---|
| 프런트 매터 키 | slug:, sidebar_position:, tags: | 아니요 |
| 프런트 매터 값 | title: "Export Your Library" | title 및 description과 같이 독자에게 보이는 값만 |
| 가져오기 및 내보내기 (MDX) | import Callout from '@site/src/components/Callout'; | 아니요 |
| 코드 블록 | shelfmark export --format epub | 아니요. 주석과 사용자에게 보이는 문자열은 별도로 결정하세요. |
| 인라인 코드 | library.json, --covers | 아니요 |
| 링크 및 이미지 대상 | (./export-options.md), (https://www.w3.org/TR/epub-33/ "EPUB 3.3") | 아니요, 따옴표 안의 선택적 제목은 예외 |
| 링크 텍스트 및 이미지 대체 텍스트 | [glossary section], ![Folder layout after an export] | 예 |
| 참조 레이블 및 정의 | [project glossary][glossary], [glossary]: ./glossary.md#terms | 레이블 [glossary]와 URL은 유지하고, 보이는 텍스트는 번역하세요 |
| 제목 | ## Keep one glossary for the whole book | 예, 하지만 먼저 고정 ID를 지정하세요(아래 참조) |
| JSX 컴포넌트 | <Callout type="warning" title="Back up first"> | 태그와 속성 이름은 유지하고, title처럼 독자가 보는 속성 값은 번역하세요 |
| 어드머니션 마커 | :::tip 및 ::: | 아니요 |
| 각주 레이블 | [^sync] | 아니요 |
| MDX 주석 | {/* Translator note: ... */} | 선택 사항입니다. 독자는 절대 볼 수 없습니다. |
나머지는 모두 일반 산문입니다: 단락, 목록 항목, 표 셀, 인용문.
테스트한 내용
우리는 Shelfmark라는 가상의 독서 앱을 위한 문서 페이지인 sample-source.mdx를 작성했습니다. 이 문서는 Docusaurus 및 유사한 문서 사이트에서 사용하는 MDX 스타일로 작성되었으며, 위 표의 각 구성 요소 예시 하나씩과 함께 두 칸 공백 강제 줄바꿈, 굵은 링크, 괄호로 끝나는 굵은 구문, 엔 대시로 표기된 두 개의 숫자 범위, 그리고 "Keep one glossary for the whole book." 제목으로 연결되는 페이지 내 링크가 포함되어 있습니다.
그런 다음 로컬 모델에게 세 가지 방식으로 중국어 간체로 번역하도록 요청했습니다:
- 전체 파일. 파일 전체를 붙여넣고 번역된 파일을 반환하도록 요청합니다.
- 지시 포함. 동일하지만 여섯 가지 규칙을 추가합니다: 프런트 매터 키 유지, 코드 내부 변경 금지, URL 및 참조 정의 유지, 임포트, 태그, 주석 유지, 각주 레이블 및 어드머니션 마커 유지, 제목, 링크 텍스트, 대체 텍스트,
title속성 번역. - 보호됨. 코드, 인라인 코드, URL, 태그, 주석, 각주 레이블, 어드머니션 마커, 프런트 매터 줄을
⟦12⟧와 같은 번호가 매겨진 자리 표시자로 바꾸고 나머지를 번역한 다음 원본을 다시 넣습니다.title및description값은 번역을 위해 노출되었고, 결과적으로 프런트 매터의---줄과 닫는 따옴표도 노출되었습니다.
모델은 LM Studio의 Qwen 3.6 27B(Q4_K_M)였으며, 사고 기능을 끄고 온도 0.3으로 설정했고, 64GB 메모리의 Apple M4 Max에서 실행했습니다. 2026년 10월 25일에 각 조건을 세 번씩, 총 아홉 번 실행했습니다. 스크립트는 run_llm_markdown_test.py이며, 아홉 개의 출력 모두 편집 없이 공개되었습니다. README에 모든 파일이 나열되어 있습니다. 로컬 모델을 설정해 본 적이 없다면 로컬 LLM 번역 가이드에서 설정을 다룹니다.
이것은 하나의 모델, 하나의 언어 쌍, 하나의 페이지에 대한 작은 테스트였습니다. 어떤 문제가 발생하는지, 그리고 이를 어떻게 잡아낼 수 있는지 보여줍니다. 모델이나 프롬프트의 순위를 매기지는 않습니다.
결과: 구문은 살아남았지만 링크와 결정은 그렇지 않았다
우리는 check_markdown_translation.py로 모든 출력을 확인하고 remark와 MDX 컴파일러로 각각을 렌더링했습니다. 전체 검사기 출력은 check-results.txt에 있으며, results.csv에는 실행당 한 행이 있습니다.
| 검사 | 전체 파일 | 지시됨 | 보호됨 |
|---|---|---|---|
| MDX로 컴파일됨 | 3/3 | 3/3 | 3/3 |
| 코드, 인라인 코드, URL, 표, 태그, 작업 목록 변경 없음 | 3/3 | 3/3 | 3/3 |
| 굵게 및 기울임꼴이 여전히 렌더링됨 | 3/3 | 3/3 | 3/3 |
| 프런트 매터 그대로 유지 | 3/3 | 3/3 | 0/3 |
| 페이지 내 링크가 여전히 해당 제목을 가리킴 | 0/3 | 0/3 | 0/3 |
| 코드 주석은 작성된 그대로 유지됨 | 0/3 | 3개 중 0개 | 3개 중 3개 |
콜아웃 title 번역됨 | 3개 중 3개 | 3개 중 3개 | 3개 중 0개 |
처음 몇 행은 좋은 소식입니다. 이 모델은 전체 파일을 붙여넣었을 때 Markdown 구문을 그대로 반환했으며, 코드, URL, 표에서 수정할 부분을 찾지 못했습니다. 마지막 네 행이 실제 작업이 필요한 부분인데, 사이트를 빌드할 때 이들 중 어느 것도 오류로 나타나지 않습니다.
1. 번역된 제목이 해당 제목으로 향하는 링크를 깨뜨림
샘플은 [glossary section](#keep-one-glossary-for-the-whole-book)로 용어집 섹션에 링크합니다. 모든 실행에서 제목을 번역했고(당연히 그래야 하듯이), 모든 실행에서 링크 대상을 유지했습니다(당연히 그래야 하듯이). 결과는 더 이상 존재하지 않는 ID로 향하는 링크입니다.
대부분의 Markdown 도구는 제목 텍스트로 제목 ID를 만듭니다. Pandoc 문서는 식별자를 "제목 텍스트를 기반으로" 한다고 설명하고, Docusaurus는 경고합니다 — "링크가 깨지지 않도록 기존 ID를 업데이트하지 않고" 제목을 변경하거나 번역하고 싶을 수 있다고. 제목이 为整本书保留一个术语表로 읽히면 생성된 ID는 중국어가 되고, #keep-one-glossary-for-the-whole-book은 어디로도 스크롤되지 않습니다. 페이지는 여전히 빌드되므로 아무것도 알려주지 않습니다.
번역 후에 링크를 편집해서 이 문제를 해결할 수도 없습니다. 아홉 번의 실행에서 모델은 같은 제목을 네 가지 방식으로 썼기 때문에, 재번역할 때마다 생성된 ID가 다시 바뀔 것입니다. 해결책은 번역 전에 있습니다: 링크되는 각 제목에 고정 ID를 부여하세요.
- Docusaurus MDX에서는
## Keep one glossary for the whole book {/* #keep-one-glossary */}를 사용하세요. Docusaurus는 이전{#id}형식이 "더 이상 유효한 MDX 구문이 아니다"라고 언급하며,write-heading-ids명령이 모든 문서에 ID를 자동으로 추가합니다. - Pandoc 및 PHP Markdown Extra를 따르는 Markdown 프로세서에서는 제목 끝에
{#keep-one-glossary}를 사용하세요.
그러면 ID는 모든 언어에서 동일하게 유지되며, 검사기는 이를 링크 대상으로 처리합니다.
2. 코드 주석은 번역되고, 표시 문자열은 번역되지 않음
샘플에는 셸 주석과 YAML 주석이 있습니다:
library:
name: "My books" # shown in the sidebar
sort: title
전체 파일을 보낸 여섯 번의 실행 모두 두 주석을 번역했으며, 프롬프트에 "펜스 코드 블록 내부의 내용은 변경하지 마세요"라고 명시된 세 번의 실행도 포함됩니다. 코드 자체는 아홉 번의 실행 모두에서 손대지 않았습니다. 한편 주석에서 사이드바에 표시된다고 하는 "My books"는 아홉 번의 실행 모두에서 영어로 남았습니다. 모델은 현지화된 페이지에 필요한 것과 정반대의 일을 했습니다: 개발자를 위한 노트는 번역하고, 독자가 보게 될 레이블은 그대로 두었습니다.
어느 선택도 모든 경우에 틀린 것은 아닙니다. 독자가 코드를 자신의 프로젝트에 복사할 튜토리얼이라면 번역된 주석이 도움이 됩니다. 코드가 저장소와 일치해야 하는 참조 문서라면 페이지가 소스와 불일치하게 됩니다. 프로젝트에 대해 한 번 결정하고, 구성 예제의 문자열 값은 직접 확인하세요. 어떤 도구도 표시 레이블과 식별자를 구분할 수 없기 때문입니다. 이런 이유로 검사기는 주석만 변경된 경우를 경고로 보고합니다.
3. 안전해 보였던 방법이 프런트 매터를 깨뜨림
보호 조건은 약속한 대로 작동했습니다: 세 번의 실행 모두에서 32개의 자리 표시자가 모두 반환되어 코드, URL, 태그가 완벽하게 복원되었습니다. 하지만 우리 스크립트는 자리 표시자 밖에 두 가지 작은 것들을 남겼습니다: 프런트 매터를 여는 --- 줄과, 번역된 제목 및 설명 뒤의 닫는 따옴표입니다. 모델은 세 번의 실행 모두에서 이들을 모두 없앴습니다. 파일은 다음과 같이 시작했습니다:
title: "导出您的书库
description: "在更换设备之前,请备份您的笔记和书籍。
slug: export-library
여는 ---가 없으면 Markdown 파서는 더 이상 프런트 매터를 인식하지 않습니다. 여섯 줄의 YAML과 닫는 ---를 제목으로 읽기 때문에 페이지는 YAML을 포함한 큰 제목으로 시작할 것입니다. 여전히 MDX로 컴파일되었습니다. ---를 다시 넣는 것만으로는 충분하지 않습니다: YAML 파서가 닫히지 않은 두 따옴표를 거부합니다.
교훈은 자리 표시자를 피하라는 것이 아닙니다. 노출된 것은 무엇이든 변경되어 돌아올 수 있으며, 프런트 매터는 페이지의 일부로 모델에 전혀 보내지 말아야 한다는 것입니다. 이를 파싱하고, 독자에게 보이는 값(title, description)만 별도의 문자열로 보내고, 번역을 원래 구조에 다시 작성하세요. 이는 몇 줄의 코드로 전체 문제 범주를 제거합니다.
중국어, 일본어, 한국어의 굵은 텍스트와 범위
아홉 번의 실행에서는 나타나지 않았지만, 페이지를 조용히 깨뜨리기 때문에 확인할 가치가 있는 두 가지 문제가 더 있습니다. 둘 다 BookTranslator 자체 블로그에서 발생했습니다. 블로그는 MDX로 작성되어 27개 언어로 기계 번역되며, 한 예약 번역 실행에서 두 개의 한국어 기사가 구조 검사를 세 번 연속으로 통과하지 못했습니다. 원인은 아래 두 패턴이었습니다.
구두점으로 끝나는 굵은 글씨. CommonMark는 ** 양옆의 문자를 보고 굵은 글씨를 열거나 닫을 수 있는지 판단합니다. 구두점 바로 뒤에 오는 닫는 **는 뒤에 공백, 구두점, 또는 줄 끝이 와야 합니다. 이것이 오른쪽 측면 규칙입니다. 영어는 항상 그 자리에 공백이 있습니다. 중국어, 일본어, 한국어는 종종 그렇지 않습니다. 그래서 打开**设置 → 导出(测试版)**并选择는 별표가 그대로 보입니다. 굵은 글씨가 전각 괄호로 끝나고 바로 并가 뒤따르기 때문입니다. 괄호를 뺀 같은 줄은 굵게 렌더링되고, ** 뒤에 공백이 있는 줄도 마찬가지입니다. 굵은 링크도 같은 문제가 있습니다. 링크가 )로 끝나기 때문입니다. 点击**[导出](./export-options.md)**打开는 실패하고, 한국어 **[API](./api.md)**는도 실패합니다.
우리 모델은 모든 굵은 구문 양쪽에 공백을 넣었기 때문에 출력이 올바르게 렌더링되었습니다. 나중에 중국어 조판 관례를 따르려고 그 공백을 제거하는 편집자는 버그를 다시 불러옵니다. render-cases.md에서 일곱 가지 변형을 모두 시험해 볼 수 있습니다.
물결표를 사용한 숫자 범위. 중국어와 일본어 텍스트는 종종 범위를 3~5로 씁니다. remark 기반 GitHub Flavored Markdown 사이트는 기본적으로 단일 물결표를 취소선으로 바꿉니다. remark-gfm 옵션 singleTilde는 변경하지 않는 한 true입니다. 한 문단에 두 개의 범위가 있으면, 예를 들어 需要 3~5 分钟,生成 10~20 MB 的文件夹는 그 사이 텍스트에 취소선이 그려집니다. 우리 샘플은 en dash를 사용했고, 여덟 번의 실행은 en dash를 유지했지만 아홉 번째는 3 到 5로 썼습니다. 범위에는 en dash나 단어를 요청하거나 remark 설정에서 singleTilde: false로 지정하세요.
구조 검사기는 두 패턴을 모두 표시합니다. 실제로 렌더링되는 내용을 확인하려면 render_check.mjs가 원문과 번역문에서 굵은 글씨, 기울임꼴, 취소선, 남은 별표를 세고 MDX 컴파일을 시도합니다:
npm install @mdx-js/mdx@3 unified@11 remark-parse@11 remark-gfm@4
node render_check.mjs source.mdx translated.mdx
번역문에서 굵은 노드가 더 적거나 별표가 더 많으면 일부 강조가 닫히지 않았다는 뜻입니다.
견고한 워크플로
-
먼저 제목 ID를 고정하세요. 링크가 걸리는 모든 제목에 사이트 생성기가 지원하는 구문으로 명시적 ID를 추가하세요. 원문 언어에서 한 번만 하고, 번역 전에 커밋하세요.
-
프런트 매터를 페이지에서 분리하세요.
title과description처럼 독자가 보는 값만 별도 문자열로 번역하고, 원래 YAML에 다시 쓰세요. -
코드 정책을 정하세요. 주석을 번역할지 여부, 구성 예제의 표시 문자열을 번역할지 여부. 샘플의 Shelfmark처럼 변경하면 안 되는 제품 이름 옆에 번역 용어집에 적어 두세요.
-
본문을 번역하세요. Markdown을 파싱하는 도구를 사용하거나, 명시적 규칙과 함께 전체 페이지를 보내세요. 우리 테스트에서는 전체 페이지를 보냈을 때 구문이 그대로 유지되었습니다. 플레이스홀더를 사용한다면 완전한 구조를 보호하고 반쯤 노출된 것은 남기지 마세요.
-
구조 검사를 실행하세요.
python3 check_markdown_translation.py source.mdx translated.mdx --keep Shelfmark프런트 매터 키와 값, import, 코드 블록, 인라인 코드, 링크 대상, 참조 정의, 제목 수준, 페이지 내 링크 대상, JSX 태그와 속성, MDX 주석, 어드모니션 마커, 각주 레이블, 표 모양, 작업 목록 상자, 강제 줄바꿈, 닫히지 않는 강조, 물결표 범위, 유지하도록 요청한 용어를 비교합니다. 번역 품질은 판단하지 않습니다.
-
CJK 대상의 렌더링을
render_check.mjs로 확인하고, 구두점 뒤에 오는 닫는**뒤에 공백을 추가하세요. -
사이트를 빌드하고 링크를 클릭하세요. 각 언어로 페이지를 열고, 모든 페이지 내 링크를 클릭하고, 구성 예제를 읽으세요. 빌드 성공은 파일이 컴파일된다는 것만 증명합니다.
정확성이 중요한 문서라면 기술 번역 워크플로의 용어 및 검토 단계가 이 위에 적용됩니다.
BookTranslator가 적합한 곳
BookTranslator의 TXT 번역기는 .txt, .md, .mdx 파일을 받습니다. 제품 페이지에 따르면 단락, 줄바꿈, 특수 문자, 텍스트 구조를 유지하면서 번역하고, 큰 파일을 세그먼트로 나누어 문맥을 고려한 번역을 하므로, Markdown으로 작성된 매뉴얼이나 책 전체를 조각으로 붙여넣지 않고 업로드할 수 있습니다.
제품 페이지는 프런트 매터, 코드 블록, MDX 컴포넌트에 대한 특별 처리를 설명하지 않으며, 이 글을 위해 이 샘플을 BookTranslator로 실행하지도 않았습니다. 어떤 도구를 사용하든 번역 전에 1–3단계를, 번역 후에 5–7단계를 수행하세요. 고정 ID가 없는 제목은 여전히 링크를 잃고, 물결표로 쓴 범위는 여전히 취소선이 됩니다. 여러 언어로 책을 출판한다면 다국어 자가 출판 가이드가 나머지 작업을 다룹니다.
테스트 파일 받기
이 테스트의 모든 내용은 자유롭게 재사용할 수 있습니다:
- sample-source.mdx: 73줄 영어 샘플;
- check_markdown_translation.py: 구조 검사기 (Python 표준 라이브러리만 사용);
- render_check.mjs 및 render-cases.md: 렌더링 검사와 일곱 가지 CJK 테스트 사례;
- run_llm_markdown_test.py: OpenAI 호환 로컬 서버를 위한 세 가지 프롬프트 조건;
- outputs 폴더에 있는 아홉 개의 출력물과 check-results.txt, results.csv를 확인하세요.





