浏览代码

feat(bundler): wix localization, closes #3174 (#3179)

Lucas Fernandes Nogueira 3 年之前
父节点
当前提交
af329f2722

+ 6 - 0
.changes/wix-localization.md

@@ -0,0 +1,6 @@
+---
+"cli.rs": patch
+"tauri-bundler": patch
+---
+
+Allow setting the localization file for WiX.

+ 29 - 5
core/tauri-utils/src/config.rs

@@ -122,8 +122,32 @@ pub struct MacConfig {
   pub entitlements: Option<String>,
 }
 
-fn default_language() -> String {
-  "en-US".into()
+/// Configuration for a target language for the WiX build.
+#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct WixLanguageConfig {
+  /// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
+  pub locale_path: Option<String>,
+}
+
+/// The languages to build using WiX.
+#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(untagged)]
+pub enum WixLanguage {
+  /// A single language to build, without configuration.
+  One(String),
+  /// A list of languages to build, without configuration.
+  List(Vec<String>),
+  /// A map of languages and its configuration.
+  Localized(HashMap<String, WixLanguageConfig>),
+}
+
+impl Default for WixLanguage {
+  fn default() -> Self {
+    Self::One("en-US".into())
+  }
 }
 
 /// Configuration for the MSI bundle using WiX.
@@ -131,9 +155,9 @@ fn default_language() -> String {
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct WixConfig {
-  /// The installer language. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
-  #[serde(default = "default_language")]
-  pub language: String,
+  /// The installer languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
+  #[serde(default)]
+  pub language: WixLanguage,
   /// A custom .wxs template to use.
   pub template: Option<PathBuf>,
   /// A list of paths to .wxs files with WiX fragments to use.

+ 159 - 148
examples/api/src-tauri/Cargo.lock

@@ -195,9 +195,9 @@ dependencies = [
 
 [[package]]
 name = "atk"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba"
+checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd"
 dependencies = [
  "atk-sys",
  "bitflags",
@@ -207,14 +207,14 @@ dependencies = [
 
 [[package]]
 name = "atk-sys"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea"
+checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
@@ -379,9 +379,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
 
 [[package]]
 name = "cairo-rs"
-version = "0.14.9"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482"
+checksum = "b869e97a87170f96762f9f178eae8c461147e722ba21dd8814105bf5716bf14a"
 dependencies = [
  "bitflags",
  "cairo-sys-rs",
@@ -392,13 +392,13 @@ dependencies = [
 
 [[package]]
 name = "cairo-sys-rs"
-version = "0.14.9"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570"
+checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
 dependencies = [
- "glib-sys",
+ "glib-sys 0.15.4",
  "libc",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
@@ -1146,9 +1146,9 @@ dependencies = [
 
 [[package]]
 name = "gdk"
-version = "0.14.3"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b"
+checksum = "614258e81ec35ed8770e64a0838f3a47f95b398bc51e724d3b3fa09c1ee0f8d5"
 dependencies = [
  "bitflags",
  "cairo-rs",
@@ -1162,10 +1162,11 @@ dependencies = [
 
 [[package]]
 name = "gdk-pixbuf"
-version = "0.14.0"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534192cb8f01daeb8fab2c8d4baa8f9aae5b7a39130525779f5c2608e235b10f"
+checksum = "73aa2f5de1b45710da90a55863276667dc3a3264aaf6a2aeace62bb015244d49"
 dependencies = [
+ "bitflags",
  "gdk-pixbuf-sys",
  "gio",
  "glib",
@@ -1174,44 +1175,44 @@ dependencies = [
 
 [[package]]
 name = "gdk-pixbuf-sys"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f097c0704201fbc8f69c1762dc58c6947c8bb188b8ed0bc7e65259f1894fe590"
+checksum = "413424d9818621fa3cfc8a3a915cdb89a7c3c507d56761b4ec83a9a98e587171"
 dependencies = [
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.15.4",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
 name = "gdk-sys"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e"
+checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88"
 dependencies = [
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.15.4",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
  "pango-sys",
  "pkg-config",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
 name = "gdkx11-sys"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38cefbc8ac7be19c9b51f54fbd7cef48b70495a4cb23a812e2137e75b484b29d"
+checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178"
 dependencies = [
  "gdk-sys",
- "glib-sys",
+ "glib-sys 0.15.4",
  "libc",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
  "x11",
 ]
 
@@ -1272,15 +1273,15 @@ dependencies = [
 
 [[package]]
 name = "gio"
-version = "0.14.8"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0"
+checksum = "56f906022fe89505b4e7b6c59a62d4ca3c090904f286cf61f1b245afcb20897d"
 dependencies = [
  "bitflags",
  "futures-channel",
  "futures-core",
  "futures-io",
- "gio-sys",
+ "gio-sys 0.15.4",
  "glib",
  "libc",
  "once_cell",
@@ -1293,18 +1294,31 @@ version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.14.0",
+ "gobject-sys 0.14.0",
  "libc",
  "system-deps 3.2.0",
  "winapi",
 ]
 
+[[package]]
+name = "gio-sys"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97225e1b9c7c48ed7fec4377fdc72702965bfbfd3a944b928ccbb5d8ed82ccc9"
+dependencies = [
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
+ "libc",
+ "system-deps 6.0.1",
+ "winapi",
+]
+
 [[package]]
 name = "glib"
-version = "0.14.8"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4"
+checksum = "e385b6c17a1add7d0fbc64d38e2e742346d3e8b22e5fa3734e5cdca2be24028d"
 dependencies = [
  "bitflags",
  "futures-channel",
@@ -1312,21 +1326,22 @@ dependencies = [
  "futures-executor",
  "futures-task",
  "glib-macros",
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
  "once_cell",
  "smallvec",
+ "thiserror",
 ]
 
 [[package]]
 name = "glib-macros"
-version = "0.14.1"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518"
+checksum = "e58b262ff65ef771003873cea8c10e0fe854f1c508d48d62a4111a1ff163f7d1"
 dependencies = [
  "anyhow",
- "heck 0.3.3",
+ "heck 0.4.0",
  "proc-macro-crate 1.1.0",
  "proc-macro-error",
  "proc-macro2",
@@ -1344,6 +1359,16 @@ dependencies = [
  "system-deps 3.2.0",
 ]
 
+[[package]]
+name = "glib-sys"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4f08dd67f74b223fedbbb30e73145b9acd444e67cc4d77d0598659b7eebe7e"
+dependencies = [
+ "libc",
+ "system-deps 6.0.1",
+]
+
 [[package]]
 name = "glob"
 version = "0.3.0"
@@ -1369,16 +1394,27 @@ version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5"
 dependencies = [
- "glib-sys",
+ "glib-sys 0.14.0",
  "libc",
  "system-deps 3.2.0",
 ]
 
+[[package]]
+name = "gobject-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6edb1f0b3e4c08e2a0a490d1082ba9e902cdff8ff07091e85c6caec60d17e2ab"
+dependencies = [
+ "glib-sys 0.15.4",
+ "libc",
+ "system-deps 6.0.1",
+]
+
 [[package]]
 name = "gtk"
-version = "0.14.3"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c"
+checksum = "c7978eaec05bea63947c801d29a21372f2ed39aec0bf56bf7725d3599094675e"
 dependencies = [
  "atk",
  "bitflags",
@@ -1399,30 +1435,29 @@ dependencies = [
 
 [[package]]
 name = "gtk-sys"
-version = "0.14.0"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e"
+checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84"
 dependencies = [
  "atk-sys",
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
  "gdk-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.15.4",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
  "pango-sys",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
 name = "gtk3-macros"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79"
+checksum = "8c891188af69e77a1e8a0b1746fbd03b9b396e7d34d518c5331b15950259f541"
 dependencies = [
  "anyhow",
- "heck 0.3.3",
  "proc-macro-crate 1.1.0",
  "proc-macro-error",
  "proc-macro2",
@@ -1602,9 +1637,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
 
 [[package]]
 name = "javascriptcore-rs"
-version = "0.15.2"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e207780c1d1cd3c36056695e44010a19dd481574a2106cd2540edda4128a9794"
+checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c"
 dependencies = [
  "bitflags",
  "glib",
@@ -1613,12 +1648,12 @@ dependencies = [
 
 [[package]]
 name = "javascriptcore-rs-sys"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2adf2de824b178d76c6017da59f4e7e95de49a766b584c59d47821a6c3dce9e2"
+checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
  "system-deps 5.0.0",
 ]
@@ -1678,9 +1713,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libappindicator"
-version = "0.6.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84928b9cf11786337deb7befb0605295149c2bd5dcdcf31697d7d2444a48b7ee"
+checksum = "97b29fab3280d59f3d06725f75da9ef9a1b001b2c748b1abfebd1c966c61d7de"
 dependencies = [
  "glib",
  "gtk",
@@ -1691,9 +1726,9 @@ dependencies = [
 
 [[package]]
 name = "libappindicator-sys"
-version = "0.6.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "237b254c74da51cf41ad9cc2aadd9dc9c08e41779937162257f05a214a98d09b"
+checksum = "90bdcb8c5cfc11febe2ff3f18386d6cb7d29f464cbaf6b286985c3f1a501d74f"
 dependencies = [
  "gtk-sys",
  "pkg-config",
@@ -2134,9 +2169,9 @@ dependencies = [
 
 [[package]]
 name = "pango"
-version = "0.14.8"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581"
+checksum = "79211eff430c29cc38c69e0ab54bc78fa1568121ca9737707eee7f92a8417a94"
 dependencies = [
  "bitflags",
  "glib",
@@ -2147,14 +2182,14 @@ dependencies = [
 
 [[package]]
 name = "pango-sys"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2367099ca5e761546ba1d501955079f097caa186bb53ce0f718dca99ac1942fe"
+checksum = "7022c2fb88cd2d9d55e1a708a8c53a3ae8678234c4a54bf623400aeb7f31fac2"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "libc",
- "system-deps 3.2.0",
+ "system-deps 6.0.1",
 ]
 
 [[package]]
@@ -2651,14 +2686,14 @@ dependencies = [
 
 [[package]]
 name = "rfd"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b0c25b610bf37d9874ff224ab2791ff2272bedeb5638a2dca8b18e1270ed69b"
+checksum = "d4e3a2a1461f8ef8023ff00ce76f877c112cf1ffd601d95ba01a41e820af1f3d"
 dependencies = [
  "block",
  "dispatch",
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "gtk-sys",
  "js-sys",
  "lazy_static",
@@ -2669,7 +2704,7 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "windows 0.29.0",
+ "windows 0.30.0",
 ]
 
 [[package]]
@@ -3016,9 +3051,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f056675eda9a7417163e5f742bb119e8e1d385edd2ada8f7031a7230a3ec10a"
 dependencies = [
  "bitflags",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.14.0",
+ "glib-sys 0.14.0",
+ "gobject-sys 0.14.0",
  "libc",
  "system-deps 5.0.0",
 ]
@@ -3169,7 +3204,7 @@ dependencies = [
  "strum_macros 0.21.1",
  "thiserror",
  "toml",
- "version-compare",
+ "version-compare 0.0.11",
 ]
 
 [[package]]
@@ -3182,13 +3217,26 @@ dependencies = [
  "heck 0.3.3",
  "pkg-config",
  "toml",
- "version-compare",
+ "version-compare 0.0.11",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad3a97fdef3daf935d929b3e97e5a6a680cd4622e40c2941ca0875d6566416f8"
+dependencies = [
+ "cfg-expr 0.9.1",
+ "heck 0.4.0",
+ "pkg-config",
+ "toml",
+ "version-compare 0.1.0",
 ]
 
 [[package]]
 name = "tao"
-version = "0.5.2"
-source = "git+https://github.com/tauri-apps/tao?branch=next#6ee36748252e3ce26e0341464ebbab1d1adb8c28"
+version = "0.6.0"
+source = "git+https://github.com/tauri-apps/tao?branch=dev#553055f4f44c6c4ba917a6254e1dfd3fbc1d8a3a"
 dependencies = [
  "bitflags",
  "cairo-rs",
@@ -3204,7 +3252,7 @@ dependencies = [
  "gdkx11-sys",
  "gio",
  "glib",
- "glib-sys",
+ "glib-sys 0.15.4",
  "gtk",
  "instant",
  "lazy_static",
@@ -3693,6 +3741,12 @@ version = "0.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b"
 
+[[package]]
+name = "version-compare"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
@@ -3806,19 +3860,19 @@ dependencies = [
 
 [[package]]
 name = "webkit2gtk"
-version = "0.16.0"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07654baccd8874fc7c99cc33c27052fb02804276102dff0f78f981669316e0e9"
+checksum = "2cbd39499e917de9dad36eb11c09f665eb984d432638ae7971feed98eb96df88"
 dependencies = [
  "bitflags",
  "cairo-rs",
  "gdk",
  "gdk-sys",
  "gio",
- "gio-sys",
+ "gio-sys 0.15.4",
  "glib",
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "gtk",
  "gtk-sys",
  "javascriptcore-rs",
@@ -3829,18 +3883,18 @@ dependencies = [
 
 [[package]]
 name = "webkit2gtk-sys"
-version = "0.16.0"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854a0cbf3570541bf13df70aac23826da7cd88f27a722b7b2043f32637373113"
+checksum = "ddcce6f1e0fc7715d651dba29875741509f5fc12f4e2976907272a74405f2b01"
 dependencies = [
  "atk-sys",
  "bitflags",
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
  "gdk-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.15.4",
+ "glib-sys 0.15.4",
+ "gobject-sys 0.15.1",
  "gtk-sys",
  "javascriptcore-rs-sys",
  "libc",
@@ -3945,26 +3999,13 @@ dependencies = [
  "windows_x86_64_msvc 0.24.0",
 ]
 
-[[package]]
-name = "windows"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3"
-dependencies = [
- "windows_aarch64_msvc 0.29.0",
- "windows_i686_gnu 0.29.0",
- "windows_i686_msvc 0.29.0",
- "windows_x86_64_gnu 0.29.0",
- "windows_x86_64_msvc 0.29.0",
-]
-
 [[package]]
 name = "windows"
 version = "0.30.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
 dependencies = [
- "windows_aarch64_msvc 0.30.0",
+ "windows_aarch64_msvc",
  "windows_i686_gnu 0.30.0",
  "windows_i686_msvc 0.30.0",
  "windows_x86_64_gnu 0.30.0",
@@ -3981,12 +4022,6 @@ dependencies = [
  "windows_reader",
 ]
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.30.0"
@@ -4009,12 +4044,6 @@ version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0866510a3eca9aed73a077490bbbf03e5eaac4e1fd70849d89539e5830501fd"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.30.0"
@@ -4027,12 +4056,6 @@ version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf0ffed56b7e9369a29078d2ab3aaeceea48eb58999d2cff3aa2494a275b95c6"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.30.0"
@@ -4069,12 +4092,6 @@ version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "384a173630588044205a2993b6864a2f56e5a8c1e7668c07b93ec18cf4888dc4"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.30.0"
@@ -4087,12 +4104,6 @@ version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9bd8f062d8ca5446358159d79a90be12c543b3a965c847c8f3eedf14b321d399"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.30.0"
@@ -4122,7 +4133,7 @@ dependencies = [
 [[package]]
 name = "wry"
 version = "0.12.2"
-source = "git+https://github.com/tauri-apps/wry?rev=ddb695afda719e8dd32fa584b336b7c9ff574399#ddb695afda719e8dd32fa584b336b7c9ff574399"
+source = "git+https://github.com/tauri-apps/wry?rev=d25273376b88a98f4f92fc378b5aa105f19d602e#d25273376b88a98f4f92fc378b5aa105f19d602e"
 dependencies = [
  "cocoa",
  "core-graphics",
@@ -4262,18 +4273,18 @@ dependencies = [
 
 [[package]]
 name = "zstd"
-version = "0.9.2+zstd.1.5.1"
+version = "0.10.0+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54"
+checksum = "3b1365becbe415f3f0fcd024e2f7b45bacfb5bdd055f0dc113571394114e7bdd"
 dependencies = [
  "zstd-safe",
 ]
 
 [[package]]
 name = "zstd-safe"
-version = "4.1.3+zstd.1.5.1"
+version = "4.1.4+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79"
+checksum = "2f7cd17c9af1a4d6c24beb1cc54b17e2ef7b593dc92f19e9d9acad8b182bbaee"
 dependencies = [
  "libc",
  "zstd-sys",
@@ -4281,9 +4292,9 @@ dependencies = [
 
 [[package]]
 name = "zstd-sys"
-version = "1.6.2+zstd.1.5.1"
+version = "1.6.3+zstd.1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f"
+checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8"
 dependencies = [
  "cc",
  "libc",

+ 6 - 0
examples/api/src-tauri/locales/pt-BR.wxl

@@ -0,0 +1,6 @@
+<WixLocalization Culture="pt-BR" xmlns="http://schemas.microsoft.com/wix/2006/localization">
+    <String Id="LaunchApp">Executar Tauri API</String>
+    <String Id="DowngradeErrorMessage">Uma versão mais recente de Tauri API está instalada.</String>
+    <String Id="PathEnvVarFeature">Adiciona o caminho do executável de Tauri API para a variável de ambiente PATH. Isso permite Tauri API ser executado pela linha de comando.</String>
+    <String Id="InstallAppFeature">Instala Tauri API.</String>
+</WixLocalization>

+ 11 - 1
examples/api/src-tauri/tauri.conf.json

@@ -62,7 +62,17 @@
         "../../.icons/128x128@2x.png",
         "../../.icons/icon.icns",
         "../../.icons/icon.ico"
-      ]
+      ],
+      "windows": {
+        "wix": {
+          "language": {
+            "en-US": {},
+            "pt-BR": {
+              "localePath": "locales/pt-BR.wxl"
+            }
+          }
+        }
+      }
     },
     "updater": {
       "active": true,

+ 1 - 1
tooling/bundler/src/bundle.rs

@@ -22,7 +22,7 @@ pub use self::{
     Settings, SettingsBuilder, UpdaterSettings,
   },
 };
-pub use settings::{WindowsSettings, WixSettings};
+pub use settings::{WindowsSettings, WixLanguage, WixLanguageConfig, WixSettings};
 
 use common::{print_finished, print_info};
 

+ 27 - 2
tooling/bundler/src/bundle/settings.rs

@@ -176,11 +176,28 @@ pub struct MacOsSettings {
   pub info_plist_path: Option<PathBuf>,
 }
 
+/// Configuration for a target language for the WiX build.
+#[derive(Debug, Clone, Default)]
+pub struct WixLanguageConfig {
+  /// The path to a locale (`.wxl`) file. See https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html.
+  pub locale_path: Option<PathBuf>,
+}
+
+/// The languages to build using WiX.
+#[derive(Debug, Clone)]
+pub struct WixLanguage(pub Vec<(String, WixLanguageConfig)>);
+
+impl Default for WixLanguage {
+  fn default() -> Self {
+    Self(vec![("en-US".into(), Default::default())])
+  }
+}
+
 /// Settings specific to the WiX implementation.
 #[derive(Clone, Debug, Default)]
 pub struct WixSettings {
-  /// The app language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.
-  pub language: String,
+  /// The app languages to build. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.
+  pub language: WixLanguage,
   /// By default, the bundler uses an internal template.
   /// This option allows you to define your own wix file.
   pub template: Option<PathBuf>,
@@ -310,6 +327,7 @@ impl BundleBinary {
   }
 
   /// Sets the src path of the binary.
+  #[must_use]
   pub fn set_src_path(mut self, src_path: Option<String>) -> Self {
     self.src_path = src_path;
     self
@@ -381,6 +399,7 @@ impl SettingsBuilder {
   }
 
   /// Sets the project output directory. It's used as current working directory.
+  #[must_use]
   pub fn project_out_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
     self
       .project_out_directory
@@ -389,36 +408,42 @@ impl SettingsBuilder {
   }
 
   /// Enables verbose output.
+  #[must_use]
   pub fn verbose(mut self) -> Self {
     self.verbose = true;
     self
   }
 
   /// Sets the package types to create.
+  #[must_use]
   pub fn package_types(mut self, package_types: Vec<PackageType>) -> Self {
     self.package_types = Some(package_types);
     self
   }
 
   /// Sets the package settings.
+  #[must_use]
   pub fn package_settings(mut self, settings: PackageSettings) -> Self {
     self.package_settings.replace(settings);
     self
   }
 
   /// Sets the bundle settings.
+  #[must_use]
   pub fn bundle_settings(mut self, settings: BundleSettings) -> Self {
     self.bundle_settings = settings;
     self
   }
 
   /// Sets the binaries to bundle.
+  #[must_use]
   pub fn binaries(mut self, binaries: Vec<BundleBinary>) -> Self {
     self.binaries = binaries;
     self
   }
 
   /// Sets the target triple.
+  #[must_use]
   pub fn target(mut self, target: String) -> Self {
     self.target.replace(target);
     self

+ 1 - 3
tooling/bundler/src/bundle/windows/msi.rs

@@ -17,7 +17,5 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
     wix::get_and_extract_wix(&wix_path)?;
   }
 
-  let msi_path = wix::build_wix_app_installer(settings, &wix_path)?;
-
-  Ok(vec![msi_path])
+  wix::build_wix_app_installer(settings, &wix_path)
 }

+ 6 - 0
tooling/bundler/src/bundle/windows/msi/default-locale-strings.xml

@@ -0,0 +1,6 @@
+<String Id="TauriLanguage">__language__</String>
+<String Id="TauriCodepage">__codepage__</String>
+<String Id="LaunchApp">Launch __productName__</String>
+<String Id="DowngradeErrorMessage">A newer version of __productName__ is already installed.</String>
+<String Id="PathEnvVarFeature">Add the install location of the __productName__ executable to the PATH system environment variable. This allows the __productName__ executable to be called from any location.</String>
+<String Id="InstallAppFeature">Installs __productName__.</String>

+ 106 - 56
tooling/bundler/src/bundle/windows/msi/wix.rs

@@ -18,7 +18,7 @@ use zip::ZipArchive;
 
 use std::{
   collections::{BTreeMap, HashMap},
-  fs::{create_dir_all, remove_dir_all, rename, write, File},
+  fs::{create_dir_all, read_to_string, remove_dir_all, rename, write, File},
   io::{Cursor, Read, Write},
   path::{Path, PathBuf},
   process::{Command, Stdio},
@@ -132,10 +132,11 @@ impl ResourceDirectory {
       format!("{}{}", files, directories)
     } else {
       format!(
-        r#"<Directory Id="{id}" Name="{name}">{contents}</Directory>"#,
-        id = format!("I{}", Uuid::new_v4().to_simple()),
+        r#"<Directory Id="I{id}" Name="{name}">{files}{directories}</Directory>"#,
+        id = Uuid::new_v4().to_simple(),
         name = self.name,
-        contents = format!("{}{}", files, directories)
+        files = files,
+        directories = directories,
       )
     };
 
@@ -189,8 +190,8 @@ fn download_and_verify(url: &str, hash: &str) -> crate::Result<Vec<u8>> {
   }
 }
 
-/// The installer directory of the app.
-fn app_installer_dir(settings: &Settings) -> crate::Result<PathBuf> {
+/// The app installer output path.
+fn app_installer_output_path(settings: &Settings, language: &str) -> crate::Result<PathBuf> {
   let arch = match settings.binary_arch() {
     "x86" => "x86",
     "x86_64" => "x64",
@@ -203,10 +204,11 @@ fn app_installer_dir(settings: &Settings) -> crate::Result<PathBuf> {
   };
 
   let package_base_name = format!(
-    "{}_{}_{}",
+    "{}_{}_{}_{}",
     settings.main_binary_name().replace(".exe", ""),
     settings.version_string(),
-    arch
+    arch,
+    language,
   );
 
   Ok(
@@ -366,7 +368,7 @@ fn run_light(
 pub fn build_wix_app_installer(
   settings: &Settings,
   wix_toolset_path: &Path,
-) -> crate::Result<PathBuf> {
+) -> crate::Result<Vec<PathBuf>> {
   let arch = match settings.binary_arch() {
     "x86_64" => "x64",
     "x86" => "x86",
@@ -429,7 +431,7 @@ pub fn build_wix_app_installer(
       if license.ends_with(".rtf") {
         data.insert("license", to_json(license));
       } else {
-        let license_contents = std::fs::read_to_string(&license)?;
+        let license_contents = read_to_string(&license)?;
         let license_rtf = format!(
           r#"{{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{{\fonttbl{{\f0\fnil\fcharset0 Calibri;}}}}
 {{\*\generator Riched20 10.0.18362}}\viewkind4\uc1
@@ -448,25 +450,12 @@ pub fn build_wix_app_installer(
     }
   }
 
-  let (language, language_metadata) = if let Some(wix) = &settings.windows().wix {
-    let metadata = language_map.get(&wix.language).unwrap_or_else(|| {
-      panic!(
-        "Language {} not found. It must be one of {}",
-        wix.language,
-        language_map
-          .keys()
-          .cloned()
-          .collect::<Vec<String>>()
-          .join(", ")
-      )
-    });
-    (wix.language.clone(), metadata)
-  } else {
-    common::print_info("Wix settings not found. Using `en-US` as language.")?;
-    ("en-US".into(), language_map.get("en-US").unwrap())
-  };
-  data.insert("language_id", to_json(language_metadata.lang_id));
-  data.insert("ascii_codepage", to_json(language_metadata.ascii_code));
+  let configured_languages = settings
+    .windows()
+    .wix
+    .as_ref()
+    .map(|w| w.language.clone())
+    .unwrap_or_default();
 
   data.insert("product_name", to_json(settings.product_name()));
   data.insert("version", to_json(settings.version_string()));
@@ -539,7 +528,7 @@ pub fn build_wix_app_installer(
     enable_elevated_update_task = wix.enable_elevated_update_task;
 
     if let Some(temp_path) = &wix.template {
-      let template = std::fs::read_to_string(temp_path)?;
+      let template = read_to_string(temp_path)?;
       handlebars
         .register_template_string("main.wxs", &template)
         .map_err(|e| e.to_string())
@@ -552,8 +541,7 @@ pub fn build_wix_app_installer(
         .file_name()
         .unwrap()
         .to_string_lossy()
-        .into_owned()
-        .to_string();
+        .into_owned();
       data.insert(
         "banner_path",
         to_json(copy_icon(settings, &filename, banner_path)?),
@@ -565,8 +553,7 @@ pub fn build_wix_app_installer(
         .file_name()
         .unwrap()
         .to_string_lossy()
-        .into_owned()
-        .to_string();
+        .into_owned();
       data.insert(
         "dialog_image_path",
         to_json(copy_icon(settings, &filename, dialog_image_path)?),
@@ -612,7 +599,7 @@ pub fn build_wix_app_installer(
       .expect("Failed to setup Update Task Installer handlebars");
     let temp_ps1_path = output_path.join("install-task.ps1");
     let install_script_content = skip_uac_task_installer.render("install-task.ps1", &data)?;
-    write(&temp_ps1_path, install_script_content.clone())?;
+    write(&temp_ps1_path, install_script_content)?;
 
     // Create the Powershell script to uninstall the task
     let mut skip_uac_task_uninstaller = Handlebars::new();
@@ -623,7 +610,7 @@ pub fn build_wix_app_installer(
       .expect("Failed to setup Update Task Uninstaller handlebars");
     let temp_ps1_path = output_path.join("uninstall-task.ps1");
     let install_script_content = skip_uac_task_uninstaller.render("uninstall-task.ps1", &data)?;
-    write(&temp_ps1_path, install_script_content.clone())?;
+    write(&temp_ps1_path, install_script_content)?;
 
     data.insert("enable_elevated_update_task", to_json(true));
   }
@@ -642,27 +629,90 @@ pub fn build_wix_app_installer(
     run_candle(settings, wix_toolset_path, &output_path, wxs)?;
   }
 
-  let arguments = vec![
-    format!("-cultures:{}", language.to_lowercase()),
-    "*.wixobj".into(),
-  ];
-  let msi_output_path = output_path.join("output.msi");
-  let msi_path = app_installer_dir(settings)?;
-  create_dir_all(msi_path.parent().unwrap())?;
-
-  common::print_info(format!("running light to produce {}", msi_path.display()).as_str())?;
-
-  run_light(
-    wix_toolset_path,
-    &output_path,
-    arguments,
-    &msi_output_path,
-    settings,
-  )?;
-  rename(&msi_output_path, &msi_path)?;
-  try_sign(&msi_path)?;
+  let mut output_paths = Vec::new();
+
+  for (language, language_config) in configured_languages.0 {
+    let language_metadata = language_map.get(&language).unwrap_or_else(|| {
+      panic!(
+        "Language {} not found. It must be one of {}",
+        language,
+        language_map
+          .keys()
+          .cloned()
+          .collect::<Vec<String>>()
+          .join(", ")
+      )
+    });
+
+    let locale_contents = match language_config.locale_path {
+      Some(p) => read_to_string(p)?,
+      None => format!(
+        r#"<WixLocalization Culture="{}" xmlns="http://schemas.microsoft.com/wix/2006/localization"></WixLocalization>"#,
+        language.to_lowercase(),
+      ),
+    };
+
+    let locale_strings = include_str!("./default-locale-strings.xml")
+      .replace("__language__", &language_metadata.lang_id.to_string())
+      .replace("__codepage__", &language_metadata.ascii_code.to_string())
+      .replace("__productName__", settings.product_name());
+
+    let mut unset_locale_strings = String::new();
+    let prefix_len = "<String ".len();
+    for locale_string in locale_strings.split('\n').filter(|s| !s.is_empty()) {
+      // strip `<String ` prefix and `>{value}</String` suffix.
+      let id = locale_string
+        .chars()
+        .skip(prefix_len)
+        .take(locale_string.find('>').unwrap() - prefix_len)
+        .collect::<String>();
+      if !locale_contents.contains(&id) {
+        unset_locale_strings.push_str(locale_string);
+      }
+    }
+
+    let locale_contents = locale_contents.replace(
+      "</WixLocalization>",
+      &format!("{}</WixLocalization>", unset_locale_strings),
+    );
+    let locale_path = output_path.join("locale.wxl");
+    {
+      let mut fileout = File::create(&locale_path).expect("Failed to create locale file");
+      fileout.write_all(locale_contents.as_bytes())?;
+    }
+
+    let arguments = vec![
+      format!(
+        "-cultures:{}",
+        if language == "en-US" {
+          language.to_lowercase()
+        } else {
+          format!("{};en-US", language.to_lowercase())
+        }
+      ),
+      "-loc".into(),
+      locale_path.display().to_string(),
+      "*.wixobj".into(),
+    ];
+    let msi_output_path = output_path.join("output.msi");
+    let msi_path = app_installer_output_path(settings, &language)?;
+    create_dir_all(msi_path.parent().unwrap())?;
+
+    common::print_info(format!("running light to produce {}", msi_path.display()).as_str())?;
+
+    run_light(
+      wix_toolset_path,
+      &output_path,
+      arguments,
+      &msi_output_path,
+      settings,
+    )?;
+    rename(&msi_output_path, &msi_path)?;
+    try_sign(&msi_path)?;
+    output_paths.push(msi_path);
+  }
 
-  Ok(msi_path)
+  Ok(output_paths)
 }
 
 /// Generates the data required for the external binaries and extra binaries bundling.

+ 8 - 9
tooling/bundler/src/bundle/windows/templates/main.wxs

@@ -13,20 +13,19 @@
             Id="*"
             Name="{{{product_name}}}"
             UpgradeCode="{{{upgrade_code}}}"
-            Language="{{language_id}}"
-            Codepage="{{ascii_codepage}}"
+            Language="!(loc.TauriLanguage)"
             Manufacturer="{{{manufacturer}}}"
             Version="{{{version}}}">
 
         <Package Id="*"
                  Keywords="Installer"
                  InstallerVersion="450"
-                 Languages="{{language_id}}"
+                 Languages="0"
                  Compressed="yes"
                  InstallScope="perMachine"
-                 SummaryCodepage="{{ascii_codepage}}"/>
+                 SummaryCodepage="!(loc.TauriCodepage)"/>
 
-        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed."
+        <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeErrorMessage)"
           AllowSameVersionUpgrades="yes" />
 
         <Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
@@ -52,7 +51,7 @@
         </Property>
 
         <!-- launch app checkbox -->
-        <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch {{{product_name}}}" />
+        <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="!(loc.LaunchApp)" />
         <Property Id="WixShellExecTarget" Value="{{{app_exe_source}}}" />
         <CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
 
@@ -158,7 +157,7 @@
 
         {{#each merge_modules as |msm| ~}}
         <DirectoryRef Id="TARGETDIR">
-            <Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="0"/>
+            <Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="!(loc.TauriLanguage)" />
         </DirectoryRef>
 
         <Feature Id="{{ msm.name }}" Title="{{ msm.name }}" AllowAdvertise="no" Display="hidden" Level="1">
@@ -169,7 +168,7 @@
         <Feature
                 Id="MainProgram"
                 Title="Application"
-                Description="Installs {{{product_name}}}."
+                Description="!(loc.InstallAppFeature)"
                 Level="1"
                 ConfigurableDirectory="INSTALLDIR"
                 AllowAdvertise="no"
@@ -200,7 +199,7 @@
             <Feature
                 Id="Environment"
                 Title="PATH Environment Variable"
-                Description="Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location."
+                Description="!(loc.PathEnvVarFeature)"
                 Level="1"
                 Absent="allow">
             <ComponentRef Id="Path"/>

+ 43 - 2
tooling/cli.rs/schema.json

@@ -2132,9 +2132,13 @@
           }
         },
         "language": {
-          "description": "The installer language. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.",
+          "description": "The installer languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.",
           "default": "en-US",
-          "type": "string"
+          "allOf": [
+            {
+              "$ref": "#/definitions/WixLanguage"
+            }
+          ]
         },
         "license": {
           "description": "The path to the license file to render on the installer.\n\nMust be an RTF file, so if a different extension is provided, we convert it to the RTF format.",
@@ -2165,6 +2169,43 @@
         }
       },
       "additionalProperties": false
+    },
+    "WixLanguage": {
+      "description": "The languages to build using WiX.",
+      "anyOf": [
+        {
+          "description": "A single language to build, without configuration.",
+          "type": "string"
+        },
+        {
+          "description": "A list of languages to build, without configuration.",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        {
+          "description": "A map of languages and its configuration.",
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "#/definitions/WixLanguageConfig"
+          }
+        }
+      ]
+    },
+    "WixLanguageConfig": {
+      "description": "Configuration for a target language for the WiX build.",
+      "type": "object",
+      "properties": {
+        "localePath": {
+          "description": "The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.",
+          "type": [
+            "string",
+            "null"
+          ]
+        }
+      },
+      "additionalProperties": false
     }
   }
 }

+ 1 - 1
tooling/cli.rs/src/build.rs

@@ -224,7 +224,7 @@ pub fn command(options: Options) -> Result<()> {
       } else {
         panic!(
           "Unexpected target architecture {}",
-          target.split("_").next().unwrap()
+          target.split('_').next().unwrap()
         )
       };
       let (filename, vcruntime_msm) = if arch == "x86" {

+ 1 - 1
tooling/cli.rs/src/helpers/app_paths.rs

@@ -11,7 +11,7 @@ fn get_tauri_dir() -> PathBuf {
     &current_dir()
       .expect("failed to read cwd")
       .join("**/tauri.conf.json")
-      .to_string_lossy()
+      .to_string_lossy(),
   )
   .unwrap()
   .filter_map(Result::ok)

+ 26 - 9
tooling/cli.rs/src/helpers/config.rs

@@ -9,9 +9,34 @@ use serde_json::Value as JsonValue;
 
 pub use tauri_utils::config::*;
 
+use std::{
+  env::set_var,
+  process::exit,
+  sync::{Arc, Mutex},
+};
+
+pub type ConfigHandle = Arc<Mutex<Option<Config>>>;
+
 pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
   tauri_bundler::WixSettings {
-    language: config.language,
+    language: tauri_bundler::WixLanguage(match config.language {
+      WixLanguage::One(lang) => vec![(lang, Default::default())],
+      WixLanguage::List(languages) => languages
+        .into_iter()
+        .map(|lang| (lang, Default::default()))
+        .collect(),
+      WixLanguage::Localized(languages) => languages
+        .into_iter()
+        .map(|(lang, config)| {
+          (
+            lang,
+            tauri_bundler::WixLanguageConfig {
+              locale_path: config.locale_path.map(Into::into),
+            },
+          )
+        })
+        .collect(),
+    }),
     template: config.template,
     fragment_paths: config.fragment_paths,
     component_group_refs: config.component_group_refs,
@@ -27,14 +52,6 @@ pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
   }
 }
 
-use std::{
-  env::set_var,
-  process::exit,
-  sync::{Arc, Mutex},
-};
-
-pub type ConfigHandle = Arc<Mutex<Option<Config>>>;
-
 fn config_handle() -> &'static ConfigHandle {
   static CONFING_HANDLE: Lazy<ConfigHandle> = Lazy::new(Default::default);
   &CONFING_HANDLE