A .NET localization library where each message is a class — its name is the key, its properties are the placeholders. No magic strings, no naming conventions to memorize, no resource files to keep in sync.
Supported Platforms: .NET 8 | .NET 10
Documentation | CHANGELOG | NuGet: Core | NuGet: AspNetCore
Most localization frameworks start with a string key — "errors.required_field" — and a separately maintained template — "{0} is required". You pick the key by convention, match positional arguments by hand, and hope everything stays consistent across languages and refactors.
What if the message itself were a type?
public class RequiredFieldError(string fieldName) : ILocalizable
{
public string FieldName { get; } = fieldName;
}The fully-qualified type name is the key. The properties are the placeholders. Rename a class and the compiler tells you; rename a property and the template follows. There is nothing to keep in sync by hand.
This is a well-established idea — types as messages — applied to localization. The result is a system that is type-safe by design and simple by default: one DI call registers everything, culture fallback works automatically, and you can swap providers without touching the rest of your code.
- Class-based message modeling — the type name is the key, properties are the placeholders
- Culture fallback chain — exact → parent → invariant, automatic
- Multi-level provider resolution — self, DI, registry, fallback
- Nested localization —
ILocalizableproperties are formatted recursively through the full resolution chain - JSON registry — load templates from embedded resources, files, or raw strings
- JSON serialization converters — localize properties during serialization
- ASP.NET Core integration — per-request culture resolution, automatic JSON response localization, and ProblemDetails support via Bogoware.Localization.AspNetCore
- DI integration — single-call setup via
IServiceCollection - Near-zero dependencies (core) — only
Microsoft.Extensions.DependencyInjection.AbstractionsandMicrosoft.Extensions.Logging.Abstractions
dotnet add package Bogoware.Localization// 1. Define a localizable type — this *is* the message
public class RequiredFieldError(string fieldName) : ILocalizable
{
public string FieldName { get; } = fieldName;
}
// 2. Register with one DI call (scans assembly for JSON templates)
services.AddLocalization(typeof(RequiredFieldError).Assembly);
// 3. Format — culture fallback is automatic
var message = formatter.Format(new RequiredFieldError("Email"));
// → "'Email' is required" (en-US)
// → "'Email' è obbligatorio" (it-IT)Visit the documentation site for the Quick Start guide, detailed walkthroughs, and API reference.