Skip to content

Custom Providers

While JSON templates cover most use cases, you can implement custom localization providers for more complex scenarios.

ILocalizationProvider (Self-Provider)

Implement directly on your localizable type for full control:

public class DynamicError : ILocalizable, ILocalizationProvider
{
public string Code { get; init; }
public string Localize(CultureInfo culture)
{
// Load from database, remote service, etc.
return LoadFromDatabase(Code, culture);
}
}

This is the highest priority in the resolution chain — it bypasses the registry entirely.

ILocalizationProvider<T> (DI Provider)

Register a provider for a specific type via dependency injection:

public class RichOrderErrorProvider : ILocalizationProvider<OrderError>
{
private readonly IOrderRepository _orders;
public RichOrderErrorProvider(IOrderRepository orders)
{
_orders = orders;
}
public string? Localize(OrderError instance, CultureInfo culture)
{
var order = _orders.Find(instance.OrderId);
if (order is null) return null; // Fall through to next provider
return culture.Name switch
{
"it-IT" => $"Ordine #{order.Number} fallito: {instance.Reason}",
_ => $"Order #{order.Number} failed: {instance.Reason}"
};
}
}

Register in DI:

services.AddSingleton<ILocalizationProvider<OrderError>, RichOrderErrorProvider>();

When to Use Custom Providers

ScenarioRecommended Approach
Static, template-based stringsJSON templates
Dynamic content (database, API)ILocalizationProvider
Type-specific logic with DI dependenciesILocalizationProvider<T>
Generic types (e.g. Result<T>)ILocalizationProvider or ILocalizationProvider<T>
TestingInMemoryLocalizationRegistry