mkdocs-material에서 md 파일 글에 대해 한/영 병기를 하고 싶어. 안녕하세요. Hello. Everyone. 이런 식으로 말이야. 이 때 영어는 좀 연하고 작게 표기하고 싶은데, 이걸 md에 어떤 마크를 추가해서 자동으로 그렇게 렌더링 해줬으면 좋겠어. 안녕하세요. Hello. Everyone 이런 식으로 말이야. 이건 내가 생각한 예시니까 더 좋은 방안이 있으면 추천해도 좋아.

좋아요! “한국어 원문 + 연하고 작은 영어 번역”은 마크다운 속성 리스트(attr_list) 를 쓰는 방법이 가장 간단하고, 원하시면 <en> 같은 커스텀 태그로도 가능합니다. 두 가지를 모두 보여드릴게요.


방법 A: 가장 간단 — 문단에 클래스 붙이기 (attr_list)

1) mkdocs.yml 설정

theme:
  name: material

markdown_extensions:
  - attr_list          # ← 이 줄 추가

extra_css:
  - stylesheets/translation.css   # ← 커스텀 CSS 등록

2) docs/stylesheets/translation.css 작성

/* 영어 번역 공통 스타일 */
.md-typeset .en {
  font-size: 0.85em;   /* 조금 작게 */
  opacity: 0.65;       /* 연하게 */
}

/* 한국어 문단 바로 다음에 오는 영어 번역은 간격을 조금만 */
.md-typeset p + p.en {
  margin-top: 0.25em;
}

3) 마크다운에서 사용

안녕하세요.
Hello. Everyone. {.en}
  • Hello… {.en} 처럼 문장 끝에{.en} 을 붙이면, 렌더링 시 <p class="en">…</p>가 되어 위 CSS가 적용됩니다.

  • 문장 중간 인라인 으로 쓰고 싶으면:

    안녕하세요. <span class="en">Hello. Everyone.</span>
    

방법 B: 커스텀 태그 <en> 그대로 쓰기

원하신 표기대로 써도 됩니다. Python-Markdown은 기본적으로 인라인 HTML 통과 를 허용하므로 아래처럼 작성하고 CSS에서 <en>을 스타일링하세요.

1) 마크다운

안녕하세요.
<en>Hello. Everyone.</en>

2) translation.css

/* 알 수 없는 태그도 CSS에서 그대로 선택 가능 */
.md-typeset en {
  font-size: 0.85em;
  opacity: 0.65;
  display: block;           /* 줄바꿈 형태라면 block이 보기 좋음 */
  margin-top: 0.25em;
}

참고: HTML 표준상 “커스텀 요소”는 보통 이름에 하이픈이 들어갑니다(<en-text> 등). 굳이 표준을 따르려면 <en-text>로 쓰고 CSS도 en-text로 지정해도 됩니다. 다만 실제 동작은 <en>도 문제없습니다.


(선택) 접근성까지 고려한 버전

번역 텍스트에 lang="en"을 더해주면 스크린리더가 언어 전환을 인지합니다.

Hello. Everyone. { .en lang=en }

CSS는 그대로 .en에 걸려서 시각 스타일은 유지되고, 보조공학 친화성만 올라갑니다.


어떤 방법을 쓰면 좋을까?

  • 가장 권장: 방법 A (attr_list)

    • 마크다운 문법을 유지하면서도 깔끔하게 클래스만 부여 → 관리 쉬움
  • 원하신 표기를 그대로 쓰고 싶다면: 방법 B (<en>…</en>)

    • 취향/가독성 면에서 선호하면 사용, 다만 HTML 태그를 직접 쓰는 점만 유의

둘 중 하나만 채택하셔도 되고, 문단은{.en}, 인라인은 <span class="en">…</span>처럼 혼용해도 깔끔하게 관리됩니다.

필요하시면 목록/표/인용문 등에서의 미세 간격 조정용 CSS도 이어서 만들어 드릴게요.

안 되는데 띄어쓰기나 줄바꿈 같은걸 잘 해야하나?

가능해! 주로 띄어쓰기/줄바꿈설정 로딩 에서 자주 미끄러져요. 아래 순서대로 한 번만 점검해보죠. (복붙용 최소 예제도 맨 아래에 넣었어요.)


1) 설정이 진짜 켜졌는지 확인

  • mkdocs.yml정확히 다음이 있어야 해요.
    theme:
      name: material
        
    markdown_extensions:
      - attr_list        # ← 이게 있어야 {.en}이 먹습니다.
        
    extra_css:
      - stylesheets/translation.css
    
  • docs/stylesheets/translation.css 파일이 실제로 존재하는지 확인.

  • mkdocs serve를 돌리는 중에 extra_css를 추가했다면, 서버를 한 번 재시작 해 주세요. (가끔 새 CSS를 못 읽고 있습니다.)

2) 줄바꿈 규칙 이해 (여기서 많이 틀어집니다)

마크다운에서:

  • 빈 줄(공백 줄) = 새 문단(<p>…</p>)

  • 그냥 줄바꿈 하나 = 같은 문단 안에서 공백 하나(줄바꿈이 사라짐)

  • 줄 끝에 공백 두 칸 + 엔터 = 강제 줄바꿈(<br>)

원하는 형태가 “한국어 한 줄 + (조금 연하고 작은) 영어 한 줄”이라면,

  • 문단을 분리 하거나(빈 줄 1개),

  • 강제 줄바꿈 을 쓰거나(한국어 줄 끝 두 칸 + 엔터)
    둘 중 하나를 반드시 해 주세요.


3) 실제 작성 예제 3가지

A. 가장 간단: 문단 분리 + {.en}

안녕하세요.

