Skip to main content

Bilingual EPUB Toolkit — two monolingual EPUBs in, one facing-text book out, with the translation blurred until tapped

Live demo · 中文说明 · Installation · Usage · Translation · Limitations

PyPI version License: Apache-2.0 Python 3.9+ No required dependencies beyond lxml

Builds facing-text bilingual EPUBs. Each paragraph is followed by its translation, blurred until tapped, so the original can be read first and the translation consulted only when needed.

Works with any standard EPUB and any language pair. Chapter structure and paragraph pairing are derived from the files themselves — no per-book configuration. All processing is local.

  • merge two monolingual editions into one bilingual book
  • split a bilingual book back into separate languages
  • remerge an existing bilingual book with different settings
  • translate the missing side via a model API, or via a coding agent
  • Command line, terminal wizard, and local web interface, in English or Chinese

Installation

pip install bilingual-epub-toolkit

Chinese Traditional ↔ Simplified conversion requires opencc:

pip install "bilingual-epub-toolkit[chinese]"

Requires Python 3.9 or later. The only runtime dependency is lxml.

Three commands are installed:

Command Interface
bilingual-epub command line
bilingual-epub-tui terminal wizard, prompts instead of flags
bilingual-epub-web local web page with drag-and-drop

Interface language follows the system locale and can be set with --lang en or --lang zh.

Quick start

Two sample books ship with the repository:

bilingual-epub merge --a examples/sample-en.epub --b examples/sample-fr.epub --out demo.epub

Open demo.epub in Apple Books or any EPUB 3 reader. The samples are an original short story written for this project, so no third-party rights are involved. A hosted instance is available at epub.starry-files.duckdns.org for trying the tool without installing it; uploads there are capped at 25 MB and deleted after 30 minutes.

Two EPUBs picked, uploaded, and merged, ending with the per-chapter alignment table

Usage

merge

Combine two monolingual editions:

bilingual-epub merge \
  --a english.epub --b french.epub --out bilingual.epub \
  --blur-side b --blur 0.25em
Option Effect
--blur-side a|b which side is hidden until tapped (default b)
--no-blur plain facing text, nothing hidden
--blur CSS length; em units scale with the reader's font size
--title, --author override the combined metadata
--convert-side, --convert opencc script conversion, e.g. --convert-side b --convert tw2sp

A per-chapter table is printed after each merge, reporting how many paragraphs paired one to one.

split

Separate a bilingual book by language:

bilingual-epub split --in bilingual.epub --out-dir ./split/ --langs en,fr

Language is read from each block's lang attribute. Omitting --langs writes out every language found.

remerge

Re-render an existing bilingual book with different settings:

bilingual-epub remerge --in old.epub --out new.epub --blur-side a --blur 0.35em

Equivalent to a split followed by a merge. Also converts bilingual books from other sources into this tap-to-reveal format.

Translation

When only one edition exists, the second can be generated. Both routes produce a translation with the same block count and order as the source, so the subsequent merge pairs every paragraph exactly.

With a coding agent

Installs a skill that lets an agent such as Claude Code drive the toolkit, translating on an existing subscription rather than a metered API:

bilingual-epub skill      # writes .claude/skills/bilingual-epub/SKILL.md

The skill file is also available from the hosted instance.

The underlying commands can be used directly:

bilingual-epub export-text --in book.epub --out book.json
# translate book.json into translated.json
bilingual-epub import-text --export book.json --text translated.json \
    --out book.zh.epub --lang zh

import-text accepts a JSON array of strings, a JSON object with a blocks list, or plain text with one block per line. Files whose block count differs from the source are rejected.

With a model API

bilingual-epub translate --in book.epub --out book.zh.epub --to zh \
    --base-url https://api.deepseek.com/v1 --api-key "$KEY" --model deepseek-chat
Option Effect
--dialect openai /chat/completions; OpenAI, DeepSeek, Moonshot, Zhipu, SiliconFlow, OpenRouter, Groq, Together, Ollama, LM Studio, vLLM (default)
--dialect anthropic /v1/messages
--dry-run report block, character and request counts without sending anything
--batch-size paragraphs per request (default 20)
--cache progress file; an interrupted run resumes from it

Credentials may also be supplied through BILINGUAL_API_KEY, OPENAI_API_KEY or ANTHROPIC_API_KEY. Batches returning the wrong number of blocks are retried, then subdivided, and are never accepted as-is.

Self-hosting

The web interface defaults to a single trusted user on localhost. --public adapts it for a shared host:

bilingual-epub-web --public --port 8799

Public mode rejects server-side file paths, isolates each visitor's files, rate-limits by address, caps uploads at 25 MB, and removes idle sessions after 30 minutes (--ttl).

Cloudflare Turnstile can be enabled to filter automated traffic while keeping the service open to anyone:

export TURNSTILE_SITEKEY=0x...
export TURNSTILE_SECRET=0x...
bilingual-epub-web --public

Jobs are then verified before running, and refused if Cloudflare is unreachable. Without keys, no widget is rendered and no verification occurs.

How it works

Chapter boundaries are located by reading the EPUB container, OPF, manifest and spine, then selecting the heading level that recurs at chapter frequency.

Paragraph pairing uses Gale–Church alignment: a length-based statistical model solved by dynamic programming, weighted so that co-occurring headings anchor the sequence. It handles one-to-one pairs as well as paragraphs split or merged in translation.

Every output block carries a lang attribute, which is what allows split to reverse a merge. The tap-to-reveal behaviour uses CSS :target with a small progressive-enhancement script, degrading to plain visible text where neither is supported.

Limitations

Limitation Behaviour
DRM-protected files Encrypted EPUBs yield no extractable text; the tool reports this and stops.
Books without headings Degrade to a single chapter. Text is preserved; chapter divisions are not.
Statistical alignment Editions that add, cut or restructure text pair imperfectly. The printed table reports the rate.
Untagged bilingual books split requires per-paragraph lang attributes. Books lacking them cannot be separated.

No EPUB files are included in this repository apart from the generated samples, and *.epub is gitignored.

BookAlign attacks the same pairing problem from the other side: multilingual embeddings (LaBSE) plus dynamic programming, comparing what paragraphs mean rather than how long they are. That reaches the case the table above cannot — a pairing that is wrong but unambiguous, where the lengths happen to line up and a length model stays confident. It is aimed at novels, particularly Japanese originals against Chinese translations, and it runs a review-first workflow rather than one pass end to end.

The two are complementary. This one needs no model and runs anywhere; that one reads the text, at the cost of a local LaBSE model and realistically a GPU. If your two editions differ enough that length alone mispairs them, start there.

Development

git clone https://github.com/StarryGuli/bilingual-epub-toolkit.git
cd bilingual-epub-toolkit
pip install -e ".[dev,chinese]"
pytest && ruff check .

Test fixtures are synthetic EPUBs constructed in code rather than checked-in book files; see tests/conftest.py. Coverage includes the three operations end to end, both cover image formats, the translation round trip against a stub API, public-mode isolation and rate limiting, and the error paths.

Coverage and conventions are described in CONTRIBUTING.md.

License

Apache-2.0. See LICENSE.

Metadata

Release files for bilingual-epub-toolkit 0.1.6

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

Source distribution (sdist)

Source distribution for bilingual-epub-toolkit 0.1.6
File Size Uploaded
bilingual_epub_toolkit-0.1.6.tar.gz 3.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for bilingual-epub-toolkit 0.1.6
File Interpreter ABI Platform
bilingual_epub_toolkit-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 3.7 MB

Release files / bilingual_epub_toolkit-0.1.6.tar.gz

Download URL bilingual_epub_toolkit-0.1.6.tar.gz
Size 3.6 MB
Tags Source
SHA-256 checksum
How to use checksums
b5fe962edb3db63579462f8a7bdfc62d3104abd757b05990cfe2a467074b8d53
BLAKE2b-256 checksum
How to use checksums
396228504f5061363a36528d0f310017debcb65e1d422eb16dabf9c15456549e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / bilingual_epub_toolkit-0.1.6-py3-none-any.whl

Download URL bilingual_epub_toolkit-0.1.6-py3-none-any.whl
Size 107.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25def646a144323b43aea18ece45ccbd5208323431c7ef480534c6c1d6e92426
BLAKE2b-256 checksum
How to use checksums
7ecc3aa403df9a2885ba6a344cd6530a389a70bf9a358a5b995225c8f3c75de0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

This release

0.1.6 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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