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:
Thanks for your help and shared ideas!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With