views:

111

answers:

1

Scenario

A .NET/WPF desktop application should be localized (or in MS terms globalized) to a different language than English. That is, the UI needs to be adopted completely (labels, icons, ...).

Log file entries, audit trail entries, and other outputs of the application, however, should remain in English to allow the English-speaking service/support personnel reviewing it. They do not speak French or Chinese.

The application relies on RESX files for accomplishing the localization.

The Enterprise Library Validation Block is used for the validation of business rules on the object model.

Now suppose there is a service that validates its given object model arguments prior to executing the real business logic. Under some circumstances it receives invalid object model arguments but continues the execution with best effort. The provision of invalid object model data, however, should be logged in the audit trail and in the log file.

Example of a service using the validation block.

public class Service : IService
{
    public void MyMethod(MyObjectModelObject obj)
    {
        Validator validator = ValidationFactory.CreateValidator(typeof(MyObjectModelObject));
        ValidationResults results = validator.Validate(this);

        // !!! The messages in the validation results are now already localized to CurrentCulture.

        // ... build a log message: msg
        if (results.Count > 0)
        {
            Logger.Log(msg);
        }
    }
}

As stated in the code comment, when you've called Validate() on the EnterpriseLibrary validator, the validation messages are already localized to French and you have no chance to write them to a e.g. English log file.

In other areas of our application we use a message class that encapsulates the resource id and parameters until we are certain which culture we want to use the resolve the actual string value. You could call it a deferred resource resolution.

Any ideas how to introduce a similar mechanism to the Enterprise Library Validation block? Ideas so far:

  • Switching the CurrentCulture temporarily (I don't like that and it solves only half the problem)
  • Patch the Enterprise Library Validation Block (I don't like that, too)

Thanks for your help and shared ideas!

+1  A: 

When we had a requirement for deferred resource resolution we abandoned using the MessageTemplateResourceName and instead put our resource id as the MessageTemplate property. We then use that id later to look up the resource string value using the current culture.

We standardized the naming convention for the id something like this: RULESET_RULESETQUALIFIER_OPERATION_OBJECT_PROPERTY_VALIDATIONTYPE. e.g. RULESET_BMW_INSERT_CAR_YEAR_RANGE or RULESET_BMW_UPDATE_CAR_COLOR_LENGTH etc.

In the VAB configuration this would look something like:

<property name="Color">
   <validator lowerBound="0" lowerBoundType="Ignore" upperBound="50"
    upperBoundType="Inclusive" negated="false" messageTemplate="RULESET_BMW_INSERT_CAR_COLOR_LENGTH"
    messageTemplateResourceName="" messageTemplateResourceType=""
    tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    name="String Length Validator" />
                    </property>

The major downside is that you lose the ability to easily use the message template tokens to make the message slightly dynamic. This could be accomplished but the token values would have to be stored somewhere (e.g. you message class) so that they could be substituted later when the message string is evaluated.

You may also want to consider creating multiple resource files for each of the audiences the messages are targeted for. That is, one resource for user messages and one for technical messages. So you could have UserMessages.resources, UserMessages.fr-BE.resources for the user messages. Then in another resource file duplicate the ids with different messages for logging (LogMessages.resources). That way you could have additional technical information for the log message. This might be overkill though.

We then access the string values using the ResourceManager:

ResourceManager userResourceManager = 
    new ResourceManager("UserMessages", Assembly.GetExecutingAssembly());

string userMessage = userResourceManager.GetString(resourceId);

ResourceManager logResourceManager = 
    new ResourceManager("LogMessages", Assembly.GetExecutingAssembly());

// Can also try to use InvariantCulture instead of "en"
string messageToLog = logResourceManager.GetString(resourceId,  new CultureInfo("en"));
//alternative to ensure you get the english user message value:
//    string messageToLog = userResourceManager.GetString(resourceId,  new CultureInfo("en"));


You could abstract this away into a helper class or add it to your message class. You may also have to build some code to extract the ValidationResults and create message classes with the information you require.

Tuzo
Thanks a lot for your input Tuzo! The mentioned downside is a big downside for us - we definitely need dynamically composed messages. We are currently changing the implementation of the EntLib Validation Block to work completely with our message class instead of eagerly resolved strings. A rather cumbersome task but in the end we have a one approach throughout our application. Big downside is maintenance, but the validation block has been pretty stable at its core concepts in the latest releases.
olli