A part of my responsibilities is globalization of our primary application. The application consists of a mix of WinForms and WPF. Localization of WinForms is done using the built-in mechanism provided, which sucks. That right, I said it, it sucks. You identify the window/control as localizable and then pick a language other than default. This will create a resource file for you of the chosen language. Anytime you change any values while the window/control is selected with a language chosen other than default, those values will be moved from the default Designer.cs (if they haven’t already been moved) into a default resource file for you and add your new values to the resource file for the language. This sounds well and good, like everything should be sunshine and rainbows. But it often moves in a ton of stuff that wasn’t changed, sometimes won’t move in stuff right, and sometimes wipes stuff from the Designer.cs and then somehow misses adding it to the resource file. I mean, having a Designer.cs is a serious pain to begin with, but to mess it and the resource files up is considerably more of a pain.
So I was hoping when it got to WPF things would get easier. But Microsoft doesn’t seem to have planned out globalization for WPF. It doesn’t mean they didn’t think about it. It just doesn’t seem like developer experience was a part of the thought process. If you want to read what Microsoft has to day on the subject check out Globalization and Localization but I’ll give a quick summary below.
The first method they give has to do with using msbuild to add x:Uid attributes to all your xaml and another tool, LocBaml Tool, to generate satellite assemblies that contain the translated dlls. If you have names in all your xaml this might be nice. You’d have to set up a post-build process to generate the satellite assemblies but it’s not too bad. The problem, however, is if you’re doing MVVM dev. Since we rarely name our controls, when the uids get auto-generated they look like x:Uid=”TextBlock_1″ which is pretty ugly and if you use the LocBaml tool to generate your file for translation, that uid is meaningless. I’m surprised this is the first method as it is the ugliest. They go into quite a bit of detail and leave the second and third methods nearly empty.
The second method they give is using ResourceDictionary. As mentioned examples are pretty anemic and there is a better sample over at codeproject called Globalization in WPF using ResourceDictionary. This is actually pretty nice. The best part about this one is that because you are dynamically changing the dictionary on the fly you’re values can change on the fly too if the user chooses to.
The third method, and the one I ultimately choose was to use resource files with static references to the resource properties. The single example Microsoft gives is useless so check out WPF Localization for Dummies. I choose this method only because that is how localization is done in WinForms and since our application is a blend of both WinForms and WPF it made sense to be consistent.
Now obviously for any project of real size, just using the resource file in the Properties isn’t going to help. You’ll end up with a ton of strings and images in here that just starts looking horrible and becomes hard to manage. I’ve thrown together a sample project that shows using a resource file for English and Spanish. Note that I’ve directly modified the project file to identify that the resource files are dependent upon MainWindow.xaml. I’m not sure if there’s an easier way to do this but I just add the dependentUpon element in the project xml. This just makes the solution a little bit cleaner. Also notice that I named it MainWindowResx, this is to prevent class name conflicts as the name of the resource class will be the name of the file.
It is important to remember, once you add the resource file, that you open it in the designer and change the “AccessModifier” from internal to public. The problem is that the class will default to internal but will still not allow xaml to find the value. To fix this you need to make the AccessModifier public. Otherwise you will get a “StaticExtension value cannot be resolved to an enumeration, static field, or static property.” exception.
So after doing all this work to get things ready for globalization I began to think there had to be a better way. I’m not sure if the better path is a Visual Studio Extension or if it should be a custom tool that sits external and modifies the project file. This is something I’ll write about on and off as I work on the project in my personal time. If you have any feedback as I’m going through the process please feel free to leave comments or email me.