A full-stack web app for translating SubRip (.srt) subtitle files using OpenAI, Claude, Gemini, and DeepSeek — with real-time progress, structure preservation, and per-user file isolation.
- 🚀 Key Features
- 🏗️ Architecture
- ⚙️ Installation & Setup
- 🔧 Configuration
- ⚡ How It Works
- 🌐 Deployment (Coolify)
- ☕ Support the Project
- 📄 License
- Web UI: Modern React interface with drag-and-drop file upload, real-time progress bars, and settings management
- Per-User Isolation: Each browser gets a unique session — uploaded files and translations are private via session cookies
- Client-Side Settings: All settings (API key, model, language, matching/removal words) stored in IndexedDB — nothing sensitive on the server
- Real-Time Progress: WebSocket-based live translation progress that persists across page navigation
- Parallel Translation: Subtitles are translated concurrently (configurable concurrency) for faster results
- Structure Preservation: Maintains exact SRT structure including cue indices, timestamps, and line counts
- HTML Tag Protection: Preserves inline HTML tags (
<i>,<b>,<font>, etc.) and entities - Word Replacement System: Post-translation term replacement using
source --> targetmatching files - Word Removal: Remove unwanted words/patterns from translations
- Smart Credits Management: Automatically detects, replaces, and inserts translator credits at optimal locations
- Bulk Edit/Delete: Batch operations for matching words and removal words management
- Old Files Browser: View previously uploaded and translated files with download/delete actions
- Automatic Cleanup: Files older than 7 days are automatically deleted; each file shows days remaining
┌─────────────────────────────────────────────────────┐
│ Frontend (React 19 + Vite + Tailwind CSS 4) │
│ │
│ IndexedDB ─── settings, matching words, │
│ removal words (per-browser) │
│ Pages ──────── Home (translate), Old Files, Settings │
│ Context ────── TranslationContext (global state) │
└──────────────────────┬──────────────────────────────┘
│ /api/* REST + /ws/* WebSocket
┌──────────────────────▼──────────────────────────────┐
│ Backend (FastAPI + Uvicorn) │
│ │
│ SessionCookieMiddleware ─── UUID cookie per browser │
│ File Storage ───────────── data/subtitles/{sid}/ │
│ data/translated/{sid}/ │
│ Translation ────────────── OpenAI API (parallel) │
│ WebSocket ──────────────── Real-time progress │
└─────────────────────────────────────────────────────┘
srt-translator/
├── backend/
│ ├── main.py # FastAPI app + session middleware
│ ├── config.py # Directory constants
│ ├── core/
│ │ ├── translate.py # SRTTranslator engine
│ │ ├── openai_client.py # OpenAI API wrapper
│ │ ├── placeholders.py # HTML/word protection & replacement
│ │ ├── credits.py # Credits detection & replacement
│ │ └── word_removal.py # Word removal logic
│ ├── routers/
│ │ ├── files.py # Upload, list, download, delete
│ │ └── translation.py # Translate + WebSocket progress
│ ├── schemas/
│ │ ├── files.py # FileInfo, UploadResponse models
│ │ └── translation.py # TranslationRequest, Settings models
│ └── services/
│ ├── file_service.py # Session-scoped file operations
│ └── translation_service.py # Job management + parallel translation
├── frontend/
│ └── src/
│ ├── App.tsx # Routes + providers
│ ├── api/ # REST client, filesApi, translationApi
│ ├── components/ # Button, FileDropZone, ProgressBar, etc.
│ ├── contexts/
│ │ └── TranslationContext.tsx # Global translation state
│ ├── hooks/ # useSettings, useFileUpload
│ ├── pages/
│ │ ├── HomePage.tsx # Main translate page
│ │ ├── OldFilesPage.tsx # Browse uploaded/translated files
│ │ └── settings/ # General, MatchingWords, RemoveWords
│ ├── types/ # TypeScript type definitions
│ └── utils/
│ ├── constants.ts # API URLs, AI platform options
│ ├── db.ts # IndexedDB operations
│ └── helpers.ts # Formatting utilities
├── Dockerfile # Multi-stage build (Node + Python)
├── .dockerignore
└── data/ # Runtime file storage (ephemeral in Docker)
- Python 3.13+
- Node.js 22+
- npm
git clone https://github.com/ntamasM/srt-translator.git
cd srt-translatorcd backend
pip install -r requirements.txt
uvicorn main:app --reload --port 8000cd frontend
npm install
npm run devThe frontend dev server proxies /api and /ws requests to the backend at localhost:8000.
Open http://localhost:5173 in your browser.
Build and run with Docker:
docker build -t srt-translator .
docker run -p 8000:8000 srt-translatorThe app serves both the API and the built frontend on port 8000.
All settings are configured through the web UI and stored in your browser's IndexedDB. Nothing is stored on the server.
| Setting | Default | Description |
|---|---|---|
| AI Platform | OpenAI | AI provider (OpenAI, Gemini, Claude) |
| API Key | — | Your API key (stored only in your browser) |
| Model | gpt-4o-mini | Model to use for translation |
| Temperature | 0.2 | Sampling temperature (0–2) |
| Top P | 0.1 | Top-p sampling parameter (0–1) |
| Source Language | en | Source language code |
| Target Language | el | Target language code |
| Translator Name | Ntamas | Name for translator credits |
| Case-Insensitive Matching | false | Match words regardless of case |
| Replace Credits | true | Replace existing translator credits |
| Add Credits | true | Add translator credits to output |
| Append Credits at End | false | Force credits at end of file |
Manage word replacement pairs via Settings → Matching Words. Supports bulk edit and bulk delete. Format: source → target.
These are sent with each translation request and applied post-translation.
Manage words to remove via Settings → Remove Words. Supports bulk edit and bulk delete.
The matching system applies post-translation word replacement:
- Translation: AI translates the subtitle
- Replacement: Specified terms are replaced using your matching word pairs
- Boundaries: Uses word boundaries to avoid partial replacements
Remove unwanted words or patterns from subtitles:
- Normal words: Uses word boundaries (removes "word" from "word text" but not from "password")
- Special patterns: Removes pattern anywhere it appears (e.g.,
{\an8})
- Replace old credits: Detects and replaces existing translator credits
- Add new credits: Analyzes timing gaps (≥5 seconds) to find optimal placement
- Fallback: If no suitable gap exists, credits are added at the end
- Force end: Option to always place credits at the end of the file
- Credit Replacement — Replace existing translator credits (if enabled)
- Word Removal — Remove specified words from original text
- Translation — Translate text using OpenAI (parallel, 4 concurrent)
- Word Replacement — Apply matching word pairs
- Structure Restoration — Restore formatting and timing
- Credits Insertion — Add translator credits at optimal location
Each browser receives a unique session cookie (session_id, 30-day expiry). All file operations are scoped to this session:
- Uploads go to
data/subtitles/{session_id}/ - Translations output to
data/translated/{session_id}/ - One user cannot see another user's files
Translation progress is managed by a global React Context (TranslationContext). This means:
- You can navigate to Settings while a translation is running
- Progress bars, completed files, and download links persist when you return
- The WebSocket connection stays alive across page changes
To prevent unbounded disk usage, the server automatically cleans up old files:
- Files older than 7 days (based on modification time) are deleted
- Cleanup runs on server startup and every hour thereafter
- Applies to both uploaded and translated files across all sessions
- Empty session directories are removed after cleanup
- The Old Files page shows a countdown badge on each file indicating days remaining before deletion
To deploy on Coolify:
- Add Resource → GitHub repository → select
srt-translator - Build Pack →
Dockerfile - Ports Exposes →
8000 - Domains → your domain (e.g.,
https://srt-translator.example.com) - Deploy
If using Cloudflare proxy, set SSL mode to Full.
The Dockerfile uses a multi-stage build:
- Stage 1:
node:22-alpinebuilds the frontend (npm ci && npm run build) - Stage 2:
python:3.13-slimruns the backend + serves the built frontend - Built-in health check at
/api/health
If this tool has been helpful, consider supporting its development!
- ⭐ Star this repository on GitHub
- 🐛 Report bugs or suggest features
- 💬 Spread the word to other subtitle translators
MIT License — see LICENSE file for details.