How FluentRead Differs from Other Bilingual Translation Extensions: Architecture Deep Dive

FluentRead implements bilingual translation through a queue-driven, modular architecture that isolates translation engines, implements persistent local caching, and uses MutationObserver-based DOM injection—features rarely found in traditional browser extensions that typically rely on hard-coded API calls and tooltip overlays.

Most browser extensions handle bilingual translation through simple API calls and DOM overlays that break when pages update dynamically. The open-source FluentRead project (bistutu/fluentread) fundamentally reimagines how bilingual translation extensions should work, employing a sophisticated task queue system and modular service architecture that prioritizes performance, privacy, and extensibility.

Modular Translation Engine Architecture

Unlike extensions that hard-code a single provider directly into UI scripts, FluentRead isolates each translation engine in dedicated service modules under entrypoints/service/*.ts. Each adapter—whether for Google, DeepL, Azure, or Claude—exposes a simple async function that the central router consumes.

The translateApi.ts utility acts as the unified gateway, routing every request through translateQueue.ts. This separation of concerns allows developers to add new translation providers without modifying core logic, simply by creating a new file in the service directory and registering it in entrypoints/service/_service.ts.

Intelligent Queue and Retry Management

Where typical bilingual translation extensions fire requests immediately and fail silently on network errors, FluentRead implements a robust task queue in entrypoints/utils/translateQueue.ts. This system stores pending translation tasks, limits concurrent API calls to prevent rate limiting, and automatically retries failed requests with exponential back-off configured via TranslateOptions.

The queue manages lifecycle events meticulously. When users navigate away from a page, cancelAllTranslations clears pending tasks to prevent unnecessary network traffic. This approach eliminates the "fire and forget" fragility common in simpler extensions.

Persistent Local Caching

FluentRead's cache.ts module stores both raw translation results and bilingual-rendered output in chrome.storage.local. When users re-visit content or re-select previously translated text, the extension retrieves results instantly without redundant API calls.

This contrasts sharply with most bilingual translation extensions that lack persistent caching, forcing repeated remote requests for identical snippets and increasing latency.

DOM-Aware Bilingual Rendering

Traditional extensions overlay tooltips or replace original nodes, breaking page layouts and failing when content loads dynamically (infinite scroll, SPAs). FluentRead's handleBilingualTranslation function in entrypoints/main/trans.ts creates a wrapper node (fluent-read-bilingual) containing the original text alongside a fluent-read-bilingual-content element.

A MutationObserver watches the DOM continuously, ensuring newly loaded content receives automatic bilingual treatment without user intervention. This approach preserves the original document structure while supporting modern web applications that update content dynamically.

Dual Interaction Modes: Slide vs. Hover

FluentRead distinguishes between slide-translation and hover-translation through the slide flag. Slide mode triggers on mouse movement across the page and removes bilingual nodes instantly to avoid visual clutter. Hover mode displays a persistent bilingual block when users pause over specific elements.

Most competing extensions support only a single hover-tooltip style. FluentRead's dual-mode system adapts to different reading workflows, configured through config.ts and option.ts alongside extensive UI customization options including hotkeys, debounce delays, and per-site bilingual toggles.

Privacy-First Multi-Engine Fallback

FluentRead stores all translation results locally and never transmits user text to analytics endpoints—a critical differentiator from extensions that rely on cloud-hosted back-ends or telemetry. If the primary translation engine fails, the queue system can automatically re-queue requests with alternative services (Google → Azure → DeepL) without user intervention, ensuring translation continuity that single-engine extensions cannot match.

Implementation Examples

Triggering Bilingual Translation on Hover

// entrypoints/main/trans.ts – core hover handler
import { handleTranslation } from '@/entrypoints/main/trans';

document.addEventListener('mousemove', (e) => {
  // delayTime of 300 ms gives enough time for a hover
  handleTranslation(e.clientX, e.clientY, 300);
});

Programmatic Bilingual Translation

import { handleBilingualTranslation } from '@/entrypoints/main/trans';

const node = document.querySelector('.article-content p');
if (node) {
  // `false` = hover mode (keeps the bilingual node visible)
  handleBilingualTranslation(node, false);
}

Adding a Custom Translation Service

// src/entrypoints/service/custom.ts
import { method } from '../utils/constant';
import { config } from '@/entrypoints/utils/config';

export default async function custom(message: any) {
  const resp = await fetch('https://my-translator/api', {
    method: method.POST,
    body: JSON.stringify({ text: message.origin, from: config.from, to: config.to })
  });
  const { translation } = await resp.json();
  return translation;
}

Register this service in entrypoints/service/_service.ts to integrate it into the routing system.

Direct Queue Usage

import { enqueueTranslation } from '@/entrypoints/utils/translateQueue';

enqueueTranslation(async () => {
  const result = await translateText('你好世界', 'Example Page');
  console.log('Result →', result);
});

Summary

  • FluentRead employs a modular service architecture isolating translation engines in entrypoints/service/*.ts, unlike monolithic extensions.
  • A centralized task queue (translateQueue.ts) manages concurrency, retries, and cancellation, eliminating the fragile "fire and forget" approach of typical extensions.
  • Persistent local caching via cache.ts prevents redundant API calls for previously translated content.
  • MutationObserver-based rendering in handleBilingualTranslation supports dynamic DOM updates without breaking page layouts.
  • Dual interaction modes (slide vs. hover) and extensive customization through config.ts provide flexibility rare in bilingual translation extensions.
  • Privacy-first design with local-only storage and automatic multi-engine fallback ensures reliability without compromising user data.

Frequently Asked Questions

How does FluentRead handle dynamic content loading on modern web apps?

FluentRead uses a MutationObserver in entrypoints/main/trans.ts to monitor DOM changes continuously. When new content appears (infinite scroll, AJAX updates), the observer triggers handleBilingualTranslation automatically. This differs from traditional extensions that target static pages and fail when content updates dynamically.

Can I add my own translation API to FluentRead?

Yes. Create a new file in entrypoints/service/ (e.g., custom.ts) exporting an async function that accepts the message object and returns translated text. Register the service in entrypoints/service/_service.ts, and translateApi.ts will route requests to your implementation automatically, leveraging the existing queue and caching infrastructure.

Why does FluentRead use a queue system for translations?

The queue in entrypoints/utils/translateQueue.ts prevents rate limiting by controlling concurrent requests, implements exponential back-off for retries, and allows cancellation when users navigate away. Most bilingual translation extensions lack this layer, causing failed translations during network fluctuations and unnecessary API costs from duplicate requests.

Is my translation data private when using FluentRead?

Yes. FluentRead stores all results in chrome.storage.local via cache.ts and never transmits user text to remote analytics servers. The extension is fully open-source (available at bistutu/fluentread), allowing verification that translation data remains on your local device unlike cloud-dependent alternatives.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →