第 37/60 天
引言
当你的 AI 漫画在国内平台积累了第一批读者后,下一个增长曲线往往在海外。Webtoon、Manga Plus、Tapas 等平台上有大量华语创作者用 AI 工具批量产出的漫画,通过多语言发布触达全球读者。但横在面前的第一道坎就是语言:漫画是图文混合媒介,翻译不只是「把台词翻成外语」,还要处理画面里的文字、气泡布局、字体风格,以及跨文化的语境适配。
传统漫画本地化需要译者、嵌字师、修图师三个工种协作,单话成本高、周期长。而 AI 漫画本身以「批量、快速」为核心优势,如果本地化环节成为瓶颈,就与整个工作流背道而驰。好消息是:OCR 文字识别 + 大模型翻译 + 图像区域重绘 + 自动嵌字,这套「AI 本地化流水线」已经足够成熟,个人创作者完全可以在几小时内完成一话的多语言版本。
本文将带你从零搭建一套 AI 漫画翻译与本地化工作流,覆盖文字提取、机器翻译、去字重绘、嵌字排版、发布适配五个环节,并提供可直接复用的脚本与工具选型。
核心概念
什么是漫画本地化(Localization)
与「直译(Translation)」不同,本地化要考虑:
– 长度适配:日语/中文台词通常短,英语/德语普遍长 20-40%,气泡可能放不下
– 文化适配:谐音梗、方言、特定文化梗需要意译甚至改写
– 阅读习惯:日语/中文竖排或从左到右,阿拉伯语从右到左,可能需要镜像翻页
– 格式习惯:拟声词(SFX)在英文漫画中常用斜体手写体
三层处理模型
漫画画面中的文字可以分成三类,处理方式完全不同:
| 文字类型 | 例子 | 处理方式 |
|---|---|---|
| 台词(气泡内) | 对话、内心独白 | OCR → 翻译 → 去字 → 重嵌 |
| 画面内文字 | 招牌、报纸、屏幕 UI | OCR → 翻译 → 区域内重绘(inpainting) |
| 拟声词(SFX) | 「轰!」「咚咚」 | 通常保留或音译,风格化处理 |
AI 本地化流水线的四步骨架
OCR 提取台词 → LLM 批量翻译 → 原图去字重绘 → 翻译文本嵌字排版
每一环都有对应的主流工具,下面实战步骤会给出完整可跑的方案。
实战步骤
第一步:环境准备(Shell)
先安装 OCR 引擎与 Python 依赖。漫画文字通常为印刷体,PaddleOCR 在中文、日文、英文混合场景表现优秀;tesseract 更轻量但多语言混合稍弱。
# 系统依赖(Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y tesseract-ocr
tesseract-ocr-chi-sim tesseract-ocr-jpn tesseract-ocr-eng
python3-pip
# Python 依赖
pip install paddleocr paddlepaddle
opencv-python-headless pillow
deep-translator requests
# 验证 OCR
python3 -c "from paddleocr import PaddleOCR; print('PaddleOCR OK')"
第二步:OCR 提取台词(Python)
对单页漫画做 OCR,输出带坐标的文本块 JSON。坐标是后面「去字重绘」和「重新嵌字」的关键输入。
# extract_text.py
import json
from paddleocr import PaddleOCR
ocr = PaddleOCR(lang="ch", use_angle_cls=True) # 中文为主,可切 "japan" 等
def extract_page(image_path, out_json):
result = ocr.ocr(image_path, cls=True)
boxes, text_blocks = [], []
for line in result[0]:
if not line:
continue
box, (text, conf) = line[0], line[1]
if conf < 0.6: # 过滤低置信度
continue
boxes.append(box)
text_blocks.append({
"text": text,
"conf": round(float(conf), 3),
"box": box, # 四个角坐标 [左上,右上,右下,左下]
})
with open(out_json, "w", encoding="utf-8") as f:
json.dump({"image": image_path, "blocks": text_blocks},
f, ensure_ascii=False, indent=2)
print(f"提取 {len(text_blocks)} 个文本块 -> {out_json}")
if __name__ == "__main__":
import sys
extract_page(sys.argv[1], sys.argv[2])
# 批量处理整个目录的页面
for img in pages/*.png; do
out="/tmp/ocr/$(basename "${img%.*}").json"
python3 extract_text.py "$img" "$out"
done
第三步:LLM 批量翻译(Python + JSON)
将 OCR 结果按「文本块 → 上下文段落」喂给大模型,带上气泡顺序与角色信息以保持连贯。这里以 OpenAI 兼容 API 为例,本地 Ollama 同理。
# translate.py
import json, sys, time
import requests
API_URL = "https://api.openai.com/v1/chat/completions"
API_KEY="REPLACE_ME" # 用环境变量或密钥管理,勿硬编码真实密钥
TARGET_LANG = "English"
def translate_batch(blocks, lang=TARGET_LANG):
# 把多个文本块打包成一个请求,减少调用次数
lines = [f"[{i}] {b['text']}" for i, b in enumerate(blocks)]
prompt = (
f"你是一名专业漫画翻译。把下面标号的中文台词翻译成{lang},"
"保持口语化和角色语气。输出 JSON:{"0": "...", "1": "..."}nn"
+ "n".join(lines)
)
resp = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": prompt}],
"response_format": {"type": "json_object"},
"temperature": 0.3,
},
timeout=60,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
if __name__ == "__main__":
data = json.load(open(sys.argv[1], encoding="utf-8"))
translated = json.loads(translate_batch(data["blocks"]))
for i, b in enumerate(data["blocks"]):
b["translated"] = translated.get(str(i), b["text"])
json.dump(data, open(sys.argv[2], "w", encoding="utf-8"),
ensure_ascii=False, indent=2)
print(f"翻译完成 -> {sys.argv[2]}")
第四步:原图去字重绘(Python + 配置)
用 Stable Diffusion 的 inpainting 功能把原台词区域「抹掉」并重绘背景。推荐 ComfyUI 或 AUTOMATIC1111 的 api/inpaint 接口,以 JSON 提交 mask(由 OCR 坐标膨胀得到)。
# inpaint_config.yaml
api_url: "http://127.0.0.1:7860/sdapi/v1/img2img"
model: "sd_xl_base"
denoising_strength: 0.65 # 去字区域重绘强度,太高会改背景
steps: 30
cfg_scale: 7
seed: -1 # -1 随机
mask_expand_px: 8 # 坐标膨胀,覆盖描边
prompt: "clean empty background, same style, no text, no letters"
negative_prompt: "text, letters, words, watermark"
# inpaint_page.py
import base64, io, json, sys, yaml
import cv2, numpy as np
import requests
def build_mask(img, boxes, expand=8):
mask = np.zeros(img.shape[:2], dtype=np.uint8)
for box in boxes:
pts = np.array([[int(x) for x in p] for p in box], np.int32)
cv2.fillPoly(mask, [pts], 255)
return cv2.dilate(mask, np.ones((expand, expand), np.uint8))
def inpaint(image_path, boxes, cfg, out_path):
img = cv2.imread(image_path)
mask = build_mask(img, boxes, cfg["mask_expand_px"])
_, buf = cv2.imencode(".png", img)
_, mbuf = cv2.imencode(".png", mask)
payload = {
"init_images": [base64.b64encode(buf).decode()],
"mask": base64.b64encode(mbuf).decode(),
"prompt": cfg["prompt"],
"negative_prompt": cfg["negative_prompt"],
"steps": cfg["steps"],
"cfg_scale": cfg["cfg_scale"],
"denoising_strength": cfg["denoising_strength"],
"seed": cfg["seed"],
"sampler_name": "Euler a",
}
r = requests.post(cfg["api_url"], json=payload, timeout=300).json()
img_out = base64.b64decode(r["images"][0])
open(out_path, "wb").write(img_out)
print("去字完成 ->", out_path)
if __name__ == "__main__":
cfg = yaml.safe_load(open("inpaint_config.yaml"))
data = json.load(open(sys.argv[1]))
inpaint(data["image"], [b["box"] for b in data["blocks"]], cfg, sys.argv[2])
第五步:自动嵌字排版(Python)
根据 OCR 坐标把翻译后的文字绘制回气泡位置,自动按宽高换行、缩放字号。这是保证「嵌字不溢出」的关键。
# embed_text.py
import json, sys
from PIL import Image, ImageDraw, ImageFont
def fit_font(draw, text, box_w, box_h, font_path, max_size=40):
size = max_size
while size > 10:
font = ImageFont.truetype(font_path, size)
w = draw.textlength(text, font=font)
lines = wrap(draw, text, font, box_w)
total_h = len(lines) * (size * 1.2)
if w <= box_w and total_h <= box_h:
return font, lines
size -= 2
return ImageFont.truetype(font_path, 10), wrap(draw, text, font, box_w)
def wrap(draw, text, font, max_w):
lines, cur = [], ""
for ch in text:
if draw.textlength(cur + ch, font=font) > max_w:
lines.append(cur); cur = ch
else:
cur += ch
if cur: lines.append(cur)
return lines
def embed(image_path, data, out_path):
img = Image.open(image_path).convert("RGB")
draw = ImageDraw.Draw(img)
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
for b in data["blocks"]:
xs = [p[0] for p in b["box"]]; ys = [p[1] for p in b["box"]]
x0, y0, x1, y1 = min(xs), min(ys), max(xs), max(ys)
box_w, box_h = x1 - x0, y1 - y0
font, lines = fit_font(draw, b.get("translated", b["text"]),
box_w, box_h, font_path)
y = y0
for line in lines:
w = draw.textlength(line, font=font)
draw.text((x0 + (box_w - w) / 2, y), line, fill="black", font=font)
y += int(font.size * 1.2)
img.save(out_path)
print("嵌字完成 ->", out_path)
if __name__ == "__main__":
data = json.load(open(sys.argv[1]))
embed(data["image"], data, sys.argv[2])
# 一键跑完整流水线
bash -x localize_pipeline.sh
# 内部依次执行:
# 1. python3 extract_text.py pages/p01.png /tmp/ocr/p01.json
# 2. python3 translate.py /tmp/ocr/p01.json /tmp/ocr/p01_tr.json
# 3. python3 inpaint_page.py /tmp/ocr/p01_tr.json pages/p01_clean.png
# 4. python3 embed_text.py /tmp/ocr/p01_tr.json pages/p01_clean.png out/p01_en.png
第六步:发布适配(Shell / JSON)
多语言版本通常需要独立标题、描述与标签。以 Webtoon 风格的平台 API 为例,用 JSON 提交多语言元数据。
{
"series_id": "aiverse-001",
"episode": 37,
"locales": [
{
"lang": "zh-CN",
"title": "AI漫剧制 | 第 37 天:漫画翻译与本地化",
"description": "用 AI 流水线把漫画本地化为多语言。"
},
{
"lang": "en-US",
"title": "AI Manga Series | Day 37: Translation & Localization",
"description": "Build an AI pipeline to localize comics into multiple languages."
},
{
"lang": "ja-JP",
"title": "AI漫画シリーズ | 第37日:翻訳とローカライズ",
"description": "AIパイプラインで漫画を多言語化する方法。"
}
],
"assets": {
"page_dir": "out/",
"cover": "out/cover_en.png"
}
}
# 本地校验译后文字是否溢出气泡(对比原始坐标面积)
python3 - << 'PY'
import json, sys
from PIL import Image, ImageDraw, ImageFont
data = json.load(open("/tmp/ocr/p01_tr.json"))
img = Image.open(data["image"]); d = ImageDraw.Draw(img)
f = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20)
overflow = 0
for b in data["blocks"]:
xs = [p[0] for p in b["box"]]; ys = [p[1] for p in b["box"]]
w, h = max(xs)-min(xs), max(ys)-min(ys)
if d.textlength(b.get("translated", b["text"]), font=f) > w * 1.1:
overflow += 1
print(f"疑似溢出文本块: {overflow}")
PY
常见问题
Q1:为什么不用 Google 翻译直接翻译全文?
A:Google 翻译缺乏漫画上下文(谁在说话、什么语气、是否有梗),容易翻出书面腔。大模型能结合整段气泡与剧情保持角色一致性;且 Google 翻译对短句口语的断句也不如 LLM 自然。
Q2:OCR 提取坐标不准怎么办?
A:三个方向:① 提高输入分辨率,漫画页建议 1500px 以上再 OCR;② 用 use_angle_cls=True 处理旋转文字;③ 提高置信度阈值到 0.7,宁可漏检再人工补,也不要错误框污染去字区域。
Q3:去字重绘会不会把背景也改坏?
A:控制 denoising_strength(0.5-0.7)与 mask_expand_px(6-10),并用负面提示词排除文字;对于大面积台词(如整页文本框),建议按气泡逐个 inpaint 而不是整块一次重绘,背景一致性更高。
Q4:英文翻译总是比原气泡长,嵌字溢出怎么办?
A:① 调用 fit_font 自动降字号;② 设置 lang 偏好缩写(如英文常用「don’t→dont」漫画体);③ 若仍然放不下,可在去字阶段把气泡适当放大一点(膨胀 mask 后重绘),给英文留出空间。
Q5:拟声词(SFX)需要翻译吗?
A:视平台而定。英文漫画通常保留原拟声词或改为英文手写体(如「咚→BOOM」),因为拟声词偏象形、改动成本高。建议用风格迁移模型(如 SD 的 ip-adapter)只替换 SFX 区域,保持手写笔触。
Q6:版权方面,翻译他人作品需要注意什么?
A:翻译本质是二次创作,若原作受版权保护,需要取得授权。对自己用 AI 生成、享有完整版权的漫画做本地化则没有障碍。同时注意:训练翻译专用模型的语料与平台条款需符合当地法律。
总结
- AI 本地化流水线四步法:OCR 提取台词 → LLM 批量翻译 → 去字重绘 → 自动嵌字,个人创作者即可在几小时内完成一话多语言版。
- 坐标是关键资产:OCR 输出的文本块坐标同时服务「去字 mask」和「嵌字定位」,一定要结构化保存成 JSON,贯穿整条流水线。
- 上下文翻译优于逐句翻译:把整页/整话台词打包给 LLM 并带上角色信息,翻译质量与连贯性远高于逐句机翻。
- 长度与文化适配是本地化核心:字号自适应、气泡扩容、文化梗意译,决定了海外读者体验是「看得懂」还是「读得爽」。
- AI 漫画的多语言发布是天然的规模优势:内容可复制、成本接近零,善用 AI 流水线,一份创作就能吃透全球市场。
明天进入第 38 天,我们将探讨 AI 漫画的印刷与出版准备,为你的作品从屏幕走向实体铺路。















暂无评论内容