18

We are facing with an strange error with localization of Required attribute.

We have the following code:

    public class AnswersGroupViewModel
    {
        public int IDAnswerGroup { get; set; }
        public int IDEvaluator { get; set; }
        public List<AnswersViewModel> Answers { get; set; }
    }

    public class AnswersViewModel
    {        
        public string Text{ get; set; }      
        [Required(ErrorMessageResourceName = "RequiredMessage", ErrorMessageResourceType = typeof(Resources.Language))]
        public int IDAnswer{ get; set; }
    }

The problem is that the right translation of "RequiredMessage" is not being picked up from resource file, although it is present (we have RequiredMessage on both spanish and russian resource files).

Attributes like Display are working and being translated, but seems to be a problem with the Required attribute.

Here is an example image:

Labels are translated to russian, but required message is in spanish

Error is shown with a @Html.ValidationMessage

Thanks in advance for your help.

Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
rubenfa
  • 831
  • 1
  • 7
  • 23
  • And does your translation look like "Please enter {0}" ? Obviously Please enter will be in russian or whatever, I'm asking if you include the {0}. – Ondrej Svejdar Apr 21 '15 at 10:14
  • Not in this case. Only a message saying the field is required. – rubenfa Apr 21 '15 at 10:18
  • Does the problem only concern client side validation or server side validation or both? – Tomi Niemenmaa Apr 22 '15 at 10:44
  • Validation is performed on server. ModelState.IsValid is false so it is returned to View – rubenfa Apr 22 '15 at 14:25
  • not technically true. Validation is **configured** on server, but it is enforced on client and optionally on model checking – Dave Alperovich Apr 22 '15 at 14:35
  • @DaveAlperovich I mean, in this case, a POST is performed (go to de server and back) – rubenfa Apr 22 '15 at 14:44
  • are you letting the user pick a language or having them connect from a Russian browser (lan setting). Those are 2 completely different approaches! In order to test our current approach you need a user connecting from a russian browser. If you want to pass the language, you want a custom require attribute that takes a language type – Dave Alperovich Apr 23 '15 at 02:07

3 Answers3

8

I would start with setting the globalization element in web.config

<system.web>
  <globalization enableClientBasedCulture="true" culture="auto" uiCulture="auto"/>

Next I would check the culture you are getting in a Controller (log it in DB or pass to a view with @ViewBag

culture = CultureInfo.InstalledUICulture.IetfLanguageTag;
if (HttpContext.Current != null && HttpContext.Current.Request.UserLanguages != null)
{
    culture = Request.UserLanguages[0];     
}

Confirm that the values you use to identify Culture are the values being used by headers


Followup

You are using globalization headers to check for user language, but are setting user language manually in browser.

2 approaches to try:

1) set user language in browser using Javascript:

<script type="text/javascript"> 
     Globalize.culture("@ViewBag.Culture");
</script>

Or if culture is set in Javascript (vs C#)

<script type="text/javascript"> 
     Globalize.culture(culturevariable);
</script>

2) ParameterisedRequiredAttribute

public class ParameterisedRequiredAttribute : RequiredAttribute
{
    private string[] _replacements { get; set; }

    public ParameterisedRequiredAttribute(params string[] replacements)
    {
        _replacements = replacements;

        ErrorMessageResourceName = ErrorMessagesErrors.SpecificFieldRequired;
        ErrorMessageResourceType = typeof(ErrorMessages);
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, (object[])_replacements);
    }
}

The first approach seems like a better solution if you are manually setting the language. Simply put, the annotation would have to be re-set everytime the user changes language modes.

Another thing to consider is that the way you currently set language (through headers) is probably your best solution. While this is harder to test because you need a client with dedicated language, this is the way your users are best distinguished (by headers).

Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
  • We already have that line on Web.config. But our line is lightly different. We have "auto:es" on tags "culture" and "uiCulture". – rubenfa Apr 22 '15 at 14:13
  • @rubenfa, try changing it to just "auto" for `uiCulture`. if that doesn't work, we can create a custom attribute. – Dave Alperovich Apr 22 '15 at 14:35
  • Upss, you are right. Culture is always "es-ES" on controller. If I set uiCulture as "ru" on web.config, error message is translated. With auto does not work. We are using [code52](http://code52.org/aspnet-internationalization/tutorial.html) to change language, and it seems the problem is there. – rubenfa Apr 22 '15 at 15:20
  • @rubenfa, try this hack to see if it changes anything `` – Dave Alperovich Apr 22 '15 at 16:23
  • @rubenfa, I suspect the problem is in the headers you are getting. When you checked `Request.UserLanguages` you were checking the request headers from the requester's browser. Just curious, have you tried setting `uiCulture="auto"`? – Dave Alperovich Apr 22 '15 at 16:32
  • @rubenfa, also, please explain why you would expect non-spanish headers. Have you attempted to make request from a Russian setting browser? If you send me a URL, I can check it from english. – Dave Alperovich Apr 22 '15 at 16:34
  • That is !! I was thinking that if I change language from UI (we have a option to change it), the culture should change too. But as you say, headers are sent with spanish header. Changing the language on the browser solves the problem. So, I need a custom attribute? – rubenfa Apr 23 '15 at 07:25
  • @rubenfa, I followed up with an answer. I have created a custom attribute and a javascript instruction to change the language on the client. As long as your user has chosen the language once the client is rendered, this should work. The custom attribute is more difficult because I can't see a way for you to re-assign it to the model dynamically in a comfortable way -- annotations are meant to be set at time of model instantiation. – Dave Alperovich Apr 23 '15 at 08:25
  • @rubenfa, also keep in mind, that while the JS sample I added is probably the best way to dynamically changes the language by your current use, you real users are likely to use proper headers (Russian users will have Russian headers). So while you will struggle to test your current implementation, it is already correct. If any of this is unclear, feel free to followup with questions. – Dave Alperovich Apr 23 '15 at 08:28
  • Many thanks for your time Dave. I have changed some things with some external libraries, and Javascript way worked well after that. – rubenfa Apr 28 '15 at 07:28
1

Make sure that your current thread context has been updated to use the correct CultureInfo based on the web client's Request headers. This should be automatic if you have the <globalization ..> tag defined. Otherwise, add a filter to intercept the request and perform the update.

var cultureName = "de-DE"; // get this from request headers
var ci = new CultureInfo(cultureName);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;

See also From where CultureInfo.CurrentCulture reads culture

Community
  • 1
  • 1
Jon Davis
  • 6,562
  • 5
  • 43
  • 60
1

I have created a sample app in order to reproduce your issue. It works for me so I'll give you my current setup.

I have a Web Application project referencing a library assembly Resources.

I have 2 resx files in the Resource project (language.resx and language.fr.resx). Both files have the same properties:

  • Build Action: Embedded Resource,
  • Custom Tool: PublicResXFileCodeGenerator,
  • Custom Tool Namespace: MyResources

The ViewModel AnswersViewModel has a IDAnswer attribute with the following attribute:

[Required(ErrorMessageResourceName = "RequiredMessage", 
          ErrorMessageResourceType = typeof(MyResources.Language))]

I have added the namespace MyResource to the web.config file in the View folder and I have updated the section system.web of the main web.config by adding

<globalization enableClientBasedCulture="true" culture="auto" uiCulture="auto"/>

I hope it will help.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291