I have my resource files in separate assembly MyApp.Resources.dll. I can use the resources without any problem but the issue appears when I want to change (localize) the default validation messages:
"The {0} field is required." and "The value '{0}' is not valid for {1}."
The solution DefaultModelBinder.ResourceClassKey = "MyApp.Resources.Global";
does not work because it requires the ResourceClassKey to be under App_GlobalResources folder in the web project.
What should be the fix for me ?
Regards
I have found solution for this case (when resources are in separate assembly).
To get it working you should create custom ResourceProviderFactory and register it as default ResourceProviderFactoryType in <globalization>
web.config section.
Setup Localization
// Modify web.config in run-time and setup custom ResourceProviderFactory
var globalization = WebConfigurationManager.GetSection("system.web/globalization") as GlobalizationSection;
var readonlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
readonlyField.SetValue(globalization, false);
globalization.ResourceProviderFactoryType = typeof(ValidationResourceProviderFactory).FullName;
var resourcesClass = typeof(ValidationResources).FullName;
DefaultModelBinder.ResourceClassKey = resourcesClass;
ValidationExtensions.ResourceClassKey = resourcesClass;
ValidationResourceProviderFactory
public sealed class ValidationResourceProviderFactory: System.Web.Compilation.ResourceProviderFactory
{
public ValidationResourceProviderFactory()
{
}
public override IResourceProvider CreateGlobalResourceProvider(string classKey)
{
return new GlobalResourceProvider(classKey);
}
public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
throw new NotImplementedException("Local resources are not supported yet");
}
}
GlobalResourceProvider
public class GlobalResourceProvider : IResourceProvider
{
public GlobalResourceProvider(string classKey)
{
Throw.IfBadArgument(() => String.IsNullOrEmpty(classKey), "classKey");
var type = Type.GetType(classKey, false);
if (type == null)
{
var asmName = classKey;
var className = classKey;
while(asmName.IndexOf(".") > -1 && type == null)
{
asmName = asmName.Substring (0, asmName.LastIndexOf("."));
className = classKey.Substring(asmName.Length + 1);
type = Type.GetType(classKey + "," + asmName, false);
}
}
Throw.IfNullArgument(type, "type");
Manager = CreateResourceManager(classKey, type.Assembly);
}
public ResourceManager Manager { get; set; }
#region IResourceProvider implementation
public IResourceReader ResourceReader { get; set; }
public object GetObject(string resourceKey, CultureInfo culture)
{
return Manager.GetObject(resourceKey, culture);
}
#endregion
private ResourceManager CreateResourceManager(string classKey, Assembly assembly)
{
return new ResourceManager(classKey, assembly);
}
}
UPD
RESX for ValidationResources
Just add new resources class as ValidationResources and place provided XML there
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<!-- Schema definited removed -->
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Accept" xml:space="preserve">
<value>Please enter a value with a valid mimetype.</value>
</data>
<data name="Creditcard" xml:space="preserve">
<value>Please enter a valid credit card number.</value>
</data>
<data name="Date" xml:space="preserve">
<value>Please enter a valid date.</value>
</data>
<data name="DateISO" xml:space="preserve">
<value>Please enter a valid date (ISO).</value>
</data>
<data name="DateTime" xml:space="preserve">
<value>Please enter a valid date and time.</value>
</data>
<data name="Digits" xml:space="preserve">
<value>Please enter only digits.</value>
</data>
<data name="Email" xml:space="preserve">
<value>Please enter a valid email address.</value>
</data>
<data name="EqualTo" xml:space="preserve">
<value>Please enter the same value again.</value>
</data>
<data name="FieldMustBeDate" xml:space="preserve">
<value>Please enter a valid date for "{0}".</value>
<comment>Localization for legacy MVC ClientDataTypeModelValidatorProvider</comment>
</data>
<data name="FieldMustBeNumeric" xml:space="preserve">
<value>Please enter a valid number for "{0}".</value>
<comment>Localization for legacy MVC ClientDataTypeModelValidatorProvider</comment>
</data>
<data name="InvalidPropertyValue" xml:space="preserve">
<value>Invalid property value: {0}</value>
</data>
<data name="Max" xml:space="preserve">
<value>Please enter a value less than or equal to {0}.</value>
</data>
<data name="MaxLength" xml:space="preserve">
<value>Please enter no more than {0} characters.</value>
</data>
<data name="Min" xml:space="preserve">
<value>Please enter a value greater than or equal to {0}.</value>
</data>
<data name="MinLength" xml:space="preserve">
<value>Please enter at least {0} characters.</value>
</data>
<data name="Number" xml:space="preserve">
<value>Please enter a valid number.</value>
</data>
<data name="PropertyValueInvalid" xml:space="preserve">
<value>The value "{0}" is invalid for the property "{1}"</value>
<comment>Localization for legacy MVC DefaultModelBinder</comment>
</data>
<data name="PropertyValueRequired" xml:space="preserve">
<value>The "{0}" field is required.</value>
<comment>Localization for legacy MVC DefaultModelBinder</comment>
</data>
<data name="Range" xml:space="preserve">
<value>Please enter a value between {1} and {2}.</value>
</data>
<data name="RangeClient" xml:space="preserve">
<value>Please enter a value between {0} and {1}.</value>
</data>
<data name="RangeLength" xml:space="preserve">
<value>Please enter a value between {0} and {1} characters long.</value>
</data>
<data name="Remote" xml:space="preserve">
<value>Please fix this field.</value>
</data>
<data name="SignedInt" xml:space="preserve">
<value>Please enter an integer value, sign allowed.</value>
</data>
<data name="Time" xml:space="preserve">
<value>Please enter a valid time.</value>
</data>
<data name="UnsignedInt" xml:space="preserve">
<value>Please enter a positive integer value.</value>
</data>
<data name="Url" xml:space="preserve">
<value>Please enter a valid URL.</value>
</data>
<data name="ValueNull" xml:space="preserve">
<value><null></value>
</data>
</root>
There should be static properties on the DefaultModelBinder that you can set that will change the localization of the error messages...
http://forums.asp.net/p/1512140/3608427.aspx
Create a global resource class in
App_GlobalResources, and set
DefaultModelBinder.ResourceClassKey to
the name of this class (for example,
if you made "Messages.resx", then set
ResourceClassKey to "Messages").
There are two strings you can override
in MVC 2:
The string value for
"PropertyValueInvalid" is used when
the data the user entered isn't
compatible with the data type (for
example, typing in "abc" for an
integer field). The default message
for this is: "The value '{0}' is not
valid for {1}." The string value for
"PropertyValueRequired" is used when
the user did not enter any data for a
field which is not nullable (for
example, an integer field). The
default message for this is: "A value
is required."