Hello. Everyone. {.en}

포인트: 한국어와 영어 사이에 빈 줄 이 있어야 영어가 별도 문단 이 되고, {.en}이 그 문단에 적용됩니다.

B. 같은 문단 안에서 줄만 바꾸고 싶을 때 (간격 더 촘촘)

안녕하세요.··
<span class="en">Hello. Everyone.</span>

한국어 줄 끝의 ··(공백 두 칸)가 강제 줄바꿈 입니다.
<span>은 HTML이므로 줄바꿈을 위해 <br>를 써도 됩니다:

md

안녕하세요.<br>
<span class="en">Hello. Everyone.</span>

C. 커스텀 태그 <en> 그대로

안녕하세요.
<en>Hello. Everyone.</en>

인라인 HTML은 기본 허용이라 별도 확장 필요 없습니다. (블록 HTML로 감싸 복잡하게 쓰는 경우에만 md_in_html이 필요)


4) CSS (Material 테마 기준, 복붙)

docs/stylesheets/translation.css

/* 영어 번역 공통 스타일 */
.md-typeset .en,
.md-typeset en,
.md-typeset span.en {
  font-size: 0.85em;   /* 조금 작게 */
  opacity: 0.65;       /* 연하게 */
}

/* 문단으로 분리한 경우: 한국어 문단 바로 뒤 영어 문단의 위 여백 줄이기 */
.md-typeset p + p.en {
  margin-top: 0.25em;
}

/* 줄바꿈(브레이크) 뒤에 오는 인라인 번역을 '한 줄'처럼 보이게 */
.md-typeset span.en {
  display: inline-block;   /* <br> 다음에 내려오면 한 줄로 떨어져 보임 */
  margin-top: 0.25em;
}

/* 커스텀 태그 <en>을 블록처럼 쓰고 싶다면 */
.md-typeset en {
  display: block;
  margin-top: 0.25em;
}

5) 체크리스트(문제 계속되면 여기서 잡힙니다)

  1. mkdocs.ymlattr_list정확히 들어가 있다. (오탈자, 들여쓰기 주의)

  2. extra_css 경로가 실제 파일 위치와 일치 한다. (docs/stylesheets/translation.css)

  3. 서버 재시작 후 새 CSS가 로드된다.

  4. 마크다운에서 영어 줄을 별도 문단 으로 만들려면 빈 줄 1개 ,
    같은 문단 안 줄바꿈 이면 한국어 줄 끝에 공백 2칸 또는 <br>.

  5. 개발자도구에서 .en 규칙이 실제로 적용 되는지 확인한다. (선택자 앞에 .md-typeset를 붙이면 우선순위 문제를 대부분 이깁니다.)


6) 복붙용 “완전 최소 예제”

mkdocs.yml

site_name: demo
theme:
  name: material
markdown_extensions:
  - attr_list
extra_css:
  - stylesheets/translation.css

docs/stylesheets/translation.css → 위 CSS 그대로

docs/index.md

# 데모

안녕하세요.

Hello. Everyone. {.en}

---

안녕하세요.··
<span class="en">Hello. Everyone.</span>

---

안녕하세요.
<en>Hello. Everyone.</en>

이대로 하면 3가지 방식 모두 “연하고 작게” 나옵니다.
그래도 안 되면, 사용하신 mkdocs.yml과 한두 줄의 md 예제를 붙여주시면 바로 원인 pinpoint 해드릴게요!

다음 줄에다가 {: .en}을 쓰니까 되네 안녕하세요. (공백 두개) Hello. Everyone. {: .en} 이런 식으로 말이야. 근데 문제는 한글까지 같이 연하게 변해버려.

맞아요—그 증상은 {: .en}이 “이전 블록 전체”에 붙어서 그래요.
지금 쓰신 형태는 안녕하세요. 뒤의 공백 두 칸 때문에 한국어+영어가 같은 문단(<p>…</p>) 안에 들어갑니다. 그러면 {: .en}이 그 문단 전체 에 적용되어 한글까지 연해져요.

아래 셋 중 하나로 고치면 딱 해결됩니다.


✅ 방법 1: 문단을 분리 하고 영어 줄에만 클래스 붙이기 (가장 깔끔)

안녕하세요.

Hello. Everyone. {.en}
  • 포인트: 한국어와 영어 사이에 빈 줄 1개 → 서로 다른 문단이 되면서 .en은 영어 문단에만 적용.

✅ 방법 2: 앞줄에 속성 배치 해서 “다음 블록”에만 적용

안녕하세요.

{:.en lang=en}
Hello. Everyone.
  • {: .en}영어 문단 앞줄 에 두면, 바로 아래 블록(영어 문단) 에만 적용됩니다.

  • 접근성까지 챙기려면 lang=en도 같이 두세요.


✅ 방법 3: 같은 문단을 유지 하려면 <br> + 인라인 span

안녕하세요.<br>
<span class="en" lang="en">Hello. Everyone.</span>
  • 한국어 줄 끝 공백 두 칸 대신 <br>로 줄바꿈하고, 영어만 <span class="en">…</span>로 감싸면 됩니다.

왜 이런가요? (핵심 정리)

  • 공백 두 칸 + 엔터 = 같은 문단 안의 <br>

  • {: .en} (단독 줄) = 바로 앞 블록 에 붙음 → 같은 문단이면 한글+영어 모두 에 적용됨

  • 해결책은 블록을 나누거나(방법 1/2), 인라인만 감싸기(방법 3)입니다.

필요하면, 지금 쓰시는 실제 한두 줄을 붙여 주시면 그 형태에 맞춰 가장 간단한 문법으로 딱 맞춰 드릴게요!

