How I Translated My .NET App to 5 Languages for $0 (As a Solo Dev)
I'm a solo developer with a medical practice app built in .NET. It started as an English-only app, then users asked for Greek. Then more languages. That's when I discovered localization platforms cost $100-500/month.
For a side project? No way.
So I built my own solution. Here's how you can translate your .NET app for free too.
The Problem
I had 400+ strings in my app. I needed translations for at least Greek and English, ideally more. My options:
| Option | Cost | Time |
|---|---|---|
| Lokalise | $120/mo | Fast |
| Phrase | $25/mo | Fast |
| Manual Google Translate | $0 | Hours of copy-paste hell |
| Hire translator | $100+ | Days |
None of these worked for a solo dev with a side project.
The Solution: Local LLM Translation
What if you could run a translation model on your own machine? No API costs, no rate limits, no data leaving your computer.
That's exactly what I built into LRM (Localization Resource Manager).
Setup (5 minutes)
1. Install Ollama (the local LLM runtime):
# Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows/Mac - download from https://ollama.ai
2. Pull a model:
ollama pull llama3.2
# Or for better quality: ollama pull llama3.1:8b
3. Install LRM:
# Linux (one-liner)
curl -sSL https://raw.githubusercontent.com/nickprotop/LocalizationManager/main/install-lrm.sh | bash
# Or via APT (Ubuntu/Debian)
sudo add-apt-repository ppa:nickprotop/lrm-tool
sudo apt install lrm-standalone
# Windows/macOS - download from GitHub releases
# https://github.com/nickprotop/LocalizationManager/releases
That's it. No API keys, no accounts, no credit cards.
Translate Everything
# Navigate to your project
cd MyApp/Resources
# Translate all missing keys to Spanish, French, German
lrm translate --only-missing --provider ollama --target-languages es,fr,de
That's it. LRM finds all your resource files, identifies missing translations, and fills them in using the local LLM.
400+ keys × 3 languages = 1,200+ translations. Zero dollars. Under 10 minutes.
No GPU? No Problem.
Don't have a beefy GPU? Use the free online providers - still $0:
MyMemory (No signup required)
lrm translate --only-missing --provider mymemory --target-languages es,fr,de
5,000 characters/day free. Enough for most projects.
Lingva (Google Translate quality, free)
lrm translate --only-missing --provider lingva --target-languages es,fr,de
Uses open-source proxy to Google Translate. No API key needed.
Real Example
Here's my actual workflow for adding Italian to my app:
# Add a new language file
lrm add-language --culture it
# Check what's missing
lrm stats
Localization Statistics
┌───────────────┬────────────┬───────────┬───────┬──────────┬───────────┐
│ Language │ Total Keys │ Completed │ Empty │ Coverage │ File Size │
├───────────────┼────────────┼───────────┼───────┼──────────┼───────────┤
│ Default │ 12 │ 12 │ 0 │ 100.0% │ 1.8 KB │
│ Deutsch (de) │ 12 │ 5 │ 7 │ 41.7% │ 1.6 KB │
│ français (fr) │ 12 │ 7 │ 5 │ 58.3% │ 1.7 KB │
│ italiano (it) │ 12 │ 0 │ 12 │ 0.0% │ 1.6 KB │
└───────────────┴────────────┴───────────┴───────┴──────────┴───────────┘
Italian at 0%! Let's fix that:
# Translate Italian with one command
lrm translate --only-missing --provider ollama --target-languages it
╭────────────────────┬──────────┬─────────────────────────────────────┬────────╮
│ Key │ Language │ Translation │ Status │
├────────────────────┼──────────┼─────────────────────────────────────┼────────┤
│ AppTitle │ it │ Responsabile dello studio medico │ ✓ │
│ WelcomeMessage │ it │ Eccoci di nuovo, {0}! │ ✓ │
│ LoginButton │ it │ Accedi │ ✓ │
│ LogoutButton │ it │ Disconnettere │ ✓ │
│ SaveButton │ it │ Salva │ ✓ │
│ CancelButton │ it │ Cancella │ ✓ │
│ DeleteConfirmation │ it │ Sei sicuro di voler eliminare... │ ✓ │
│ ErrorOccurred │ it │ Si è verificato un errore... │ ✓ │
│ PatientName │ it │ Nome del paziente │ ✓ │
│ AppointmentDate │ it │ Data prenotazione │ ✓ │
│ NoResultsFound │ it │ Nessun risultato trovato │ ✓ │
│ SearchPlaceholder │ it │ Cerca pazienti │ ✓ │
╰────────────────────┴──────────┴─────────────────────────────────────┴────────╯
Translated 12 of 12 items.
12 keys translated in seconds. Done.
The CLI uses colorized output - missing translations show in red, complete in green.
Quality Comparison
"But is the quality good enough?"
I compared Ollama (llama3.2) vs Google Translate vs DeepL for my app strings:
| Provider | Quality | Cost | Speed |
|---|---|---|---|
| DeepL | Excellent | $20/mo | Fast |
| Very Good | Pay-per-use | Fast | |
| Ollama (llama3.2) | Good | $0 | Medium |
| MyMemory | Good | $0 | Fast |
For UI strings like "Save", "Cancel", "Error occurred"? Ollama is more than good enough.
For marketing copy or legal text? Maybe pay for DeepL.
For 90% of app localization? Free works fine.
More Than Just Translation
Once I built the translation part, I kept going. LRM now does:
# Validate resource files (find issues)
lrm validate
⚠ Validation found 12 issue(s)
Empty Values
┌──────────┬───────────────────────────────────────────────────────────────────┐
│ Language │ Empty Keys │
├──────────┼───────────────────────────────────────────────────────────────────┤
│ de │ CancelButton, DeleteConfirmation, ErrorOccurred, PatientName... │
│ fr │ ErrorOccurred, PatientName, AppointmentDate, NoResultsFound... │
└──────────┴───────────────────────────────────────────────────────────────────┘
Find Dead Code (.NET only)
This one's a game-changer. LRM scans your C#, Razor, and XAML files to find:
- Missing keys - used in code but not defined in .resx (runtime errors waiting to happen!)
- Unused keys - defined in .resx but never used in code (dead translations)
lrm scan --source-path ./src
✓ Scanned 3 files
Found 18 key references (16 unique keys)
Missing Keys (in code, not in .resx)
╭─────────────────────┬────────────┬───────────────────────────╮
│ Key │ References │ Locations │
├─────────────────────┼────────────┼───────────────────────────┤
│ AboutUs │ 1 │ HomeController.cs:31 │
│ AppSubtitle │ 1 │ HomeController.cs:22 │
│ CompanyDescription │ 1 │ HomeController.cs:32 │
│ GetStarted │ 1 │ Index.cshtml:9 │
│ OperationFailed │ 1 │ NotificationService.cs:23 │
│ OperationSuccessful │ 1 │ NotificationService.cs:17 │
│ ... and 7 more │ │ │
╰─────────────────────┴────────────┴───────────────────────────╯
✗ Found 13 missing keys and 2 unused keys
13 keys used in code that don't exist in my .resx files! Those would be runtime errors. Now I can add them before users see broken UI.
On a larger project (my actual app), the scan also found the opposite:
✓ Scanned 497 files
Found 607 key references (394 unique keys)
Unused Keys (in .resx, not in code)
╭────────────────────────────┬───────╮
│ Key │ Count │
├────────────────────────────┼───────┤
│ ActiveAdmissions │ - │
│ AdmissionCount │ - │
│ DailyAdmissions │ - │
│ LanguageAutoDetect │ - │
│ MarkAsComplete │ - │
│ ... and 51 more │ │
╰────────────────────────────┴───────╯
✗ Found 0 missing keys and 56 unused keys
56 dead keys I can clean up. Less clutter, smaller resource files.
More Tools
# Interactive editor (TUI)
lrm edit
# Convert between formats
lrm convert --from resx --to json
# Export for translators
lrm export --format csv
Works With Your Stack
LRM supports what you're already using:
- .resx (WPF, WinForms, ASP.NET)
- JSON (ASP.NET Core, Blazor)
- i18next (React, Vue, Angular)
- Android strings.xml
- iOS .strings
All formats, one tool.
The Workflow That Actually Works
Here's my complete localization workflow now:
# 1. Add new English strings normally in your app
# 2. Validate (catch issues early)
lrm validate
# 3. Translate missing keys
lrm translate --only-missing --provider ollama
# 4. Quick review in TUI
lrm edit
# 5. Commit
git add Resources/
git commit -m "Add translations"
Total time: 5 minutes. Total cost: $0.
Why I Open-Sourced It
I built this for myself. Then I realized other solo devs have the same problem.
So I open-sourced it: github.com/nickprotop/LocalizationManager
- MIT License (use it however you want)
- Works 100% offline
- No accounts, no tracking
- Self-host everything
From Side Project to Product
What started as "I need to translate my app" grew into something bigger. The CLI worked so well that I kept adding features. Then I built a web UI. Then cloud sync. Then OTA updates.
Now it's a full platform: lrm-cloud.com
The cloud adds what solo devs need when they grow:
- Format agnostic - One project syncs to RESX, JSON, or i18next
- Web editor - Edit translations from anywhere, no CLI needed
- OTA updates - Update translations without redeploying (first OTA for .NET!)
- GitHub sync - Bidirectional sync with your repo
- Translation Memory - Reuses past translations, saves API costs
- Glossary - Enforce consistent terminology
- Invite translators - They edit in browser, you review and approve
- Free tier - 3 projects, 5K chars/month - enough for side projects
But here's the thing: the CLI is still 100% free and open source. The cloud is optional - and self-hosted if you want!
I'm not ashamed of building a product from solving my own problem. That's how the best tools get built. You scratch your own itch, then share the solution.
TL;DR
# Install (Linux)
curl -sSL https://raw.githubusercontent.com/nickprotop/LocalizationManager/main/install-lrm.sh | bash
ollama pull llama3.2
# Translate your app for free
cd YourProject/Resources
lrm translate --only-missing --provider ollama --target-languages es,fr,de,it,pt
# Done. $0. 5 minutes.
Got questions? Open an issue on GitHub or find me on Reddit.
Found it useful? A GitHub star helps others discover it.
Tags: dotnet, localization, i18n, translation, ollama, llm, open-source, csharp, blazor, aspnetcore
Top comments (0)