Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Localization and DataAnnotations. GlobalResourceProxyGenerator and PublicResxFileCodeGenerator

Why do DataAnnotation attributes have difficulty accessing resources created by PublicResxFileCodeGenerator?

I find that the following attribute:

[Compare("NewPassword", ErrorMessageResourceName = "RegisterModel_ConfirmPasswordError", ErrorMessageResourceType = typeof(Resources.Global))]

Will fail to find the resource if it has been created with PublicResxFileCodeGenerator. However an identical resource created with GlobalResourceProxyGenerator will work correctly. Both resource files are set to Content and live in App_GlobalResources. I've tried putting the default language in App_LocalResources too but it seems to make no difference. My test being that my secondary language (GlobalResourceProxyGenerator) works but my primary language (PublicResxFileCodeGenerator) throws an exception (it fails to find the resource file). If I switch both to GlobalResourceProxyGenerator then everything is fine (but obviously there is no public access).

Does anyone know why this is? I'd like to move the resources into another assembly in the future.

like image 296
Quibblesome Avatar asked Feb 03 '13 17:02

Quibblesome


1 Answers

That's because you placed your resource file inside the App_GlobalResources folder which is a special folder in ASP.NET. This should work if you put your resources file somewhere else. This could also be a completely separate project from your ASP.NET MVC application.

Here are the steps you could make this work:

  1. Create a new ASP.NET MVC 3 application using the default internet template
  2. Add a ~/Messages.resx file containing the RegisterModel_ConfirmPasswordError resource string
  3. Set the custom tool to PublicResXFileCodeGenerator for this resource file:

    enter image description here

  4. Add a model:

    public class MyViewModel
    {
        [Compare("NewPassword", 
                 ErrorMessageResourceName = "RegisterModel_ConfirmPasswordError",
                 ErrorMessageResourceType = typeof(MvcApplication1.Messages))]
        public string Password { get; set; }
    
        public string NewPassword { get; set; }
    }
    
  5. Controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new MyViewModel());
        }
    
        [HttpPost]
        public ActionResult Index(MyViewModel model)
        {
            return View(model);
        }
    }
    
  6. View:

    @model MyViewModel
    
    @using (Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Password)
            @Html.EditorFor(x => x.Password)
            @Html.ValidationMessageFor(x => x.Password)
        </div>
    
        <div>
            @Html.LabelFor(x => x.NewPassword)
            @Html.EditorFor(x => x.NewPassword)
            @Html.ValidationMessageFor(x => x.NewPassword)
        </div>
    
        <button type="submit">OK</button>
    }
    

Then you could start localizing by providing respective translations:

  • Messages.fr-FR.resx
  • Messages.de-DE.resx
  • Messages.it-IT.resx
  • Messages.es-ES.resx
  • ...

UPDATE:

I was asked in the comments section what's so special about the App_GlobalResources folder and why it doesn't work with it. Well, actually you could make it work. All you need to do is set the Build Action to Embedded Resource. By default when you add a file to the App_GlobalResources folder, Visual Studio set it to Content meaning that this resource will not be incorporated into the runtime assembly and ASP.NET MVC cannot find it:

enter image description here

like image 184
Darin Dimitrov Avatar answered Oct 03 '22 21:10

Darin Dimitrov