아니 왜 자꾸 {.en}이라고 하는건데, 이거 동작하는거야?

맞아요, 헷갈릴 만해요. 정리하면:

  1. {.en} = {:.en}
  • 둘 다 attr_list 확장 문법입니다. (콜론은 선택)

  • 동작 조건: mkdocs.ymlmarkdown_extensions: - attr_list가 있어야 합니다.

  1. 붙는 위치 규칙(핵심)
  • {: .en} 같은 속성 줄은 “바로 앞 블록”에 붙습니다.

  • 한글 줄 끝에 공백 두 개를 넣으면 한글+영어가 한 문단(같은 블록) 이라서, {: .en}이 둘 다에 적용돼요 → 그래서 한글까지 연해진 것.

  1. 원하는 효과(영어만 연하게) 만드는 법 – 3가지 중 하나 선택
  • 문단 분리 + 영어에만 클래스 (가장 간단)
    안녕하세요.
        
    Hello. Everyone. {.en}
    

또는 ```md 안녕하세요.

Hello. Everyone.
{:.en}
```

빈 줄 하나로 영어를 별도 문단 으로 만들면 .en이 영어 문단에만 적용됩니다.

  • 같은 문단을 유지해야 한다면 → 줄바꿈은 <br>로, 영어만 감싸기
    안녕하세요.<br>
    <span class="en" lang="en">Hello. Everyone.</span>
    
  • 커스텀 태그 선호 시
    안녕하세요.
    <en lang="en">Hello. Everyone.</en>
    
  1. CSS 예시(Material 테마 기준)
/* 영어 번역을 작고 연하게 */
.md-typeset .en,
.md-typeset en {
  font-size: 0.85em;
  opacity: 0.65;
}

/* 한국어 문단 바로 다음 영어 문단 간격 다듬기 */
.md-typeset p + p.en { margin-top: 0.25em; }

/* <en>을 줄바꿈 형태로 쓰려면 */
.md-typeset en { display: block; margin-top: 0.25em; }
  1. 빠른 체크리스트
  • mkdocs.yml- attr_list가 있음?

  • CSS가 extra_css로 로드됨?

  • 한글 줄 끝 공백 두 개 로 줄바꿈을 만들고 있지는 않음? (그럼 같은 문단이 됩니다)

  • 영어를 별도 문단 으로 만들었는지, 아니면 <span class="en">…</span>인라인만 감쌌는지

요약: {.en}/{:.en}은 제대로 동작합니다. 다만 “속성은 바로 앞 블록 전체 에 붙는다”는 규칙 때문에, 영어만 스타일링하려면 영어를 별도 블록으로 분리 하거나 영어만 인라인으로 감싸야 합니다.

아니 그냥 Hello. Everyone. {.en} 이렇게 최종 렌더링 되는데

아하, 그럼 상황이 확실해요.
Hello. Everyone. {.en}가 그대로 글자로 보이는 건 MkDocs(=Python-Markdown)에서는 문단(paragraph) 끝에{.en}을 붙이는 문법이 동작하지 않기 때문입니다. 그건 주로 Kramdown(Jekyll) 쪽 문법이고, MkDocs에서는 단독 줄의 속성 블록 을 써야 해요.

아래처럼 고치면 됩니다—차근차근:

1) 결론부터: 이렇게 쓰세요

안녕하세요.

{:.en lang=en}
Hello. Everyone.
  • 포인트 1: 한국어와 영어 사이에 빈 줄 1개 → 서로 다른 문단 이 됩니다.

  • 포인트 2: {: .en}“바로 아래 블록(영어 문단)” 에만 붙게 앞줄에 둡니다.

  • 포인트 3: 접근성용으로 lang=en도 같이 달아주면 좋아요.

2) 왜 Hello. Everyone. {.en}가 안 먹나?

  • MkDocs의 attr_list 는 “단독 줄 의 속성 블록”이나 “특정 요소 바로 뒤(예: 이미지, 링크, 헤더 등)”에서 주로 작동합니다.

  • 평범한 문단 텍스트 뒤에 같은 줄로 {.en}을 붙이는 방식은 지원하지 않아서 그대로 렌더링돼요.

3) 공백 두 칸(+엔터) 쓰면 왜 한글까지 흐려지나?

  • 줄 끝 공백 두 칸같은 문단 안 <br>만 만들어요.

  • 그래서 “안녕하세요 + <br> + Hello …”가 한 문단 이 되고,
    뒤줄의 {: .en}을 쓰면 그 한 문단 전체 에 클래스가 붙어 한글까지 연해집니다.

  • 해결: 공백 두 칸을 지우고 빈 줄문단 분리 하거나, 같은 문단을 유지하고 싶다면 영어만 인라인으로 감싸기 👇

안녕하세요.<br>
<span class="en" lang="en">Hello. Everyone.</span>

4) CSS(그대로 쓰셔도 됩니다)

/* 영어 번역: 조금 작고 연하게 */
.md-typeset .en,
.md-typeset en {
  font-size: 0.85em;
  opacity: 0.65;
}

/* <en> 커스텀 태그를 블록처럼 쓸 때 */
.md-typeset en {
  display: block;
  margin-top: 0.25em;
}

/* 문단 분리 케이스: 한글 문단 바로 뒤 영어 문단의 간격 */
.md-typeset p + p.en { margin-top: 0.25em; }

5) 빠른 체크

  1. mkdocs.ymlmarkdown_extensions: - attr_list 들어가 있나요?

  2. 한국어 줄 끝 공백 두 칸 제거하고 빈 줄 로 문단 분리했나요?

  3. 영어 문단 앞줄{: .en}을 뒀나요? (뒤줄×, 같은 줄×)

