epub-translator

repository·main·Indexed 21 days ago

https://github.com/oomol-lab/epub-translator

A tool for transforming EPUB files into bilingual editions using AI-powered translations. It preserves original formatting, images, and structure while providing side-by-side text. The library supports OpenAI, Azure OpenAI, and OpenAI-compatible services, featuring a Dual-LLM architecture to optimize translation quality and XML structure preservation, persistent caching for progress recovery, and customizable insertion modes via SubmitKind.

Tokens
14.3K
Snippets
46
Records
62
Agent score
68%

What's inside epub-translator

  1. Use custom user prompts in translation templates

    main

    When using the translation API, you can provide a user_prompt to guide the LLM. In version 0.1.7 and later, this prompt is automatically wrapped in <rules> tags within the translation template to ensure the LLM follows your instructions.

    {% if user_prompt -%}
    <rules>
    {{ user_prompt }}
    </rules>
    {% endif -%}
  2. Understand the Sparse ID architecture and positioning cues

    main

    The Fill Stage relies on specific identifiers and metadata to map translated text back to the XML structure:

    • Elements with IDs: Used for elements that require disambiguation when multiple similar structures exist (e.g., multiple <span id="5">Principia</span> tags).
    • Elements without IDs: Matched based on position and tag name.
    • data-orig-len attribute: Represents the token count of the original text. This serves as a positioning hint for the LLM, helping it understand the scale of the translation (e.g., identifying when a short source word expands into a long target phrase).
  3. Optimize translation with a Dual-LLM architecture

    main

    For better results, you can use two different LLM instances: one optimized for creative translation and another optimized for maintaining XML structure (filling).

    • translation_llm: Use a higher temperature (e.g., 0.8) for more natural and creative language translation.
    • fill_llm: Use a lower temperature (e.g., 0.3) to ensure strict adherence to the EPUB/XML structure during the filling process.
    from epub_translator import LLM, translate, language, SubmitKind
    
    translation_llm = LLM(
        key="your-api-key",
        url="https://api.openai.com/v1",
        model="gpt-4",
        token_encoding="o200k_base",
        temperature=0.8,
    )
    
    fill_llm = LLM(
        key="your-api-key",
        url="https://api.openai.com/v1",
        model="gpt-4",
        token_encoding="o200k_base",
        temperature=0.3,
    )
    
    translate(
        source_path="source.epub",
        target_path="translated.epub",
        target_language=language.CHINESE,
        submit=SubmitKind.APPEND_BLOCK,
        translation_llm=translation_llm,
        fill_llm=fill_llm,
    )
  4. Track statistics for Dual-LLM setups

    main

    If you use separate LLM instances for translation and filling (via translation_llm and fill_llm parameters in translate), each instance tracks its own independent statistics. This allows you to monitor the cost and usage of each specific role separately.

    translation_llm = LLM(key="...", url="...", model="gpt-4", token_encoding="o200k_base")
    fill_llm = LLM(key="...", url="...", model="gpt-4", token_encoding="o200k_base")
    
    translate(
        source_path="source.epub",
        target_path="translated.epub",
        target_language=language.ENGLISH,
        submit=SubmitKind.APPEND_BLOCK,
        translation_llm=translation_llm,
        fill_llm=fill_llm,
    )
    
    print(f"Translation tokens: {translation_llm.total_tokens}")
    print(f"Fill tokens: {fill_llm.total_tokens}")
    print(f"Combined total: {translation_llm.total_tokens + fill_llm.total_tokens}")
  5. Use Dual-LLM architecture for improved translation quality

    main

    Version 0.1.2 introduces a Dual-LLM architecture that allows you to provide separate LLM instances for different stages of the translation process. This optimizes quality by using different settings for creative translation versus structural XML filling:

    • translation_llm: Used for the actual text translation. It is recommended to use a higher temperature (e.g., 0.8) for more natural, creative output.
    • fill_llm: Used for filling the XML structure. It is recommended to use a lower temperature (e.g., 0.3) for deterministic, structure-preserving results.

    By providing both, you can leverage the strengths of different models or configurations for each specific task.

    translate(
        source_path,
        target_path,
        "English",
        translation_llm=translation_llm,
        fill_llm=fill_llm,
    )
  6. Track tokens for dual LLM setups

    main

    If you provide separate LLM instances for translation and filling, each instance tracks its own statistics independently. You can access translation_llm.total_tokens and fill_llm.total_tokens to see the breakdown.

    translation_llm = LLM(key="...", url="...", model="gpt-4", token_encoding="o200k_base")
    fill_llm = LLM(key="...", url="...", model="gpt-4", token_encoding="o200k_base")
    
    translate(
        source_path="source.epub",
        target_path="translated.epub",
        target_language=language.CHINESE,
        submit=SubmitKind.APPEND_BLOCK,
        translation_llm=translation_llm,
        fill_llm=fill_llm,
    )
    
    print(f"Translation tokens: {translation_llm.total_tokens}")
    print(f"Fill tokens: {fill_llm.total_tokens}")
    print(f"Total: {translation_llm.total_tokens + fill_llm.total_tokens}")
  7. How the EPUB Translator two-stage architecture works

    main

    The EPUB Translator uses a two-stage translation process to ensure both high translation quality and structural integrity:

    1. Translate Stage (translate_llm, temperature=0.8):

      • Converts the source text into pure text in the target language.
      • Does not preserve XML structures, focusing entirely on translation quality.
    2. Fill Stage (fill_llm, temperature=0.3):

      • Fills the translated text back into the original XML template.
      • Ensures the XML structure (tags, nesting, IDs) remains identical to the source.
      • Uses a Hill Climbing algorithm to verify structural correctness.

    This separation allows the LLM to focus on linguistic nuances in the first stage and structural precision in the second.

  8. Resolve Adjacent Element Errors (Error Type 7)

    main

    A critical error occurs when adjacent elements have different semantic types (e.g., a book title next to a year). AI models often fail by matching the wrong ID to the wrong semantic type based on position.

    Error Type 7 Rule: When template elements are adjacent but have different semantic types (book title + year, person name + date, etc.), you MUST match by SEMANTIC TYPE, not by position in the translated text.

    Example:

    • Template: <span id="3">Book Title</span> in <span id="4"><a id="4">1990</a></span>
    • Translation: 《书名》于1990年出版
    • ❌ WRONG (Positional matching): 《书名》于<span id="3">1990</span>年出版 (Incorrectly matching a YEAR to a BOOK TITLE template)
    • ✓ CORRECT (Semantic matching): <span id="3">《书名》</span>于<span id="4"><a id="4">1990</a></span>年出版
    ❌ WRONG: 《书名》于<span id="3">1990</span>年出版
      (Matching by position: "1990" appears after "于", so wrapping it with id="3")
      (This is WRONG because you matched a YEAR to a template that expects a BOOK TITLE)
    
    ✓ CORRECT: <span id="3">《书名》</span>于<span id="4"><a id="4">1990</a></span>年出版
      (Matching by SEMANTIC TYPE: book title → book title, year → year)
  9. Core Architecture of the Fill Stage Prompt

    main

    The fill.jinja prompt (used in the translation process) relies on a specific architecture to ensure XML/tag structure preservation during translation. The following components are considered critical and must not be removed:

    1. CRITICAL RULES: Establishes that translation fluency is SECONDARY to structure preservation. If a natural translation breaks the required XML template structure, the AI must prioritize inserting the required tags even if it disrupts the flow.
    2. ID Handling: Defines how to treat tags. Tags with id="X" are disambiguation markers for similar elements; tags without IDs are matched by position and name. NEVER add, remove, or change id attributes.
    3. SEMANTIC Matching: Emphasizes that translation may change word order, so the AI must use semantic matching rather than positional matching to wrap tags.
    4. STEP-BY-STEP Instructions: Provides operational guidance (e.g., counting elements, mapping source to translation, and verifying) rather than just theoretical rules.
    IMPORTANT: Translation fluency is SECONDARY to structure preservation.
    If the translated text flows naturally but doesn't match template structure,
    you MUST break the flow to insert required tags.
  10. Configure translation insertion modes with SubmitKind

    main

    The submit parameter determines how the translated text is integrated into the EPUB structure. Use the SubmitKind enum to choose a mode:

    • SubmitKind.REPLACE: Replaces original content with translation. Results in a single-language book.
    • SubmitKind.APPEND_TEXT: Appends translation as inline text immediately after the original content. Both languages appear in the same paragraph.
    • SubmitKind.APPEND_BLOCK (Recommended): Appends translations as separate block elements (paragraphs) after the original. This is ideal for side-by-side bilingual reading.
    from epub_translator import SubmitKind
    
    # For bilingual books (recommended)
    translate(..., submit=SubmitKind.APPEND_BLOCK, ...)
    
    # For single-language translation
    translate(..., submit=SubmitKind.REPLACE, ...)
  11. Configure translation insertion with SubmitKind modes

    main

    The submit parameter determines how the translated text is integrated into the EPUB document. Use the SubmitKind enum to choose a mode:

    • SubmitKind.REPLACE: Replaces the original text with the translation. This produces a monolingual output.
    • SubmitKind.APPEND_TEXT: Appends the translation as inline text immediately following the original. This produces a bilingual output where both languages appear in the same paragraph.
    • SubmitKind.APPEND_BLOCK: Appends the translation as a separate block element (e.g., a new paragraph) after the original. This is the recommended mode for clear bilingual reading.
    from epub_translator import SubmitKind
    
    # Recommended for bilingual reading
    translate(..., submit=SubmitKind.APPEND_BLOCK, ...)
    
    # For a single-language version
    translate(..., submit=SubmitKind.REPLACE, ...)
  12. Implement Temperature Increment Mechanism

    main

    To help the AI escape infinite retry loops (deadlocks), the system uses a temperature increment mechanism.

    Mechanism:

    • The initial attempt uses a low temperature (e.g., 0.2) for high determinism.
    • For every subsequent retry, the temperature increases exponentially (e.g., 0.2 → 0.5 → 0.65 → 0.725...).

    Note: Temperature increment is a safety measure, not a primary solution. If the prompt's cognitive framework (like Error Type 7) is incorrect, increasing temperature will not solve the underlying logic error.

    temperature=(0.2, 0.9)