Language support in Windows Installer can be confusing until you understand how Windows Installer queries for and uses languages. Some wonder why Windows Installer packages have two different places to set the language. Some wonder why, for example, localized packages install using a different language than the user’s default UI language. Some of these answers are in the Windows Installer SDK documentation, while some is not. Described in this post is how languages are used from before the package is opened to what happens during installation.
In general, Windows Installer looks for resources in the following order:
- Requested language
- User’s language
- System’s language (GetSystemDefaultLangID())
Windows Installer installation packages – both .msi and .msp files – are not executables and must be processed. Until that file can even be opened, Windows Installer queries for the user’s default UI language using
GetUserDefaultUILanguage() on platform that support this function (Windows 2000 and newer NT-based platforms), falling back on
GetUserDefaultLangID(). If this should fail for any reason or Windows Installer is running entirely in the SYSTEM context,
GetSystemDefaultLangID() is used. With every try, resources that are not available for a specific locale (ex: “en-US”, or 1033) cause Windows Installer to lookup resources using a neutral locale (ex: “en”, or 9). Messages displayed before the package is loaded would include error messages for errors like
ERROR_INSTALL_PACKAGE_OPEN_FAILED (1619), etc.
Once the package is initially opened, Windows Installer uses the language of the product listed in the Template summary property,
PID_TEMPLATE. If multiple languages are listed – which is really only valid for .msm files, or merge modules – the first language listed is used. A 0 for this field or leaving it empty indicates that the package is a neutral language package, and Windows Installer will search for resources in the order listed above, starting with the user’s language.
The first language listed in
PID_TEMPLATE is also registered for the product, which means this is the value returned from
MsiGetLanguage() also returns this value when passed a product handle.
INSTALLLOGMODE_COMMONDATA messages sent to UI handlers use this language until the ProductLanguage property can be read or if that property is not present. Most importantly when building transforms or patches, the first language listed in
PID_TEMPLATE is the language used for transform validation.
When the package is opened and the ProductLanguage property can be read, this language is used for message lookup for
INSTALLLOGMODE_COMMONDATA messages, ActionText, and Error messages, if available. If the ActionText or Error tables are empty or don’t contain a record for a particular action or error, Windows Installer uses the ProductLanguage to lookup messages in the search order listed previously. ProductLanguage would be the requested languages in this case.
On Vista, the ProductLanguage property is also used to dictate the language used for the UAC prompt, as well as information recorded in the event log and during system restore processing.
It is highly recommended that the language specified in the
PID_TEMPLATE property and the ProductLanguage property match, or UI will be inconsistent.
Remembering the search order and considering what’s available during different phases of Windows Installer execution can help identify how Windows Installer selects a language. If a package isn’t available, Windows Installer has only the preferences set for the user or system. After a package is loaded but before the data structures can be read, Windows Installer uses the
PID_TEMPLATE property in the summary information stream. Once the package is fully loaded the ProductLanguage property in the Property table can be used.
I thank Carolyn Napier, the Windows Installer team‘s development lead, for a big help in identifying some of the more obscure uses of which language properties that weren’t documented or easily discoverable.