원하시면 지금 문제 나는 실제 3~4줄 붙여 주시면, 그 형태에 딱 맞춰 “한 줄 수정” 버전으로 바로 고쳐드릴게요!

markdown 패키지만 가지고 한 번 테스트 해보고 싶어. 쥬피터 노트북 예시 만들어줘

좋아! Python-Markdown만 으로 테스트해볼 수 있는 쥬피터 노트북을 만들어 놨어.
여기서 내려받아 로컬에서 실행해 보세요:

Download the Jupyter Notebook

노트북에 들어있는 것 (step-by-step)

  1. !pip install markdown — Python-Markdown 설치

  2. attr_list 확장 켠 렌더 함수 + CSS

  3. 케이스별 미리보기:

    • A : Hello. Everyone. {.en}동작 안 하는 예시를 눈으로 확인

    • B : {: .en} 줄을 에 두면 다음 블록 에 적용

    • C : {: .en} 줄을 에 두면 이전 블록 에 적용

    • D : 빈 줄로 문단 분리 후 영어 문단에만 {:.en} 적용

    • E : 같은 문단 유지 → <br> + <span class="en">…</span>

    • F : <en>…</en> 커스텀 태그

사용 팁 (핵심만)

  • 문단 텍스트 같은 줄 끝의{.en} 는 보통 문단에는 적용되지 않음 → A 케이스로 확인 가능

  • 문단에 클래스 주려면 속성만 있는 줄({:.en})을 앞 또는 뒤 에 둬서 그 블록에 붙이기(B/C)

  • 한국어/영어를 다른 문단 으로 분리하면(D) 제어가 가장 깔끔

  • 한 문단 내 줄바꿈이면(E) 영어만 <span class="en">로 감싸기

노트북 돌려보시고, 혹시 특정 케이스가 기대와 다르게 나오면 그 셀 출력 캡처나 원문을 붙여줘—원인 딱 집어서 고쳐드릴게!

보기 힘들어 그냥 본문에 써줘

오케이—노트북 파일 말고, 그대로 복붙해서 돌릴 수 있는 셀 을 본문에 씁니다.
아래 두 셀만 Jupyter에 순서대로 붙여넣고 실행하세요. (첫 셀은 설치, 두 번째 셀은 케이스 A~F를 한 번에 렌더/미리보기)


셀 1 — 설치

!pip -q install markdown

셀 2 — 렌더 도우미 + 케이스 A~F 미리보기

from IPython.display import HTML, display
import markdown

CSS = '''
.md-typeset .en,
.md-typeset en {
  font-size: 0.85em;
  opacity: 0.65;
}
.md-typeset en {
  display: block;
  margin-top: 0.25em;
}
.md-typeset p + p.en {  /* 한국어 문단 바로 다음 영어 문단 간격 */
  margin-top: 0.25em;
}
/* 보기 편하도록 테두리 */
.demo {
  border: 1px solid #ddd;
  padding: 12px;
  margin: 8px 0 24px 0;
  border-radius: 6px;
  background: #fafafa;
}
'''

def render_md(md_text, title=""):
    html = markdown.markdown(
        md_text,
        extensions=[
            "attr_list",   # 핵심 확장
            # "extra",     # 원하면 추가(여기에도 attr_list 포함됨)
            # "md_in_html" # 복잡한 HTML 블록 내 마크다운이 필요할 때
        ]
    )
    wrapped = f"""
    <style>{CSS}</style>
    <div class="demo">
      <div><strong>Markdown 원문</strong></div>
      <pre style="white-space:pre-wrap">{md_text}</pre>
      <div style="margin-top:8px"><strong>렌더 결과</strong></div>
      <div class="md-typeset">{html}</div>
    </div>
    """
    display(HTML(f"<h3>{title}</h3>" + wrapped))

# ───────── 테스트 케이스 ─────────

# A) 같은 줄 끝에 {.en} → 일반 문단에서는 보통 "동작 안 함" (그대로 텍스트로 보임)
case_A = """Hello. Everyone. {.en}"""

# B) 속성 줄을 '앞'에 두면 → "다음 블록"에 적용
case_B = """{:.en lang=en}
Hello. Everyone."""

# C) 속성 줄을 '뒤'에 두면 → "이전 블록"에 적용
case_C = """Hello. Everyone.
{:.en lang=en}"""

# D) 문단 분리(빈 줄) 후 영어 문단에만 적용
case_D = """안녕하세요.

Hello. Everyone.
{:.en}"""

# E) 같은 문단 유지: 줄바꿈은 <br>, 영어만 인라인으로 감싸기
case_E = """안녕하세요.<br>
<span class="en" lang="en">Hello. Everyone.</span>"""

# F) 커스텀 태그 <en>
case_F = """안녕하세요.
<en lang="en">Hello. Everyone.</en>"""

# 렌더
render_md(case_A, "A) same-line `{.en}` (문단에는 보통 적용 안 됨)")
render_md(case_B, "B) attr 줄을 앞에 → 다음 블록에 적용")
render_md(case_C, "C) attr 줄을 뒤에 → 이전 블록에 적용")
render_md(case_D, "D) 문단 분리 + 영어 문단만 스타일")
render_md(case_E, "E) 같은 문단 유지: <br> + <span class='en'>")
render_md(case_F, "F) 커스텀 태그 <en>")

