1

I'm using WiX v3.9 R2 to create my installer. I have the installer localized into several languages, works great. However, it always uses the English name of the application in Programs & Features, no matter what language the format and display language is set to.

How do I tell it to use a localized name for my application, in Programs & Features?

Edit:

To clarify: I think what I need is a way to change the Bundle's name attribute (or, [WixBundleName]) at install time, based on the user's language. Is that possible?

Or failing that, how do people currently use WiX Bundle, and end up with the program's name localized in Programs and Features?

Edit 2: From the WiX site:

WixBundleName - gets the name of the bundle (from Bundle/@Name). This variable can also be set by the bootstrapper application to modify the bundle name at runtime.

So it sounds like this can be done - but how?

Edit 3: Here is my current Bundle, for reference. The last item in the chain is my application.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
    <Bundle Name="Map Creator"
            Version="$(var.ProductVersion)" 
            Manufacturer="FMOsoft" 
            IconSourceFile="$(var.KarteDir)\assets\logo\ico\icon.ico" 
            UpgradeCode="[GUID]" 
            AboutUrl="http://fmosoft.com/MapCreator"
            Copyright="Copyright© 2015, FMOsoft">

        <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkSidebarLicense">
          <bal:WixStandardBootstrapperApplication
            LocalizationFile="$(var.KarteDir)\install\win\L10n\installer\thm_en_us.wxl"
            ThemeFile="$(var.KarteDir)\install\win\HyperlinkSidebarTheme_fmosoft.xml"
            LogoFile="$(var.KarteDir)\assets\logo\bmp\logo_64.bmp"
            LogoSideFile="logoside.bmp"
            ShowVersion="yes"
            LicenseFile="$(var.KarteDir)\install\license_en.html"
            SuppressOptionsUI="yes"
            LaunchTarget="[ProgramFiles6432Folder]\FMOsoft\MapCreator\MapCreator.exe"
            />

          <!-- WixStdBa requires specific names, at run time, in the temporary .BA1 folder.  XML and png file is only needed for layout changes. 
               Payload/@Name for .wxl must be <lcid>\thm.wxl 
               Payload/@Name for .xml must be <lcid>\thm.xml AND must also have logo.png in same folder.--> 
          <Payload Id="theme_en_us" Compressed="yes"  Name="1033\thm.wxl" SourceFile="$(var.KarteDir)\install\win\L10n\installer\thm_en_us.wxl"/> 
          <Payload Id="license_en_us" Compressed="yes"  Name="1033\license.html" SourceFile="$(var.KarteDir)\install\license_en.html"/> 
          <Payload Id="theme_es_es" Compressed="yes"  Name="1034\thm.wxl" SourceFile="$(var.KarteDir)\install\win\L10n\installer\thm_es_es.wxl"/> 
          <Payload Id="license_es_es" Compressed="yes"  Name="1034\license.html" SourceFile="$(var.KarteDir)\install\license_es.html"/> 
          <Payload Id="theme_fr_fr_xml" Compressed="yes"  Name="1036\thm.xml" SourceFile="$(var.KarteDir)\install\win\L10n\installer\HyperlinkSidebarTheme_fmosoft_fr.xml"/> 
          <Payload Id="theme_fr_fr" Compressed="yes"  Name="1036\thm.wxl" SourceFile="$(var.KarteDir)\install\win\L10n\installer\thm_fr_fr.wxl"/> 
          <Payload Id="theme_fr_fr_logo" Compressed="yes"  Name="1036\logo.png" SourceFile="$(var.KarteDir)\assets\logo\bmp\logo_64.bmp"/> 
          <Payload Id="theme_fr_fr_sidelogo" Compressed="yes"  Name="1036\logoside.png" SourceFile="$(var.KarteDir)\install\win\logoside.bmp"/> 
          <Payload Id="license_fr_fr" Compressed="yes"  Name="1036\license.html" SourceFile="$(var.KarteDir)\install\license_fr.html"/> 
          <Payload Id="theme_pt_br" Compressed="yes"  Name="1046\thm.wxl" SourceFile="$(var.KarteDir)\install\win\L10n\installer\thm_pt_br.wxl"/>
          <Payload Id="license_pt_br" Compressed="yes"  Name="1046\license.html" SourceFile="$(var.KarteDir)\install\license_pt.html"/> 
          <Payload Id="theme_id" Compressed="yes"  Name="1057\thm.wxl" SourceFile="$(var.KarteDir)\install\win\L10n\installer\thm_id.wxl"/>
          <Payload Id="license_id" Compressed="yes"  Name="1057\license.html" SourceFile="$(var.KarteDir)\install\license_id.html"/> 
        </BootstrapperApplicationRef>

        <WixVariable Id="WixStdbaLicenseUrl" Value="license.html" />
        <Variable Name="MyProductVersion" Value="$(var.ProductVersion)" />
        <!-- There must be a better way to show the localized name of the app in the setup, but this works. -->
        <Variable Name="AppName_es" Value="Creador de Mapas"/>
        <Variable Name="AppName_fr" Value="Créateur de Carte"/>
        <Variable Name="AppName_pt" Value="Criador de Mapas"/>
        <Variable Name="AppName_id" Value="Pencipta Peta"/>

        <Chain>
            <ExePackage SourceFile="vcredist_x64.exe" PerMachine="yes" Permanent="yes" Vital="yes" Compressed="yes" InstallCommand="/quiet /norestart" InstallCondition="VersionNT64"/>
            <ExePackage SourceFile="vcredist_x86.exe" PerMachine="yes" Permanent="yes" Vital="yes" Compressed="yes" InstallCommand="/quiet /norestart" InstallCondition="NOT VersionNT64"/>
            <PackageGroupRef Id="OldMapEditorPackage"/>
            <MsiPackage Vital="yes" SourceFile="$(var.BuildDir)out\map-creator-$(var.Platform).msi" />
        </Chain>
    </Bundle>
