How To Write Custom FxCop Rules

I love FxCop. What I like about it most is that you can write your own FxCop rules. That means that when you define a coding rule in your development standard you can write a custom FxCop rule for it and get FxCop to enforce the rule. Certainly this speeds up code reviews but it also relieves us all from the dull drudgery of wading through code for the silly little things and allows us to spend our time using our brains instead.

I've been writing custom FxCop rules for a number of years and although I'm a big fan the part where I get to writing the rules has traditionally been a lot of pain. The reason is that there wasn't any documentation for the SDK - you had to work it out yourself. I wrote Chapter 13 of .NET Internationalization explaining how to write custom rules but this was focused more on what it meant to the world of internationalization than all of the details of how to write rules. Well now the pain can go away for everyone. Everyone put their hands together for Jason Kresowaty. Jason took it upon himself to write the documentation for the FxCop SDK! Microsoft didn't ask him to do it, he just did it anyway. And it's very good (you can download it here). What's even better is he wrote Introspector (available for download at the same location). Introspector does for introspection of assemblies what Reflector does for reflection of assemblies. FxCop is based on Microsoft's introspection engine (FxCop hasn't used reflection for some years now) so an Introspection tool is a godsend (or a Jason-send in this case).

Thanks, Jason, this is a big deal.

(Alternatively, if you don't want to read the docs I gave a presentation on this at a VBUG Conference and you can download a video of this presentation here and the slides here).

 

Technorati Tags: FxCop, Jason Kresowaty, Introspector, Introspection

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: GuySmithFerrier
Posted on: Thursday, July 10, 2008 at 9:50 PM
Tags:
Categories: Internationalization | Miscellaneous - Technical
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Public Strongly Typed Resources With PublicResXFileCodeGenerator

One of the new localization features in Visual Studio 2008 is the PublicResXFileCodeGenerator. In both Visual Studio 2005 and 2008 you can create a resource file (e.g. ExceptionResources.resx) and Visual Studio will associate a .Designer.cs file (e.g. ExceptionResources.Designer.cs) with it. The .Designer.cs file contains a strongly typed resource class that allows you to refer to the resource entries as properties of the generated class (e.g. ExceptionResources.CustomExceptionMessage). This system works well but the class and its properties are marked as internal and consequently they can only be accessed from within the same assembly. Visual Studio 2008 allows these classes and their properties to be public. By default the resources are still internal and you can see this by selecting the .resx file in Solution Explorer and then bringing up the Properties Window. You will see a property called Custom Tool and it will be set to ResXFileCodeGenerator. This is the code generator that uses the .NET Framework's StronglyTypedResourceBuilder class to generate the code for the .Designer.cs file. The StronglyTypedResourceBuilder (and its associated command line tool ResGen) both accept a parameter to generate public resources but the ResXFileCodeGenerator sets this value to false and so the resources are internal. Visual Studio 2008, however, introduces a new generator called a PublicResXFileCodeGenerator and this generator sets this value to true. To generate public strongly typed resource classes instead of internal ones set the Custom Tool to PublicResXFileCodeGenerator.

Alternatively you can set the Access Modifier drop down in the Resource Editor to Public to achieve the same result (there are three values: Internal, Public and No code generation). It is worth knowing that this Visual Studio 2008 feature is simply a design time feature and the generated code is still .NET Framework 2.0 compatible.

Of course, it is also possible to write your own replacement for the ResXFileCodeGenerator and PublicResXFileCodeGenerator and you can download an example of this at http://www.dotneti18n.com/downloads.aspx.

Technorati Tags: Localization, Internationalization, ResXFileCodeGenerator, PublicResXFileCodeGenerator, Strongly Typed Resource Classes, resx

Currently rated 4.4 by 7 people

  • Currently 4.428571/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Visual Studio 2008 And Generate Local Resources

Some of the changes in Visual Studio 2008 are a bit subtle. One of them is the way the Generate Local Resources option works (Tools | Generate Local Resources is used to make a web form localizable). In both Visual Studio 2005 and 2008 Generate Local Resources adds culture="auto" and uiculture="auto" to the page’s directives. These settings make the page responsive to the browser’s language setting (in IE7 select Tools | Internet Options and click on the Languages button). This is very convenient for testing and can also be acceptable for development purposes depending on how you expect to learn your user’s language preference. However, it is slightly flawed in Visual Studio 2005. The problem is that if you set the culture and uiculture in the website’s web.config (in order to force the entire website to use a single culture) the values are overridden at the page level rendering the web.config’s values meaningless. The solution would appear to be obvious and that is to delete the culture="auto" and uiculture="auto" attributes from the page directives. This solution works fine but unfortunately Visual Studio 2005 doesn’t respect your change and the next time you run Generate Local Resources the values are restored.

In Visual Studio 2005 the simplest solution to this problem is to remember to delete the culture and uiculture attributes from the page immediately after you run Generate Local Resources. An alternative but potentially debateable solution is to set the culture and uiculture values to a valid culture that you will never use - effectively a dummy culture. Then you override the page’s InitializeCulture method and check the culture and uiculture values to see if they are the dummy culture and if they are you set the culture back to your chosen culture.

Visual Studio 2008, however, solves this problem simply by only adding the culture and uiculture attributes the first time it is run on a page and so if you delete them then they stay deleted. This is a small change to Visual Studio but it has a big impact if this is your model. Remember this is a change to the Visual Studio 2008 IDE and so you can benefit from this in your .NET Framework 2.0 applications if you maintain them using Visual Studio 2008 (because it is multi-targetting).

Technorati Tags: Visual Studio 2008, Visual Studio 2005, Generate Local Resources, Localization, Internationalization

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Monday, March 31, 2008 at 8:59 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

MSDN Article: Internationalizing ASP.NET AJAX

My first MSDN Magazine article entitled "Around the World with ASP.NET AJAX Applications" has been published in the January 2008 issue. The article is all about how to localize and globalize ASP.NET AJAX applications. The article also explains how the assembly-based localization model works and you can download the Script Resource Viewer utility that I wrote to diagnose this behavior here. You can also download the slides that accompany the presentation I have given on Internationalizing ASP.NET AJAX here.

Technorati Tags: Internationalizing ASP.NET AJAX, Internationalization, Localization, Globalization, ASP.NET, ASP.NET AJAX, MSDN Magazine

Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Thursday, December 20, 2007 at 9:15 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (4) | Post RSSRSS comment feed

Setting The ClickOnce PublishState In Internationalized Applications

There is quite a bit to know about deploying internationalized applications using ClickOnce. I cover this subject in the Windows Forms Specifics chapter of my book .NET Internationalization and also in my presentation "Deploying Internationalized Applications Using ClickOnce" (slides, MSDN Webcast). One of the problems that arises in this process is how to set the PublishState of localized files that are deployed using ClickOnce. This post explains the utilities that I have written for this purpose. You can download the source code the book (including these utilities) from http://www.dotneti18n.com/downloads.aspx.

Here’s a very quick introduction to the issue that this post is concerned with (the whole subject is much bigger than just what I’m covering here). Say you have a Windows Forms application that has been localized into many different cultures (e.g. French, French (France), French (Canada), Spanish, Spanish (Spain) and Spanish (Mexico)). Now open up the project’s Properties, select the Publish tab, click on the "Application Files..." button and check the "Show all files" checkbox. You will see all of the localized resource files (e.g. fr\WindowsApplication1.resources.dll, fr-FR\WindowsApplication1.resources.dll etc.). By default the Publish Status will be set to "Auto". This means that whether the file is included or not is determined by the Publish Language. In some scenarios (e.g. publishing families of related cultures) you want to take explicit control over this process and manually include or exclude these files regardless of the setting of the Publish Language. This is easy to do with Visual Studio - you just drop down the combo box and set it to Include or Exclude. It is much harder to do with msbuild (where it is arguably more important because you want to automate the build process) and that’s what the utilities in this post are for.

In Visual Studio if you set the French resources to Include you will see the following section added to the .csproj or .vbproj file in one of the ItemsGroup elements:-

PublishFile Include="fr\WindowsApplication1.resources"

This PublishFile will have a PublishState that is set to "Include". There is no way to set this value using msbuild. Until now. Introducing the SetPublishState command line utility and the SetPublishStateTask msbuild task (these are included in the downloadable Visual Studio 2005 source code for the .NET Internationalization book that you can download here).

The SetPublishState command line utility can be called like this:-

SetPublishState WindowsApplication1.csproj fr:Include fr-FR:Include fr-CA:Include All:Exclude

The first parameter is the name of the project file to change. The remaining parameters are a list of cultures and whether to set them to Include or Exclude. Notice the "All" parameter at the end of the list. This optional parameter specifies what to set all of the remaining other cultures to. So in this example the fr, fr-FR and fr-CA cultures are set to Include and the es, es-ES and es-MX cultures are set to Exclude. This would be correct if you wanted to publish the French cultures in one operation. Having performed the publish operation you would then execute the following command:-

SetPublishState WindowsAppl

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Sunday, November 04, 2007 at 6:49 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Preserving Windows Forms resx Comments

One of the problems with auto-generated files is that if you make changes to them then your changes are lost the next time the file is re-generated. This happens with resx files for localized Windows Forms. If you set Form.Localizable to true and then edit the resx file directly then the next time Visual Studio rebuilds the resx file your changes are lost. One reason why you might want to edit the resx file is to add comments to the entries but obviously these comments will be lost. In fairness to Visual Studio a clear and strong warning is given when you attempt to edit a form’s resx file directly so it is difficult to make this mistake accidentally. However, the question remains "what if you wanted to preserve your comments between rebuilds of the resx file ?" This is achievable using the CopyComments method shown below. This method accepts an input resx and an output resx and copies the comments from the input resx to corresponding entries in the output resx. The idea is that prior to allowing Visual Studio to rebuild the resx file you take a copy of it. After Visual Studio has rebuilt the resx file (and the comments have been lost) you run this utility to copy the comments back into the ’main’ resx file.

public static Boolean CopyComments(string inputResX, string outputResX)
{
bool changesMade = false;

// populate a Hashtable containing the DataNodes in the output file
Hashtable output = new Hashtable();
using (ResXResourceReader reader = new ResXResourceReader(outputResX))
{
reader.UseResXDataNodes = true;
IEnumerator enumerator = reader.GetEnumerator();
while (enumerator.MoveNext())
{
DictionaryEntry entry = (DictionaryEntry)enumerator.Current;

ResXDataNode dataNode = (ResXDataNode)entry.Value;
output.Add(dataNode.Name, dataNode);
}
}

// search the Hashtable for equivalent DataNodes in the input file
using (ResXResourceReader reader = new ResXResourceReader(inputResX))
{
reader.UseResXDataNodes = true;
IEnumerator enumerator = reader.GetEnumerator();
while (enumerator.MoveNext())
{
DictionaryEntry entry = (DictionaryEntry)enumerator.Current;

ResXDataNode inputDataNode = (ResXDataNode)entry.Value;

if (output.ContainsKey(inputDataNode.Name))
{
ResXDataNode outputDataNode = (ResXDataNode)output[inputDataNode.Name];
if (!String.IsNullOrEmpty(inputDataNode.Comment) &&
outputDataNode.Comment != inputDataNode.Comment)
{
// update the output resx’s comments with the input resx’s comments
outputDataNode.Comment = inputDataNode.Comment;
changesMade = true;
}
}
}
}
if (changesMade)
{
// write the changes back to the output file
using (ResXResourceWriter writer = new ResXResourceWriter(outputResX))
{
foreach (DictionaryEntry entry in output)
{
writer.AddResource(entry.Key.ToString(), entry.Value);
}
writer.Generate();
writer.Close();
}
}

return changesMade;
}
}

