Shinkansen Browser Extension

repository·main·Indexed 19 days ago

https://github.com/jimmysu0309/shinkansen

A high-performance, privacy-centric browser extension for translating web pages, YouTube subtitles, and documents (PDF/EPUB). It supports multiple AI engines, including Google Gemini and OpenAI-compatible custom models, and features local data storage for privacy. Key capabilities include real-time YouTube subtitle translation with AI smart sentence splitting, layout-preserving PDF translation, EPUB eBook translation with terminology consistency, and specialized workflows for Google Docs.

Tokens
45.7K
Snippets
72
Records
181
Agent score
64%

What's inside Shinkansen

  1. Overview of Shinkansen features

    main

    Shinkansen is a privacy-first translation extension that supports:

    • Multi-language support: Translate into 8 languages (Traditional/Simplified Chinese, English, Japanese, Korean, Spanish, French, German).
    • Translation Modes:
      • Overlay: Replaces text in place while preserving layout, fonts, and links.
      • Bilingual: Appends translations as new paragraphs with four visual treatments (subtle background tint, left border, dotted underline, or none).
    • Engines: Supports Gemini (AI-based, requires API key), Google Translate (unofficial free endpoint, no key required), and Custom models (any OpenAI-compatible endpoint like OpenRouter, Together, Groq, or local Ollama).
    • Specialized Translation:
      • YouTube: Real-time subtitle translation and AI-powered semantic re-segmentation for ASR captions.
      • Documents: Full-page translation for PDF (preserving layout) and EPUB (supporting book-wide glossaries).
      • Chinese Variant Conversion: Local, offline conversion between Simplified and Traditional Chinese using built-in OpenCC dictionaries.
    • Customization: Includes custom glossaries (global and domain-specific), blocked-word lists, and auto-translate whitelists for specific domains.
  2. Overview of Shinkansen features and scope

    main

    Shinkansen is a privacy-focused Chrome Extension designed to translate web content (English or other foreign languages) into Traditional Chinese (Taiwan).

    Core Capabilities (as of v2.0.84):

    • Web Translation: Toggle between Gemini (Option+S) and Google Translate (Option+G). Supports single-language overwrite or bilingual side-by-side modes. Uses progressive batch injection and allows restoring original text.
    • Bilingual Mode: Appends translations using a <shinkansen-translation> wrapper. Supports 4 visual marking styles.
    • YouTube Subtitle Translation: Supports XHR pre-translation and on-the-fly backup. Handles ASR (Automatic Speech Recognition) with specialized sentence boundary alignment to prevent duplicate LLM calls. Works on both desktop and m.youtube.com.
    • SPA Support: Uses History API interception, URL polling, and MutationObserver to handle Single Page Applications.
    • Terminology Management: Automatically extracts proper nouns using Gemini before translation. Supports global and domain-specific fixed glossaries that override LLM translations.
    • Translation Cache: Uses chrome.storage.local with SHA-1 keys to cache results.
    • Floating Icon: A draggable icon (content-floating-icon.js) for quick access. Supports short presses for default translation and long presses for preset menus. Includes specific safety positioning for iPadOS to avoid system gesture conflicts.
    • Target Languages: Supports Traditional Chinese (Taiwan), Simplified Chinese (China), and English. For non-Taiwanese targets, a {targetLanguage} variable is injected into the universal prompt.

    Out of Scope:

    • Mouse hover translation, input box translation, or text selection translation.
    • Third-party paid services like DeepL or Yandex.
    • Google Cloud Translation v2 API (paid version).
    • Multi-language UI or light/dark theme switching.
  3. Use the 'Deduplicate Annotation' feature for bilingual text

    main

    The dedupeAnnotation feature manages how bilingual annotations (e.g., Original (Translation)) appear throughout the book.

    How it works

    When a term is identified as an annotation (using the A (B) format with full-width parentheses), you can configure its behavior:

    • Show once: The first occurrence in the book retains the full Original (Translation) format.
    • Subsequent occurrences: You can choose to either use only the Original text or only the Translation for all following appearances.

    Automatic Formatting

    • Token Reversal: If you select "Subsequent use Original", the translation field automatically flips to Original (Translation). If you select "Subsequent use Translation" or uncheck the option, it flips to Translation (Original).
    • Book Titles: The system respects book title markers (《》). When reversing tokens, the markers stay with the lead token (e.g., 《Title》 (Translation) $\leftrightarrow$ 《Translation》 (Title)).
  4. Understand the Glossary (Terminology) Strategy

    main

    Shinkansen uses a glossary system to ensure consistency in technical terms and proper nouns across long documents. The system behaves differently based on the document length (measured in batches).

    Glossary Lifecycle:

    1. Extraction: Before translating, Gemini is called to extract a glossary of entities (People, Places, Technical Terms, Works) from the text.
    2. Thresholds:
      • Small docs (≤ skipThreshold, default 1 batch): Glossary is skipped entirely.
      • Medium docs (skipThreshold < batches ≤ blockingThreshold, default 10): 'Fire-and-forget' mode. Translation starts immediately without waiting for the glossary.
      • Large docs (> blockingThreshold): 'Blocking' mode. The system waits for the glossary to be fully generated before starting translation to ensure consistency.

    Configuration:

    • glossary.enabled: Set to true to use this feature (default is false).
    • glossary.maxTerms: Maximum number of terms to extract (default 200).
    • glossary.timeoutMs: Timeout for glossary extraction (default 60000ms).
    • glossary.skipThreshold / glossary.blockingThreshold: Controls the batch-count logic described above.
  5. Detect and Resolve Terminology Drift (Multiple Translations for One Source)

    main

    The system detects "Terminology Drift"—when the same source term is translated into different variations throughout a document. The workflow is as follows:

    1. Candidate Mining: The system identifies potential terms by looking for continuous uppercase words or Katakana sequences in the source text that appear in $\ge 2$ paragraphs.
    2. Comparison Extraction: The system samples up to 6 translation segments and uses an LLM via the SCAN_TERM_RENDERINGS command to identify the actual string variations used in the translation.
    3. Aggregation: The system filters out hallucinations and groups similar variations. If $\ge 2$ distinct translations are found for one source term, it is flagged as a "Drift Case."
    4. Resolution:
      • The results page lists each variation with context (surrounding 40 characters).
      • Apply: Selecting a preferred translation replaces all other variations in the relevant paragraphs using editedHtml logic. This is applied to the current session/translation.
      • Add to Terminology: You can independently click a button to write the selected translation into the project's global terminology source for future translations.
      • Skip: If a variation is intentional (e.g., due to context like date formats), you can skip it to prevent future detection.
  6. Use Strong Segment Markers for Weak LLMs

    main

    When using custom providers with batch translation, Shinkansen can use strong segment markers to prevent weak models (like quantized local LLMs) from mistranslating segment numbers into the target text.

    • Strong Marker (useStrongSegMarker: true): Uses <<<SHINKANSEN_SEG-N>>>. This is more robust but adds ~7 tokens of overhead per segment.
    • Compact Marker (useStrongSegMarker: false): Uses «N». This is more token-efficient but may be less reliable with weaker models.

    Note: The Gemini primary path always uses the compact «N» format and is not affected by this toggle.

  7. Configure translation shortcuts and presets

    main

    Shinkansen uses three customizable presets, each mapped to a shortcut. You can customize the engine (Gemini or Google Translate), the specific model (e.g., Flash, Flash Lite, Pro), the display label, and the keybinding itself in the "Translation shortcuts" section of the settings page.

    Default Presets

    ShortcutDefault engineDefault modelBest for
    Alt+A / Option+AGeminiFlash LiteCasual translation, max savings
    Alt+S / Option+SGeminiFlashDaily reading, best quality / value
    Alt+D / Option+DGoogle TranslateNo API key needed, fast, free

    Keybinding Behavior

    • Customizing keys: Click a preset's key field in settings and press the desired combination.
      • Chrome: Use ⌥ Option or ⌃ Control.
      • Safari (Mac/iPad/iPhone): Use ⌃ Control (iOS Safari does not pass or to web pages).
      • Browser-level overrides: You can also adjust default keys via chrome://extensions/shortcuts (Chrome) or about:addons (Firefox).
    • Unified cancel/restore logic:
      • Press a shortcut while translating $\rightarrow$ cancel translation immediately.
      • Press a shortcut while translated $\rightarrow$ restore the original language.
    • Cross-tab continuity: If you trigger a translation in Tab A, any link opened from Tab A into a new tab (via Cmd-click, Ctrl-click, target="_blank", or window.open) will automatically use the same preset. This does not apply to tabs opened via bookmarks, URLs, or external apps.
    <!-- Note: Keybindings are configured via the UI in the settings page -->
  8. Trace the page translation data flow

    main

    When a user triggers a page translation (via Option+S or the Popup), the following lifecycle occurs:

    1. Collection: content.js calls collectParagraphs() to traverse the DOM and gather translation units.
    2. Batching: packBatches() groups these units into batches based on character budget and maximum paragraph counts.
    3. Terminology: A glossary/terminology process is applied (strategy depends on article length).
    4. Execution: runWithConcurrency() sends batches in parallel. Each batch is sent via a TRANSLATE_BATCH message to the background.js service worker.
    5. API Call: The background handler checks the cache.js first. If there is a cache miss, it calls the Gemini API.
    6. Injection: As batches return, injectTranslation immediately updates the DOM, and the content-toast.js system updates the user on progress.
    7. Completion: Once all batches are processed, a success Toast is displayed showing token usage, estimated cost, and cache hit rate.
  9. How Language Detection Works

    main

    Shinkansen uses several signals to detect the source language and decide whether to skip translation (if the source matches the target):

    SignalDetected Language
    htmlLang starts with jaja
    htmlLang starts with koko
    Hiragana/Katakana ratio > 5%ja
    Hangul (U+AC00-D7AF) ratio > 5%ko
    CJK ratio $\ge$ 0.5 + Simplified char ratio $\ge$ 0.2zh-Hans (Target: zh-CN)
    CJK ratio $\ge$ 0.5 + Simplified char ratio < 0.2zh-Hant (Target: zh-TW)
    ASCII letter ratio $\ge$ 0.5 + CJK ratio < 0.05en

    Note on Latin-based languages: For es, fr, and de, the detector defaults to en. Shinkansen will attempt to translate them to ensure accuracy, even if it occasionally results in 'echoing' (re-translating English to English).

  10. How Instapaper integration works

    main

    Shinkansen uses the Instapaper Full API (OAuth 1.0a + xAuth) via lib/instapaper.js to save translated content.

    Key technical details:

    • Content Injection: Instead of sending just a URL (which would cause Instapaper to re-crawl the original untranslated source), Shinkansen sends the cleaned, translated body text via the content parameter in the bookmarks/add endpoint. This ensures the saved article contains the translation.
    • Privacy Settings: The is_private_from_source parameter is not set. This allows Instapaper to use the provided content while maintaining the original source URL link. Setting this parameter would make the bookmark private and lose the source link.
    • Authentication: Users authenticate via the options page using their Instapaper email and password to exchange credentials for an instapaperXAuth token. Only instapaperToken, instapaperTokenSecret, and instapaperUsername are stored in storage.sync; the password is discarded after use.
    • Trigger Paths:
      • Popup Button: The #send-to-instapaper-btn appears only if instapaperEnabled is true and the user is linked. Clicking it triggers saveToInstapaper directly from the popup to avoid background suspension issues on iOS.
      • Hotkey (Alt+I): Triggers the commands.send-to-instapaper command. The background script extracts the page HTML and calls saveToInstapaper.
    // Triggered via popup or Alt+I
    // Uses lib/instapaper.js
    saveToInstapaper(content, url, title);
  11. How translation cache keys are constructed

    main

    Shinkansen uses a sophisticated keying system for its translation cache (tc_) to ensure that different translation contexts (different models, prompts, or languages) do not collide.

    A base key is formed by tc_ + the SHA-1 hash of the original text (43 characters total). Various suffixes are then appended to differentiate the context:

    SuffixDescription
    _ytYouTube subtitle mode
    _gtGoogle Translate web mode
    _gt_ytGoogle Translate subtitle mode
    _ocCustom OpenAI-compatible provider
    '' (empty)Standard Gemini web translation
    _g<hash>Includes a glossary (hash of the combined glossary terms)
    _b<hash>Includes a forbidden terms list (hash of the sorted forbidden terms)
    _m<model>Includes the model name (safe string)
    _m<baseUrlHash>_<model>For custom providers: includes baseUrl hash and model name
    _lang<targetLang>Includes the target language (e.g., _langen). Note: zh-TW does not use this suffix for backward compatibility.
    _t<n.nn>For document translation: includes the temperature (e.g., _t1.00)
    _x<hash>For document translation: includes a hash of the extraPrompt

    Example Key Format: tc_<sha1>_g<g>_b<b>_m<m>_lang<x>_t<t>_x<x>

  12. How Shinkansen preserves inline HTML during translation

    main

    To prevent losing links and formatting, Shinkansen uses a placeholder-based serialization process (Path A):

    1. Serialization: Recursive replacement of inline elements with placeholders like ⟦N⟧...⟦/N⟧.
    2. Translation: The LLM translates the text while keeping placeholders intact.
    3. Deserialization: Reconstructs the DocumentFragment using the placeholders.
    4. Injection: Injects the fragment into the target element.

    Supported Inline Tags (PRESERVE_INLINE_TAGS): A, STRONG, B, EM, I, CODE, MARK, U, S, SUB, SUP, KBD, ABBR, CITE, Q, SMALL, DEL, INS, VAR, SAMP, TIME.

    Special Rules:

    • <span>: Only preserved if it has a class or non-empty style attribute.
    • Atomic Preservation: Elements like <sup class="reference"> are treated as atomic units (cloned into a slot) and their internal text is not sent to the LLM.