Before I describe how Windows Forms localization works in Whidbey, let us first take a look at how it works in VS 2002/2003. Consider a Form with a Button control on it. When you mark it Localizable = True in the property grid, the designer generates code for the Button that looks like this:
.button1.AccessibleDescription = resources.GetString(“button1.AccessibleDescription”);
this.button1.AccessibleName = resources.GetString(“button1.AccessibleName”);
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject(“button1.Anchor”)));
this.button1.BackgroundImage = ((System.Drawing.Image)(resources.GetObject(“button1.BackgroundImage”)));
this.button1.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject(“button1.Dock”)));
this.button1.Enabled = ((bool)(resources.GetObject(“button1.Enabled”)));
this.button1.FlatStyle = ((System.Windows.Forms.FlatStyle)(resources.GetObject(“button1.FlatStyle”)));
this.button1.Font = ((System.Drawing.Font)(resources.GetObject(“button1.Font”)));
this.button1.Image = ((System.Drawing.Image)(resources.GetObject(“button1.Image”)));
this.button1.ImageAlign = ((System.Drawing.ContentAlignment)(resources.GetObject(“button1.ImageAlign”)));
where ‘resources’ is an instance of the ResourceManager class. Basically, the serializer pushes the values of all properties on Button that are marked with LocalizableAttribute.Yes to the resource file and generates code to pull these values when the form is constructed. This allows the localizer to set different values for these properties based on the culture. Depending on the culture the application gets run under, the property values will be pulled back from the appropriate resource (with fallback to neutral and default culture resources when values are not found in a specific culture resource file).
So if you are developing a component, all you need to do is mark the set of properties on it that you think can vary based on culture with LocalizableAttribute.Yes and you are all set. If you are an app developer, things are even simpler – just set Localizable = True on your Form.
You will notice, though, that there is some room for improvement in this model. Here are some drawbacks:
1) Does not scale very well – Components typically have a large number of localizable properties – for example, the Button control has about 20 of them. This means that each time the form is constructed, 20 property values are retrieved from the resource file and set on the Button. For a large form with 100 controls, that adds up to 2000 properties on average. The interesting thing here is that these properties are only potentially localizable – it is possible and even likely that only a small number of them have actually been localized in different cultures. For example, if the button does not have a background image in the default culture, it most likely doesn’t in other cultures as well. Yet, the code will retrieve the value (null) from the resource file on form construction.
2) Code bloat in InitializeComponent – In a non-localized form, the serializer will typically only generate code for properties that have been set to non-default values. However, when you turn localization on, it will generate code for all localizable properties, as shown above. This usually means a lot more lines of code in InitializeComponent.
3) Clutter in the resource file – Each of the localizable properties also get their values pushed into the resource files, even properties that have default values and for which the developer probably has no intention of setting non-default values.
So in Whidbey, we spent some time thinking about how we could improve this model and address these drawbacks. Obviously, we had to remain backward compatible, so we couldn’t simply start over and invent a new model. I will now describe what we did come up with.
In Whidbey, we support two localization models – PropertyAssignment and PropertyReflection. PropertyAssignment is similar to the model described above. PropertyReflection indicates that the designer will use the ComponentResourceManager class to reflect on properties by name to fill them at runtime. This uses reflection at runtime so it can be slow, but it scales better for large numbers of properties with default values. The designer loader can specify how localization should work through a CodeDomLocalizationProvider, but let’s not discuss those details now – what’s important is that the Visual Studio designer in Whidbey uses the PropertyReflection model by default.
This means that the code in InitializeComponent will look simply like:
As mentioned above, this call will use reflection to pull values from the appropriate resource file. The designer will only push non-default property values into the resources, so typically these will be far fewer than in the VS 2002/2003. So essentially, we are now optimizing for the common case – where a large number of properties have default values. The code in InitializeComponent is very concise and we only push non-default values into resources, so (2) and (3) are addressed too.