Skip to main content

manga-translator

CI PyPI Python License: MIT

Translate a folder of manga pages, or a few selected pages, from any supported language to any other. The CLI finds speech balloons and light narration boxes, reads them with Tesseract, translates with locally installed Argos models, and reletters the page in your target font.

  • Offline by default. Detection, OCR and translation all run on your machine. Nothing is uploaded, and no model is downloaded during translation.
  • LLM post-editing is optional. An OpenAI-compatible model can polish the offline drafts when you ask for it with --llm; otherwise no LLM credential is read and no network request is made.
  • Nothing is overwritten blind. Outputs go to a separate directory, and every region that fails to translate or fit is reported instead of clipped.

Two commands ship in the package: manga-translate (detect → OCR → translate → reletter) and manga-bubbles (analyze and reletter by hand, no translation).


Requirements

Requirement Why Notes
Python 3.10+ runtime
Tesseract OCR reads the original text plus a language pack per source language
Argos package for your language pair offline translation installed once by manga-translate models
A font with your target script reletters the page CJK needs a CJK font; Latin/Cyrillic/Greek work with DejaVu

OCR language packs, Argos translation packages and target fonts are three independent things. Changing the target language usually means installing a font; changing the source language usually means installing an OCR pack.

Installation

1. System packages (Debian/Ubuntu)

sudo apt-get install tesseract-ocr tesseract-ocr-eng   # English source
# Japanese source also needs:
#   tesseract-ocr-jpn tesseract-ocr-jpn-vert
# A CJK font is only needed when the TARGET is Japanese/Chinese/Korean:
#   fonts-noto-cjk

2. The CLI

With pip:

python3 -m venv .venv
.venv/bin/python -m pip install -e .

With uv (faster, and what CI uses):

uv venv .venv
uv pip install --python .venv/bin/python -e .

Or straight from PyPI, once published:

pip install manga-translator
# or
uv tool install manga-translator

argostranslate depends on PyTorch. On a GPU-less machine, install the CPU build first to avoid pulling several gigabytes of CUDA wheels:

uv pip install --python .venv/bin/python torch --index-url https://download.pytorch.org/whl/cpu
uv pip install --python .venv/bin/python -e .

Verify:

.venv/bin/manga-translate --help

Usage

Step 1 — install the translation route (once, needs internet)

Translation needs an Argos package for the pair you want. models resolves the shortest route, direct or through a pivot such as Japanese → English → Indonesian, and downloads only the missing packages:

.venv/bin/manga-translate models --source ja --target id
Installed offline route: ja->en, en->id

This is the only command that touches the network. Run it again for each new pair; it is a no-op when the route is already installed.

Step 2 — translate

# A whole folder, subfolders included
.venv/bin/manga-translate translate ./pages --source ja --target id --output ./translated-id

# Just a few pages
.venv/bin/manga-translate translate cover.jpeg page-01.jpeg \
  --source ja --target id --output ./translated-id

Outputs are PNGs under --output (default translated-TARGET), keeping the input folder's relative paths. Existing outputs are left alone unless you pass --overwrite.

Language codes

--source / --target are Argos codes; --ocr-lang is a Tesseract code. The Tesseract code is inferred from --source for the common languages, and can always be overridden:

ja → jpn en → eng id → ind
zh → chi_sim ko → kor es → spa
# Vertical Japanese lettering
.venv/bin/manga-translate translate ./pages --source ja --target id \
  --ocr-lang jpn_vert --psm 5

Use --font /path/to/font.ttf whenever the default target font cannot render your script; Japanese/Chinese/Korean default to /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc and fail with an actionable message if it is absent.

Reading the report

Every run writes translation-report.json next to the images:

{
  "source": "en", "target": "id", "llm": false,
  "pages": [{
    "input": "pages/panel-1.jpeg",
    "output": "translated-id/panel-1.png",
    "status": "ok",
    "regions": [{
      "id": 1,
      "bbox": [576, 4, 856, 436],
      "source": "AT THE MOMENT OF MY DEATH, ...",
      "translation": "Pada saat kematianku, ...",
      "status": "translated"
    }]
  }]
}
Status Meaning
translated OCR'd, translated and reletted
offline_fallback LLM text did not fit; the offline draft was used instead
untranslated translator returned nothing new; the balloon was left untouched
unfittable translation cannot fit at the minimum font size; nothing erased
no_text detector found no text in this region
failed OCR or render error; artwork untouched

Page statuses are ok, partial (any region failed) or no_text. The command exits 0 when every page is ok, 1 when any page is partial/failed, and 2 on a configuration error. Always eyeball the output: OCR, detection and inpainting are all approximate.

Missed a balloon? Annotate it

Open the image, read the coordinates, and add a region the detectors did not find. Give points inside the balloon outline:

{"panel-1.jpeg": [{"polygon": [[903, 1276], [948, 1281], [998, 1298], [999, 1362], [986, 1465]]}]}
.venv/bin/manga-translate translate panel-1.jpeg --source en --target id \
  --regions regions.json --output ./translated-id

Keys are the input filename, or the relative path inside a folder input. A bbox: [left, top, right, bottom] works instead of a polygon. Keep the shape inset: it masks both the erase step and the fitted text, so a polygon that spans artwork can erase art.

Optional: LLM post-editing

