Cross-posted from In Absentia – a blog by Igal Tabachnik.
Visual Studio 2010 in Traditional Chinese edition
I had an interesting bug submitted by a user of Isolator, he wrote that our product was crashing in his Visual Studio 2010, which was a Traditional Chinese edition.
Typemock Isolator, 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span style="color: black;">... </span><span style="color: blue;">string</span><span style="color: black;"> resourceName; ResourceManager resourceManager </span><span style="color: black;">=</span> <span style="color: blue;">new</span><span style="color: black;"> ResourceManager(</span><span style="color: maroon;">"</span><span style="color: maroon;">MyAddin.CommandBar</span><span style="color: maroon;">"</span><span style="color: black;">, Assembly.GetExecutingAssembly()); CultureInfo cultureInfo </span><span style="color: black;">=</span> <span style="color: blue;">new</span><span style="color: black;"> CultureInfo(_applicationObject.LocaleID); </span><span style="color: blue;">if</span><span style="color: black;">(cultureInfo.TwoLetterISOLanguageName </span><span style="color: black;">==</span> <span style="color: maroon;">"</span><span style="color: maroon;">zh</span><span style="color: maroon;">"</span><span style="color: black;">) { System.Globalization.CultureInfo parentCultureInfo </span><span style="color: black;">=</span><span style="color: black;"> cultureInfo.Parent; resourceName </span><span style="color: black;">=</span><span style="color: black;"> String.Concat(parentCultureInfo.Name, </span><span style="color: maroon;">"</span><span style="color: maroon;">Tools</span><span style="color: maroon;">"</span><span style="color: black;">); } </span><span style="color: blue;">else</span><span style="color: black;"> { resourceName </span><span style="color: black;">=</span><span style="color: black;"> String.Concat(cultureInfo.TwoLetterISOLanguageName, </span><span style="color: maroon;">"</span><span style="color: maroon;">Tools</span><span style="color: maroon;">"</span><span style="color: black;">); } toolsMenuName </span><span style="color: black;">=</span><span style="color: black;"> resourceManager.GetString(resourceName); ...</span> |
Saved by the Parent Property of the “CultureInfo”
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">string</span><span style="color: black;"> ChineseTwoLetterISOLanguageName </span><span style="color: black;">=</span> <span style="color: maroon;">"</span><span style="color: maroon;">zh</span><span style="color: maroon;">"</span><span style="color: black;">; </span><span style="color: blue;">private</span> <span style="color: blue;">string</span><span style="color: black;"> GetCurrentLocaleName(CultureInfo cultureInfo) { </span><span style="color: green;">//</span><span style="color: green;"> Chinese (Traditional and Simplified) use "old" locale code, </span><span style="color: green;">//</span><span style="color: green;"> zh-CHT and zh-CHS, which is the name of the parent culture</span> <span style="color: blue;">if</span><span style="color: black;"> (cultureInfo.TwoLetterISOLanguageName </span><span style="color: black;">==</span><span style="color: black;"> ChineseTwoLetterISOLanguageName) { </span><span style="color: blue;">return</span><span style="color: black;"> cultureInfo.Parent.Name; } </span><span style="color: blue;">return</span><span style="color: black;"> cultureInfo.TwoLetterISOLanguageName; }</span> |