무엇이 보이면 정상인가 (체크 포인트)

  • A : Hello. Everyone. {.en}그대로 글자 로 보임 → 일반 문단에서는 같은 줄 끝 {.en}안 먹는다 는 증거.

  • B : 영어 줄이 작고 연하게 보임 → {:.en} (속성만 있는 줄)이 아래 블록(다음 문단) 에 적용.

  • C : 영어 줄이 작고 연하게 보임 → {:.en}위 블록(이전 문단) 에 적용.

  • D : 한국어/영어가 서로 다른 문단 이며, 영어만 작고 연함.

  • E : 한 문단으로 보이되, <br> 다음 줄의 영어만 작고 연함.

  • F : <en>…</en>작고 연함. (커스텀 태그를 CSS에서 선택자로 잡음)

결론: 문단 텍스트 같은 줄 끝의{.en}은 기대대로 동작하지 않습니다.
문단에 클래스를 주려면 속성만 있는 줄({:.en})앞/뒤 에 두거나,
같은 문단을 유지하려면 <br> + <span class="en">…</span> 방식이 가장 확실합니다.

원하시면 이 케이스 중 딱 맞는 패턴을 골라 MkDocs에 그대로 이식하는 예시도 바로 적어드릴게요.

아무리 해도 그냥 앞 뒤 줄 띄워야함

맞아요. 결론 = 블록 속성 줄({: .en})은 “독립 블록”이어야 해서 앞뒤로 빈 줄을 둬서 ‘완전히 분리’하면 가장 안전합니다. 특히 Python-Markdown/MkDocs 조합에서는 이 규칙이 까다로워서, 실전에서는 “앞·뒤 줄 띄우기”가 사실상 정석이에요.

아래 3가지 항상 잘 먹는 패턴 만 써주세요.


1) “이전 문단(영어)”에 적용 — 가장 추천

안녕하세요.

Hello. Everyone.

{: .en lang=en}
  • 포인트: {: .en} 위·아래 모두 빈 줄 → 속성 줄이 완전히 독립된 단락이 되어 바로 위 문단(영어) 에만 붙습니다.

2) “다음 문단(영어)”에 적용

안녕하세요.

{: .en lang=en}

Hello. Everyone.
  • 포인트: 마찬가지로 속성 줄 앞·뒤 빈 줄바로 아래 문단(영어) 에만 붙습니다.

3) 같은 문단을 유지해야 할 때(줄바꿈만) — 인라인 방식

안녕하세요.<br>
<span class="en" lang="en">Hello. Everyone.</span>
  • 블록 속성 규칙을 건드리지 않아서 환경 차이에 덜 민감합니다.

CSS (Material 기준, 공통)

.md-typeset .en, .md-typeset en {
  font-size: 0.85em;   /* 작게 */
  opacity: 0.65;       /* 연하게 */
}
.md-typeset en {        /* <en>을 블록처럼 쓰고 싶을 때 */
  display: block;
  margin-top: 0.25em;
}
.md-typeset p + p.en {  /* 한글 문단 바로 뒤 영어 문단 간격 다듬기 */
  margin-top: 0.25em;
}

왜 이렇게 해야 하나? (짧게)

  • 속성 줄은 그 자체가 하나의 “단락(block)”이어야 인식됩니다.

  • 빈 줄이 없으면 파서가 “같은 문단의 일반 텍스트”로 오해해서 {:.en}그대로 글자 로 보이거나, 한 문단 전체(한글+영어)에 적용되는 일이 생깁니다.

  • 그래서 속성 줄을 위/아래 빈 줄로 고립 시키는 습관을 들이면 어디서든 안정적으로 동작합니다.

원하시면 지금 작성하신 실제 몇 줄을 보내 주시면, 위 패턴으로 최소 수정 버전을 바로 잡아드릴게요.

pymdownx의 caret 처럼 간단히 가만의 을 추가할 수 없을까

좋아, caret(예:^^text^^)처럼 간단한 기호로
<span class="en">…</span>을 자동으로 넣고 싶다면 작은 커스텀 Markdown 확장 을 하나 추가하면 딱 해결돼요.
아래대로 하면 %%영문%%처럼 쓰기만 해도 <span class="en" lang="en">영문</span>으로 렌더됩니다.


1) 파이썬 확장 파일 만들기 (5~10줄짜리 핵심)

프로젝트 루트(보통 mkdocs.yml 옆)에 en_span.py 파일을 만들고 아래를 복붙하세요.

# en_span.py
import re
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
from xml.etree import ElementTree as etree

class EnSpanInline(InlineProcessor):
    def __init__(self, pattern, md, klass="en", lang="en"):
        super().__init__(pattern, md)
        self.klass = klass
        self.lang = lang

    def handleMatch(self, m, data):
        text = m.group(1).replace(r'\%%', '%%')  # \%% → %
        el = etree.Element("span")
        el.set("class", self.klass)
        if self.lang:
            el.set("lang", self.lang)
        el.text = text
        return el, m.start(0), m.end(0)

class EnSpanExtension(Extension):
    def __init__(self, **kwargs):
        self.config = {
            "delim": ["%%", "Inline delimiter, e.g. %%, ^^, !!"],
            "class": ["en", "CSS class name"],
            "lang":  ["en", "lang attribute ('' to disable)"],
        }
        super().__init__(**kwargs)

    def extendMarkdown(self, md):
        delim = self.getConfig("delim")
        pattern = rf"(?<!\\){re.escape(delim)}(.+?)(?<!\\){re.escape(delim)}"
        proc = EnSpanInline(pattern, md,
                            klass=self.getConfig("class"),
                            lang=self.getConfig("lang"))
        # 175: em/strong(EMPHASIS)보다 살짝 앞서도록 등록
        md.inlinePatterns.register(proc, "en-span", 175)

def makeExtension(**kwargs):
    return EnSpanExtension(**kwargs)
  • 기본 구분자는 %%…%% 입니다.

  • 텍스트 안에서 %%문자 그대로 쓰고 싶으면 \%%로 이스케이프하세요.

  • 이미 pymdownx.caret(^^)을 쓰고 있다면, 충돌 없이 %%만 쓰면 됩니다. (원하면 delim: "^^"로 바꿀 수 있어요)


