<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Multilingual on ICE-ICE-BEAR-BLOG</title><link>https://ice-ice-bear.github.io/ko/tags/multilingual/</link><description>Recent content in Multilingual on ICE-ICE-BEAR-BLOG</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Fri, 10 Apr 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://ice-ice-bear.github.io/ko/tags/multilingual/index.xml" rel="self" type="application/rss+xml"/><item><title>이중 언어 Hugo 블로그 구축 — 한국어-영어 자동 퍼블리싱 파이프라인</title><link>https://ice-ice-bear.github.io/ko/posts/2026-04-10-bilingual-hugo/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0900</pubDate><guid>https://ice-ice-bear.github.io/ko/posts/2026-04-10-bilingual-hugo/</guid><description>&lt;img src="https://ice-ice-bear.github.io/" alt="Featured image of post 이중 언어 Hugo 블로그 구축 — 한국어-영어 자동 퍼블리싱 파이프라인" /&gt;&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;하나의 언어로만 존재하는 블로그는 독자의 절반을 놓친다. 오늘 Hugo의 이중 언어 퍼블리싱 파이프라인을 구축했다. 포스트를 언어별 디렉토리로 라우팅하고, 현지화된 제목이 포함된 커버 이미지를 각 언어별로 생성하며, 번역 쌍을 자동으로 연결한다 — CLI의 &lt;code&gt;--language&lt;/code&gt; 플래그 하나로.&lt;/p&gt;
&lt;h2 id="hugo의-두-가지-번역-방식"&gt;Hugo의 두 가지 번역 방식
&lt;/h2&gt;&lt;p&gt;Hugo는 다국어 콘텐츠를 두 가지 방식으로 지원한다:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;파일명 기반&lt;/strong&gt;: 같은 디렉토리에 &lt;code&gt;about.en.md&lt;/code&gt; / &lt;code&gt;about.ko.md&lt;/code&gt;. 소규모 사이트에는 간단하지만, 파일이 많아지면 복잡해진다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;콘텐츠 디렉토리 기반&lt;/strong&gt;: &lt;code&gt;content/en/posts/&lt;/code&gt; / &lt;code&gt;content/ko/posts/&lt;/code&gt;로 언어별 디렉토리 트리를 분리. CLI 자동화에 더 적합하다 — 언어가 파일명이 아닌 라우팅 결정이 된다.&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 subgraph "파일명 기반"
 D1["content/posts/"] --&gt; F1["post.en.md"]
 D1 --&gt; F2["post.ko.md"]
 end

 subgraph "콘텐츠 디렉토리 기반"
 D2["content/en/posts/"] --&gt; F3["post.md"]
 D3["content/ko/posts/"] --&gt; F4["post.md"]
 end

 style D2 fill:#e8f5e9
 style D3 fill:#e8f5e9&lt;/pre&gt;&lt;p&gt;log-blog는 콘텐츠 디렉토리 방식을 사용한다. config에서 언어 코드를 디렉토리에 매핑한다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;language_content_dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ko&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content/ko/posts&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content/en/posts&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="콘텐츠-라우팅-content_path_for"&gt;콘텐츠 라우팅: &lt;code&gt;content_path_for()&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;라우팅 함수는 최소한의 코드다 — 딕셔너리 룩업과 폴백:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content/posts&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 폴백&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;language_content_dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;content_path_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_language&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;language_content_dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repo_path_resolved&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;language_content_dirs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;publish --language ko&lt;/code&gt;를 호출하면 포스트가 &lt;code&gt;content/ko/posts/&lt;/code&gt;에 저장된다. &lt;code&gt;--language&lt;/code&gt; 없이 실행하면 &lt;code&gt;default_language&lt;/code&gt; 설정을 따른다. &lt;code&gt;language_content_dirs&lt;/code&gt;에 해당 언어가 없으면 범용 &lt;code&gt;content_dir&lt;/code&gt;로 폴백한다.&lt;/p&gt;