</Wix>
David Burson
  • 2,947
  • 7
  • 32
  • 55

2 Answers2

2

There's an open feature request for this: http://wixtoolset.org/issues/4045/. This solution would allow you to provide a resource dll that Programs & Features would query for the display name, so it would change if the user changes the OS language.

Until someone implements that feature, you're right that this can be done by setting the WixBundleName variable. If you are writing a custom BA, you would call IBootstrapperEngine::SetVariableString.

If you're using WixStdBA, you'll have to use the BAFunctions dll that isn't very well documented to call this method. There is a sample project here. Note that the WixStdBALanguageId variable was just recently added that would help you know what the current language is.

To add the BAFunctions.dll, simply add it as a payload:

<BootstrapperApplicationRef ...>
  <Payload Name="BAFunctions.dll" SourceFile="path\to\your.dll" />
</BootstrapperApplicationRef>
Sean Hall
  • 7,629
  • 2
  • 29
  • 44
  • I'm not sure I understand that feature request - I'm new to all this. What I'm trying to do, I think, is change the bundle @name at install time, based on the user's language. – David Burson Mar 26 '15 at 13:54
  • thanks! How do I "use the BAFunctions dll"? From the sample project seems I should build my own BAFunctions.dll, is that right? If so, how do I tell my Bundle to use my BAFunctions.dll? – David Burson Mar 27 '15 at 15:15
  • I tried referencing [WixStdBALanguageId] in my wxl and it was empty string. Is WixStdBALanguageId available in WiX 3.9 R2? Or is it something in new development? If it's in 3.9 R2, where can I use it? – David Burson Mar 27 '15 at 17:11
  • @DavidBurson Updated to include how to include the BAFunctions.dll. It's in v3.10 which is available in the weekly releases at http://wixtoolset.org. `WixStdBALanguageId` is a Burn Variable that you would use in your C++ code, I don't believe you access to your wxl file in BAFunctions. – Sean Hall Mar 27 '15 at 17:15
  • So the BAFunctions.dll is NOT something I need to build myself - I just use it, right? But it is only in WiX 3.10? Also, I'm confused: I'm not writing any C++ code for my installer. Light creates it based on my bundle.wxs. So where do I reference WixStdBALanguageId? Sorry for my ignorance! – David Burson Mar 27 '15 at 17:37
  • @DavidBurson, sorry I shouldn't have answered two questions in one comment. BAFunctions.dll is something you'll have to write in C++. Support for this was added in v3.8. WixStdBALanguageId is not in 3.9 R2, it's in the latest v3.10. – Sean Hall Mar 27 '15 at 17:40
  • Thanks, that clears some things up for me! On wixtoolset.org/releases, it says, "These development releases may be unstable and are therefore not recommended for production use." Any idea when the stable release of v3.10 will be? – David Burson Mar 27 '15 at 18:21
  • @DavidBurson The main feature of v3.10 is VS2015 support, so whenever Microsoft releases the RTM version of VS2015. – Sean Hall Mar 27 '15 at 18:26
  • I'm not currently using Visual Studio or .NET. My application has no .NET dependencies. The BAFunctions sample project is a VC++ application. If I do use it as a template and just pop in the code to change WixBundleName based on WixStdBALanguageId, will the resulting BAFunctions.dll add more dependencies (such as .NET)? Or can I just reference my BAFunctions.dll as you noted in your answer, and there's no other dependencies I have to deal with? – David Burson Mar 27 '15 at 18:39
  • 1
    @DavidBurson The sample project is statically linked, so it has no dependencies. This is also how WixStdBA is built. – Sean Hall Mar 27 '15 at 18:50
0