2) mkdocs.yml에 확장 등록

theme:
  name: material

markdown_extensions:
  - attr_list
  - 'en_span:EnSpanExtension'   # 또는 그냥 - en_span  (makeExtension가 있으므로)
  # 옵션을 바꾸고 싶다면:
  # - en_span:
  #     delim: "%%"   # 바꾸려면 "^^" 등
  #     class: "en"
  #     lang: "en"

extra_css:
  - stylesheets/translation.css

주의: MkDocs가 이 모듈을 import할 수 있어야 해요.
가장 간단히는 MkDocs 실행할 때 프로젝트 루트를 파이썬 경로에 추가하세요.

  • macOS/Linux: PYTHONPATH=. mkdocs serve

  • Windows(PowerShell): $env:PYTHONPATH="."; mkdocs serve

(혹은 en_span.py를 패키지로 설치하거나, venv의 site-packages에 넣어도 됩니다.)


3) CSS (Material 기준)

docs/stylesheets/translation.css:

.md-typeset .en {
  font-size: 0.85em;   /* 조금 작게 */
  opacity: 0.65;       /* 연하게 */
}

원하면 줄 간격 조정, 색상 등도 여기서 커스텀하면 됩니다.


4) 사용법

그냥 본문에 이렇게 쓰면 끝:

안녕하세요. %%Hello. Everyone.%%

렌더링 결과(핵심만):

<p>안녕하세요. <span class="en" lang="en">Hello. Everyone.</span></p>
  • 블록으로 쓰고 싶으면 기존대로 {:.en}을 쓰거나, <en>…</en>를 쓰세요(이미 해두신 CSS로 스타일 적용 가능).

  • 같은 문단에서 줄을 바꾸고 싶다면 여전히 <br>을 쓰는 게 가장 확실합니다:

    안녕하세요.<br>%%Hello. Everyone.%%
    

5) 대안(플러그인 없이 가능한 꼼수)

플러그인을 전혀 쓰기 싫다면, 아래 꼼수도 가능은 합니다.

*Hello. Everyone.*{.en}

CSS로 기울임을 제거:

.md-typeset em.en { font-style: normal; }

이렇게 하면 <em class="en">…</em>이 되는데, 시맨틱이 살짝 어긋나므로 권장도는 낮고 ,
가능하면 위의 커스텀 확장(%%…%%) 방식을 추천합니다.


필요하면 구분자^^!! 등으로 바꾼 버전도 바로 만들어 드릴게요. (옵션 한 줄만 바꾸면 됩니다)

No module names 'en_span' 파일을 여기 넣는거 맞아?

맞아요, 그 에러는 MkDocs가en_span 모듈을 import 못 해서 나는 거예요.
핵심은 파일 위치 + 실행 위치 + 파이썬 경로(PYTHONPATH) 입니다. 아래대로 차근차근 해보세요.


0) 올바른 폴더 구조 (가장 단순한 형태)

pgsql

your-project/
├─ mkdocs.yml
├─ en_span.py          ← 여기!
└─ docs/
   ├─ index.md
   └─ stylesheets/
      └─ translation.css

중요: en_span.pydocs/ 안이 아니라 mkdocs.yml과 같은 디렉터리(프로젝트 루트) 에 두세요.


1) mkdocs.yml 설정

가장 기본:

markdown_extensions:
  - en_span

옵션을 쓰고 싶다면:

markdown_extensions:
  - en_span:
      delim: "%%"
      class: "en"
      lang: "en"

또는 클래스 경로로도 가능:

markdown_extensions:
  - 'en_span:EnSpanExtension'

2) 올바른 실행 위치에서 서버 시작

반드시 프로젝트 루트(=mkdocs.yml이 있는 위치) 로 이동해서 실행하세요.

  • macOS / Linux
    cd your-project
    PYTHONPATH=. mkdocs serve
    
  • Windows PowerShell
    cd your-project
    $env:PYTHONPATH="."; mkdocs serve
    
  • Windows CMD
    bat
        
    cd your-project
    set PYTHONPATH=.
    mkdocs serve
    

왜? MkDocs가 확장 모듈을 현재 파이썬 경로 에서 찾는데, 환경/실행 방법에 따라 현재 디렉터리가 경로에 안 잡힐 수 있습니다. PYTHONPATH=. 로 프로젝트 루트를 확실히 넣어주면 import 에러가 사라집니다.


3) 빠른 자가 점검 (문제 계속되면 여기서 잡힙니다)

  1. 파일명 이 정확히 en_span.py 인가요? (en-span.py ×)

  2. en_span.py 안에 makeExtension(**kwargs) 함수가 존재하나요? (필수)

  3. mkdocs serve프로젝트 루트 에서 실행했나요? (다른 폴더에서 실행 ×)

  4. mkdocs.ymlmarkdown_extensions: - en_span 이 들어갔나요? (오탈자 ×)

  5. 그래도 안 되면, 완전한 모듈 경로 로 시도: ```yaml markdown_extensions:
    • 'en_span:EnSpanExtension' ```
  6. 여전히 안 되면, 패키지 폴더로 감싸기 :
    your-project/
    ├─ mkdocs.yml
    ├─ local_ext/
    │  ├─ __init__.py
    │  └─ en_span.py
    └─ docs/...
    

그리고 mkdocs.yml: yaml markdown_extensions: - local_ext.en_span

(이때도 PYTHONPATH=. 권장)


4) 동작 확인용 최소 예제

docs/index.md

