File Structure for Translation Service Implementations in FluentRead: A Modular Architecture Guide
FluentRead organizes every translation engine as an isolated module under entrypoints/service/, with each engine exporting a single async function that handles API communication, while a central registry in _service.ts maps service identifiers to these implementations for dispatch by the background script.
FluentRead is an open-source browser extension that unifies multiple translation providers under a single interface. Understanding the file structure for translation service implementations in FluentRead is essential for contributors looking to add new engines or customize existing ones. The codebase follows a strict modular pattern where each translation service is self-contained, making the system highly extensible and maintainable.
Modular Architecture in entrypoints/service/
One File Per Translation Engine
Each translation provider lives in its own TypeScript file within entrypoints/service/. This isolation ensures that HTTP logic, authentication handling, and response parsing for each provider never interferes with others.
Key implementation files include:
microsoft.ts– Microsoft Translator with token refresh logicyoudao.ts– Youdao translation with signature generationdeepl.ts– DeepL API integration with proxy supportdeeplx.ts– DeepLX self-hosted implementationgoogle.ts– Google Translate enginexiaoniu.ts– 小牛翻译 integrationtencent.ts– 腾讯云翻译hunyuan-translation.ts– 腾讯混元大模型翻译openai.ts,gemini.ts,claude.ts– AI-based translation services
Standardized Function Signature
Every engine module exports a default async function with a consistent signature. This standardization allows the central registry to invoke any service interchangeably.
// entrypoints/service/youdao.ts (example pattern)
export default async function youdao(message: any): Promise<string> {
// Extract text and context from message
const { origin, context } = message;
// Perform API call with config from utils/config.ts
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ q: origin, from: config.from, to: config.to })
});
// Parse and return translated text
const data = await response.json();
return data.translation[0];
}
Central Service Registry in _service.ts
The entrypoints/service/_service.ts file acts as the central dispatcher. It imports all engine modules and exports a ServiceMap object that maps service identifiers to their implementations.
// entrypoints/service/_service.ts
import microsoft from './microsoft';
import youdao from './youdao';
import deepl from './deepl';
import google from './google';
// ... other imports
export const _service = {
[services.microsoft]: microsoft,
[services.youdao]: youdao,
[services.deepl]: deepl,
[services.google]: google,
// ... other mappings
};
The services constants are defined in entrypoints/utils/option.ts, ensuring type safety across the codebase. This registry pattern allows the background script to look up and invoke the correct engine dynamically based on user configuration.
Request Flow from UI to Translation Engine
Understanding the file structure requires seeing how data flows through the system:
- UI Layer: Components call
translateText()fromentrypoints/utils/translateApi.ts - Queue Management:
translateApi.tsusesentrypoints/utils/translateQueue.tsto limit concurrent requests (default: 6 parallel translations) - Background Script: The queued task sends a
runtime.sendMessagetoentrypoints/background.ts - Service Dispatch:
background.tsretrieves the currentconfig.servicevalue and executes_service[config.service](message) - Engine Execution: The specific module in
entrypoints/service/performs the HTTP request and returns the translated string
Adding a New Translation Service
To implement a new provider, modify only two files:
Step 1: Create the engine file in entrypoints/service/
// entrypoints/service/libreTranslate.ts
import { config } from "@/entrypoints/utils/config";
export default async function libreTranslate(message: any): Promise<string> {
const url = config.proxy[config.service] || "https://libretranslate.com/translate";
const resp = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
q: message.origin,
source: config.from === "auto" ? "auto" : config.from,
target: config.to,
format: "html"
})
});
if (!resp.ok) {
throw new Error(`LibreTranslate failed: ${resp.status}`);
}
const result = await resp.json();
return result.translatedText;
}
Step 2: Register in entrypoints/service/_service.ts
import libreTranslate from "./libreTranslate";
export const _service = {
// ... existing services
[services.libreTranslate]: libreTranslate,
};
Step 3 (Optional): Add the identifier to entrypoints/utils/option.ts if not already present.
Key Files in the Translation Architecture
| File | Purpose |
|---|---|
entrypoints/service/ |
Directory containing one module per translation engine (e.g., microsoft.ts, youdao.ts, deepl.ts) |
entrypoints/service/_service.ts |
Central registry mapping service identifiers to implementation functions |
entrypoints/utils/option.ts |
Defines services constants and helper predicates (isMachine, isAI) |
entrypoints/utils/translateApi.ts |
Public API for UI components; handles caching, retries, and queueing |
entrypoints/utils/translateQueue.ts |
Concurrency controller limiting parallel translation requests |
entrypoints/background.ts |
Message broker that dispatches requests to the appropriate service module |
entrypoints/utils/config.ts |
Runtime configuration store (selected service, API keys, language pairs) |
Summary
- FluentRead uses a modular file structure where each translation service is isolated in its own file under
entrypoints/service/. - The
_service.tsregistry acts as a central dispatcher, mapping service identifiers to engine implementations. - Adding a new provider requires creating a single file in
entrypoints/service/and registering it in_service.ts. - The architecture separates concerns: UI components use
translateApi.ts, queue management handles concurrency, andbackground.tsroutes requests to the specific engine. - This design makes the codebase extensible, testable, and maintainable for contributors adding new translation providers.
Frequently Asked Questions
Where are translation services implemented in FluentRead?
All translation engines are implemented as individual TypeScript modules in the entrypoints/service/ directory. Each file (such as microsoft.ts, youdao.ts, or deepl.ts) exports a single async function that handles the HTTP communication with that specific provider's API.
How do I add a new translation service to FluentRead?
To add a new service, create a new file in entrypoints/service/ that exports a default async function receiving a message object and returning a translated string. Then import and register this function in entrypoints/service/_service.ts by adding it to the _service map using the appropriate service identifier from utils/option.ts.
What is the role of _service.ts in FluentRead's translation architecture?
The entrypoints/service/_service.ts file serves as the central registry that maps service identifiers (like services.microsoft or services.youdao) to their corresponding implementation functions. When the background script receives a translation request, it uses this map to dispatch the request to the correct engine module based on the user's current configuration.
How does FluentRead handle concurrent translation requests?
FluentRead implements a concurrency controller in entrypoints/utils/translateQueue.ts that limits the number of simultaneous translation requests (defaulting to 6 parallel translations). When UI components call translateText() from entrypoints/utils/translateApi.ts, requests are enqueued and processed according to this limit, preventing rate limiting issues with external APIs and managing browser resource usage.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →