Follow me on Twitter

First time here?

Check out the Archive, and Subscribe to the RSS feed.

Unit test for verifying references from DataAnnotation validation to the ErrorMessageResourceName value

This post was written on March 4, 2010 17:52 by MartinHN

I love the new model validation features in System.ComponentModel.DataAnnotations. One thing I don’t like though, is that the ErrorMessageResourceName is loosely typed. The ErrorMessageResourceType, however, is a System.Type which will be strongly typed by assigning its value using the typeof(Namespace.ResourceSetType) method.

Since there’s no build-breaking reference between a resource file and the value of the ErrorMessageResourceName on all classes where you use it, I thought it would be cool to have a unit test that verifies the existence of all referenced resource keys.

Remember to add a reference to System.ComponentModel.DataAnnotations.

Code

 

/// <summary>
/// Verifies that all properties that are decorated with validation data-annotations, refers to 
/// an existing resource. This will make sure, that missing resources are not referenced.
/// </summary>
[TestMethod]
public void All_Properties_With_Validation_Annotations_Must_Refer_To_Existing_Resource()
{
    Assembly assembly = Assembly.Load(new AssemblyName("MyApp.Model.Namespace"));
    var types = assembly.GetTypes().Where<Type>(t => t.IsClass && !t.IsAbstract);

    foreach (var type in types)
    {
        var properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            var attributes = property.GetCustomAttributes(true);

            foreach (var item in attributes)
            {
                if (item is ValidationAttribute)
                {
                    ValidationAttribute val = item as ValidationAttribute;

                    Assert.IsNotNull(val);

                    if (val.ErrorMessageResourceType != null)
                    {
                        Assert.AreNotEqual(String.Empty, val.ErrorMessageResourceName, 
                            String.Format(@"Validation Error Resource specified on property: 
                        {0}.{1} is empty!", type.ToString(), property.Name));

                        try
                        {
                            ResourceManager rm = new ResourceManager(val.ErrorMessageResourceType);
                            string resourceValue = rm.GetString(val.ErrorMessageResourceName);
                            Assert.IsFalse(String.IsNullOrEmpty(resourceValue), 
                                String.Format(@"The value of the Validation Error Resource specified on property: 
                            {0}.{1} is empty!", type.ToString(), property.Name));
                        }
                        catch (MissingManifestResourceException)
                        {
                            Assert.Fail(String.Format(@"Validation Error Resource specified on property: 
                            {0}.{1} could not be found!", type.ToString(), property.Name));
                        }
                    }
                }
            }
        }
    }
}

Automatically translate Global and Local Resource (resx) files

This post was written on March 13, 2009 22:31 by MartinHN

Yesterday, I blogged about how you can use Google Translate to translate a string in C#. To make it more useful than just a simple translator, and because I need to translate some Global Resource files for an E-commerce website that I’m working on, I wanted to create a small Windows Application in C# that could read a Global Resource file (.resx) and translate it into a selected language using the method for translating a word in C# that i blogged about yesterday.

image

This is how it looks so far. You simply select the resource file you want to translate. Select the current language of the resource file in the middle box, and select the language you want to translate it to in the last box. Click Translate at it should work. The new resource file will be saved in the same location as the application itself.

Designing for Internationalization

This post was written on June 10, 2008 20:17 by MartinHN

Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace for handling things like this. That is the System.Globalization namespace, where you will find a lot of classes for handling your every day globalization tasks. I'm not going to cover anything in this namespace now, if you want to get your hands dirty take a look at this video: http://asp.net/learn/videos/video-40.aspx where you will see how to use local and global resources for your application.

Using a global resource file for a place to store display text on buttons, labels, validation controls etc. is fine. But if you have an e-commerce site, selling products in multiple regions with different languages, you need an extra level. That level is a way to globalize e.g. the name and description of your product. When a user changes language, the name and description of your "display product details page" should change accordingly.

For me, the ideal solution should not result in extra database columns like name_us, name_da, name_es. This would be a very static solution, as you would have to change your database whenever a new language is added to your application. Nor should it require extra tables, so you need to join like hell, when you need to select a product.

I've decided to store e.g. the Name values as XML in the database, and parse that XML into a Dictionary<string, string> property on my Product object, with the key of the Dictionary being the language code (en-US for US English). To me this seems to work just fine. My database design is not getting more complex, and I can get and set values quite easily.

The XML string that goes into my ProductName column in the database table, looks like this:

<cultures><culture code="en-us">Logitech SmartCam 124</culture></cultures>

On my Product object, the Name property is a Dictionary:

private Dictionary<string, string> _Name;

public Dictionary<string, string> Name
{
  get { return _Name; }
  set
  {
    if (_Name != value) MarkDirty("Name");
    _Name = value;
  }
}

When I need to get the US English value of the Product.Name property, I call:

p.Name["en-us"]

To get a Dictionary from my XML, I use this helper method:

public static Dictionary<string, string> GetDictionary(string xml)
{
  if (String.IsNullOrEmpty(xml))
    return new Dictionary<string, string>(owner);
    
  XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);
    
  Dictionary<string, string> dic = new Dictionary<string, string>();

  foreach (XmlNode node in doc.DocumentElement)
  {
    dic.Add(node.Attributes["code"].Value, node.InnerText);
  }

  return dic;
}

And when I want to update my database, after I change the Name of the Product, I convert to Dictionary to an XML string using this helper method:

public static string GetXmlDocument(Dictionary<string, string> dic)
{
  XmlDocument doc = new XmlDocument();
  XmlNode docElement = doc.CreateNode(XmlNodeType.Element, "cultures", "");

  foreach (string key in dic.Keys)
  {
    XmlNode node = doc.CreateNode(XmlNodeType.Element, "culture", "");
    XmlAttribute att = doc.CreateAttribute("code");
    att.Value = key;
    node.Attributes.Append(att);

    node.InnerText = dic[key];

    docElement.AppendChild(node);
  }

  doc.AppendChild(docElement);

  return doc.OuterXml;
}

If you need more information on this huge topic, take a look at the ASP.NET Wiki: http://wiki.asp.net/page.aspx/55/internationalization/

Powered by BlogEngine.NET 1.6.0.0

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2010