<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 龚旭东</title>
    <description>The latest articles on DEV Community by 龚旭东 (@jacob_gong).</description>
    <link>https://dev.to/jacob_gong</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3990499%2F9316bfd0-9c5c-4fa1-b4f1-fc2b24128e3e.jpg</url>
      <title>DEV Community: 龚旭东</title>
      <link>https://dev.to/jacob_gong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jacob_gong"/>
    <language>en</language>
    <item>
      <title>How We Translate Entire Books with LLMs Without Losing Context</title>
      <dc:creator>龚旭东</dc:creator>
      <pubDate>Thu, 18 Jun 2026 23:19:00 +0000</pubDate>
      <link>https://dev.to/jacob_gong/how-we-translate-entire-books-with-llms-without-losing-context-2em5</link>
      <guid>https://dev.to/jacob_gong/how-we-translate-entire-books-with-llms-without-losing-context-2em5</guid>
      <description>&lt;p&gt;&lt;em&gt;Our chunking strategy that keeps chapters coherent, respects context windows, and handles multi-lingual books.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: books don’t fit in a prompt
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://lectulibre.com" rel="noopener noreferrer"&gt;LectuLibre&lt;/a&gt;, we translate entire books — novels, technical manuals, poetry — using large language models. It sounds simple: feed each paragraph to an LLM, concatenate results, done. But the moment we tried a 300‑page EPUB, chaos ensued. Chapters bled into each other, sentences were chopped mid‑word, and the translation of chapter 5 had no idea what happened in chapter 4.&lt;/p&gt;

&lt;p&gt;LLMs have limited context windows. Even the massive 200K token window of Claude 3 can’t hold a whole 150K‑word book. And even if it could, the cost and latency would be absurd. We needed a way to split the book into manageable chunks while preserving enough context so that the translation remains coherent across thousands of pages.&lt;/p&gt;

&lt;p&gt;Here’s how we designed a chunking pipeline that respects your wallet, the context window, and the book’s narrative flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: extract structure, not just text
&lt;/h2&gt;

&lt;p&gt;Naively splitting by character count is a recipe for disaster. Instead, we first parse the document to understand its logical units: chapters, sections, headings. For EPUB, we use &lt;code&gt;ebooklib&lt;/code&gt;; for PDF, &lt;code&gt;pdfplumber&lt;/code&gt;. Both give us a stream of items (paragraphs, headings) that we then organize into a tree of chapters and sub‑sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ebooklib&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ebooklib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;epub&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_chapters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epub_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;epub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_epub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epub_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chapters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_items_of_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ebooklib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ITEM_DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Simplified: each document is a chapter
&lt;/span&gt;        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_content&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;chapters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chapters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, we use &lt;code&gt;BeautifulSoup&lt;/code&gt; to extract &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; text and identify heading tags (&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;) to build a table of contents. This way, even if a chapter is 20,000 tokens, we keep it together as a single unit until later splitting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: sentence‑aware splitting with token budgets
&lt;/h2&gt;

&lt;p&gt;A chapter still needs to be broken down to fit the model’s context window. But we never split mid‑sentence. We use &lt;code&gt;spaCy&lt;/code&gt; to tokenize the text into sentences, then greedily group them until we hit a token limit.&lt;/p&gt;

&lt;p&gt;Why not simple character‑based splitting? Because sentences carry semantic boundaries. Breaking inside a sentence occasionally produces artefacts like “He walked to the sta‑” / “‑tion.” LLMs are forgiving but not that forgiving.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;  &lt;span class="c1"&gt;# for accurate token count
&lt;/span&gt;
&lt;span class="n"&gt;nlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en_core_web_sm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-tokenizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# custom tokenizer for Claude
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sentence_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sents&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunk_sentences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap_sentences&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;current_chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sent_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sent_tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Store chunk with a sliding overlap
&lt;/span&gt;            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Overlap: take last `overlap_sentences` from the chunk just concluded
&lt;/span&gt;            &lt;span class="n"&gt;current_chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap_sentences&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap_sentences&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;current_chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;current_chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;current_token_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sent_tokens&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set &lt;code&gt;max_tokens&lt;/code&gt; to 1800, leaving room for the system prompt, context from previous chunks, and the model’s response. That’s for Claude Haiku, which has a 32K context window. For longer‑context models we’d scale up, but keeping chunks smaller also means faster, cheaper API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: passing context across chunks
&lt;/h2&gt;