안녕하세요. %%Hello. Everyone.%%

en_span.py (핵심 부분만 재확인)

import re
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
from xml.etree import ElementTree as etree

class EnSpanInline(InlineProcessor):
    def __init__(self, pattern, md, klass="en", lang="en"):
        super().__init__(pattern, md)
        self.klass = klass
        self.lang = lang
    def handleMatch(self, m, data):
        text = m.group(1).replace(r'\%%', '%%')
        el = etree.Element("span")
        el.set("class", self.klass)
        if self.lang:
            el.set("lang", self.lang)
        el.text = text
        return el, m.start(0), m.end(0)

class EnSpanExtension(Extension):
    def __init__(self, **kwargs):
        self.config = {
            "delim": ["%%", "Inline delimiter"],
            "class": ["en", "CSS class"],
            "lang":  ["en", "lang attribute"],
        }
        super().__init__(**kwargs)
    def extendMarkdown(self, md):
        delim = self.getConfig("delim")
        pattern = rf"(?<!\\){re.escape(delim)}(.+?)(?<!\\){re.escape(delim)}"
        md.inlinePatterns.register(
            EnSpanInline(pattern, md,
                         klass=self.getConfig("class"),
                         lang=self.getConfig("lang")),
            "en-span", 175
        )

def makeExtension(**kwargs):
    return EnSpanExtension(**kwargs)

5) 그래도 같은 에러면?

  • 콘솔에서 한 번 직접 import 해보세요:

    • macOS/Linux:
      cd your-project
      PYTHONPATH=. python -c "import en_span; print(en_span)"
      
    • Windows PowerShell:
      cd your-project
      $env:PYTHONPATH="."; python -c "import en_span; print(en_span)"
      

이게 실패하면 경로 문제 가 확실합니다. 위 2)번(실행 위치)과 3)번(패키지 폴더) 방법으로 정리하면 해결됩니다.


원하시면 지금 프로젝트 트리(대충 tree 결과나 스크린샷)와 mkdocs.yml의 해당 부분을 보내 주세요. 그대로 맞춰서 한 줄도 헷갈리지 않게 수정본 만들어 드릴게요.

그냥 패키지 설치를 해버려야겠어. markdown_en_span 이라는 패키지로 만들어주고 src 폴더와 pyproject.toml을 여기에 포함시켜서 설치할 수 있게 해줘

좋아—로컬에서 바로 설치 가능한 패키지 로 만들어 드릴게요.
패키지명은 요청하신 대로 markdown_en_span, src 레이아웃 + pyproject.toml 포함입니다.


1) 디렉터리 구조 만들기

your-project/
├─ pyproject.toml
└─ src/
   └─ markdown_en_span/
      └─ __init__.py

2) pyproject.toml

[build-system]
requires = ["hatchling>=1.18"]
build-backend = "hatchling.build"

[project]
name = "markdown_en_span"
version = "0.1.0"
description = "Python-Markdown inline extension to wrap %%text%% as <span class='en' lang='en'>text</span>."
readme = "README.md"
requires-python = ">=3.8"
authors = [{name = "Your Name"}]
dependencies = ["Markdown>=3.3"]

[project.urls]
Homepage = "https://example.com/markdown_en_span"

# Python-Markdown이 이 확장을 'en_span' 이름으로 인식하게 하는 엔트리포인트
[project.entry-points."markdown.extensions"]
en_span = "markdown_en_span:makeExtension"

포인트

  • 엔트리포인트 덕분에 mkdocs.yml에서 markdown_extensions: - en_span처럼 짧은 이름 으로 쓸 수 있어요.

  • 배포명(name)과 모듈명(폴더)은 둘 다 markdown_en_span로 통일.


3) src/markdown_en_span/__init__.py

import re
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
from xml.etree import ElementTree as etree

__all__ = ["EnSpanExtension", "makeExtension"]
__version__ = "0.1.0"

class EnSpanInline(InlineProcessor):
    """
    Wraps %%text%% (기본) → <span class="en" lang="en">text</span>
    - 구분자는 옵션으로 변경 가능 (예: ^^ 또는 !!)
    - \%% 처럼 백슬래시로 이스케이프하면 리터럴 %% 출력
    """
    def __init__(self, delim: str, md, css_class: str = "en", lang: str = "en"):
        # DOTALL 활성화: 필요 시 여러 줄도 매칭
        pattern = rf"(?s)(?<!\\){re.escape(delim)}(.+?)(?<!\\){re.escape(delim)}"
        super().__init__(pattern, md)
        self.delim = delim
        self.css_class = css_class
        self.lang = lang

    def handleMatch(self, m, data):
        # 본문 텍스트
        text = m.group(1)
        # \%% → %% (사용자가 리터럴 구분자를 넣고 싶을 때)
        text = text.replace("\\" + self.delim, self.delim)

        el = etree.Element("span")
        el.set("class", self.css_class)
        if self.lang:
            el.set("lang", self.lang)
        el.text = text
        return el, m.start(0), m.end(0)

class EnSpanExtension(Extension):
    """
    확장 옵션:
      - delim: 기본 "%%"
      - class: 기본 "en"
      - lang:  기본 "en" (빈 문자열이면 lang 속성 생략)
    """
    def __init__(self, **kwargs):
        self.config = {
            "delim": ["%%", "Inline delimiter, e.g. %%, ^^, !!"],
            "class": ["en", "CSS class name to set on <span>"],
            "lang":  ["en", "lang attribute value ('' to disable)"],
        }
        super().__init__(**kwargs)

    def extendMarkdown(self, md):
        delim = self.getConfig("delim")
        css_class = self.getConfig("class")
        lang = self.getConfig("lang")
        proc = EnSpanInline(delim=delim, md=md, css_class=css_class, lang=lang)
        # emphasis(EMPHASIS=170)보다 약간 앞서도록 등록
        md.inlinePatterns.register(proc, "en-span", 175)

