Skip to content

Commit 4791961

Browse files
authored
feat(bundler): allow setting wix language, closes #1976 (#1988)
1 parent 463fd00 commit 4791961

File tree

10 files changed

+247
-29
lines changed

10 files changed

+247
-29
lines changed

.changes/wix-bundle-language.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-bundler": patch
3+
---
4+
5+
Allow setting the Windows installer language and using project names that contains non-Unicode characters.

.changes/wix-config-language.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cli.rs": patch
3+
---
4+
5+
Adds `tauri > bundle > windows > wix > language` config option. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.

docs/api/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ It's composed of the following properties:
160160
{ property: "certificateThumbprint", optional: true, type: "string[]", description: `Specifies the SHA1 hash of the signing certificate.` },
161161
{ property: "timestampUrl", optional: true, type: "string[]", description: `Server to use during timestamping.` },
162162
{ property: "wix", optional: true, type: "object", child: <Properties anchorRoot="tauri.bundle.windows.wix" rows={[
163+
{ property: "language", optional: true, type: "string", description: `The installer language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.` },
163164
{ property: "template", optional: true, type: "string", description: `A custom .wxs template to use.` },
164165
{ property: "fragmentPaths", optional: true, type: "string[]", description: `A list of paths to .wxs files with WiX fragments to use.` },
165166
{ property: "componentGroupRefs", optional: true, type: "string[]", description: `The ComponentGroup element ids you want to reference from the fragments.` },

tooling/bundler/src/bundle/settings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ pub struct MacOsSettings {
175175
/// Settings specific to the WiX implementation.
176176
#[derive(Clone, Debug, Default)]
177177
pub struct WixSettings {
178+
/// The app language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.
179+
pub language: String,
178180
/// By default, the bundler uses an internal template.
179181
/// This option allows you to define your own wix file.
180182
pub template: Option<PathBuf>,
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
{
2+
"ar-SA": {
3+
"langId": 1025,
4+
"asciiCode": 1256
5+
},
6+
"ca-ES": {
7+
"langId": 1027,
8+
"asciiCode": 1252
9+
},
10+
"zh-TW": {
11+
"langId": 1028,
12+
"asciiCode": 950
13+
},
14+
"zh-CN": {
15+
"langId": 2052,
16+
"asciiCode": 936
17+
},
18+
"cs-CZ": {
19+
"langId": 1029,
20+
"asciiCode": 1250
21+
},
22+
"da-DK": {
23+
"langId": 1030,
24+
"asciiCode": 1252
25+
},
26+
"de-DE": {
27+
"langId": 1031,
28+
"asciiCode": 1252
29+
},
30+
"el-GR": {
31+
"langId": 1032,
32+
"asciiCode": 1253
33+
},
34+
"en-US": {
35+
"langId": 1033,
36+
"asciiCode": 1252
37+
},
38+
"es-ES": {
39+
"langId": 3082,
40+
"asciiCode": 1252
41+
},
42+
"et-EE": {
43+
"langId": 1061,
44+
"asciiCode": 1257
45+
},
46+
"fi-FI": {
47+
"langId": 1035,
48+
"asciiCode": 1252
49+
},
50+
"fr-FR": {
51+
"langId": 1036,
52+
"asciiCode": 1252
53+
},
54+
"he-IL": {
55+
"langId": 1037,
56+
"asciiCode": 1255
57+
},
58+
"hu-HU": {
59+
"langId": 1038,
60+
"asciiCode": 1250
61+
},
62+
"it-IT": {
63+
"langId": 1040,
64+
"asciiCode": 1252
65+
},
66+
"jp-JP": {
67+
"langId": 1041,
68+
"asciiCode": 932
69+
},
70+
"ko-KO": {
71+
"langId": 1042,
72+
"asciiCode": 949
73+
},
74+
"lt-LT": {
75+
"langId": 1063,
76+
"asciiCode": 1257
77+
},
78+
"lv-LV": {
79+
"langId": 1062,
80+
"asciiCode": 1257
81+
},
82+
"nl-NL": {
83+
"langId": 1043,
84+
"asciiCode": 1252
85+
},
86+
"nb-NO": {
87+
"langId": 1044,
88+
"asciiCode": 1252
89+
},
90+
"pl-PL": {
91+
"langId": 1045,
92+
"asciiCode": 1250
93+
},
94+
"pt-BR": {
95+
"langId": 1046,
96+
"asciiCode": 1252
97+
},
98+
"pt-PT": {
99+
"langId": 2070,
100+
"asciiCode": 1252
101+
},
102+
"ro-RO": {
103+
"langId": 1048,
104+
"asciiCode": 1250
105+
},
106+
"ru-RU": {
107+
"langId": 1049,
108+
"asciiCode": 1251
109+
},
110+
"hr-HR": {
111+
"langId": 1050,
112+
"asciiCode": 1250
113+
},
114+
"sk-SK": {
115+
"langId": 1051,
116+
"asciiCode": 1250
117+
},
118+
"sv-SE": {
119+
"langId": 1053,
120+
"asciiCode": 1252
121+
},
122+
"th-TH": {
123+
"langId": 1054,
124+
"asciiCode": 874
125+
},
126+
"tr-TR": {
127+
"langId": 1055,
128+
"asciiCode": 1254
129+
},
130+
"sl-SI": {
131+
"langId": 1060,
132+
"asciiCode": 1250
133+
},
134+
"vi-VN": {
135+
"langId": 1066,
136+
"asciiCode": 1258
137+
},
138+
"eu-ES": {
139+
"langId": 1069,
140+
"asciiCode": 1252
141+
},
142+
"bg-BG": {
143+
"langId": 1026,
144+
"asciiCode": 1251
145+
},
146+
"uk-UA": {
147+
"langId": 1058,
148+
"asciiCode": 1251
149+
},
150+
"sr-Latn-CS": {
151+
"langId": 2074,
152+
"asciiCode": 1250
153+
}
154+
}

tooling/bundler/src/bundle/windows/msi/wix.rs

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ use crate::bundle::{
1111

1212
use handlebars::{to_json, Handlebars};
1313
use regex::Regex;
14-
use serde::Serialize;
14+
use serde::{Deserialize, Serialize};
1515
use sha2::Digest;
1616
use uuid::Uuid;
1717
use zip::ZipArchive;
1818

1919
use std::{
20-
collections::BTreeMap,
21-
fs::{create_dir_all, remove_dir_all, write, File},
20+
collections::{BTreeMap, HashMap},
21+
fs::{create_dir_all, remove_dir_all, rename, write, File},
2222
io::{Cursor, Read, Write},
2323
path::{Path, PathBuf},
2424
process::{Command, Stdio},
@@ -52,6 +52,14 @@ const UUID_NAMESPACE: [u8; 16] = [
5252
/// Mapper between a resource directory name and its ResourceDirectory descriptor.
5353
type ResourceMap = BTreeMap<String, ResourceDirectory>;
5454

55+
#[derive(Debug, Deserialize)]
56+
struct LanguageMetadata {
57+
#[serde(rename = "asciiCode")]
58+
ascii_code: usize,
59+
#[serde(rename = "langId")]
60+
lang_id: usize,
61+
}
62+
5563
/// A binary to bundle with WIX.
5664
/// External binaries or additional project binaries are represented with this data structure.
5765
/// This data structure is needed because WIX requires each path to have its own `id` and `guid`.
@@ -313,10 +321,10 @@ fn run_candle(
313321
fn run_light(
314322
wix_toolset_path: &Path,
315323
build_path: &Path,
316-
wixobjs: &[&str],
324+
arguments: Vec<String>,
317325
output_path: &Path,
318326
settings: &Settings,
319-
) -> crate::Result<PathBuf> {
327+
) -> crate::Result<()> {
320328
let light_exe = wix_toolset_path.join("light.exe");
321329

322330
let mut args: Vec<String> = vec![
@@ -326,8 +334,8 @@ fn run_light(
326334
output_path.display().to_string(),
327335
];
328336

329-
for p in wixobjs {
330-
args.push((*p).to_string());
337+
for p in arguments {
338+
args.push(p);
331339
}
332340

333341
let mut cmd = Command::new(&light_exe);
@@ -336,19 +344,16 @@ fn run_light(
336344
.stdout(Stdio::piped())
337345
.current_dir(build_path);
338346

339-
common::print_info(format!("running light to produce {}", output_path.display()).as_str())?;
340-
common::execute_with_verbosity(&mut cmd, settings)
341-
.map(|_| output_path.to_path_buf())
342-
.map_err(|_| {
343-
crate::Error::ShellScriptError(format!(
344-
"error running light.exe{}",
345-
if settings.is_verbose() {
346-
""
347-
} else {
348-
", try running with --verbose to see command output"
349-
}
350-
))
351-
})
347+
common::execute_with_verbosity(&mut cmd, settings).map_err(|_| {
348+
crate::Error::ShellScriptError(format!(
349+
"error running light.exe{}",
350+
if settings.is_verbose() {
351+
""
352+
} else {
353+
", try running with --verbose to see command output"
354+
}
355+
))
356+
})
352357
}
353358

354359
// fn get_icon_data() -> crate::Result<()> {
@@ -406,6 +411,29 @@ pub fn build_wix_app_installer(
406411

407412
let mut data = BTreeMap::new();
408413

414+
let language_map: HashMap<String, LanguageMetadata> =
415+
serde_json::from_str(include_str!("./languages.json")).unwrap();
416+
417+
let (language, language_metadata) = if let Some(wix) = &settings.windows().wix {
418+
let metadata = language_map.get(&wix.language).unwrap_or_else(|| {
419+
panic!(
420+
"Language {} not found. It must be one of {}",
421+
wix.language,
422+
language_map
423+
.keys()
424+
.cloned()
425+
.collect::<Vec<String>>()
426+
.join(", ")
427+
)
428+
});
429+
(wix.language.clone(), metadata)
430+
} else {
431+
common::print_info("Wix settings not found. Using `en-US` as language.")?;
432+
("en-US".into(), language_map.get("en-US").unwrap())
433+
};
434+
data.insert("language_id", to_json(language_metadata.lang_id));
435+
data.insert("ascii_codepage", to_json(language_metadata.ascii_code));
436+
409437
data.insert("product_name", to_json(settings.product_name()));
410438
data.insert("version", to_json(settings.version_string()));
411439
let manufacturer = settings.bundle_identifier().to_string();
@@ -511,16 +539,26 @@ pub fn build_wix_app_installer(
511539
run_candle(settings, wix_toolset_path, &output_path, wxs)?;
512540
}
513541

514-
let wixobjs = vec!["*.wixobj"];
515-
let target = run_light(
542+
let arguments = vec![
543+
format!("-cultures:{}", language.to_lowercase()),
544+
"*.wixobj".into(),
545+
];
546+
let msi_output_path = output_path.join("output.msi");
547+
let msi_path = app_installer_dir(settings)?;
548+
create_dir_all(msi_path.parent().unwrap())?;
549+
550+
common::print_info(format!("running light to produce {}", msi_path.display()).as_str())?;
551+
552+
run_light(
516553
wix_toolset_path,
517554
&output_path,
518-
&wixobjs,
519-
&app_installer_dir(settings)?,
555+
arguments,
556+
&msi_output_path,
520557
settings,
521558
)?;
559+
rename(&msi_output_path, &msi_path)?;
522560

523-
Ok(target)
561+
Ok(msi_path)
524562
}
525563

526564
/// Generates the data required for the external binaries and extra binaries bundling.

tooling/bundler/src/bundle/windows/templates/main.wxs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@
1313
Id="*"
1414
Name="{{{product_name}}}"
1515
UpgradeCode="{{{upgrade_code}}}"
16-
Language="1033"
17-
Codepage="1252"
16+
Language="{{language_id}}"
17+
Codepage="{{ascii_codepage}}"
1818
Manufacturer="{{{manufacturer}}}"
1919
Version="{{{version}}}">
2020

2121
<Package Id="*"
2222
Keywords="Installer"
2323
InstallerVersion="450"
24-
Languages="1033"
24+
Languages="{{language_id}}"
2525
Compressed="yes"
2626
InstallScope="perMachine"
27-
SummaryCodepage="1252"/>
27+
SummaryCodepage="{{ascii_codepage}}"/>
2828

2929
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed."
3030
MigrateFeatures="yes" />

tooling/cli.rs/config_definition.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,16 @@ pub struct MacConfig {
5353
pub entitlements: Option<String>,
5454
}
5555

56+
fn default_language() -> String {
57+
"en-US".into()
58+
}
59+
5660
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
5761
#[serde(rename_all = "camelCase", deny_unknown_fields)]
5862
pub struct WixConfig {
63+
/// App language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.
64+
#[serde(default = "default_language")]
65+
pub language: String,
5966
pub template: Option<PathBuf>,
6067
#[serde(default)]
6168
pub fragment_paths: Vec<PathBuf>,

tooling/cli.rs/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,11 @@
12891289
"type": "string"
12901290
}
12911291
},
1292+
"language": {
1293+
"description": "App language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.",
1294+
"default": "en-US",
1295+
"type": "string"
1296+
},
12921297
"mergeRefs": {
12931298
"default": [],
12941299
"type": "array",

tooling/cli.rs/src/helpers/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub use config_definition::*;
1616
impl From<WixConfig> for tauri_bundler::WixSettings {
1717
fn from(config: WixConfig) -> tauri_bundler::WixSettings {
1818
tauri_bundler::WixSettings {
19+
language: config.language,
1920
template: config.template,
2021
fragment_paths: config.fragment_paths,
2122
component_group_refs: config.component_group_refs,

0 commit comments

Comments
 (0)