The Problem
Running real‑time voice translation on a small, offline device is hard. Cloud services add latency, cost, and a dependency on connectivity that many kiosk or handheld deployments cannot tolerate. This repo delivers a turnkey solution that runs a Gemma 4 model locally, captures microphone audio, and returns spoken translations without an internet connection.
What This Does
The project bundles a React/Vite UI and a Python FastAPI backend that together:
- Front‑end (
frontend/src/App.jsx) captures microphone input viauseAudioRecorder, sends the raw waveform to the backend, and displays the translated text and synthesized speech in a terminal‑style UI (components/LanguageLane.jsx,components/ResponseDrawer.jsx). - Back‑end (
backend/server.py) receives audio, forwards it to Moonshine for speech‑to‑text, then sends the text to LiteRT‑LM to run thegemma4‑e2bmodel locally. The resulting translation is returned as JSON, and the TTS engine converts it back to audio. - Deployment scripts (
deploy-pi.sh,start.sh,setup.sh) automate the creation of a Python virtual environment, installation of dependencies, download of the LiteRT model, and launch of all services.
How It Is Wired
Entry Points
start.sh– orchestrates all services.- In dev mode: runs
node --inspect frontend/(Vite dev server) andpython -m backend.server. - In prod mode: builds the React app (
npm run build), startsbackend/server.py(serves/dist/), and the LiteRT inference server (lite-rt start -m gemma4-e2b).
Control Flow
frontend/src/main.jsxmountsApp.jsx.App.jsxusesuseAudioRecorderto capture mic data.- Recorder calls
fetch('/api/transcribe', {body: audioBlob}). backend/server.pyreceives the POST, streams the blob to Moonshine (moonshine.stt), gets text.- Text is sent to LiteRT (
lite-rt run) via the API exposed inserver.py. - The response JSON contains
translationand a URL to an audio file. App.jsxfetches the audio and plays it, while the translated text is displayed inResponseDrawer.jsx.
Key Functions
server.transcribe()– handles/api/transcribe.server.translate()– proxies to LiteRT.server.tts()– streams audio back via Moonshine.
The backend is the single point with the widest blast radius: any change to the inference pipeline must be reflected in server.py. The frontend is decoupled; UI changes rarely touch the backend.
How To Use It
Setup
# Clone
git clone https://github.com/moses-y/gemma-translator
# Make scripts executable
chmod +x setup.sh download_model.sh start.sh deploy-pi.sh
# Install Python deps
./setup.sh # creates venv, installs FastAPI, Moonshine, etc.
# Download the Gemma 4 model
./download_model.sh
Run Locally (Development)
./start.sh # starts Vite dev server (http://localhost:5173) and backend
Run in Production Mode
./start.sh --prod # builds UI, serves via backend on http://localhost:3000
Deploy to Raspberry Pi 5
./deploy-pi.sh # installs systemd unit, builds UI, configures LXDE kiosk
Configuration
- No external env vars are required for the provided scripts.
- Model path is hard‑coded in
download_model.shto~/.lite-rt/lm/gemma4-e2b.
Real‑World Use
An 8 GB Raspberry Pi 5 Raspberry‑Pi kiosk can sit in a museum exhibit and translate spoken questions into the exhibit’s language, all on a small 480×320 touchscreen. The UI remains responsive because the model inference runs locally and audio capture is piped directly to the backend.
Code Health & Issues
- Medium/SDLC – No test files detected – 0 tests across 31 files.
- Medium/SDLC – No CI/CD pipeline detected – no
.github/orci/folder. - No obvious security or licensing gaps beyond the MIT license.
The Bottom Line
This repo delivers a functional, offline voice‑translation stack that’s easy to deploy on a Raspberry Pi 5. The architecture is clear, but the lack of automated tests and CI limits confidence in future changes. Teams wanting a quick prototype can adopt it immediately; teams requiring production‑grade reliability should add tests and a CI pipeline.