Closed grizoood closed 1 month ago
You can do it very easily by changing the language and calling BuildMsi
again:
project.Language = "en-US";
project.BuildMsi($"setup.{project.Language}.msi");
project.Language = "fr-FR";
project.BuildMsi($"setup.{project.Language}.msi");
If you want to use custom wxl files you can set them too:
project.LocalizationFile = @".\languages\custom_de-de.wxl";
I'm trying to do this:
project.Name = "!(loc.ApplicationName)";
But I get errors:
Cannot find the Binary file 'Product name - Application name.wxl'. The following paths were checked: Product name - Application name.wxl
Cannot find the Binary file 'Product name - Application name.licence.rtf'. The following paths were checked: Product name - Application name.licence.rtf
Cannot find the Binary file 'Nom du produit - Nom de l'application.wxl'. The following paths were checked: Nom du produit - Nom de l'application.wxl
Cannot find the Binary file 'Nom du produit - Nom de l'application.licence.rtf'. The following paths were checked: Nom du produit - Nom de l'application.licence.rtf
I want to set a name based on language otherwise I can directly set the name and not use wxl file.
project.Name = "!(loc.ApplicationName)";
It's not how WixSharp works but you can achieve the desired outcome by simply reading the app name with C# from the wxl file:
project.Name = XDocument.Load(project.LocalizationFile)
.FindAll("String")
.First(x => x.HasAttribute("Id", "ApplicationName"))
.Attr("Value");
And I just added a new more convenient extension method that embeds the code above. Thus from the next release you will be able to do it even as simple as below:
project.Name = project.LocalizationFile.GetLocalizedString("ApplicationName");
One last question which is somewhat related.
How can we retrieve the ApplicationName variable to display the correct translation in the interface.
I want to replace "MESSAGE fr-FR or en-US" with "ApplicationName" variable which comes from the wxl file
static void Msi_UIInitialized(SetupEventArgs e)
{
//MessageBox.Show(e.ToString(), "UIInitialized");
e.ManagedUI.Shell.CustomErrorDescription = "MESSAGE fr-FR or en-US";
e.ManagedUI.Shell.ErrorDetected = true;
e.Result = ActionResult.UserExit;
}
It's good I found.
static void Msi_UIInitialized(SetupEventArgs e)
{
//MessageBox.Show(e.ToString(), "UIInitialized");
MsiRuntime runtime = e.ManagedUI.Shell.MsiRuntime();
e.ManagedUI.Shell.CustomErrorDescription = runtime.UIText["ApplicationName"];
e.ManagedUI.Shell.ErrorDetected = true;
e.Result = ActionResult.UserExit;
}
static void Msi_UIInitialized(SetupEventArgs e)
{
MsiRuntime runtime = e.ManagedUI.Shell.MsiRuntime();
// at this point `runtime` is initialized with the appropriate wxl so you do not need to decide
// as in "<String Id="CustomError" Overridable="yes" Value="MyCustom Error"></String>"
e.ManagedUI.Shell.CustomErrorDescription = runtime.Localize("CustomError");
e.ManagedUI.Shell.ErrorDetected = true;
e.Result = ActionResult.UserExit;
}
Yep, the same thing :)
Is it possible to chain the variables like this:
<WixLocalization Culture="en-US" Codepage="1252" xmlns="http://wixtoolset.org/schemas/v4/wxl">
<String Id="Name" Value="Application test"></String>
<String Id="ApplicationName" Value="MyProduct - !(loc.Name)"></String>
</WixLocalization>
ApplicationName => "MyProduct - Application test"
Or should I do this:
<WixLocalization Culture="en-US" Codepage="1252" xmlns="http://wixtoolset.org/schemas/v4/wxl">
<String Id="Name" Value="Application test"></String>
<String Id="ApplicationName" Value="MyProduct - Application test"></String>
</WixLocalization>
I know that in Wix it is possible but in WixSharp, I don't know.
There are two use-cases for that:
Formatted localization with the dynamic template (C# string):
var msg = "[ProductName] Setup".LocalizeWith(runtime.Localize);
MessageBox.Show(msg, "1");
With the product name being either MSI property or a string from the language file. And "[ProductName] Setup" is your template. BTW !(loc.Name)
is a WiX proprietary syntax that they integrated with MSBuild process but it has no direct support outside of WiX VS project.
Another use-case is the template in the language file. Every entry there can reference another entry from the same language file by enclosing the entry Id in the square brackets:
And now you can use it like this:
msg = "[CustomError]".LocalizeWith(runtime.Localize);
MessageBox.Show(msg, "2");
Great, thank you @oleg-shilo
I encounter another problem, I want to have 2 identical products with the same guid, but when I do this:
static void Main()
{
var project = new ManagedProject("MyProduct",
new Dir(@"%AppData%\My Company\My Product",
new File("Program.cs")));
project.GUID = new Guid("6fe30b47-2577-43ad-9095-1861ba25889b");
project.ManagedUI = ManagedUI.DefaultWpf;
project.Language = "fr-FR";
project.LocalizationFile = $@".\languages\custom_{project.Language}.wxl";
project.Name = $"Test-{language}";
project.BuildMsi($"{project.Name}.msi");
project.Language = "en-US";
project.LocalizationFile = $@".\languages\custom_{project.Language}.wxl";
project.Name = $"Test-{language}";
project.BuildMsi($"{project.Name}.msi");
}
It doesn't compile and tells me this:
I think it doesn't save the :
Because project name is redefined in : Test-en-US
So I tried to create two ManagedProject :
static void Main()
{
Generate("en-US");
Generate("fr-FR");
}
static void Generate(string language)
{
var project = new ManagedProject("MyProduct",
new Dir(@"%AppData%\My Company\My Product",
new File("Program.cs")));
project.GUID = new Guid("6fe30b47-2577-43ad-9095-1861ba25889b");
project.ManagedUI = ManagedUI.DefaultWpf;
project.Language = language;
project.LocalizationFile = $@".\languages\custom_{project.Language}.wxl";
project.Name = $"Test-{language}";
project.BuildMsi($"{project.Name}.msi");
}
This works, it generates the two msi outputs :
Test-en-US.msi
Test-fr-FR.msi
The problem is found when executing the msi. For example I launch the msi: "en-US", everything is fine it installs it but subsequently if I launch the msi "fr-FR", I would like it to open the maintenance interface because it is the same product. Currently it shows me this:
hallo, i had same issue with my standard language en-US msi. It happens, because you dont have set MajorUpdateStrategy. Just set it default . Should work after recreate msi new with newer version.
I still get the same error adding this:
project.MajorUpgradeStrategy = MajorUpgradeStrategy.Default;
What I want is to have 2 msi, one in French and one in English that I install one or the other I want it to be the same product (they have the same version). It's just the name of the product that changes.
Did you have a look at this sample? Maybe it is what you are looking for?
import is codeline in sample :
static void RunAsBA()
{
// Debug.Assert(false);
// A poor-man BA. Provided only as an example for showing how to let user select the language and run the corresponding localized msi.
// BuildMsiWithLanguageSelectionBootstrapper with a true BA is a better choice but currently WiX4 has a defect preventing
// showing msi internal UI from teh custom BA.
ConsoleHelper.HideConsoleWindow();
var msiFile = io.Path.GetFullPath("MyProduct.msi");
try
{
var installed = AppSearch.IsProductInstalled("{6fe30b47-2577-43ad-9095-1861ca25889c}");
if (installed)
{
Process.Start("msiexec", $"/x \"{msiFile}\"");
}
else
{
var view = new MainView();
if (view.ShowDialog() == true)
{
if (view.SupportedLanguages.FirstOrDefault().LCID == view.SelectedLanguage.LCID) // default language
Process.Start("msiexec", $"/i \"{msiFile}\"");
else
Process.Start("msiexec", $"/i \"{msiFile}\" TRANSFORMS=:{view.SelectedLanguage.LCID}");
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Neverind... It looks like @Torchok19081986 is handling it well :) thank you
ähm.. I got several similar problem / issues. I tested it, got info from example and could find out, how to deal with it. MSI Package is quit easier than BA Installer. BUT here you has best oppotunity to build cusom Installer, where you can build alwost everthing. Need some time and practice. That all. Just Changes by Wix Team or there breaking changes , i dont handle it immediatly. Just use Wix Toolset self and Wix# and practice, practice, practice. 😄😄😄.
Weird, i have 2.1.5 version but merge is red underline
merge is paramter, just do remove it, second parameter is boolean, which is true. You dont need merge as variable.
When I do "Go to Definition" in visual studio :
#region assembly WixSharp.UI, Version=2.1.5.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2
// emplacement inconnu
// Decompiled with ICSharpCode.Decompiler 8.1.1.7464
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace WixSharp;
//
// Résumé :
// Localization map. It is nothing else but a specialized version of a generic string-to-string
// Dictionary.
public class ResourcesData : Dictionary<string, string>
{
//
// Résumé :
// Gets or sets the value associated with the specified key.
//
// Paramètres :
// key:
// The key.
public new string this[string key]
{
get
{
if (!ContainsKey(key))
{
return null;
}
return base[key];
}
set
{
base[key] = value;
}
}
//
// Résumé :
// Initializes from WiX localization data (*.wxl).
//
// Paramètres :
// wxlData:
// The WXL file bytes.
public void InitFromWxl(byte[] wxlData)
{
Clear();
if (wxlData == null || !wxlData.Any())
{
return;
}
string tempFileName = Path.GetTempFileName();
XDocument xDocument;
try
{
System.IO.File.WriteAllBytes(tempFileName, wxlData);
xDocument = XDocument.Load(tempFileName);
}
catch
{
throw new Exception("The localization XML data is in invalid format.");
}
finally
{
System.IO.File.Delete(tempFileName);
}
Func<XElement, string> elementSelector = (XElement element) => element.GetAttribute("Value") ?? element.Value;
foreach (KeyValuePair<string, string> item in (from x in xDocument.Descendants()
where x.Name.LocalName == "String"
select x).ToDictionary((XElement x) => x.Attribute("Id").Value, elementSelector))
{
Add(item.Key, item.Value);
}
}
}
#if false // Journal de décompilation
'34' éléments dans le cache
------------------
Résoudre : 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll'
------------------
Résoudre : 'WixToolset.Mba.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=a7d136314861246c'
Un seul assembly trouvé : 'WixToolset.Mba.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=a7d136314861246c'
Charger à partir de : 'C:\Users\A.MINET\.nuget\packages\wixtoolset.mba.core\4.0.1\lib\net20\WixToolset.Mba.Core.dll'
------------------
Résoudre : 'WixSharp, Version=2.1.5.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2'
Un seul assembly trouvé : 'WixSharp, Version=2.1.5.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2'
Charger à partir de : 'C:\Users\A.MINET\.nuget\packages\wixsharp_wix4.bin\2.1.5\lib\WixSharp.dll'
------------------
Résoudre : 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xml.Linq.dll'
------------------
Résoudre : 'WixToolset.Dtf.WindowsInstaller, Version=4.0.0.0, Culture=neutral, PublicKeyToken=a7d136314861246c'
Un seul assembly trouvé : 'WixToolset.Dtf.WindowsInstaller, Version=4.0.0.0, Culture=neutral, PublicKeyToken=a7d136314861246c'
Charger à partir de : 'C:\Users\A.MINET\.nuget\packages\wixtoolset.dtf.windowsinstaller\4.0.1\lib\net20\WixToolset.Dtf.WindowsInstaller.dll'
------------------
Résoudre : 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Un seul assembly trouvé : 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Drawing.dll'
------------------
Résoudre : 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll'
------------------
Résoudre : 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll'
------------------
Résoudre : 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Core.dll'
------------------
Résoudre : 'System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Un seul assembly trouvé : 'System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Charger à partir de : 'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.FileSystem.dll'
#endif
Let me check the package in case it was a packaging problem.
Ah... all good. The change to the new signature was done for WiX3 stream and has not yet propagated to WiX4 stream of work. So you can just drop that last arg in the method call.
Ok I'll delete that :)
I took your example exactly with wix4 :/
error WIX0103: Cannot find the Binary file 'WixUI_de-DE.wxl'. The following paths were checked: WixUI_de-DE.wxl
error WIX0103: Cannot find the Binary file 'WixUI_el-GR.wxl'. The following paths were checked: WixUI_el-GR.wxl
No work :
project.AddBinary(new Binary(new Id("de_xsl"), @"WixUI_de-DE.wxl"))
.AddBinary(new Binary(new Id("gr_xsl"), @"WixUI_el-GR.wxl"));
Work :
project.AddBinary(new Binary(new Id("de_xsl"), @"CycleScroller\WixSharp Setup1 WPF UI\WixUI_de-DE.wxl"))
.AddBinary(new Binary(new Id("gr_xsl"), @"CycleScroller\WixSharp Setup1 WPF UI\WixUI_el-GR.wxl"));
Yes, the sample has this at line 77:
I had to add the full path, I don't know why because the Script file is in the same location.
CycleScroller\WixSharp Setup1 WPF UI\{FILE_NAME}.wxl"
The file path is relative to the "working dir". If you used VS template then it is where the project file is. If you create your project other way then it is what your project settings say.
You an always check it by putting in the script file:
Console.WriteLine(Environment.CurrentDirectory);
You can also overwrite this mechanism by providing a new root directory: project.SourceBaseDir = .....
Ah yes indeed, the problem came from here:
project.SourceBaseDir = @"..\..\";
I'm closing the topic for now, thank you both again for your help ;)
Hi,
Need help with MSI language !
In Wix, it is possible to define languages:
And then we can create different language files :
fr-FR.wxl file :
en-US.wxl file :
Product.wxs file :
And this generates 2 msi output.
How can I do the same thing with wixsharp?