The $0 Localization Stack for Solo .NET Developers
Source: Dev.to
The Problem
I had 400+ strings in my app. I needed translations for at least Greek and English, ideally more. My options were:
| Option | Cost | Speed |
|---|---|---|
| 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 Idea
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).
1️⃣ Install Ollama (the local LLM runtime)
# Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows / macOS – download from https://ollama.ai
2️⃣ Pull a model
ollama pull llama3.2 # default
# 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.
4️⃣ Translate
# 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
LRM finds all your .resx 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? Use free online providers (still $0)
lrm translate --only-missing --provider mymemory --target-languages es,fr,de
# 5,000 characters/day free – enough for most projects
lrm translate --only-missing --provider lingva --target-languages es,fr,de
# Uses an open‑source proxy to Google Translate – no API key needed
Adding Italian – A Real‑World Walkthrough
# 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 is at 0 % – let’s fix that:
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 │ ✓ │
╰─────────────────────┴──────────┴─────────────────────────────────────┴────────╯
12 of 12 items translated in seconds. Done.
The CLI uses colorized output – missing translations appear in red, completed ones in green.
“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 |
- UI strings like “Save”, “Cancel”, “Error occurred”? Ollama is more than good enough.
- Marketing copy or legal text? Maybe pay for DeepL.
- 90 % of app localization? Free works fine.
Beyond Translation – Validation & Cleanup
Once the translation part was done, LRM grew to include validation:
lrm validate
⚠ Validation found 12 issue(s)
Empty Values
┌──────────┬───────────────────────────────────────────────────────────────────┐
│ Language │ Empty Keys │
├──────────┼───────────────────────────────────────────────────────────────────┤
│ de │ CancelButton, DeleteConfirmation, ErrorOccurred, PatientName… │
│ fr │ ErrorOccurred, PatientName, AppointmentDate, NoResultsFound… │
└──────────┴───────────────────────────────────────────────────────────────────┘
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
.resxbut never referenced (bloat)
You can then clean them up with a single command:
lrm prune-unused
TL;DR – The Whole Workflow
# 1️⃣ Install Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# 2️⃣ Pull a model
ollama pull llama3.2
# 3️⃣ Install LRM
curl -sSL https://raw.githubusercontent.com/nickprotop/LocalizationManager/main/install-lrm.sh | bash
# 4️⃣ Translate missing strings
cd MyApp/Resources
lrm translate --only-missing --provider ollama --target-languages es,fr,de,it
# 5️⃣ Validate & prune
lrm validate
lrm prune-unused
Result: A fully localized .NET app in 5 languages for $0 and under 10 minutes of work. 🎉
sed 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
.resxfiles!
Those would be runtime errors. Now I can add them before users see a broken UI.
On a larger project (my actual app), the scan also found the opposite:
lrm scan --source-path ./src
✓ 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.
Interactive editor (TUI)
lrm edit
Convert between formats
lrm convert --from resx --to json
Export for translators
lrm export --format csv
Supported formats (one tool)
.resx– WPF, WinForms, ASP.NET- JSON – ASP.NET Core, Blazor
- i18next – React, Vue, Angular
- Android
strings.xml - iOS
.strings
All formats, one tool.
My complete localization workflow
-
Add new English strings normally in your app.
-
Validate (catch issues early)
lrm validate -
Translate missing keys
lrm translate --only-missing --provider ollama -
Quick review in TUI
lrm edit -
Commit
git add Resources/ git commit -m "Add translations"
Total time: 5 minutes Total cost: $0.
Open‑source project
- Repository:
- License: MIT (use it however you want)
- Features: 100 % offline, no accounts, no tracking, self‑hostable.
What started as “I need to translate my app” grew into something bigger. The CLI worked so well that I kept adding features, then built a web UI, cloud sync, and OTA updates. Now it’s a full platform.
Cloud adds what solo devs need when they grow
| Feature | Description |
|---|---|
| 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 the browser, you review and approve |
| Free tier | 3 projects, 5 K chars/month – enough for side projects |
The CLI remains 100 % free and open source. The cloud is optional – and self‑hostable if you want!
Installation (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.
Need help?
- Open an issue on GitHub.
- Find me on Reddit.
If you found this useful, a GitHub star helps others discover it.
Tags: dotnet, localization, i18n, translation, ollama, llm, open-source, csharp, blazor, aspnetcore