Localization of string in CS file.

Topics: Localization
Editor
Jul 23, 2011 at 3:33 AM

I have read the documentation concerning localization of strings in a CS file and am having trouble getting it to all work. I have my PO file for my module declared as follows:

#:  CyberStride.Contacts.Models.RequiredFieldAttribute
#| msgid "The {0} field is required."
msgid "The {0} field is required."
msgstr "The {0} de requiro."

Then in my cs file:

public class RequiredFieldAttribute : RequiredAttribute
    {
        public RequiredFieldAttribute()
        {
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        public override string FormatErrorMessage(string name)
        {
            return T("The {0} field is required.",name).Text;
        }
    }

 

I then adorn the Email field of the Model with the RequiredFieldAttribute method attribute. The code is getting hit, but it seems like it is not finding the lookup string in the PO file because I always get the English string back, even though I am in French. Any help would be great.

 

 

 

Editor
Jul 23, 2011 at 3:43 AM

Is it because I have T set to NullLocalizer.Instance ? I don't think I can pass in a reference to the localizer through a method attribute, correct?

Coordinator
Jul 25, 2011 at 10:35 PM

You might want to break into a debugger on that last line of code and see what is happening.

Developer
Aug 4, 2011 at 1:51 PM

Did you manage to localize your attributes Message?

There is the same problem in Orchard.Comments :

#: Orchard.Comments.Annotations.RequiredAttribute
#| msgid "The {0} is not valid."
msgid "The {0} is not valid."
msgstr "{0} invalide."

I'm trying to localize the strings in French but nothing changes in validation messages.

Editor
Aug 4, 2011 at 1:56 PM

I did not. What I had to do was basically in the ContactController.cs add the following code and validate by hand:

 

if (string.IsNullOrEmpty(model.Name))
            {
                ModelState.AddModelError("Name", T("The Name field is required."));
            }

 

Then in the PO file I referenced the ContactController.cs file. For some reason my override file never got hit by the localizer.

Sep 4, 2011 at 1:02 PM

I got around this using a hack in the controller as follows.  This just loops through the errors and translates them one by one.

if (!ModelState.IsValid)
{                
	foreach (ModelState state in ModelState.Values)
	{
		if ( state.Errors.Count > 0 ) {
			//Rebuild the ModelErrorCollection and re-assign it after because the ModelError.ErrorMessage is get{} only.
			ModelErrorCollection errorCollection = new ModelErrorCollection();
			foreach ( ModelError e in state.Errors) {
				ModelError error = new ModelError(  T(e.ErrorMessage).ToString() );
				errorCollection.Add(error);
			}
			state.Errors.Clear(); 
			foreach ( var e in errorCollection ) state.Errors.Add( e ); //doesn't have an AddRange
		}                
	}                
	return View(form);
}

 

This approach will let you use (basically) normal attribute ErrorMessage in things like view models :

        [Required( ErrorMessage="A Name is required.")]
        [StringLength(100)( ErrorMessage="Name can be a maximum of 100 characters.")]
        public String Name { get; set; }

I have only used the code in some simple places, so if you use and and find problems, post updates here and I'll do the same.

Sep 9, 2011 at 1:11 PM

Indeed it is a good hack. But with this approach you have to manually add the strings to your .po-files, right? The "extract translation" (explained here: http://weblogs.asp.net/bleroy/archive/2011/01/13/creating-and-maintaining-orchard-translations.aspx) will not find the "dynamically" translated strings, unfortunately.

Sep 12, 2011 at 8:01 PM

Good point JLedel.  I never thought of the implications for the Translation Manager when I wrote it.  (probably because I haven't yet used it )

So in summary, this is a bad hack to use for modules donated to the gallery, since people contributing to the translations will most likely miss the translation of the text in the attributes  (i.e. the translation manager will not detect the strings in the attributes and automatically create entries in the .po files (nor track changes) )

For myself, it is good hack because my modules are not public, so it won't affect those people contributing to translations.   It allows simpler/cleaner initial coding, with the added advantage that when the translation problem in custom attributes is fixed, I can drop in a custom attributes without major refactoring of existing code.

I am trying to think if there is some way to make 'a duplicated dummy string with T()' so the translation manager will detect them but if I'm not mistaken, it always include a 'reference string' (basically a path to the originating file)?

Sep 12, 2011 at 9:21 PM
Edited Sep 12, 2011 at 9:22 PM

The hack itself is indeed good. However, issue #17754 Model validation should use Localized Data Annotations (http://orchard.codeplex.com/workitem/17754) should be fixed in the 1.3 release according to the details. I have no idea in how this issue is/will be solved, but I assume it will be compatible with the Translation Manager and at the same time keep our code clean.

So I will relax and wait until Orchard 1.3 ships, and I think I've read in a post by Bertrand that it should be ready this month, but I'm not sure about that.

Sep 17, 2011 at 3:33 PM

The issue is fixed in changeset http://orchard.codeplex.com/SourceControl/changeset/changes/5a20645193e1. So you should be able to use the normal attribute ErrorMessage.

Sep 17, 2012 at 8:42 AM

Hi,

We are using Orchard v1.4, I am facing issue for translating text in CS file. I am using T constructor injection for this purpose. If I try to localize the text in controller it works fine, however if I try to localize the text in other CS files it does not work. can anyone throw some light on what is going wrong or how should it be done.

Thanks in advance.

Regards,

Ambrish

Coordinator
Sep 17, 2012 at 5:29 PM

It's impossible to say without knowing specifically what you're doing.

Sep 18, 2012 at 6:27 AM

Ok, Let me explain...

We have a class which returns months (List<SelectListItem>) e.g. jan, feb, mar etc. and it is bound to drop down list. For localizing months we are doing the following.

In class e.g. example.cs -

Defined a property: public Localizer T { get; set; }

In Constructor: T = NullLocalizer.Instance;

and T is used like below (in a method in same class)

new SelectListItem { Value = string.Empty, Text = T("Month").Text }, // Maand
                new SelectListItem { Value = "1", Text = T("jan").Text },
                new SelectListItem { Value = "2", Text = T("feb").Text },
                new SelectListItem { Value = "3", Text = T("maa").Text },.....

then we put translations for these in Q42 module for nl-BE and en-US cultures and set the culture to nl-BE in orchard and verified the code. What we found is, the T has the target as null (due to this scope is not associated) because of this the translation does not happen. If we move this same code to a controller then everything works fine.

Am I missing on anything here?

let me know if this is clear enough or you need more information.

Coordinator
Sep 18, 2012 at 5:07 PM

My guess would be that you are newing up that class instead of injecting it, so its dependencies are not properly injected. Maybe. But you didn't provide the consuming code so it's still hard to say.

Sep 20, 2012 at 4:34 PM

-> Below is the cs file where localization is required using T.

    public class Months
    {

        public Months()
        {
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        public Collection<SelectListItem> GetMonthList()
        {
            return new Collection<SelectListItem>
            {
                new SelectListItem { Value = string.Empty, Text = T("Month").Text }, // Maand
                new SelectListItem { Value = "1", Text = T("jan").Text },
                new SelectListItem { Value = "2", Text = T("feb").Text },
                new SelectListItem { Value = "3", Text = T("maa").Text },
                new SelectListItem { Value = "4", Text = T("apr").Text },
                new SelectListItem { Value = "5", Text = T("mei").Text },
                new SelectListItem { Value = "6", Text = T("jun").Text },
                new SelectListItem { Value = "7", Text = T("jul").Text },
                new SelectListItem { Value = "8", Text = T("aug").Text },
                new SelectListItem { Value = "9", Text = T("sep").Text },
                new SelectListItem { Value = "10", Text = T("okt").Text },
                new SelectListItem { Value = "11", Text = T("nov").Text },
                new SelectListItem { Value = "12", Text = T("dec").Text },
            };
        }

    }

 

-> below is the model

    public class SampleModel
    {
        public int SelectedMonth { get; set; }

        public List<SelectListItem> LocalizedMonths { get; set; }
    }

 

-> below is the Controller

    public class SampleController : Controller
    {

        [HttpGet]
        public ActionResult Index()
        {
            var model = new SampleModel();
            var months = new Months();

           Var localizedmonths = months.GetMonthList();

            model.LocalizedMonths = localizedmonths;

            return View(model);
        }

     }

 

-> below is the view, the SampleModel is bound to this view.

The localized months are being consumed in this view i.e. bound to a drop down list.

    @model SampleModel

    @using (Html.BeginForm(……))
    {

         @Html.DropDownListFor(model => model.SelectedMonth, Model.LocalizedMonths)

     }

 

Coordinator
Sep 21, 2012 at 8:49 PM

Yes, that's what I said above: you are newing up Months instead of injecting it, so its own dependencies never get injected.

Sep 25, 2012 at 11:15 AM

you are correct, it works.

Thanks a lot.

Feb 1, 2013 at 6:02 PM
Hello ambrishatlapure, could you explain to me how you managed to "inject" in your situation? I think i may have the same error.

Thamks in advance.