&lt;p&gt;The real magic is what we do &lt;em&gt;between&lt;/em&gt; chunks. A standalone translation of chunk #5 has no clue that the protagonist just entered a dark cave in chunk #4. Two techniques solved this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sliding window of previous sentences&lt;/strong&gt; — we include the last 5–10 sentences from the preceding chunk directly in the prompt as “context left.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A running summary&lt;/strong&gt; — after translating a chunk, we ask the LLM to generate a one‑sentence summary of that chunk. This summary is accumulated and fed into every subsequent prompt, so the model remembers high‑level events.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previous_context_sentences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary_so_far&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;context_left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previous_context_sentences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are translating a book. Here is a summary of the story so far:
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;summary_so_far&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

    And the previous text (for immediate context):
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context_left&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;

    Now translate the following text to Spanish, preserving tone and style:
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The summary is generated using a separate, cheap call (we use DeepSeek for summaries, even if the main translation uses Claude). This keeps the context token usage minimal while still giving long‑range coherence.&lt;/p&gt;

&lt;p&gt;Why not just include the entire previous chunk? That doubles the token count per call. On a 200K‑word book, that adds up to hundreds of dollars. Summaries cut that cost by ~80% with negligible quality loss.&lt;/p&gt;

&lt;p&gt;The translation loop then looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;overall_summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="n"&gt;previous_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;full_translation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chapter_chunks&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_chunks_by_chapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;chapter_summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chapter_chunks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;previous_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;chapter_summary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;overall_summary&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;translated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;full_translation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Update context: keep last 5 sentences of the translated chunk as next context
&lt;/span&gt;        &lt;span class="n"&gt;trans_sents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sentence_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;previous_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trans_sents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

        &lt;span class="c1"&gt;# Generate chunk summary asynchronously to save time
&lt;/span&gt;        &lt;span class="n"&gt;chunk_summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize this passage in one sentence: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;chapter_summary&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk_summary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;overall_summary&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chapter_summary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We process chunks concurrently using &lt;code&gt;asyncio&lt;/code&gt; and &lt;code&gt;httpx&lt;/code&gt; to keep translation times reasonable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real‑world results and trade‑offs
&lt;/h2&gt;

&lt;p&gt;Translating a 120K‑word Spanish novel (“El Quijote”) into English took about 4 minutes end‑to‑end with Claude 3 Haiku. Total API cost: $0.67. The translation was surprisingly fluid — chapters felt connected, and the occasional flashback or pronoun reference (“she” referring to a character introduced three pages earlier) was correctly resolved. Without the context pipeline, the same book would have been riddled with inconsistencies.&lt;/p&gt;

&lt;p&gt;We experimented with other models: DeepSeek‑V3 gave similar quality at half the price but with higher latency, making it better for batch jobs where speed isn’t critical. GPT‑4 Turbo reproduced stylistic flourishes more naturally, but its 16K context window forced us to use even smaller chunks, which sometimes fragmented dialogue. Claude struck the best balance.&lt;/p&gt;

&lt;p&gt;But it’s not perfect. Humor and idioms still occasionally fall flat because the summary can’t encapsulate a running joke. Code blocks and tables inside technical books need special handling — we’re working on a parser that detects them and wraps them in &lt;code&gt;[CODE]&lt;/code&gt; markers so the LLM doesn’t try to translate variable names. And poetry, with its line breaks and meter, remains a challenge; we’re considering a dedicated poetry‑aware chunker.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key takeaway
&lt;/h2&gt;

