Friday, October 03, 2008

WPF Localization

There are a good number of articles out there on how to localize WPF resources, including the main one on MSDN.  However, they are all quite long-winded and having now gone through all the necessary steps I thought I'd just document exactly what you have to do.  Hopefully this will be of some help to others.

The support for localization within WPF is actually pretty good, but unfortunately it is not quite as integrated with the IDE as one might like, and does involve some work on the command line.  For all you "point'n'click" warriors, I think the best I can do is to send you here.

Step 1:

Download and compile locbaml.  You can find it here.  Once you've downloaded and "installed" it, head to the directory you chose and go into the "csharp" folder.  In there, you'll see a csproj file.  Either open that in Visual Studio and build, or run msbuild from the command prompt.  Either way, you'll end up with a nice new locbaml.exe just a couple of seconds later.

Step 2:

Create yourself a new WPF project within Visual Studio, and the close the IDE straight away.  Open up the new .csproj file in your favorite text editor, and add the following property to the first PropertyGroup section:

<UICulture>en-GB</UICulture>

where "en-GB" is the culture that you are developing in.  For a full list of culture codes, head here.

Open up the project again, and open the AssemblyInfo.cs file.  Add the following line to the bottom:

[assembly: NeutralResourcesLanguage("en-GB", UltimateResourceFallbackLocation.Satellite)]

If you build & run now, your app should work just fine.  If you take a peek in the bin\debug directory, you'll notice that VS has created an en-BG directory and popped a new dll in there.  This dll is a satellite assembly containing just the resources for that culture. 

Step 3:

Before localizing, let's add some content to our application. Open up Window1.xaml, and add the following as a child of the Grid element:

<Button>Hello</Button>

You can look at all of Fran's blogs for details on how to make it prettier :)

Step 4:

Here's where we drop down to the command line.  Spin up a VS Command Prompt, and cd to the directory that contains your project.  Run the following:

msbuild /t:updateuid

This will walk all the XAML in your project and add Uid attributes to pretty much everything.   It does mess the XAML up a smidge, but that's just how it goes.

Step 5:

Copy the locbaml.exe that you created in step 1 to the bin/debug directory.  Then run:

LocBaml.exe /parse en-GB\WpfLocalization.resources.dll

If all goes well, you should now have a .CSV file containing all the resource information which locbaml has extracted from the default resources dll.  Mine looks something like this:

WpfLocalization1.g.en-GB.resources:window1.baml,Window_1:WpfLocalization.Window1.$Content,None,True,True,,#Grid_1;
WpfLocalization1.g.en-GB.resources:window1.baml,Window_1:System.Windows.Window.Title,Title,True,True,,Window1
WpfLocalization1.g.en-GB.resources:window1.baml,Window_1:System.Windows.FrameworkElement.Height,None,False,True,,300
WpfLocalization1.g.en-GB.resources:window1.baml,Window_1:System.Windows.FrameworkElement.Width,None,False,True,,300
WpfLocalization1.g.en-GB.resources:window1.baml,Button_1:System.Windows.Controls.Button.$Content,Button,True,True,,Hello

The columns in this file are:

  • Baml Name - the name of the compiled XAML stream that is held in the resource dll
  • Resource Key - maps back to the Uid attributes that we created in Step 4.  Actually, it's even better than that, since it doesn't just have the content of the element, it also allows for the modification of some of the other properties.  So in the example above, on the element with the Uid of Window_1, I can change the $Content, Title, Height and Width properties.
  • Localization Category - A value from the LocalizationCategory enumeration
  • Readable: Is the value visible for translation
  • Modifiable: Is the value modifiable during the translation
  • Comments: Any comments - more on this later
  • Value: The value of the property.  This is the chap that needs translating

Step 6:

Translate the file.  Can't help you there :)

Step 7:

So you've now got a new version of the CSV file in the new culture (let's say it's es-ES).  From this, we need to build a new satellite assembly.  Drop the file into the bin\debug directory alongside the original, and then run this:

locbaml /generate en-GB\WpfLocalization.resources.dll /trans:WpfLocalization.resources.CSV /out:es-ES /cul:es-ES

It should be pretty obvious what's happening here - from the information present in the original resource dll, and with the updated values in the CSV, generate a new resource dll in the es-ES directory, for the culture es-ES.  Note that you need to create the output directory first.

Step 8:

Test it.  Either change your locale in the control panel, or add some code to the constructor of your App class (in App.xaml.cs):

public App()
{
   CultureInfo ci = new CultureInfo("es-ES");
   Thread.CurrentThread.CurrentCulture = ci;
   Thread.CurrentThread.CurrentUICulture = ci;
}

Note that you do need to do this pretty early on in the application lifecycle; once resource start being read, then you are fixed in whatever locale you were at the time.  You can't use this approach to switch languages dynamically once the app is loaded - if that's a requirement, then I'm afraid you need to head back to google.

Conclusion

That's it.  It's a bit of an effort first time round, but once you get used to things it's pretty simple.  To summarise:

  • You can pretty much develop your WPF app as normal. (just add the UICulture to the .csproj and the NeutralResourceLanguage attribute to AssemblyInfo.cs)
  • At suitable points, run "msbuild /t:updateuids" to refresh your Uids, and run "locbaml /parse" to get your latest CSV
  • Once translated, run "locbaml /generate" to build a new resource DLL.

What I like is that it doesn't really impact day-to-day development, and it doesn't require a recompile of the main application to add additional languages.

I'll do another blog shortly that extends this to include the localization of strings that are used within code (e.g., exception messages etc.)

1 comment:

Unknown said...

If you need a localization tool to manage software translation, I recommend the online localization platform https://poeditor.com/