Local Development
Contents
Prerequisites
- .NET 10 SDK
- At least one translation model downloaded — see docs/models.md
- (Optional) A Whisper model for STT — see Whisper
- (Optional) Piper voices for TTS — see Piper TTS
- (Optional, required for TTS) espeak-ng installed on PATH —
apt-get install -y espeak-ng/ Windows MSI /brew install espeak-ng
Run
From the repository root:
dotnet run --project src/Lopatnov.Translate.Grpc
The gRPC server starts on http://localhost:5100 (HTTP/2, no TLS).
Try it with grpcurl
Install grpcurl:
winget install fullstorydev.grpcurl # Windows
brew install grpcurl # macOS
Translate text (bash / macOS / Linux):
grpcurl -plaintext -d '{
"text": "Привіт, як справи?",
"source_language": "uk",
"target_language": "en"
}' localhost:5100 lopatnov.translate.v1.TranslateService/TranslateText
Translate text (PowerShell):
$body = '{"text":"Привіт, як справи?","source_language":"uk","target_language":"en"}'
grpcurl -plaintext -d $body localhost:5100 lopatnov.translate.v1.TranslateService/TranslateText
Transcribe audio (PowerShell):
$audioBase64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes("recording.wav"))
$body = "{`"audio_data`": `"$audioBase64`", `"language`": `"auto`"}"
grpcurl -plaintext -d $body localhost:5100 lopatnov.translate.v1.TranslateService/TranscribeAudio
Synthesize speech (bash):
grpcurl -plaintext \
-d '{"text": "Hello, world!", "language": "en"}' \
localhost:5100 lopatnov.translate.v1.TranslateService/SynthesizeSpeech \
| jq -r '.audioData' | base64 -d > output.wav
Full API examples: docs/api.md.
Configuration
Override any appsettings.json setting via environment variable (double underscore = section nesting).
Common overrides for local development:
| Variable | Default (local) | Description |
|---|---|---|
Translation__DefaultModel |
m2m100_418M |
Default translation model |
Translation__AudioToText |
whisper-small |
STT model key; set to "" to disable |
Translation__AutoDetect |
lid-176-ftz |
Language detection model key |
Translation__ModelTtlMinutes |
30 |
Minutes idle before model is unloaded |
Translation__TextToAudio__en |
piper-en-US |
TTS voice key for English |
Models__m2m100_418M__Path |
../../models/translate/m2m100_418M |
Path override for M2M-100 418M |
Models__whisper-small__Path |
../../models/audio-to-text/whisper.cpp/ggml-small.bin |
Path override for Whisper small |
Models__piper-en-US__Path |
../../models/text-to-audio/piper-voices/en_US/en_US-joe-medium.onnx |
Path override for Piper English voice |
All Models__<key>__Path variables follow the same pattern. See docs/models.md for all model keys.
Testing
Unit tests
No model files required. Run from the repository root:
dotnet test --filter "Category!=Integration"
Coverage:
| Project | What’s tested |
|---|---|
Core.Tests |
LanguageCodeConverter, HeuristicLanguageDetector, FastTextLanguageDetector (unit) |
Nllb.Tests |
NllbTokenizer encode/decode round-trip |
M2M100.Tests |
M2M100Tokenizer — language token IDs, BPE encode/decode |
Whisper.Tests |
WhisperRecognizer.ResampleToWhisperFormat (unit), lazy/dispose guards |
Piper.Tests |
BuildPhonemeIds, BuildPhonemeIdsFromText, WAV encoder, guard tests |
Grpc.Tests |
TranslateGrpcService — model dispatch, allowlist, auto-detect, language format conversion, GPU provider selection |
Integration tests
Require model files. Tests skip automatically if the model is not found.
dotnet test --filter "Category=Integration"
What runs with models present:
| Project | Models needed | What’s tested |
|---|---|---|
Core.Tests |
lid.176.ftz (FastText), model_v3.bin (GlotLID) |
Language detection accuracy across 10+ languages |
Nllb.Tests |
NLLB-200 600M | Ukrainian→English, Russian→English translation |
M2M100.Tests |
M2M-100 418M | Ukrainian→English, Russian→English translation; tokenizer round-trips |
Whisper.Tests |
ggml-small.bin |
Silent audio pipeline (model load, inference, result structure) |
Piper.Tests |
en_US-joe-medium.onnx + espeak-ng |
Phonemization pipeline (espeak-ng IPA → phoneme IDs) and synthesis |
Override model paths with environment variables if your models are in a non-default location:
Models__whisper-small__Path=/path/to/ggml-small.bin \
dotnet test --filter "Category=Integration"