The $0 Localization Stack for Solo .NET Developers

Published: (December 29, 2025 at 05:58 PM EST)
6 min read
Source: Dev.to

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:

OptionCostSpeed
Lokalise$120/moFast
Phrase$25/moFast
Manual Google Translate$0Hours 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:

ProviderQualityCostSpeed
DeepLExcellent$20/moFast
GoogleVery GoodPay‑per‑useFast
Ollama (llama3.2)Good$0Medium
MyMemoryGood$0Fast
  • 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 .resx but 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 .resx files!
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

  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.

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

FeatureDescription
Format‑agnosticOne project syncs to RESX, JSON, or i18next
Web editorEdit translations from anywhere, no CLI needed
OTA updatesUpdate translations without redeploying (first OTA for .NET!)
GitHub syncBidirectional sync with your repo
Translation MemoryReuses past translations, saves API costs
GlossaryEnforce consistent terminology
Invite translatorsThey edit in the browser, you review and approve
Free tier3 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

Back to Blog

Related posts

Read more »

Bga Buses (MUX Challenge)

What I am Building Bus route finder, from where you are to your destination. Navigate Bucaramanga with breeze. My Pitch Video link to video can be added here D...