&lt;p&gt;If you’re building long‑document translation using LLMs, invest in a pipeline that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Respects document structure&lt;/strong&gt; (chapters, paragraphs) before splitting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Splits on sentences&lt;/strong&gt;, and always leaves room for context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provides both immediate context&lt;/strong&gt; (last few sentences) and &lt;strong&gt;global context&lt;/strong&gt; (summaries) to each chunk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uses separate, cheap models&lt;/strong&gt; for auxiliary tasks like summarization to keep costs down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our code is not open‑source yet, but we plan to release the core chunking library once we’ve battle‑tested it on more formats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you handle context in LLM translations?&lt;/strong&gt; We’re especially curious about handling highly technical books with equations, footnotes, and cross‑references. Drop your ideas in the comments — let’s figure this out together.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>The Hidden Masterpieces of Japanese Crime Fiction: Novels Still Waiting for English Translation</title>
      <dc:creator>龚旭东</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:42:59 +0000</pubDate>
      <link>https://dev.to/jacob_gong/the-hidden-masterpieces-of-japanese-crime-fiction-novels-still-waiting-for-english-translation-54gk</link>
      <guid>https://dev.to/jacob_gong/the-hidden-masterpieces-of-japanese-crime-fiction-novels-still-waiting-for-english-translation-54gk</guid>
      <description>&lt;p&gt;&lt;em&gt;Discover the works of Seishi Yokomizo, Soji Shimada, and other giants of the genre that remain out of reach for English-only readers.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’re a fan of Japanese mystery novels in English, you’ve probably noticed a golden age of translation in recent years. Publishers like Pushkin Vertigo, Bento Books, and Locked Room International have brought us long-awaited classics by &lt;strong&gt;Seishi Yokomizo&lt;/strong&gt;, &lt;strong&gt;Soji Shimada&lt;/strong&gt;, and other masters of &lt;strong&gt;Japanese crime fiction&lt;/strong&gt;. But for every &lt;em&gt;Honjin Murders&lt;/em&gt; or &lt;em&gt;Tokyo Zodiac Murders&lt;/em&gt; that finally lands on our shelves, dozens of equally brilliant works remain hidden behind the language barrier.&lt;/p&gt;

&lt;p&gt;I still remember the frustration of finishing &lt;strong&gt;Yokomizo’s&lt;/strong&gt; &lt;em&gt;The Inugami Curse&lt;/em&gt; and discovering that only a handful of his 77 Kosuke Kindaichi novels had been translated. The same goes for &lt;strong&gt;Shimada’s&lt;/strong&gt; intricate locked-room puzzles—the English-speaking world has tasted only a fraction of his genius. This article is for readers who want to go deeper, who refuse to let language be a wall between them and the stories they crave. We’ll explore the untranslated jewels of Japanese mystery, and I’ll show you practical ways to access them now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Japanese Crime Fiction Captivates the World
&lt;/h2&gt;

&lt;p&gt;Japanese mystery novels aren’t just puzzles with a Tokyo backdrop. They blend meticulous plotting with a literary sensibility that often feels more adult, more psychologically nuanced than their Western counterparts. Classic &lt;em&gt;honkaku&lt;/em&gt; (orthodox) mysteries by authors like &lt;strong&gt;Yokomizo&lt;/strong&gt; and &lt;strong&gt;Shimada&lt;/strong&gt; treat the whodunit as a rigorous intellectual game, where every clue is laid out fairly for the reader. Yet the atmosphere is soaked in history, folklore, and post-war tension—elements that give these stories a depth beyond the puzzle.&lt;/p&gt;

&lt;p&gt;Meanwhile, the social crime novels of Seicho Matsumoto and the psychological thrillers of Natsuo Kirino have already proven their international appeal. But beneath the well-translated surface lies a vast reservoir of mid-century and contemporary works that Anglophone readers have never seen. Many of these are considered landmarks in Japan, yet they remain invisible abroad simply because no publisher has taken a chance on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Translation Gap: A Numbers Game
&lt;/h2&gt;

&lt;p&gt;Let’s talk numbers. &lt;strong&gt;Seishi Yokomizo&lt;/strong&gt; alone wrote over 70 novels featuring the scruffy, brilliant detective Kosuke Kindaichi. So far, Pushkin Vertigo has translated about 10 of them—impressive, but just the tip of the iceberg. &lt;strong&gt;Soji Shimada&lt;/strong&gt;, the godfather of the logic-obsessed &lt;em&gt;shin-honkaku&lt;/em&gt; movement, has had only a handful of his dozens of novels appear in English. And these are just the most famous names.&lt;/p&gt;

