The End of Silent Localization Errors in .NET Apps - Meet resx-lint
Stop silent localization errors in .NET apps. resx-lint validates .resx keys in XAML and C#, auto-fixes errors, and integrates into your build pipeline.
resx-lint is a .NET global tool that validates .resx localization keys against your XAML and C# source files. It catches missing keys before they reach production, auto-fixes common problems, and integrates directly into your build pipeline so errors appear inline in Visual Studio, Rider, and any CI output.
https://github.com/CW-Software-Apps/resx-lint
Features
- Static analysis β scans
{maui:Translate Key}in XAML andAppResources.Keyin C# and reports missing keys as build errors - Auto-fixes β removes duplicate keys, adds missing
Designer.csproperties, and backfills orphaned language entries automatically - MSBuild-native errors β emits
file(line): error TRANS001: ...format so VS/Rider show inline squiggles with clickable links - Similar key suggestions β when a key is missing, suggests the closest matches to help spot typos quickly
--what-ifmode β preview every change without touching any file- CI-friendly β
--fail-on-warningsescalates warnings to errors for stricter pipelines - Zero dependencies β single self-contained executable, no extra NuGet packages required
Installation
Global (local dev)
dotnet tool install --global ResxLint
Per-repo (recommended for teams and CI)
dotnet new tool-manifest # creates .config/dotnet-tools.json β commit this file
dotnet tool install ResxLint
New devs and CI agents just run once:
dotnet tool restore
Usage
resx-lint --project-dir <dir> --resx-file <path> [options]
| Option | Description |
|---|---|
--project-dir <dir> |
Root of the project (where .xaml and .cs files live) |
--resx-file <path> |
Path to the base .resx file (e.g. Resources/AppResources.resx) |
--what-if |
Preview all changes without writing any files |
--fail-on-warnings |
Treat TRANS006 and TRANS007 as fatal errors |
--quiet |
Suppress β OK and βΉ INFO messages β only show problems |
--help |
Show help and exit |
MSBuild Integration
Drop this target into your .csproj to validate on every build.
The snippet below checks whether resx-lint is installed before running it and emits a clear, actionable error message if it is not β instead of the cryptic exited with code 9009 you'd get otherwise:
<Target Name="ValidateTranslationKeys" BeforeTargets="BeforeBuild">
<!-- Friendly error if resx-lint is not installed -->
<Exec Command="resx-lint --help"
ConsoleToMSBuild="true"
IgnoreExitCode="true"
StandardOutputImportance="low"
StandardErrorImportance="low">
<Output TaskParameter="ExitCode" PropertyName="_ResxLintExitCode" />
</Exec>
<Error
Condition="'$(_ResxLintExitCode)' == '9009' OR '$(_ResxLintExitCode)' == '127'"
Text="resx-lint is not installed. Run: dotnet tool install --global ResxLint | Docs: https://github.com/CW-Software-Apps/resx-lint" />
<!-- Run validation -->
<Exec
Command="resx-lint --project-dir "$(MSBuildProjectDirectory)" --resx-file "$(MSBuildProjectDirectory)\Resources\AppResources.resx""
ConsoleToMSBuild="true"
IgnoreExitCode="false" />
</Target>
If the tool is missing, the build stops with:
error : resx-lint is not installed. Run: dotnet tool install --global ResxLint
Docs: https://github.com/CW-Software-Apps/resx-lint
For strict mode (warnings become errors), add --fail-on-warnings to the Exec command.
CI/CD Integration
GitHub Actions
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- name: Restore tools
run: dotnet tool restore # reads .config/dotnet-tools.json
- name: Validate translations
run: resx-lint --project-dir . --resx-file Resources/AppResources.resx --fail-on-warnings
Docker / Coolify / Self-hosted
If your CI/CD builds via Docker (e.g. Coolify on Hostinger), install the tool in your Dockerfile and run it before dotnet publish:
# Install resx-lint
RUN dotnet tool install --global ResxLint
ENV PATH="${PATH}:/root/.dotnet/tools"
# Copy sources
COPY [".", "/src/app"]
# β
Validate translations β fails the Docker build if keys are missing
RUN resx-lint \
--project-dir /src/app \
--resx-file /src/YourLibrary/Resources/AppResources.resx
# Build
RUN dotnet publish MyApp.csproj -f net10.0-android -c Release
The tool exits with code 3 on fatal errors, which causes the Docker build to fail and stops the Coolify deployment before a broken build reaches production.
Diagnostic Codes
| Code | Severity | Description | Action |
|---|---|---|---|
TRANS001 |
β Fatal | Key used in {maui:Translate Key} (XAML) not found in base .resx |
Fix manually |
TRANS002 |
π§ Auto-fix | Duplicate key found in a .resx file |
Extra occurrences removed |
TRANS003 |
π§ Auto-fix | Key in base .resx has no corresponding property in Designer.cs |
Property added automatically |
TRANS004 |
β Fatal | Key accessed as AppResources.Key (C#) not found in base .resx |
Fix manually |
TRANS005 |
π§ Auto-fix | Key exists in a language file but not in the base .resx |
Added to base with [TRADUZIR] placeholder |
TRANS006 |
β οΈ Warning | Key in base .resx has no translation in one or more language files |
Add translation or escalate with --fail-on-warnings |
TRANS007 |
β οΈ Warning | Base .resx value is empty or a placeholder like [TRADUZIR] |
Translation pending |
TRANS008 |
βΉοΈ Info | Value is identical to the base language in a translated file | May be intentional (proper nouns, numbers, etc.) |
Fatal errors (TRANS001, TRANS004) stop the build immediately. Auto-fixes (TRANS002, TRANS003, TRANS005) modify files and return exit code 1 so MSBuild restarts the build to re-validate.
Exit Codes
| Code | Meaning |
|---|---|
0 |
All OK β no issues found |
1 |
Auto-fixes were applied β restart the build to re-validate |
2 |
Invalid parameters |
3 |
Fatal errors found (TRANS001 or TRANS004) |
Output Sample
βββ 4/6 Validating XAML usage ({maui:Translate}) βββ
Views/HomePage.xaml(42) : error TRANS001 : Translation key 'WelcomeTitel' not found in AppResources.resx. Similar keys: 'WelcomeTitle'.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SUMMARY β resx-lint
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
.resx base : Resources\AppResources.resx
Keys in base : 312
Languages : 2 (AppResources.en-US.resx, AppResources.es-ES.resx)
Auto-fixes applied : 0
Warnings : 0
Fatal errors : 1
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Build cancelled: 1 fatal error(s). Fix the missing keys and rebuild.
Requirements
- .NET 10 SDK or later
License
MIT Β© CW Software
FAQ
What happens when resx-lint finds a missing translation key?
If a key used in XAML via {maui:Translate Key} or in C via AppResources.Key isn't found in the base .resx file, resx-lint reports it as a fatal error (TRANS001 or TRANS004) and stops the build.
Does resx-lint modify my files automatically?
For some issues, yes — duplicate keys, missing Designer.cs properties, and orphaned language entries are auto-fixed. Fatal errors like missing keys are not auto-fixed and must be corrected manually. Running with --what-if previews every change without writing anything.
Can I avoid installing resx-lint separately in every project?
Yes — install it per-repo with a tool manifest (dotnet new tool-manifest then dotnet tool install ResxLint) instead of globally. Commit the manifest, and any teammate or CI agent just runs dotnet tool restore to get the exact same version.
How do I make untranslated keys fail the build instead of just warning?
Add the --fail-on-warnings flag, which escalates TRANS006 (missing translation in a language file) and TRANS007 (empty or placeholder value) from warnings to fatal errors.
Will a missing resx-lint installation silently break my CI pipeline?
No — the MSBuild integration snippet checks the exit code of resx-lint --help first and emits a clear error message ("resx-lint is not installed. Run: dotnet tool install --global ResxLint") instead of the cryptic exit code 9009.
Comments
Have a question, or found an issue with the code? Drop a comment below — I read and reply to every one.