def makeExtension(**kwargs):
    return EnSpanExtension(**kwargs)

4) 설치 (로컬)

# 프로젝트 루트(= pyproject.toml 있는 곳)에서
pip install -e .
# 또는
pip install .

-e는 개발 중 수정사항이 바로 반영되는 editable 설치.


5) MkDocs 사용 예시

mkdocs.yml

theme:
  name: material

markdown_extensions:
  - en_span            # ← 방금 만든 확장 (엔트리포인트로 인식)
  # - attr_list        # (선택) 다른 용도 필요 시
  # - en_span:
  #     delim: "^^"    # 구분자 바꾸고 싶을 때
  #     class: "en"
  #     lang: "en"

extra_css:
  - stylesheets/translation.css

docs/stylesheets/translation.css

.md-typeset .en {
  font-size: 0.85em;
  opacity: 0.65;
}

docs/index.md

안녕하세요. %%Hello. Everyone.%%

→ 렌더링:

<p>안녕하세요. <span class="en" lang="en">Hello. Everyone.</span></p>

6) 팁 & 트러블슈팅

  • 구분자 바꾸기 : mkdocs.yml에 ```yaml markdown_extensions:
    • en_span: delim: "^^" class: "en" lang: "en" ```

안녕하세요. ^^Hello. Everyone.^^

  • 리터럴로%% 출력: \%% 처럼 이스케이프
    예) 가격은 \%%20 할인 → 실제 문서에 %%20 출력

  • 확장 로딩 확인 (MkDocs 외에 순수 파이썬에서)
    import markdown
    html = markdown.markdown("안녕 %%Hello%%", extensions=["en_span"])
    print(html)
    
  • 여전히 인식 안 되면

    • 설치 경로 확인: pip show markdown_en_span

    • 가상환경 재시작 / MkDocs 재시작

    • Markdown 버전 확인: python -c "import markdown, sys; print(markdown.__version__)" (3.3 이상 권장)


필요하면 README.md/LICENSE 골격도 추가해 드릴게요. 원하시는 구분자(예: ||), 기본 클래스명 변경 등 커스터마이즈 버전도 바로 만들어 줄 수 있어요.

mkdocs 루트 폴더 밑에 너가 만들어준 패키지를 위치 시킬거야. requirements.txt에 상대경로로 인스톨 추가할 수 있어?

가능해! requirements.txt 에서 상대경로 로 로컬 패키지를 설치할 수 있어요.
요청하신 구조(패키지를 MkDocs 루트 바로 아래)에 맞춰 한 번에 되는 절차를 step-by-step으로 정리할게요.


0) 폴더 구조(예시)

your-project/
├─ mkdocs.yml
├─ requirements.txt
├─ markdown_en_span/           # ← 여기 폴더가 “패키지 루트”
│  ├─ pyproject.toml
│  └─ src/
│     └─ markdown_en_span/
│        └─ __init__.py
└─ docs/
   └─ stylesheets/translation.css

중요: pyproject.toml이 들어있는 디렉터리(여기서는 ./markdown_en_span)가 pip가 인식하는 “패키지 루트”예요.


1) requirements.txt 작성

(A) 개발 중 수정사항을 바로 반영하고 싶을 때 (editable)

# MkDocs(원하면 버전 고정)
mkdocs>=1.6
mkdocs-material>=9.5

# 로컬 확장 패키지(상대경로, editable 모드)
-e ./markdown_en_span

(B) 고정 설치(수정 즉시 반영 필요 없을 때)

mkdocs>=1.6
mkdocs-material>=9.5

./markdown_en_span

두 경우 모두 상대경로는 requirements.txt 파일이 있는 위치 기준 으로 해석됩니다.


2) 설치

프로젝트 루트에서:

python -m pip install -U pip          # 권장: 최신 pip (PEP 660 editable 지원)
pip install -r requirements.txt

editable(-e) 설치가 제대로 되려면 pip가 PEP 660 을 지원해야 합니다(대부분 최신 pip OK).


3) mkdocs.yml 설정

엔트리포인트를 등록해둔 상태라면(제가 준 pyproject.toml 기준) 아래처럼 간단히 사용 가능:

theme:
  name: material

markdown_extensions:
  - en_span               # ← 방금 설치한 확장
  # - en_span:
  #     delim: "^^"       # 구분자 바꾸고 싶으면 옵션으로
  #     class: "en"
  #     lang: "en"

extra_css:
  - stylesheets/translation.css

4) 사용 예시

docs/index.md

안녕하세요. %%Hello. Everyone.%%

docs/stylesheets/translation.css

.md-typeset .en {
  font-size: 0.85em;
  opacity: 0.65;
}

5) 자주 생기는 이슈 & 체크리스트

  • 설치가 안 된다면

    • pip --version으로 최신인지 확인(가능하면 21.3+).

    • 프로젝트 루트에서 pip install -r requirements.txt 실행했는지 확인.

    • markdown_en_span/pyproject.toml 파일이 실제로 존재하는지 확인.

  • MkDocs가 확장을 못 찾는다면

    • 설치 성공 여부: pip show markdown_en_span

    • mkdocs.ymlmarkdown_extensions:en_span이 들어있는지 확인.

    • 서버 재시작: mkdocs serve 다시 실행.


원하시면 제가 제공했던 pyproject.toml/__init__.py 샘플을 그대로 붙여드릴게요(파일 내용 다시 필요하면 말해줘!).

Categories:

Updated: