日英対訳リーディング(Bilingual Reading)
概要
英語の技術PDFを、段落ごとに日英対訳表示するオフライン対応HTMLリーディング版に変換します。各章は自己完結したHTML(UTF-8、画像はBase64埋め込み、単一ファイルでオフラインでも開ける)として生成され、左側に固定された折りたたみ可能な目次、右下の🌐フローティングボタンで「英語のみ / 日本語のみ / 日英対訳」を切り替えられ、ページ遷移後も状態を保持します。
トリガーと入力
- 入力:英語版PDF(ローカルパスまたはURL。最初にローカルにダウンロード)。
- 出力:
index.html+ 前書き/後書きページ +chapter-NN.html(各章1ファイル)。最終的にZIPファイルで納品。 - PDFにブックマーク(
get_toc())がある場合、章構成はブックマークを基準とし、事前にユーザーと確認します。ユーザーが「分割しない」と確認した後、一括生成を行います。
中核ルール(厳守)
- 原文は段落ごとに完全翻訳し、要約や結合は行いません。
- 各章を1ファイルとし、章を分割しません。章内は元の本のページ順に段落を配置します。
- コードブロック(
<pre><code>)は英語のまま保持し翻訳しません。中括弧を含むコードはf-string内に記述しません。 - 日本語段落は薄い青色の背景+左側に4pxの色線を付け、英語段落と区別します。リスト項目の日本語も同様のスタイルを適用します。
- 画像は圧縮後Base64で埋め込みます。元の本の1ページを翻訳し終えるごとに、ページ区切りマーク(
page_mark(pdf_page))を1行追加し、元のPDFのページ番号を表示します。 - ファイル名は小文字ハイフン区切り(
chapter-01.html、front-matter.html)。 - 目次エントリはブックマーク階層(l2/l3)に従って生成します。前後の章へのリンクを連結し、最初と最後の章のボタンはグレーアウトします。
ワークフロー概要
① ページ範囲の決定 → ② テキスト抽出 → ③ 画像抽出 → ④ 画像圧縮・Base64変換
→ ⑤ 原文読み取り・翻訳 → ⑥ build_chN.py 作成 → ⑦ 中括弧補完 → ⑧ 実行・生成
→ ⑨ ナビゲーション連結 → 検証 → 納品
まずサンプル章(通常はch1)を生成し、ユーザーにスタイルを確認してもらいます。承認後はbook_theme.pyを再利用/継承して連続一括生成し、途中で再確認は不要です。前書き/後書きページとindex.htmlは最後に統一的に仕上げます。
詳細手順
① ページ範囲の決定
入力:書籍全体の目次(TOC)/ 章番号。PyMuPDFを使ってブックマークを読み取ります:
import fitz
doc = fitz.open("book.pdf")
toc = doc.get_toc() # [[level, title, page_1based], ...]
1-basedページ番号をPyMuPDFの0-based範囲に変換します。ブックマークのページ番号は物理的なページとずれていることが多いため、ブックマークを基準とします。 章の区切りをユーザーに提示し確認を得てから作業を開始します。
② テキスト抽出
text = "".join(doc[p].get_text("text", sort=True) for p in range(start0, end0+1))
open("chN_raw.txt", "w", encoding="utf-8").write(text)
sort=Trueで読み取り順序を保証し、複数カラムでも混同しません。各章ごとにchN_raw.txtとして原文を保存します。
③ 画像抽出
scripts/extract_assets.pyを使用:
python3 extract_assets.py book.pdf <start_page_1based> <end_page_1based> --out assets
page.get_images(full=True)でxrefごとに重複除去 →extract_image(xref)。- jpx(JPEG2000)、RGBA、Pモードは一律RGBに変換してからJPEGで保存(ブラウザ互換性のため)。
④ 画像圧縮・Base64変換
上記の手順と同時に(スクリプト内蔵):PILで開く;幅800pxを超える場合は等比で800pxに縮小(LANCZOS);JPEG品質q≈68で再保存;Base64でassets/figN-M.txtに書き出します。1枚あたり30〜80KBに圧縮。
⑤ 原文読み取り・翻訳
chN_raw.txtを段落単位で読み取り(一度に500〜600行)、段落ごとに翻訳し、図のキャプション対応を記録します(例:カラスの図は「メモ」というテキストに変換)。- コード部分は翻訳をスキップ;テーブルはHTML tableに変換;リスト形式の内容も項目ごとに翻訳し、結合しません。
⑥ ビルドスクリプト作成
build_chN.pyの先頭に from book_theme import P, LI, CODE, INLINE, H, img_b64, page_mark, page を記述し、本文はf-stringのbody変数に記述します。f-string内でP()/LI()を使用する場合は、評価させるために {P(...)} と記述する必要があります。 コードブロックは通常の文字列変数 CODE_X = '''...''' として抽出し、+ CODE_X で連結します。完全なテンプレートは scripts/book_theme.py ファイル先頭のdocstringを参照してください。
⑦ 後処理:中括弧補完
python3 scripts/fix_braces.py build_chN.py
行を走査:P(' / LI(' で始まる行の行頭に { を追加し、後方で最初に ) で終わる行の行末に } を追加します。img_b64( / page( の呼び出し行は処理対象外とし、末尾の page() 行に誤って } が追加されるのを防ぎます。
⑧ 実行・生成
python3 build_chN.py # スクリプトがファイル名とKB数を表示すれば成功
⑨ ナビゲーション連結
前/次の章へのリンクを章番号に従って設定(ch1の前はfront-matter.html、最終章の次はNone)。最初と最後の章ではdisabledプレースホルダーを使用(page()内で処理済み)。
仕上げ
- front-matter.html、closing.html(推薦図書リストを含む/含まないは事前にユーザーと協議)、
index.html総合エントリ(全章へのリンク)。 python3 -m zipfile -c bilingual_reading.zip *.htmlでZIP圧縮し、納品。
分岐と例外処理
| トリガー条件 | 処理方法 |
|---|---|
| 画像がjpx / RGBA / Pモード | extract_assets.pyで一律convertでRGBに変換後JPEG保存 |
| f-string内にコードブロックの中括弧(JSON dictなど)が含まれる | f-string内に記述せず;CODE_X = '''...''' として抽出し + CODE_X で連結 |
{<P( の誤記 | sed 's/{<P(/{P(/g' で修正 |
一括で } を追加した際に末尾の page() 行に誤って追加される | fix_braces.pyで回避済み;それでも発生した場合は該当行末の余分な } を手動削除 |
| ユーザーが「先に効果を見てから続けてほしい」と要求 | サンプル章のみ生成し、フィードバックを待つ |
| ユーザーが「コードは翻訳しないでほしい」と要求 | すべての <pre><code> は原文の英語を保持(デフォルトでこの設定) |
| その他のトラブルシューティング | references/troubleshooting.md を参照 |
リソース一覧
scripts/book_theme.py— 中核共有モジュール:CSS/JS/ページスケルトン + P、LI、CODE、INLINE、H、img_b64、page_mark、page関数。スタイル変更はここだけを修正。scripts/extract_assets.py— 手順 ③④:画像抽出、圧縮、Base64変換、assets/に書き出し。scripts/fix_braces.py— 手順 ⑦:f-stringの中括弧を一括補完。references/style-guide.md— スタイルとインタラクション仕様の詳細(配色、レイアウト、コンポーネント、検証对照表)。references/troubleshooting.md— 問題トラブルシューティング表と再利用可能な経験則。
検証チェックリスト
- サンプル章のスタイルがユーザーに承認されている(日本語段落は薄い青色背景、リスト項目も同様、ページ区切りマークが存在すること)
- 原文は段落ごとに完全翻訳、コードブロックは未翻訳、画像はBase64埋め込みでオフラインでも開ける
- 各章のHTMLは独立して開け、目次は折りたたみ可能、アンカージャンプが正常に動作する
- 🌐 ボタンの3状態切り替えが動作し、ページ遷移後も状態を保持する(localStorage)
- 前後の章へのリンクが正しく連結され、最初と最後の章のボタンはグレーアウトしている
- 全ファイルがUTF-8、ファイル名は小文字ハイフン区切り、ZIP圧縮で納品されている