The LLM receives the recognized source text and its offline draft — not the image — and rewrites the draft into more natural target language. It never replaces a translation that does not fit; the offline draft is used then.

.venv/bin/manga-translate translate ./pages --source ja --target id \
  --llm --provider netra --model deepseek/deepseek-v4-flash-0731 \
  --output ./translated-id

Credentials, in order of priority:

  1. A provider file. Defaults to ~/.omp/agent/models.yml:

    providers:
      netra:
        baseUrl: https://your-endpoint/v1
        apiKey: sk-...
        models:
          - id: deepseek/deepseek-v4-flash-0731
    

    Point elsewhere with --models-file ./models.yml; pick an entry with --provider and --model.

  2. Environment variables, which skip the file entirely and are convenient for CI:

    Variable Purpose
    MANGA_LLM_BASE_URL OpenAI-compatible base URL
    MANGA_LLM_API_KEY key for any provider
    MANGA_LLM_API_KEY_<PROVIDER> key for one provider, e.g. MANGA_LLM_API_KEY_NETRA
    MANGA_LLM_MODEL model id
    OPENAI_API_KEY last-resort fallback

Remote endpoints must be https; plain http is accepted only for loopback. --llm makes network requests and may cost money. Without it, no key is read, no config file is opened, and no request is made.

Manual relettering without translation

manga-bubbles finds balloons and gives each an ID you can translate by hand.

.venv/bin/manga-bubbles analyze panel-1.jpeg > detections.json
[{"id": 1, "bbox": [576, 4, 856, 436], "text": "I WANT TO DO IT!", "words": [ ... ]}]

Write a JSON map of only the IDs you want changed, then apply it:

{"1": "I WILL DO IT TODAY, EVEN IF IT TAKES ALL DAY!"}
.venv/bin/manga-bubbles replace panel-1.jpeg translations.json out.png \
  --font /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf

Use the same --lang and --psm for analyze and replace so the IDs line up. Balloons you do not list, and everything outside the balloon interiors, stay pixel-identical. Text too long to fit raises an error rather than clipping.

How detection works

  1. Bright-region geometry finds enclosed balloons and light rectangular narration boxes by thresholding, filling, and filtering on shape.
  2. A bundled ONNX balloon segmenter adds balloons the geometric pass cannot see — notably open balloons whose white interior flows into the page background. Its mask is inset by a few pixels before OCR, so lettering sitting outside the outline is never read as dialogue.
  3. Overlapping proposals are deduplicated, so a balloon is never OCR'd twice.

The segmenter is huyvux3005/manga109-segmentation-bubble (Apache-2.0), exported to a fixed 1600×1600 ONNX graph and run with onnxruntime only — no Ultralytics install, no download at run time. See manga_models/NOTICE and manga_models/LICENSE.

Scope and limits

  • Targets speech balloons and light narration boxes. Dark balloons, heavy screentone, and unusual shapes may be missed — annotate them with --regions.
  • Erasure covers OCR word boxes, so stylized lettering or punctuation the OCR missed can need manual retouching.
  • Right-to-left and vertical reading order is not reconstructed for you; the report lists regions top-to-bottom, left-to-right.
  • Detection is not a general artwork segmenter, and no OCR result is guaranteed.
  • Inspect before publishing anything.

Development

uv venv .venv
uv pip install --python .venv/bin/python -e .
uv pip install --python .venv/bin/python "numpy>=1.26" "opencv-python-headless>=4.11" "Pillow>=10" "PyYAML>=6" "onnxruntime>=1.17,<2"

.venv/bin/python -m unittest discover -s tests -v

Tests need Tesseract with the eng pack, DejaVu fonts, and the bundled manga_models/bubble.onnx (it ships in the wheel, so an editable install has it). The suite is deterministic, runs offline, and draws its own pages with Pillow — including the open balloon whose interior joins the page background, which geometry alone cannot separate. No third-party manga page is redistributed with this project.

examples/ is gitignored: it is the place for your own scans.

License

MIT — see LICENSE. The bundled balloon model weights are Apache-2.0; see manga_models/NOTICE.

Research basis

Metadata

Release files for manga-translator 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for manga-translator 0.3.0
File Size Uploaded
manga_translator-0.3.0.tar.gz 10.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for manga-translator 0.3.0
File Interpreter ABI Platform
manga_translator-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 20.2 MB

Release files / manga_translator-0.3.0.tar.gz

Download URL manga_translator-0.3.0.tar.gz
Size 10.1 MB
Tags Source
SHA-256 checksum
How to use checksums
193b4eecc2ef014e5129b23e4a138edd880bfe334e4cb5a7a902cbbc63b710f5
BLAKE2b-256 checksum
How to use checksums
e9f67ca5a48484b9abc35791e879119fac2e38a57e7a07b90ca79e3fd061dcc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / manga_translator-0.3.0-py3-none-any.whl

Download URL manga_translator-0.3.0-py3-none-any.whl
Size 10.1 MB
Tags Python 3
SHA-256 checksum
How to use checksums
4088924c50b791bca84f5ced6fcd168bf0d005c7a249bb01b7a36c3dd7dd120c
BLAKE2b-256 checksum
How to use checksums
3d92a3cd52cb2627bec7d307fe45e41906f00bc01b2bc549e21bf07e2659bb97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page