I'm a developer living in Sweden, and I'm still learning Swedish. The best practice material I've found isn't a course: it's public TV. SVT Play, UR, NRK and DR have huge free libraries, and almost every programme comes with subtitles in the original language.
The problem is the player. It shows one subtitle track at a time, and there is no subtitle in my language. Swedish-only subtitles are too hard at the start, and pausing to look things up kills the flow. Tools like Language Reactor solve this for Netflix and YouTube, but not for the Nordic broadcasters. So I built Nordic Dual Subs: a free browser extension that shows the broadcaster's own subtitle line together with a translation in your language, perfectly in sync.
What it does
- Two lines at once on SVT Play, SVT Barn, UR Play, NRK TV and DR TV: the original Swedish, Norwegian or Danish line, and a translation (29 languages, including Arabic, Persian, Somali, Tigrinya, Ukrainian and Chinese, right-to-left included).
- Click any word: the video pauses and you get the meaning and the base form (sprang → springa), with an English gloss from the open Folkets lexikon dictionary.
- Save words with their sentence, review them with simple spaced repetition, or export them to Anki as CSV.
- Practice keys: A / S / D for previous / repeat / next line, Shift+S replays the line slowly.
- Burned-in subtitles: some foreign films have the Swedish text in the picture. The extension reads it locally with Tesseract (WebAssembly) and translates it.
How it works (the parts that were interesting to build)
Getting the subtitles. Each broadcaster exposes its subtitle files (WebVTT and friends) through the same APIs its own player uses. The content script works out which programme is playing (all five sites are single-page apps, so it follows URL changes), the background worker fetches and parses the subtitle file, and an overlay is drawn inside the site's own player so it also works in fullscreen. The site's own subtitles are hidden so nothing overlaps.
Translation without a translation bill. I wanted this to run at zero cost. Each browser translates lines with Google's free web endpoint from its own IP, and sends [line, translation] pairs to a small backend. A line becomes trusted only when installs from two different networks submitted the identical translation (Google is deterministic, so honest clients agree, and one client can't plant a translation). Trusted lines are then served to everyone, keyed by a hash of the line, so the server never hands out subtitle text to someone who doesn't already have it. When Google rate-limits a user, a capped DeepL fallback covers the lines near the playhead.
The backend is Cloudflare's free tier. One Worker plus a D1 (SQLite) database: anonymous installs with HMAC session tokens, the shared translation cache, optional Google sign-in for syncing saved words, and Stripe for a future optional Pro plan. Free tiers have hard daily limits, so a small GitHub Action checks D1 and Workers usage every morning and mails me before anything gets close. One lesson: my end-to-end tests were quietly writing thousands of rows to the production database. They now run against an in-process mock API, with a guard that fails the test if anything tries to reach production.
One codebase, three stores. The same source builds a Chrome/Edge package and a Firefox package. The Firefox build swaps the MV3 service worker for background scripts, drops the offscreen API (OCR runs in Firefox's background page instead, which has a DOM), and declares Firefox's data-collection consent. Firefox for Android gets touch controls (⏮ ↺ ⏭ 🐢) instead of keyboard shortcuts. Publishing goes through each store's API, with one catch: Microsoft only lets the API publish an Edge extension after its first version was published by hand.
Things outside my control. DR TV only plays when you're logged in, and some programmes on all three broadcasters are geo-restricted to their own country. The first user email I got was exactly that: "it doesn't work on DR TV". The extension now says why (log in to DR, or "this programme is only available in Denmark") instead of staying silent.
Built with a lot of AI help
Full disclosure: I built this with heavy help from an AI coding assistant. It wrote much of the code and the tests, ran the end-to-end tests against real SVT and NRK programmes, and handled most of the store paperwork. I decided what to build, tested it on my own TV evenings, and pushed back when something was wrong. It's been a fun way to ship a side project in the evenings without cutting corners on tests.
Try it
It's free (everything is free during the launch period; the dual subtitles stay free), no account needed.
- Website and guides (16 languages): https://dualsubs.zexa.se?utm_source=devto&utm_medium=article&utm_campaign=launch
- Chrome Web Store: https://chromewebstore.google.com/detail/ejmbcllddngiakhpcpajkajeghmdapko?utm_source=devto&utm_medium=article&utm_campaign=launch
- Edge and Firefox (desktop + Android) versions are in store review right now.
If you're learning a Nordic language, I'd love to hear what's missing. And if you've built something similar for other broadcasters, I'm curious how you handled subtitles and translation costs.