&lt;p&gt;이 설계 덕분에 세 번째 언어(예: 일본어) 추가는 config 한 줄이면 된다 — 코드 수정 불필요.&lt;/p&gt;
&lt;h2 id="언어별-커버-이미지"&gt;언어별 커버 이미지
&lt;/h2&gt;&lt;p&gt;각 언어는 해당 언어로 제목이 렌더링된 고유한 커버 이미지를 갖는다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;static/images/posts/2026-04-10-firecrawl/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── cover-en.jpg ← &amp;#34;Deep Docs Crawling with Firecrawl&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── cover-ko.jpg ← &amp;#34;Firecrawl로 딥 문서 크롤링하기&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;image_handler.py&lt;/code&gt;의 이미지 생성기가 언어 접미사를 추가한다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cover_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cover-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cover.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rel_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/images/posts/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post_slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cover_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CLI가 올바른 &lt;code&gt;image:&lt;/code&gt; 프론트매터 경로를 자동 주입한다 — 사용자가 직접 작성할 필요 없다. &lt;code&gt;--cover-title &amp;quot;한국어 제목&amp;quot; --language ko&lt;/code&gt;를 전달하면, 생성된 이미지에 한국어 텍스트와 태그 필이 표시되고, 프론트매터는 &lt;code&gt;cover-ko.jpg&lt;/code&gt;를 가리킨다.&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TD
 PUB["publish 명령"] --&gt; |"--language ko"| ROUTE["content_path_for('ko')"]
 PUB --&gt; |"--cover-title '한국어 제목'"| IMG["generate_cover_image()"]

 ROUTE --&gt; DIR["content/ko/posts/post.md"]
 IMG --&gt; COVER["static/.../cover-ko.jpg"]

 PUB2["publish 명령"] --&gt; |"--language en"| ROUTE2["content_path_for('en')"]
 PUB2 --&gt; |"--cover-title 'English Title'"| IMG2["generate_cover_image()"]

 ROUTE2 --&gt; DIR2["content/en/posts/post.md"]
 IMG2 --&gt; COVER2["static/.../cover-en.jpg"]

 DIR -.-&gt; |"같은 파일명"| HUGO["Hugo가 번역 쌍으로 &amp;lt;br/&amp;gt; 자동 연결"]
 DIR2 -.-&gt; HUGO&lt;/pre&gt;&lt;h2 id="hugo-설정-hascjklanguage의-중요성"&gt;Hugo 설정: &lt;code&gt;hasCJKLanguage&lt;/code&gt;의 중요성
