2026-03-13

How to listen to German radio


Now is the golden age of language learning. Small language models are lightweight and free, your laptop can run them. These models can transcribe and translate European languages. You can download MP3 file of Deutchlandfunk Kultur broadcast, transcribe it with whisper, translate it with Qwen3-4B, build a PDF with transcript and translation. Now you can listen to broadcast, see the transcript and translation, pause (Winamp has global hotkeys, Ctrl+Space and Ctrl+Left work great for me), look up a word in the dictionary.

PDF script to combine transcript and translation:

pip install fpdf2 nltk

import nltk
from fpdf import FPDF
from nltk.tokenize import sent_tokenize

# setup
nltk.download('punkt')
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()

# font
pdf.add_font("Arial", style="", fname="C:/Windows/Fonts/arial.ttf")
pdf.set_font('Arial', size=11)

# layout
margin = 15
gutter = 10
col_width = (pdf.w - (margin * 2) - gutter) / 2
line_height = 6

def get_sentences(file_path):
    with open(file_path, "r", encoding="utf-8") as f:
        return sent_tokenize(f.read())

left_sentences = get_sentences("text.txt")
right_sentences = get_sentences("translation.txt")

for left_txt, right_txt in zip(left_sentences, right_sentences):
    # calculate the height needed for both sides
    # split_lines() helps us predict how many lines fpdf will create
    lines_left = pdf.multi_cell(col_width, line_height, left_txt, split_only=True)
    lines_right = pdf.multi_cell(col_width, line_height, right_txt, split_only=True)
    
    max_lines = max(len(lines_left), len(lines_right))
    row_height = max_lines * line_height + 4 # +4 for internal padding

    # check for page break
    if pdf.get_y() + row_height > pdf.page_break_trigger:
        pdf.add_page()

    start_x = pdf.get_x()
    start_y = pdf.get_y()

    # draw the border
    pdf.rect(start_x, start_y, pdf.w - (margin * 2), row_height)
    
    # draw the middle line
    pdf.line(start_x + col_width + (gutter/2), start_y, 
             start_x + col_width + (gutter/2), start_y + row_height)

    # place the text
    pdf.set_xy(start_x + 2, start_y + 2) # +2 for padding inside box
    pdf.multi_cell(col_width, line_height, left_txt, border=0, align='L')
    
    pdf.set_xy(start_x + col_width + gutter, start_y + 2)
    pdf.multi_cell(col_width, line_height, right_txt, border=0, align='L')

    # move cursor to the bottom of the common box
    pdf.set_y(start_y + row_height)

pdf.output("parallel.pdf")