So, I landed here because of creating my own custom IStringLocalizer and wanted to share my solution because @jlchavez helped me out.
I created a MongoDB IStringLocalizer and wanted to use the resources via the DataAnnotations. Problem is that DataAnnotations Attributes expect localizations via a static class exposing the resources.
One enhancement over jlchavez's answer is that this will fix the resource messages for all ValidationAttribute(s)
services.AddTransient<IValidationMetadataProvider, Models.LocalizedValidationMetadataProvider>();
services.AddOptions<MvcOptions>()
.Configure<IValidationMetadataProvider>((options, provider) =>
{
options.ModelMetadataDetailsProviders.Add(provider);
});
public class Resource
{
public string Id => Culture + "." + Name;
public string Culture { get; set; }
public string Name { get; set; }
public string Text { get; set; }
}
public class MongoLocalizerFactory : IStringLocalizerFactory
{
private readonly IMongoCollection<Resource> _resources;
public MongoLocalizerFactory(IMongoCollection<Resource> resources)
{
_resources = resources;
}
public IStringLocalizer Create(Type resourceSource)
{
return new MongoLocalizer(_resources);
}
public IStringLocalizer Create(string baseName, string location)
{
return new MongoLocalizer(_resources);
}
}
public class MongoLocalizer : IStringLocalizer
{
private readonly IMongoCollection<Resource> _resources;
public MongoLocalizer(IMongoCollection<Resource> resources)
{
_resources = resources;
}
public LocalizedString this[string name]
{
get
{
var value = GetString(name);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
var format = GetString(name);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
public IStringLocalizer WithCulture(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentCulture = culture;
return new MongoLocalizer(_resources);
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
{
var resources = _resources.Find(r => r.Culture == CultureInfo.CurrentCulture.Parent.Name).ToList();
return resources.Select(r => new LocalizedString(r.Name, r.Text, false));
}
private string GetString(string name)
{
var resource = _resources.Find(r => r.Culture == CultureInfo.CurrentCulture.Parent.Name && r.Name == name).SingleOrDefault();
if (resource != null)
{
return new LocalizedString(resource.Name, resource.Text, false);
}
return new LocalizedString(name, name, true);
}
}
public class LocalizedValidationMetadataProvider : IValidationMetadataProvider
{
private IStringLocalizer _localizer;
public LocalizedValidationMetadataProvider(IStringLocalizer localizer)
{
_localizer = localizer;
}
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
foreach(var metadata in context.ValidationMetadata.ValidatorMetadata)
{
if (metadata is ValidationAttribute attribute)
{
attribute.ErrorMessage = _localizer[attribute.ErrorMessage].Value;
}
}
}
}