&lt;/h2&gt;&lt;p&gt;한국어 콘텐츠에 필수적인 Hugo 설정 하나:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;hasCJKLanguage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 설정 없이는 Hugo가 &lt;code&gt;.Summary&lt;/code&gt;와 &lt;code&gt;.WordCount&lt;/code&gt;를 공백 기준 단어 분리로 계산한다 — 한국어, 중국어, 일본어에서는 의미 없는 결과가 나온다. 활성화하면 Hugo가 CJK 인식 분할을 사용한다.&lt;/p&gt;
&lt;p&gt;Stack 테마는 한국어 언어 지원이 내장되어 있다. &lt;code&gt;languages.ko.menu&lt;/code&gt; 아래 메뉴 항목이 자동 번역된다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ko&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;languageName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;한국어&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;포스트&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/posts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;카테고리&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/categories&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;태그&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/tags&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="번역-워크플로우"&gt;번역 워크플로우
&lt;/h2&gt;&lt;p&gt;이중 언어 포스트의 퍼블리싱 흐름:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;원본 작성&lt;/strong&gt; (보통 영어)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;한국어 독자를 위해 재작성&lt;/strong&gt; — 직역이 아니라 자연스러운 한국어 흐름으로 재구성. 기술 용어는 한국 기술 글쓰기에서 관례적인 경우 영어 유지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;같은 파일명으로 양쪽 모두 퍼블리시&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 영어 버전 → content/en/posts/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run log-blog publish post-en.md &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cover-title &lt;span class="s2"&gt;&amp;#34;English Title&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --tags &lt;span class="s2"&gt;&amp;#34;hugo,i18n&amp;#34;&lt;/span&gt; --language en
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 한국어 버전 → content/ko/posts/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run log-blog publish post-ko.md &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cover-title &lt;span class="s2"&gt;&amp;#34;한국어 제목&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --tags &lt;span class="s2"&gt;&amp;#34;hugo,i18n&amp;#34;&lt;/span&gt; --language ko
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hugo는 두 파일이 같은 파일명을 공유하는 것을 자동 감지하고 포스트 페이지에 언어 전환기를 표시한다. &lt;code&gt;.Translations&lt;/code&gt; 템플릿 변수가 연결을 처리한다.&lt;/p&gt;
&lt;h3 id="번역-가이드라인"&gt;번역 가이드라인
&lt;/h3&gt;&lt;p&gt;한국어 재작성의 핵심 규칙:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;번역&lt;/strong&gt;: title, description, 본문 텍스트, Mermaid 라벨, 섹션 헤더&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;유지&lt;/strong&gt;: tags, categories, 코드 블록, URL, CLI 명령어&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;image:&lt;/code&gt; 포함 금지&lt;/strong&gt; — CLI가 언어별 경로를 자동 주입&lt;/li&gt;
&lt;li&gt;Mermaid 안전 규칙(HTML 엔티티, 따옴표 처리된 슬래시)은 양쪽 언어에 동일하게 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="github-멀티-계정-ssh-설정"&gt;GitHub 멀티 계정 SSH 설정
&lt;/h2&gt;&lt;p&gt;한 가지 복잡한 점: 블로그 레포(&lt;code&gt;ice-ice-bear&lt;/code&gt;)는 메인 개발 계정(&lt;code&gt;lazy-mango&lt;/code&gt;)과 다른 GitHub 계정을 사용한다. SSH 키 기반 라우팅으로 해결한다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# ~/.ssh/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host github-blog
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_ed25519_blog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;블로그 레포의 remote URL이 이 별칭을 사용: &lt;code&gt;git@github-blog:ice-ice-bear/ice-ice-bear.github.io.git&lt;/code&gt;. GitHub은 SSH 키를 계정에 1:1 매핑하므로, 별칭이 올바른 키(그리고 계정)가 push에 선택되도록 보장한다.&lt;/p&gt;
&lt;h2 id="인사이트"&gt;인사이트
&lt;/h2&gt;&lt;p&gt;Hugo의 다국어 지원은 성숙했지만 문서가 방대하다 — &amp;ldquo;콘텐츠 디렉토리&amp;rdquo; vs &amp;ldquo;파일명&amp;rdquo; 결정이 전체 퍼블리싱 워크플로우에 연쇄적 영향을 미친다. CLI 기반 파이프라인에서는 콘텐츠 디렉토리가 확실히 유리하다: 언어가 모든 파일에 내장된 네이밍 컨벤션이 아닌 라우팅 파라미터가 된다.&lt;/p&gt;
&lt;p&gt;언어별 커버 이미지 패턴이 예상보다 중요했다. SNS 미리보기(Open Graph, Twitter Cards)에 커버 이미지가 표시되는데, 포스트는 한국어인데 썸네일에 &amp;ldquo;Deep Docs Crawling&amp;quot;이 적혀 있으면 이질감이 크다. 현지화된 커버 이미지가 공유 링크를 각 언어 커뮤니티에서 자연스럽게 만든다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hasCJKLanguage&lt;/code&gt; 플래그는 깨지기 전까지 보이지 않는 종류의 설정이다. 이것 없이 한국어 &lt;code&gt;.Summary&lt;/code&gt;는 의미 없는 단어 수와 잘린 미리보기를 생성한다. 한 줄 수정이지만, 문제를 발견하려면 실제로 CJK 콘텐츠로 테스트해야 한다 — 영어만으로 개발하면 절대 드러나지 않는다.&lt;/p&gt;
&lt;p&gt;가장 놀라웠던 건 이중 언어 지원에 필요한 코드가 얼마나 적었는지다. 핵심 라우팅은 딕셔너리 룩업. 커버 이미지는 파일명 접미사. 번역 연결은 파일명이 같을 때 Hugo의 내장 동작. 복잡성은 구현에 있지 않다 — 어떤 Hugo 기능을 조합하고 비라틴 스크립트에 어떤 설정이 중요한지 아는 데 있다.&lt;/p&gt;</description></item></channel></rss>