[{"data":1,"prerenderedAt":605},["ShallowReactive",2],{"\u002Fblog\u002Ffrench-translator-offline-privacy":3},{"id":4,"title":5,"body":6,"description":592,"extension":593,"meta":594,"navigation":409,"path":601,"seo":602,"stem":603,"__hash__":604},"blog\u002Fblog\u002Ffrench-translator-offline-privacy.md","We Built a Live French-to-English Translator That Runs Entirely on Your Machine",{"type":7,"value":8,"toc":578},"minimark",[9,13,16,19,24,27,250,254,257,262,270,273,276,280,295,298,325,328,332,335,338,342,352,355,359,362,365,368,371,375,378,468,478,482,510,514,540,544,557,560,563,574],[10,11,12],"p",{},"Construction companies work in a bilingual reality. Safety meetings in Quebec. Sub-trade crews that operate primarily in French. Regulatory documents from the CCQ, the CNESST, and municipal inspectors that arrive in French first — if they arrive in English at all. Even in US border states, Francophone crews and suppliers are a daily reality.",[10,14,15],{},"We kept running into the same problem: the existing translation tools all phone home. Google Translate, DeepL, even most \"local\" wrappers around Whisper — they send your audio or text to a cloud API. For a construction company handling project details, safety reports, and contract discussions, that's a non-starter. Your data ends up on someone else's server, in someone else's training set.",[10,17,18],{},"So we built our own. It runs entirely on a MacBook. No internet required after the initial model download. No API keys. No data ever leaves the machine.",[20,21,23],"h2",{"id":22},"what-it-does","What It Does",[10,25,26],{},"Speak French into your MacBook's microphone. Four seconds later, you see the English translation in your terminal. It's not perfect — no translation tool is — but it's fast, it's private, and it works on a job site with zero connectivity.",[28,29,32,56],"div",{"className":30},[31],"terminal-demo",[28,33,36,37,36,42,36,46,36,50],{"className":34},[35],"terminal-bar","\n  ",[28,38],{"className":39},[40,41],"terminal-dot","dot-red",[28,43],{"className":44},[40,45],"dot-yellow",[28,47],{"className":48},[40,49],"dot-green",[51,52,55],"span",{"className":53},[54],"terminal-title","live-french.sh large-v3",[28,57,60,65,68,70,75,77,81,83,87,89,93,95,99,101,103,107,109,113,115,117,121,123,127,129,133,135,137,141,143,148,150,155,157,160,162,164,168,170,174,176,180,182,185,187,189,193,195,199,201,205,207,210,212,214,218,220,224,226,230,232,235,237,239,243,245],{"className":58},[59],"terminal-body",[51,61,64],{"className":62},[63],"t-dim","$ live-french.sh large-v3",[66,67],"br",{},[66,69],{},[51,71,74],{"className":72},[73],"t-header","╔══════════════════════════════════════════════════╗",[66,76],{},[51,78,80],{"className":79},[73],"║       LIVE FRENCH → ENGLISH TRANSLATOR          ║",[66,82],{},[51,84,86],{"className":85},[73],"║       Model: large-v3                            ║",[66,88],{},[51,90,92],{"className":91},[73],"║       Speak French into your mic...              ║",[66,94],{},[51,96,98],{"className":97},[73],"╚══════════════════════════════════════════════════╝",[66,100],{},[66,102],{},[51,104,106],{"className":105},[63],"  Listening... (Ctrl+C to stop)",[66,108],{},[51,110,112],{"className":111},[63],"  ──────────────────────────────────────────────────",[66,114],{},[66,116],{},[51,118,120],{"className":119},[63],"  ⏳ Loading mlx-whisper 'large-v3' (Metal GPU)...",[66,122],{},[51,124,126],{"className":125},[63],"  ✓ mlx-whisper loaded (Metal GPU)",[66,128],{},[51,130,132],{"className":131},[63],"  🎤 Microphone active (sample rate: 16000Hz)",[66,134],{},[66,136],{},[51,138,140],{"className":139},[63],"[14:23:07]",[66,142],{},[51,144,147],{"className":145},[146],"t-fr","  🇫🇷  Les travailleurs ont terminé le coffrage du sous-sol cette matinée.",[66,149],{},[51,151,154],{"className":152},[153],"t-en","  🇬🇧  The workers finished the basement formwork this morning.",[66,156],{},[51,158,112],{"className":159},[63],[66,161],{},[66,163],{},[51,165,167],{"className":166},[63],"[14:23:15]",[66,169],{},[51,171,173],{"className":172},[146],"  🇫🇷  Il faut commander plus de béton avant vendredi.",[66,175],{},[51,177,179],{"className":178},[153],"  🇬🇧  We need to order more concrete before Friday.",[66,181],{},[51,183,112],{"className":184},[63],[66,186],{},[66,188],{},[51,190,192],{"className":191},[63],"[14:23:22]",[66,194],{},[51,196,198],{"className":197},[146],"  🇫🇷  L'inspecteur municipal passera demain à neuf heures.",[66,200],{},[51,202,204],{"className":203},[153],"  🇬🇧  The municipal inspector will come tomorrow at nine o'clock.",[66,206],{},[51,208,112],{"className":209},[63],[66,211],{},[66,213],{},[51,215,217],{"className":216},[63],"[14:23:31]",[66,219],{},[51,221,223],{"className":222},[146],"  🇫🇷  Le sous-traitant en électricité a signalé un problème avec le panneau principal.",[66,225],{},[51,227,229],{"className":228},[153],"  🇬🇧  The electrical subcontractor reported a problem with the main panel.",[66,231],{},[51,233,112],{"className":234},[63],[66,236],{},[66,238],{},[51,240,242],{"className":241},[63],"  Session: 4 chunks in 24s",[66,244],{},[51,246,249],{"className":247},[248],"t-warn","  🛑 Stopped.",[20,251,253],{"id":252},"the-architecture","The Architecture",[10,255,256],{},"The translator is three components wired together in a single Python script. No microservices. No Docker containers. No cloud dependencies. Just a virtualenv and a shell script.",[258,259,261],"h3",{"id":260},"_1-audiocollector-the-microphone-layer","1. AudioCollector — The Microphone Layer",[10,263,264,265,269],{},"The first component grabs audio from your MacBook's built-in microphone using ",[266,267,268],"code",{},"sounddevice",", which wraps PortAudio. It runs a continuous input stream and feeds audio into a thread-safe queue.",[10,271,272],{},"Every 4 seconds, it pulls a chunk of audio from the queue, checks if it's above a silence threshold (RMS energy check — if the room is quiet, it skips the chunk entirely), and passes it to the next stage. The audio is boosted by 4x gain because MacBook built-in mics are quiet, and it's sampled at 16kHz — the rate Whisper expects.",[10,274,275],{},"The buffer uses a 50% overlap strategy: after pulling a 4-second chunk, it keeps the second half as the start of the next chunk. This prevents word boundaries from getting cut off mid-sentence.",[258,277,279],{"id":278},"_2-whispermlx-the-transcriber","2. WhisperMLX — The Transcriber",[10,281,282,283,286,287,290,291,294],{},"This is the core. We use ",[266,284,285],{},"mlx-whisper",", a port of OpenAI's Whisper model that runs on Apple's Metal GPU framework. On an M-series MacBook, this is dramatically faster than CPU-based Whisper — we're talking real-time or better for the ",[266,288,289],{},"small"," model, and near-real-time for ",[266,292,293],{},"large-v3",".",[10,296,297],{},"The transcription pipeline:",[299,300,301,312,315],"ol",{},[302,303,304,305,307,308,311],"li",{},"The 4-second audio chunk is passed to ",[266,306,285],{}," with ",[266,309,310],{},"language=\"fr\""," — telling it to expect French specifically, which improves accuracy significantly over auto-detect.",[302,313,314],{},"It returns the transcribed French text.",[302,316,317,318,320,321,324],{},"If ",[266,319,285],{}," isn't available (e.g., the model hasn't been downloaded yet), it falls back to ",[266,322,323],{},"faster-whisper"," running on CPU with INT8 quantization. Slower, but it works.",[10,326,327],{},"The model is loaded once at startup and kept in GPU memory. After the initial warm-up transcription (a silent clip, just to trigger model loading and caching), every subsequent 4-second chunk transcribes in under a second on Metal.",[258,329,331],{"id":330},"_3-argostranslator-the-offline-translation","3. ArgosTranslator — The Offline Translation",[10,333,334],{},"Once we have French text, we pass it to Argos Translate — a completely offline, open-source translation engine. It uses a pre-trained French-to-English model that lives on disk. No API calls. No network. No data leaving the machine.",[10,336,337],{},"Argos isn't as polished as DeepL. It won't handle idiomatic expressions gracefully. But for the kind of practical, direct French that gets spoken on construction sites — scheduling updates, safety notes, material orders, inspection reports — it's accurate enough to be useful. And it's fast: translation takes under 100ms.",[258,339,341],{"id":340},"the-data-flow","The Data Flow",[343,344,349],"pre",{"className":345,"code":347,"language":348},[346],"language-text","Microphone\n    │\n    ▼\nAudioCollector (sounddevice, 16kHz, 4s chunks)\n    │  ← silence gate (RMS threshold)\n    ▼\nWhisperMLX (mlx-whisper on Metal GPU)\n    │  ← French text\n    ▼\nArgosTranslator (offline, on-disk model)\n    │  ← English text\n    ▼\nTerminal output (timestamped, colour-coded)\n","text",[266,350,347],{"__ignoreMap":351},"",[10,353,354],{},"The entire pipeline — from microphone input to translated text on screen — takes about 4–5 seconds. Most of that is the audio chunking window. The actual transcription plus translation happens in under 2 seconds on a MacBook with an M-series chip.",[20,356,358],{"id":357},"why-we-built-this","Why We Built This",[10,360,361],{},"This isn't a product. It's a tool we built because we needed it.",[10,363,364],{},"We were sitting in French-language meetings — project reviews, safety briefings, regulatory discussions — and the existing options were all the same: open a browser tab, send your audio to a server you don't control, and hope the transcription is accurate. For internal meetings with sensitive project details, that's not acceptable.",[10,366,367],{},"The construction industry operates in both official languages. If you're a GC working in Quebec, dealing with Francophone sub-trades in Ontario, managing crews in New Brunswick, or coordinating with French-speaking suppliers and inspectors, you need translation that works and that doesn't compromise your data.",[10,369,370],{},"This tool does both.",[20,372,374],{"id":373},"running-it","Running It",[10,376,377],{},"The whole thing is a single shell script. You need a MacBook with an M-series chip and a Python virtualenv with the dependencies installed.",[343,379,383],{"className":380,"code":381,"language":382,"meta":351,"style":351},"language-bash shiki shiki-themes github-light github-dark","# Clone the translator\ncd ~\u002Ffrench-translator\n\n# Run with the small model (fast, good accuracy)\n.\u002Flive-french.sh small\n\n# Run with large-v3 (best accuracy, needs Metal GPU)\n.\u002Flive-french.sh large-v3\n\n# Adjust the chunk interval (seconds)\n.\u002Flive-french.sh medium 6\n","bash",[266,384,385,393,404,411,417,427,432,438,446,451,457],{"__ignoreMap":351},[51,386,389],{"class":387,"line":388},"line",1,[51,390,392],{"class":391},"sJ8bj","# Clone the translator\n",[51,394,396,400],{"class":387,"line":395},2,[51,397,399],{"class":398},"sj4cs","cd",[51,401,403],{"class":402},"sZZnC"," ~\u002Ffrench-translator\n",[51,405,407],{"class":387,"line":406},3,[51,408,410],{"emptyLinePlaceholder":409},true,"\n",[51,412,414],{"class":387,"line":413},4,[51,415,416],{"class":391},"# Run with the small model (fast, good accuracy)\n",[51,418,420,424],{"class":387,"line":419},5,[51,421,423],{"class":422},"sScJk",".\u002Flive-french.sh",[51,425,426],{"class":402}," small\n",[51,428,430],{"class":387,"line":429},6,[51,431,410],{"emptyLinePlaceholder":409},[51,433,435],{"class":387,"line":434},7,[51,436,437],{"class":391},"# Run with large-v3 (best accuracy, needs Metal GPU)\n",[51,439,441,443],{"class":387,"line":440},8,[51,442,423],{"class":422},[51,444,445],{"class":402}," large-v3\n",[51,447,449],{"class":387,"line":448},9,[51,450,410],{"emptyLinePlaceholder":409},[51,452,454],{"class":387,"line":453},10,[51,455,456],{"class":391},"# Adjust the chunk interval (seconds)\n",[51,458,460,462,465],{"class":387,"line":459},11,[51,461,423],{"class":422},[51,463,464],{"class":402}," medium",[51,466,467],{"class":398}," 6\n",[10,469,470,471,473,474,477],{},"The first run downloads the model from HuggingFace — about 3GB for ",[266,472,293],{},". After that, everything runs offline. The models are cached locally and the script sets ",[266,475,476],{},"HF_HOME"," to a local directory so it never tries to reach the network again.",[20,479,481],{"id":480},"what-its-good-at","What It's Good At",[483,484,485,492,498,504],"ul",{},[302,486,487,491],{},[488,489,490],"strong",{},"Safety meetings"," — follow along in real time when the meeting is in French",[302,493,494,497],{},[488,495,496],{},"Quick conversations"," — get the gist of what a Francophone sub-trade is telling you",[302,499,500,503],{},[488,501,502],{},"Document review"," — pair it with a French document reader to get rough translations of written content",[302,505,506,509],{},[488,507,508],{},"Privacy-sensitive discussions"," — project finances, contract disputes, incident reports",[20,511,513],{"id":512},"what-its-not-good-at","What It's Not Good At",[483,515,516,522,528,534],{},[302,517,518,521],{},[488,519,520],{},"Idiomatic French"," — Argos will stumble on colloquial expressions and Quebec-specific slang",[302,523,524,527],{},[488,525,526],{},"Heavy accents"," — Whisper handles standard French well; strong regional accents reduce accuracy",[302,529,530,533],{},[488,531,532],{},"Noisy environments"," — the silence gate helps, but a loud job site will degrade transcription",[302,535,536,539],{},[488,537,538],{},"Legal precision"," — this is a comprehension tool, not a certified translation",[20,541,543],{"id":542},"the-code","The Code",[10,545,546,547,549,550,552,553,556],{},"The entire translator is under 260 lines of Python. No frameworks. No async runtimes. No external services. Just ",[266,548,268],{}," for audio, ",[266,551,285],{}," for transcription, and ",[266,554,555],{},"argostranslate"," for translation.",[10,558,559],{},"We built it because the alternative was sending construction project data to someone else's server. That was never going to be the answer.",[561,562],"hr",{},[10,564,565],{},[566,567,568,569,294],"em",{},"This tool was built by the Jenga IT team for our own use on construction projects. It's not a product — it's a practical solution to a real problem we kept running into. If you're a GC working in bilingual environments and this sounds useful, ",[570,571,573],"a",{"href":572},"mailto:info@jenga-it.ca","get in touch",[575,576,577],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":351,"searchDepth":395,"depth":395,"links":579},[580,581,587,588,589,590,591],{"id":22,"depth":395,"text":23},{"id":252,"depth":395,"text":253,"children":582},[583,584,585,586],{"id":260,"depth":406,"text":261},{"id":278,"depth":406,"text":279},{"id":330,"depth":406,"text":331},{"id":340,"depth":406,"text":341},{"id":357,"depth":395,"text":358},{"id":373,"depth":395,"text":374},{"id":480,"depth":395,"text":481},{"id":512,"depth":395,"text":513},{"id":542,"depth":395,"text":543},"No cloud APIs. No data leaving your MacBook. Here's how we built a real-time French → English translator using Apple's Metal GPU and offline models — and why we did it.","md",{"date":595,"readtime":596,"author":597,"initials":598,"category":599,"imagetext":600},"2026-06-03","8","Jenga IT Consulting","JIT","Tutorials & Guides","A MacBook on a desk in a quiet office showing a terminal with French text being translated to English in real time, warm desk lamp lighting","\u002Fblog\u002Ffrench-translator-offline-privacy",{"title":5,"description":592},"blog\u002Ffrench-translator-offline-privacy","uhOxsB3PnKI-0HMYb7CjjrL8S21hy2ReFzaZCzU45qI",1782696660471]