When your software doesn’t work in China

Cross-posted from In Absentia – a blog by Igal Tabachnik.

I had an interesting bug submitted by a user, he wrote that our product was crashing in his Visual Studio 2010, which was a Traditional Chinese edition.

Typemock Isolator, being a Visual Studio add-in, adds a menu next to the Tools menu in Visual Studio. It does so by looking for an index of the “Tools” menu in the main menu bar, and simply adding the “Typemock” menu after it. However, the “Tools” menu is only called that in the English locale. In other languages, the menu might be called differently. In French, for instance, it’s called “Outils”.

When creating a new Visual Studio 2008 Add-in (under Other Project Types – Extensibility), if selected the “Yes, create a ‘Tools’ menu item” in page 4 of the wizard, the following code is generated in OnConnect method after completing the wizard:

string resourceName;
ResourceManager resourceManager
= new ResourceManager("MyAddin.CommandBar", Assembly.GetExecutingAssembly());
CultureInfo cultureInfo
= new CultureInfo(_applicationObject.LocaleID);

if(cultureInfo.TwoLetterISOLanguageName == "zh")
System.Globalization.CultureInfo parentCultureInfo
= cultureInfo.Parent;
= String.Concat(parentCultureInfo.Name, "Tools");
= String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools");
= resourceManager.GetString(resourceName);

Unfortunately, this auto-generated code violates almost every known good coding practice. In particular, it gives no clues as to why “zh” is different, and why it’s handled separately.

Visual Studio menus are localized in the following way: a resource file, CommandBar.resx, is added to the project. This resource file contains translations of all common menus in Visual Studio, and each entry is defined by appending the two-letter ISO language code to the menu name, so that the entry for “Debug” in Spanish, for example, is esDebug.

There are two separate entries for both Traditional and Simplified Chinese in the resource file, and the entries are prefixed with zh-CHS and zh-CHT (zh is a short for Zhōngwén). Those identifiers refer to the old culture names. Since Windows Vista the new “zh-Hans”, and “zh-Hant” names are used, and the older names are kept for backwards compatibility. They can be found in the Parent property of the CultureInfo, specifying the current Visual Studio Locale ID (LCID).

Now that I had an understanding why “zh” was different, I was able to solve the bug by simply checking for the Chinese culture, and returning the correct identifier:

private const string ChineseTwoLetterISOLanguageName = "zh";

private string GetCurrentLocaleName(CultureInfo cultureInfo)
// Chinese (Traditional and Simplified) use "old" locale code,
// zh-CHT and zh-CHS, which is the name of the parent culture

if (cultureInfo.TwoLetterISOLanguageName == ChineseTwoLetterISOLanguageName)
return cultureInfo.Parent.Name;

return cultureInfo.TwoLetterISOLanguageName;