In case it's helpful, since it took me quite awhile to figure out, here's how I localized WixBundleName so it is localized in Programs & Features. This is based on the answer and comments from Sean Hall.

This works with WiX 3.10.0.1519 weekly build, built on Thursday, Mar. 19 2015.

/* This document describes how we build bafunctions.dll.

1. Install the version of WiX you want to use.  This is necessary because it sets some environment variables or something, 
    and provides some .h files needed to get the BAFunctions project to compile.
2. Download the wixXX-debug.zip file from the same page where the WiX installer is for the version of WiX you want.
3. Unzip and browse to [wixXX-debug\src\burn\Samples\bafunctions\ and open the project in Visual Studio 2010.
4. In WixBootstrapperBAFunction.cpp, add our code as shown below.
5. Rebuild solution in Release configuration.
6. copy bafunctions\release\bafunctions.dll to Map Creator build machine, [map_creator]\install\win\[Wix_X.X]

*/

// In WixBootstrapperBAFunction.cpp:

// comment out this line (line 32 in WiX 3.10):

    STDMETHODIMP OnDetectComplete() { return S_OK; }

/* uncomment the commented-out OnDetectComplete procedure, and in the section marked
// YOUR CODE GOES HERE
add this code:
*/
        LONGLONG lang_id = NULL;
        LPWSTR bundle_name = NULL;
        LPWSTR platform = NULL;
        LPWSTR orig_name = NULL;

        // get the language the user is using
        BalGetNumericVariable(L"WixStdBALanguageId", &lang_id);
        BalExitOnFailure(hr, "Failed to get variable 'WixStdBALanguageId'.");

        // log the language id
        char msg[66];   // 66 I'm sure is big enough
        sprintf_s(msg, 67, "%s%d", "WixStdBALanguageId is ", lang_id);
        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, msg);

        /* I don't know how to pass custom variables from the .wxs, so I tried below to use the built-in HelpTelephone.
            That doesn't work either - apparently we can only get variables listed in:
            http://wixtoolset.org/documentation/manual/v3/bundle/bundle_built_in_variables.html
        BalGetStringVariable(L"WixBundleHelpTelephone", &platform);
        BalExitOnFailure(hr, "Failed to get WixBundleHelpTelephone variable.");*/

        // Since I don't know how to pass a custom variable from the .wxs, I get the @Name attribute of the Bundle (from the .wxs),
        //  and I parse out the platform.
        BalGetStringVariable(L"WixBundleName", &orig_name);
        BalExitOnFailure(hr, "Failed to get WixBundleName variable.");

        platform = wcsrchr(orig_name, '(');

        // log the platform.
        char msg5[66];  // 66 I'm sure is big enough
        sprintf_s(msg5, 67, "%s%s", "platform is ", platform);
        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, msg5);

        // Select the localized bundle name
        // I got the language codes from https://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
        switch (lang_id) {
            case 1036:
            case 2060:
            case 3084:
            case 4108:
            case 5132:
            case 6156:
            case 7180:
            case 8204:
            case 9228:
            case 10252:
            case 11276:
            case 12300:
            case 13324:
            case 14348:
            case 15372:
            case 58380:
                // French
                bundle_name = L"Créateur de Carte";
                break;

            case 1057:
                // Indonesian
                bundle_name = L"Pencipta Peta";
                break;

            case 1046:
            case 2070:
                // Portuguese
                bundle_name = L"Criador de Mapas";
                break;

            case 1034:
            case 2058:
            case 3082:
            case 4106:
            case 5130:
            case 6154:
            case 7178:
            case 8202:
            case 9226:
            case 10250:
            case 11274:
            case 12298:
            case 13322:
            case 14346:
            case 15370:
            case 16394:
            case 17418:
            case 18442:
            case 19466:
            case 20490:
            case 21514:
            case 22538:
                // Spanish
                bundle_name = L"Creador de Mapas";
                break;

            default:
                bundle_name = L"Map Creator";
        }

        // log the new bundle name.
        char msg2[66];  // 66 I'm sure is big enough
        sprintf_s(msg2, 67, "%s%s", "bundle_name is ", bundle_name);
        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, msg2);

        // append the platform to the end of the bundle name
        wchar_t bundle_name_platform[80];
        wcscpy_s(bundle_name_platform, bundle_name);
        wcscat_s(bundle_name_platform, L" ");
        wcscat_s(bundle_name_platform, platform);

        // log the final bundle name (which includes the platform)
        char msg3[66];  // 66 I'm sure is big enough
        sprintf_s(msg3, 67, "%s%s", "bundle_name_platform is ", bundle_name_platform);
        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, msg3);

        // set the name of our bundle
        hr = m_pEngine->SetVariableString(L"WixBundleName", bundle_name_platform);
        BalExitOnFailure(hr, "Failed to set WixBundleName variable.");
David Burson
  • 2,947
  • 7
  • 32
  • 55