Localizing Bootstrapper Packages

Localizing software has traditionally been difficult process; fortunately however localizing your Bootstrapper package doesn’t have to be. If you use separate localization-specific functionality from functionality required for all packages, the localization process is quite simple.


Package vs. Product XML manifest

The key to understanding the Bootstrapper package localization process is understanding the difference between the Package.xml and Product.xml files. The naming convention comes from the following situation:

Imagine you are at a store, filled with rows and rows of boxes. You look around for a particular product that you want. Once you find the right shelf, there are several versions of that product each in their own package. Some packages are in English while others in Japanese.

Product.xml defines the base package and package.xml defines any localization-specific information or functionality to be added to it. There isn’t a significant difference in the schema between the two files, anything that the package.xml can do can also be done in the product.xml and visa versa.


What goes into Package.xml

Only localization-related functionality should go into the package.xml, the reason is to avoid duplication. If file foobar.exe needs to be executed regardless of which language the package is deployed to, then it should go in product.xml. Whereas since the package’s display name and error messages are specific to that package’s language, those strings belong in the package.xml file.

Usually the only things in the Package.xml are the EULA package file and localized strings. (Though neither are required.)


Example: The .NET FX 2.0

Each prerequisite package has its own story, the .NET FX’s goes like this:

The .NET Framework always installs dotnetfx.exe, which contains English error messages for all exceptions. Localization comes in the form of language packs, or separate programs to be ran to install the additional error strings.

To do this a new package file and command is ran in the Package.xml. So that the base dotnetfx.exe is always installed, but the particular langpack.exe is only installed if you build a Bootstrapper with that target language.


Example: SQL Server Express

SQL Server Express’s story goes like this:

The SQL Server Express redistributable (sqlexpr32.exe) is localized to a particular language. So that there is an English version of sqlexpr32.exe and a separate Japanese version of sqlexpr32.exe.

To do this, the product.xml file is empty, since there is no ‘common’ functionality. The only redistributable package file, sqlexp32.exe, is referenced in the package.xml.


File Merging

As a tester, the first question I had when I heard about this product.xml being equivalent to a package.xml file is, "what happens if there is a conflict". That is, what happens if package.xml defines a string, property, or command that is also defined in the product.xml?

Whenever there is a duplicate XML element, package.xml always wins and overwrites the product.xml element. So if you have a string "DisplayName" in package.xml and product.xml, when you build the package.xml string will be replaced with the localized one. However, if you build to a different language, one where the package.xml doesn’t contain that duplicate property, then the one in product.xml will be used.


File Merging Example: DotNetChk.exe

An example of this would be the .NET FX’s ExternalCheck. To see if the .NET Framework 2.0 is installed the Bootstrapper package employes an ExternalCheck, a program called dotnetchk.exe, to see if a particular version of the CLR is installed. If so, then the package bypasses installation. (No need to install it if it already exists.)

However, in a non-English package.xml for the .NET FX, that same ExternalCheck is also used to check if the appropriate lang pack is installed.

The situation is this: the base package manifest (product.xml) needs to check if the Framework is installed. However the localization manifest (package.xml) needs to also check if the langpack is installed. So package.xml defines an ExternalCheck with the same property but different argments to the ExternalCheck.

So when at Build time, the package.xml file overwrites the ExternalCheck defined in product.xml and that property checks for the langpack as well.

Now if an English Bootstrapper was built, since the English package.xml doesn’t overwrite that external check the one in product.xml is used.


Conclusions

By separating functionality into both package.xml and product.xml localizing Generic Bootstrapper packages is a simple process.