&lt;p&gt;The reasons are complex. Japanese-to-English literary translation is costly and time-consuming. Publishers are often risk-averse, preferring to invest in authors with proven international sales records. Kulturkampf titles that require background knowledge of Japanese society can seem daunting to market. But the result is a frustrating feast-and-famine cycle: readers devour the few available translations and are left hungry for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Authors Whose Best Work Remains Locked Away
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Seishi Yokomizo: More Than the Honjin Murders
&lt;/h3&gt;

&lt;p&gt;Yokomizo’s popularity in the West has soared since the translation of his debut, &lt;em&gt;The Honjin Murders&lt;/em&gt;. Fans adore his macabre imagination, his rural settings steeped in family curses, and his detective Kindaichi, with his stutter and unassuming charm. But many of Yokomizo’s most acclaimed novels have never been translated.&lt;/p&gt;

&lt;p&gt;For example, &lt;em&gt;Akuma no Temari Uta&lt;/em&gt; (The Devil’s Hand-Tapping Song) is considered one of the finest locked-room mysteries in the Japanese canon. It involves a series of murders linked to an old nursery rhyme, and its denouement is both shocking and emotionally devastating. Another untranslated masterpiece is &lt;em&gt;Byoinzaka no Kubikukuri no Ie&lt;/em&gt; (The House of the Hanging on Hospital Hill), a labyrinthine tale of family secrets and ritualized death. While we can hope Pushkin Vertigo will eventually bring these to light, for now they exist only in Japanese.&lt;/p&gt;

&lt;h3&gt;
  
  
  Soji Shimada: Puzzles Waiting to Be Solved
&lt;/h3&gt;

&lt;p&gt;Soji Shimada’s Mitarai Kiyoshi series revolutionized the genre in the 1980s by embracing absurdly complex tricks and a Holmes-like detective. &lt;em&gt;The Tokyo Zodiac Murders&lt;/em&gt; and &lt;em&gt;Murder in the Crooked House&lt;/em&gt; have already achieved cult status in English. Yet some of Shimada’s most daring puzzles remain untranslated.&lt;/p&gt;

&lt;p&gt;Consider &lt;em&gt;Knight of the Underground Temple&lt;/em&gt; (Chikashitsuden no Kishi), in which a body is found inside a sealed underground chamber that appears both physically and magically locked. Or &lt;em&gt;The Kobe Beef Murder Case&lt;/em&gt; (Kōbe Gyū Satsujin Jiken), a deliciously titled story involving a impossible crime on a train. Shimada’s ability to stretch plausibility to its breaking point while still playing fair is on full display in these works. Reading them is like watching a master magician reveal his secrets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Giants of Untranslated Japanese Mystery
&lt;/h3&gt;

&lt;p&gt;Beyond the two most famous names, the well of &lt;strong&gt;Japanese crime fiction&lt;/strong&gt; goes deep. Tetsuya Ayukawa, known for his train alibi puzzles, has a vast oeuvre almost entirely untouched by English publishers. His detective, Inspector Onitsura, stars in dozens of stories that manipulate timetables with devilish precision. Shizuko Natsuki, a female pioneer of psychological suspense, wrote chilling novels like &lt;em&gt;The Third Lady&lt;/em&gt;—but most of her bibliography, including her award-winning debut, &lt;em&gt;Whispering Room&lt;/em&gt;, remains inaccessible. And Akimitsu Takagi’s later works, which blend horror with detection, also await discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific Untranslated Novels You Should Know About
&lt;/h2&gt;

