How Windows Installer uses Languages

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:

  1. Requested language
  2. User’s language
    1. GetUserDefaultUILanguage()
    2. GetUserDefaultLangID()
  3. System’s language (GetSystemDefaultLangID())
  4. English

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_SERVICE_FAILURE (1601), 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 MsiGetProductInfo(). 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.