Technorati Tags: Internationalization, Windows Forms,

Posted by: Guy Smith-Ferrier
Posted on: Monday, August 06, 2007 at 9:05 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Displaying Currencies In Your Local Format

Here’s a globalization problem that was put to me recently: how to display a foreign currency in your local format ?

For example if I have a value 123,456.78 and my application is using the en-GB (English (United Kingdom)) culture then this value will display as "£123,456.78". This is correct if the value itself refers to UK Pounds but it is completely incorrect if it refers to US Dollars. If the value refers to US Dollars you could simply display the value using the "English (United States)" culture:-

123456.78.ToString("C", new CultureInfo("en-US"))

This displays "$123,456.78". The double.ToString method accepts an IFormatProvider parameter and extracts the NumberFormatInfo from the CultureInfo object that we pass.

This works in this example but only because the number and currency formatting rules for en-US are the same as for en-GB. If you use a culture with different formatting rules to that of your users then the resulting string will be confusing. For example the correct display of the same value using Hungarian Forints in Hungary is "123 546,78 Ft" but this will give rise to confusion for a reader who uses English (United Kingdom) or English (United States) formatting. What is needed is a way of displaying a foreign currency in a format that the local user will understand.

One solution is the simple DisplayCurrency class below. The static GetNumberFormatInfo method returns a NumberFormatInfo object that is a combination of the currency’s NumberFormatInfo object and the CurrentCulture’s NumberFormatInfo object.