&lt;p&gt;Here are five extraordinary Japanese mysteries that English readers can’t legally purchase in translation—and why they deserve to be on your radar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Akuma no Temari Uta (The Devil’s Hand-Tapping Song) by Seishi Yokomizo&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Why it matters&lt;/em&gt;: Often ranked among the greatest Japanese locked-room mysteries, this novel uses a macabre children’s song as the pattern for a series of murders. The solution is both ingenious and emotionally resonant, cementing Yokomizo’s reputation as a master of plotting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Underground Temple Knight (Chikashitsuden no Kishi) by Soji Shimada&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Why it matters&lt;/em&gt;: A quintessential Shimada locked-room extravaganza featuring a killing in a chamber sealed with magical symbols. The trick is characteristically wild yet logical, and the novel explores themes of obsession and illusion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Village of Doctor Ayakazu (Ayakazu-sense no Mura) by Tetsuya Ayukawa&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Why it matters&lt;/em&gt;: An intricate alibi puzzle set in a remote village where a doctor’s murder forces Inspector Onitsura to untangle a web of train schedules and lies. Ayukawa’s work is a must for fans of Crofts or Freeman.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Whispering Room (Sasayaku heya) by Shizuko Natsuki&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Why it matters&lt;/em&gt;: A tense psychological thriller about a woman whose new marriage hides dark secrets. Natsuki’s debut won the prestigious Edogawa Rampo Prize, yet it has never been published in English.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Invisible Mask (Mienai Kamen) by Akimitsu Takagi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Why it matters&lt;/em&gt;: A later Takagi novel where a Noh mask seems to hold the key to an impossible disappearance. Blending Japanese tradition with pure detection, it shows a master at the height of his powers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to Read These Novels Now (Without Waiting for Publishers)
&lt;/h2&gt;

&lt;p&gt;So what can you do if you’re dying to read these stories but don’t have strong Japanese? The good news is that technology and community have opened doors that didn’t exist a decade ago.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AI-Powered Translation Tools
&lt;/h3&gt;

&lt;p&gt;If you can obtain a digital Japanese text (e.g., from an ebook store like BookWalker or a digital library), tools like &lt;strong&gt;LectuLibre&lt;/strong&gt; let you upload the file and receive a full machine translation in minutes. While AI translations aren’t perfect—they can miss nuance, especially with cultural references or idiomatic dialogue—they have improved dramatically and can now produce readable, coherent prose. Many fans have already used these tools to enjoy untranslated manga and novels. If you’re patient and willing to gloss over occasional awkward phrasing, services like LectuLibre can be your personal key to the locked treasure chest of Japanese mystery.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Learn Japanese Through Your Love of Books
&lt;/h3&gt;

&lt;p&gt;Obviously, learning Japanese is the ultimate solution. The journey from zero to reading a Yokomizo novel is long, but it’s a rewarding hobby that pays dividends across all your interests. Start with graded readers, move on to young adult mysteries, and gradually work your way up. The &lt;em&gt;honkaku&lt;/em&gt; style, with its repetitive vocabulary around crime scenes and alibis, can actually be a helpful entry point for genre-focused learners.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Join Fan Translation Communities
&lt;/h3&gt;

&lt;p&gt;Online forums like Reddit’s r/JapaneseMystery and dedicated Discord servers bring together bilingual fans who sometimes produce unofficial translations. While the quality varies, some are excellent and keep the torch burning for forgotten classics. Just remember to support official releases when they finally arrive.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Advocate and Wait
&lt;/h3&gt;

&lt;p&gt;The more we talk about these books, the more likely publishers are to take notice. Review translated works, request them at your library, and mention untranslated titles on social media. Pushkin Vertigo and others have shown they listen to reader demand—&lt;em&gt;The Honjin Murders&lt;/em&gt; itself was published after years of fan campaigning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Japanese Mystery in English
&lt;/h2&gt;

&lt;p&gt;The recent translation wave gives us hope. Yokomizo’s entire Kindaichi series may eventually see the light of day. Shimada might finally get the broader recognition his genius deserves. And new voices like Keigo Higashino continue to build bridges between cultures.&lt;/p&gt;

&lt;p&gt;But there will always be more stories than there are translators and publishing slots. That’s where passion comes in. Whether you use AI tools, start learning Japanese, or simply voice your enthusiasm, you’re part of a global conversation that brings these novels across borders.&lt;/p&gt;

&lt;p&gt;In the meantime, I’ll be over here trying to decipher the scene of the crime in &lt;em&gt;Akuma no Temari Uta&lt;/em&gt; with a machine translation and a kanji dictionary. Won’t you join me?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy reading—or, should I say, happy detecting.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>japaneseliterature</category>
      <category>mystery</category>
      <category>translation</category>
      <category>books</category>
    </item>
  </channel>
</rss>
