WiX 3 Tutorial: Custom EULA License and MSI localization
In this part of the ongoing Wix tutorial series we’ll take a look at how to localize your MSI into different languages. We’re still the mighty SuperForm: Program that takes care of all your label color needs. :)
Localizing the MSI
With WiX 3.0 localizing an MSI is pretty much a simple and straightforward process. First let look at the WiX project Properties->Build. There you can see "Cultures to build" textbox. Put specific cultures to build into the testbox or leave it empty to build all of them. Cultures have to be in correct culture format like en-US, en-GB or de-DE.
Next we have to tell WiX which cultures we actually have in our project. Take a look at the first post in the series about Solution/Project structure and look at the Lang directory in the project structure picture. There we have de-de and en-us subfolders each with its own localized stuff. In the subfolders pay attention to the WXL files Loc_de-de.wxl and Loc_en-us.wxl. Each one has a <String Id="LANG"> under the WixLocalization root node. By including the string with id LANG we tell WiX we want that culture built. For English we have <String Id="LANG">1033</String>, for German <String Id="LANG">1031</String> in Loc_de-de.wxl and for French we’d have to create another file Loc_fr-FR.wxl and put <String Id="LANG">1036</String>. WXL files are localization files. Any string we want to localize we have to put in there. To reference it we use loc keyword like this: !(loc.IdOfTheVariable) => !(loc.MustCloseSuperForm)
This is our Loc_en-us.wxl. Note that German wxl has an identical structure but values are in German.
<?xml version="1.0" encoding="utf-8"?>
<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
<String Id="LANG">1033</String>
<String Id="ProductName">SuperForm</String>
<String Id="LicenseRtf" Overridable="yes">\Lang\en-us\EULA_en-us.rtf</String>
<String Id="ManufacturerName">My Company Name</String>
<String Id="AppNotSupported">This application is is not supported on your current OS. Minimal OS supported is Windows XP SP2</String>
<String Id="DotNetFrameworkNeeded">.NET Framework 3.5 is required. Please install the .NET Framework then run this installer again.</String>
<String Id="MustCloseSuperForm">Must close SuperForm!</String>
<String Id="SuperFormNewerVersionInstalled">A newer version of !(loc.ProductName) is already installed.</String>
<String Id="ProductKeyCheckDialog_Title">!(loc.ProductName) setup</String>
<String Id="ProductKeyCheckDialogControls_Title">!(loc.ProductName) Product check</String>
<String Id="ProductKeyCheckDialogControls_Description">Plese Enter following information to perform the licence check.</String>
<String Id="ProductKeyCheckDialogControls_FullName">Full Name:</String>
<String Id="ProductKeyCheckDialogControls_Organization">Organization:</String>
<String Id="ProductKeyCheckDialogControls_ProductKey">Product Key:</String>
<String Id="ProductKeyCheckDialogControls_InvalidProductKey">The product key you entered is invalid. Please call user support.</String>
</WixLocalization>
As you can see from the file we can use localization variables in other variables like we do for SuperFormNewerVersionInstalled string. ProductKeyCheckDialog* strings are to localize a custom dialog for Product key check which we’ll look at in the next post.
Built in dialog text localization
Under the de-de folder there’s also the WixUI_de-de.wxl file. This files contains German translations of all texts that are in WiX built in dialogs. It can be downloaded from WiX 3.0.5419.0 Source Forge site. Download the wix3-sources.zip and go to \src\ext\UIExtension\wixlib. There you’ll find already translated all WiX texts in 12 Languages.
Localizing the custom EULA license
Here it gets ugly. We can override the default EULA license easily by overriding WixUILicenseRtf WiX variable like this: <WixVariable Id="WixUILicenseRtf" Value="License.rtf" /> where License.rtf is the name of your custom EULA license file. The downside of this method is that you can only have one license file which means no localization for it. That’s why we need to make a workaround. License is checked on a dialog name LicenseAgreementDialog. What we have to do is overwrite that dialog and insert the functionality for localization.
This is a code for LicenseAgreementDialogOverwritten.wxs, an overwritten LicenseAgreementDialog that supports localization. LicenseAcceptedOverwritten replaces the LicenseAccepted built in variable.
<?xml version="1.0" encoding="UTF-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI>
<Dialog Id="LicenseAgreementDialogOverwritten" Width="370" Height="270" Title="!(loc.LicenseAgreementDlg_Title)">
<Control Id="LicenseAcceptedOverwrittenCheckBox" Type="CheckBox" X="20" Y="207" Width="330" Height="18" CheckBoxValue="1" Property="LicenseAcceptedOverwritten" Text="!(loc.LicenseAgreementDlgLicenseAcceptedCheckBox)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">CostingComplete = 1</Publish>
<Condition Action="disable">
<![CDATA[ LicenseAcceptedOverwritten <> "1" ]]>
</Condition>
<Condition Action="enable">LicenseAcceptedOverwritten = "1"</Condition>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.LicenseAgreementDlgBannerBitmap)" />
<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="140" Sunken="yes" TabSkip="no">
<!-- This is original line -->
<!--<Text SourceFile="!(wix.WixUILicenseRtf=$(var.LicenseRtf))" />-->
<!-- To enable EULA localization we change it to this -->
<Text SourceFile="$(var.ProjectDir)\!(loc.LicenseRtf)" />
<!-- In each of localization files (wxl) put line like this:
<String Id="LicenseRtf" Overridable="yes">\Lang\en-us\EULA_en-us.rtf</String>-->
</Control>
<Control Id="Print" Type="PushButton" X="112" Y="243" Width="56" Height="17" Text="!(loc.WixUIPrint)">
<Publish Event="DoAction" Value="WixUIPrintEula">1</Publish>
</Control>
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Description" Type="Text" X="25" Y="23" Width="340" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.LicenseAgreementDlgDescription)" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.LicenseAgreementDlgTitle)" />
</Dialog>
</UI>
</Fragment>
</Wix>
Look at the Control with Id "LicenseText” and read the comments. We’ve changed the original license text source to "$(var.ProjectDir)\!(loc.LicenseRtf)". var.ProjectDir is the directory of the project file. The !(loc.LicenseRtf) is where the magic
happens. Scroll up and take a look at the wxl localization file example. We have the LicenseRtf declared there and it’s been made overridable so developers can change it if they want. The value of the LicenseRtf is the path to our localized EULA relative to the WiX project directory. With little hacking we’ve achieved a fully localizable installer package.
The final step is to insert the extended LicenseAgreementDialogOverwritten license dialog into the installer GUI chain. This is how it’s done under the <UI> node of course.
<UI>
<!-- code to be discussed in later posts –>
<!-- BEGIN UI LOGIC FOR CLEAN INSTALLER -->
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDialogOverwritten">1</Publish>
<Publish Dialog="LicenseAgreementDialogOverwritten" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
<Publish Dialog="LicenseAgreementDialogOverwritten" Control="Next" Event="NewDialog" Value="ProductKeyCheckDialog">LicenseAcceptedOverwritten = "1" AND NOT OLDER_VERSION_FOUND</Publish>
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="ProductKeyCheckDialog">1</Publish>
<!-- END UI LOGIC FOR CLEAN INSTALLER –>
<!-- code to be discussed in later posts -->
</UI>
For a thing that should be simple for the end developer to do, localization can be a bit advanced for the novice WiXer. Hope this post makes the journey easier and that next versions of WiX improve this process.
WiX 3 tutorial by Mladen Prajdić navigation
- WiX 3 Tutorial: Solution/Project structure and Dev resources
- WiX 3 Tutorial: Understanding main wxs and wxi file
- WiX 3 Tutorial: Generating file/directory fragments with Heat.exe
- WiX 3 Tutorial: Custom EULA License and MSI localization
Legacy Comments
Anders
2010-04-23 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization Great Wix posts!!! I've been looking all over but this was absolutely the best tutorial I came across. Could you please, please provide the source code as well to help us newbies get started? |
Mladen
2010-04-23 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I'll probably post the source code at the end of the series when I cover all the topics. |
DBA in the making
2010-04-26 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization Any chance of formatting you rather wide XML lines with carriage returns? As is, it's causing a horizontal scroll bar to appear on my screen, which makes the whole blogs page hard to read. Which is ironic, considering the reason I was going to the blogs page; I was poking around determine what would be involved in posting a blog entry myself, regarding formatting and laying out code correctly. |
DJuan
2010-06-23 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization Any update about the registry fragment? I have already made my prototype working but without the registry harvesting. I want those REG files to be like harvesting a files where I will create a HKCU.reg and HKLM, place in a folder and run script and it will convert them to WIX 3.5 XML format. Is this the next sequel on your Wix Series? I hope you will release the complete source code sooner... |
Mladen
2010-06-23 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization @DJuan: there will be. as soon as i make it. i also don't have the source done yet. it's a slow work in progress. for now hope this helps you: <Component Id="cmpMyRegKey" Guid="{5DC982C1-EDEB-4247-923F-D9330D959FFF}"> <RegistryKey Root="HKLM" Key="Software\MyCompany\MyApp" Action="createAndRemoveOnUninstall"> <RegistryValue Type="string" Name="SomeKey" Value="SomeValue"/> </RegistryKey> </Component> |
TheShtel
2010-06-29 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization Nice article! Are you planning to upload the next tutorial of this WiX sequence any time soon? Can't wait! |
Mladen
2010-06-30 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization i will. but probably end of august. don't have time before then. |
TheShtel
2010-07-01 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I noticed that using the condition: LicenseAcceptedOverwritten = "1" AND NOT OLDER_VERSION_FOUND for going to ProductKeyCheckDialog from LicenseAgreementDialogOverwritten via "Next", is not working. the reason being: If you are running an Upgrade msi, OLDER_VERSION_FOUND is set, thus never going to the next screen. Correct me if i'm wrong? |
Mladen
2010-07-02 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization take a look here http://weblogs.sqlteam.com/mladenp/archive/2010/02/23/WiX-3-Tutorial-Generating-filedirectory-fragments-with-Heat.exe.aspx |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I'm still not able to find how to create system variable that you are creating SuperFormFilesDir. When i compile my project i get an error Undefined preprocessor variable. can you please provide detail step? |
Mladen
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization you have to add it into the system variables. are saying you don't know how to do that or is the problem something else? |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I'm asking how to do that. THanks for your quick reply. |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I'm saying is i dont know how to create enviroment variables. |
Mladen
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization a simple google search gives this result: http://vlaurie.com/computers2/Articles/environment.htm should be helpful |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization "$(WIX)bin\heat.exe" dir "..\..\..\Common\bin\$(Configuration)" -cg CommonFiles -gg -scom -sreg -sfrag -srd -dr INSTALLLOCATION -var var.SourcePathCommon -out "$(ProjectDir)\FilesFragmentCommon.wxs" Above script i use to create wxs file using heat. I already created system variable SourcePathCommon using set command. It creates wxs fragment file successfully. but I still get an error "Undefined preprocessor variable '$(var.SourcePathCommon)'." when i compile wix project. |
Mladen
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization that's because you didn't look close enough to the example. it's not var.SourcePathCommon. it's env.SourcePathCommon for environment vars. |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I tried both env. and var. no luck. |
Mladen
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization are you sure you created the var as environment and not user variable? if you have it as env variable then i have no idea what could be wrong. |
Nikhil Oza
2010-07-05 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I created using SET command. SET SourcePathCommon=C:\Test in CMD window. |
DJuan
2010-07-08 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization using SET in cmd window will get you through the User Variable. Try right-clicking on My Computer and hit Advance and go to Environment Variables then add it in System Variable below the window. By the way, Mladen. Is there a news about integrating registry files in the coming releases of WIX? I found a script but is not working. I will try to post it here so everyone can check. Thanks. |
koxp
2010-07-19 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization tahk you goood post admin |
DJuan
2010-07-21 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization I found the script to generate wix fragment from a registry file but not working on current wix 3.5. Can someone work on it? stackoverflow.com/... |
DJuan
2010-07-21 |
re: WiX 3 Tutorial: Custom EULA License and MSI localization Sorry, here it is... h t t p : //stackoverflow.com/questions/269423/how-to-generate-wix-xml-from-a-reg-file |