public class DisplayCurrency
{
public static NumberFormatInfo GetNumberFormatInfo(CultureInfo currencyCultureInfo)
{
return GetNumberFormatInfo(currencyCultureInfo, CultureInfo.CurrentCulture);
}
public static NumberFormatInfo GetNumberFormatInfo(CultureInfo currencyCultureInfo, CultureInfo displayCultureInfo)
{
NumberFormatInfo combinedNfi = (NumberFormatInfo)displayCultureInfo.NumberFormat.Clone();
NumberFormatInfo currencyNfi = currencyCultureInfo.NumberFormat;
if (currencyNfi.NumberDecimalDigits > combinedNfi.NumberDecimalDigits)
// increase the decimals to accomodate the target’s decimals
combinedNfi.NumberDecimalDigits = currencyNfi.NumberDecimalDigits;

combinedNfi.CurrencySymbol = currencyNfi.CurrencySymbol;
return combinedNfi;
}
}

The combination is minimalistic in that the new NumberFormatInfo object merely borrows the CurrencySymbol from the currency’s NumberFormatInfo object. In addition it checks the currency’s NumberDecimalDigits and increases the new NumberDecimalDigits if it is lower. This is to ensure that a currency that normally contains decimals does not have its decimals incorrectly truncated. So for example displaying "€123,456.78" when the user’s culture is "ja-JP" (Japanese (Japan)) should not truncate to "€123,456" simply because the Japanese Yen does not have decimals.

Here are the results for a few example cultures:-

hu-HU,Hungarian (Hungary), Hungarian Forint, -123 456,78 Ft, -Ft123,456.78
it-CH,Italian (Switzerland), Swiss Franc, SFr.-123’456.78, -SFr.123,456.78
ms-BN,Malay (Brunei Darussalam), Brunei Dollar, ($123.457), -$123,456.78
fr-CA,French (Canada), Canadian Dollar, (123 456,78 $), -$123,456.78

The solution is not without its problems, however. Like many globalization solutions it makes assumptions about the consumer of the information. In this case it assumes that the user is unfamiliar with the original currency. In the case of the Estonian Kroon, the Malaysian Ringgit and the Guatemalan Quetzal this may well be true. In these examples the solution helpfully displays the currency using formatting that the user is familiar with.<

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Sunday, July 29, 2007 at 4:45 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

VBUG Bournemouth: Internationalizing ASP.NET AJAX (Slides)

Many thanks to everyone in Bournemouth who turned up to the VBUG meeting at the university last night. As promised you can download the slides here.

You can also download the slides that I used for the internationalization recap here.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Friday, May 25, 2007 at 8:56 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed

Extending The CultureInfo Class Using Extension Methods

In chapter 6 of .NET Internationalization I wrote a section entitled "Extending The CultureInfo Class". I showed how to inherit from the CultureInfo class and fix the Parent and InvariantCulture properties so that they returned the subclass and the subclass behaved correctly (you can download the source code for the CultureInfoEx class here). In this post I show how to achieve the same result using an alternative approach: Extension Methods. There are two goals behind this post: (1) show how to extend the CultureInfo class and (2) illustrate why Extension Methods are worth taking a good hard look at.

Extension Methods are a new feature in the .NET Framework 3.5. They are often overlooked as a necessary part of enabling LINQ (Language Integrated Query) but they are a profoundly useful feature in their own right. To make use of Extension Methods you will need to download LINQ:-

http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx

An extension method is a method that is added to an existing class without having to recompile the original class. The immediate benefit of this feature is that you can extend classes that you do not have the source code to. The CultureInfo class springs to mind. Here’s an example of how to extend the CultureInfo class using extension methods:-


// ExtendingCultureInfo.cs
using System;
using System.Globalization;
using System.Runtime.CompilerServices;

public class ExtendingCultureInfo
{
static void Main(string[] args)
{
CultureInfo cultureInfo = new CultureInfo("en-GB");
Console.Write(cultureInfo.DisplayName + ": ");
// displays "Invoices"
Console.WriteLine(cultureInfo.ToPlural("Invoice"));
}
}

static class CultureInfoExtensions
{
[Extension]
public static string ToPlural(this CultureInfo cultureInfo, string singular)
{
if (cultureInfo.TwoLetterISOLanguageName == "en")
// English
return singular + "s";
else
return singular;
}
}

To compile this example open up a .NET Framework 2.0 command prompt and using the LINQ C# compiler (not the .NET Framework 2.0 compiler) execute the following line:-

csc ExtendingCultureInfo.cs /reference:"C:\Program Files\LINQ Preview\Bin\System.Query.dll"

The CultureInfoExtensions class includes a static method called ToPlural which converts a singular word to a plural word. (The algorithm represented here is simplistic in the extreme and is intended as an example as opposed to a realistic implementation). This static method accepts a CultureInfo parameter which is qualified with the "this" keyword. This parameter is passed by the compiler and is a reference to the object that the method was called on. The existence of this parameter explains why there is no such thing as Extension Properties. Also note that the ToPlural method is decorated with the Extension attribute to mark that the method extends another class. When the new method is used by an object it is referenced in the same way as a built in method without any special syntax required by the extension method.

So why is this so clever ? It is clever for a number of reasons. First, as I have already mentioned you do not need the original source code to extend a class and therefore this is very relevant for all classes that are part of the .NET Framework. Second, you can extend sealed classes and as I am not a big fan of sealed classes this makes me happy. Third, and this is the defining point, you do not need to be in control of the code that creates a new object of the class you want to extend. Think about this: if<

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Monday, March 12, 2007 at 8:41 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed

Custom Cultures, Vista and the IetfLanguageTag

One of the great features in the .NET Framework 2.0 is the ability to define new or replacement cultures, known as custom cultures. One of the benefits of this feature is that it allows you to export cultures from newer operating systems to older operating systems. This allows you to reap the rewards of the new culture without having to upgrade. The exported cultures are not always an exact replica of the original (for example the target operating system may not support the sort orders required by the exported culture) but it is often better than forcing your users to upgrade. I cover the subject of Custom Cultures in depth in one of the free sample chapters for .NET Internationalization.

The purpose of this post, however, is to explain why it is not so straight forwards exporting cultures from Vista as it is for other operating systems and what you can do to overcome this difference.

The problem lies in the ieftLanguageTag. The ietfLanguageTag is the culture name formatted according to the RFC 3066 standard. It is relevant because the CultureAndRegionInfoBuilder (i.e. the class that builds custom cultures and can ’export’ them) does not save the ietfLanguageTag when it runs on Vista. If you export the English (United States) ("en-US") from Windows XP SP2 and look through the generated LDML file you will find the following line:-

msLocale:ietfLanguageTag type="en-US"

If you do the same from Vista the line won’t be there. This is a problem because if you try to load the resulting LDML file in an older operating system an exception will be thrown and the culture will not be imported. Now naturaly there is no value in exporting the English (United States) culture because all operating systems include this culture but you can see that this is a problem if the whole reason for doing this is to export a Vista culture to Windows XP SP 2.

So the solution to the problem is to add the ietfLanguageTag element back into the LDML file:-

public static void SetIetfLanguageTag(string ldmlFilename, string ietfLanguageTag)
{
const string msLocaleNamespace = "http://schemas.microsoft.com/globalization/2004/08/carib/ldml";

// Vista is version 6
if (System.Environment.OSVersion.Version.Major >= 6)
{
XmlDocument xmlDocument = new XmlDocument();
using (FileStream fileStream = new FileStream(ldmlFilename, FileMode.Open))
{
xmlDocument.Load(fileStream);

XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
xmlNamespaceManager.AddNamespace("msLocale", msLocaleNamespace);

// Check to see if the LDML file is missing the IETF Language Tag.
XmlNode ietfLanguageTagXmlNode = xmlDocument.SelectSingleNode(
"//identity/special/msLocale:ietfLanguageTag", xmlNamespaceManager);
if (ietfLanguageTagXmlNode == null)
{
// the IEFT Language Tag is missing - add it back
XmlNode msLocaleXmlNode = xmlDocument.SelectSingleNode(
"//identity/special", xmlNamespaceManager);
if (msLocaleXmlNode is XmlElement)
{
XmlElement ietfLanguageTagXmlElement =
xmlDocument.CreateElement("msLocale", "ietfLanguageTag", msLocaleNamespace);
ietfLanguageTagXmlElement.SetAttribute("type", ietfLanguageTag);
msLocaleXmlNode.AppendChild(ietfLanguageTagXmlElement);
}
}
}
xmlDocument.Save(ldmlFilename);
}
}

Call this me

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by: Guy Smith-Ferrier
Posted on: Friday, March 09, 2007 at 9:26 PM
Categories: Internationalization
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (4) | Post RSSRSS comment feed