Browse Source

fix(bundler/nsis): write installer templates UTF16LE encoded, closes #7036 (#7040)

* fix(bundler/nsis): write installer templates UTF16LE encoded, closes #7036

* cleanup

* lint

* return err instead of panic

* Update .changes/nsis-encoding.md [skip ci]

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Amr Bashir 2 years ago
parent
commit
17da87d3cd

+ 5 - 0
.changes/nsis-encoding.md

@@ -0,0 +1,5 @@
+---
+'tauri-bundler': 'patch:bug'
+---
+
+Fix NSIS bundler failing to build when `productName` contained chinsese characters.

+ 0 - 1
tooling/bundler/Cargo.toml

@@ -31,7 +31,6 @@ handlebars = "4.3"
 tempfile = "3.5.0"
 log = { version = "0.4.17", features = [ "kv_unstable" ] }
 dirs-next = "2.0"
-encoding_rs = "0.8"
 os_pipe = "1"
 attohttpc = { version = "0.25", default-features = false }
 hex = "0.4"

+ 44 - 120
tooling/bundler/src/bundle/windows/nsis.rs

@@ -258,16 +258,14 @@ fn build_nsis_app_installer(
     }),
   );
 
-  let languages_data =  languages
-  .iter()
-  .filter_map(|lang| {
-    if let Some(data) = get_lang_data(lang, custom_language_files.as_ref()) {
-      Some(data)
+  let mut languages_data = Vec::new();
+  for lang in &languages {
+    if let Some(data) = get_lang_data(lang, custom_language_files.as_ref())? {
+      languages_data.push(data);
     } else {
       log::warn!("Custom tauri messages for {lang} are not translated.\nIf it is a valid language listed on <https://github.com/kichik/nsis/tree/9465c08046f00ccb6eda985abbdbf52c275c6c4d/Contrib/Language%20files>, please open a Tauri feature request\n or you can provide a custom language file for it in `tauri.conf.json > tauri > bundle > windows > nsis > custom_language_files`");
-      None
     }
-  }).collect::<Vec<_>>();
+  }
 
   data.insert("languages", to_json(languages.clone()));
   data.insert(
@@ -417,19 +415,14 @@ fn build_nsis_app_installer(
       .expect("Failed to setup handlebar template");
   }
   let installer_nsi_path = output_path.join("installer.nsi");
-  write(
+  write_ut16_le_with_bom(
     &installer_nsi_path,
-    encoding_rs::UTF_8
-      .encode(handlebars.render("installer.nsi", &data)?.as_str())
-      .0,
+    handlebars.render("installer.nsi", &data)?.as_str(),
   )?;
 
   for (lang, data) in languages_data.iter() {
-    if let Some((content, encoding)) = data {
-      write(
-        output_path.join(lang).with_extension("nsh"),
-        encoding.encode(content).0,
-      )?;
+    if let Some(content) = data {
+      write_ut16_le_with_bom(output_path.join(lang).with_extension("nsh"), content)?;
     }
   }
 
@@ -553,113 +546,44 @@ fn generate_binaries_data(settings: &Settings) -> crate::Result<BinariesMap> {
 fn get_lang_data(
   lang: &str,
   custom_lang_files: Option<&HashMap<String, PathBuf>>,
-) -> Option<(
-  String,
-  Option<(&'static str, &'static encoding_rs::Encoding)>,
-)> {
-  use encoding_rs::*;
-
+) -> crate::Result<Option<(PathBuf, Option<&'static str>)>> {
   if let Some(path) = custom_lang_files.and_then(|h| h.get(lang)) {
-    return Some((
-      dunce::canonicalize(path)
-        .unwrap()
-        .to_string_lossy()
-        .to_string(),
-      None,
-    ));
+    return Ok(Some((dunce::canonicalize(path)?, None)));
   }
 
-  let lang_file = format!("{lang}.nsh");
-  match lang.to_lowercase().as_str() {
-    "arabic" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Arabic.nsh"),
-        UTF_16LE,
-      )),
-    )),
-    "dutch" => Some((
-      lang_file,
-      Some((include_str!("./templates/nsis-languages/Dutch.nsh"), UTF_8)),
-    )),
-    "english" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/English.nsh"),
-        UTF_8,
-      )),
-    )),
-    "japanese" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Japanese.nsh"),
-        UTF_8,
-      )),
-    )),
-    "korean" => Some((
-      lang_file,
-      Some((include_str!("./templates/nsis-languages/Korean.nsh"), UTF_8)),
-    )),
-    "portuguesebr" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/PortugueseBR.nsh"),
-        UTF_8,
-      )),
-    )),
-    "tradchinese" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/TradChinese.nsh"),
-        UTF_8,
-      )),
-    )),
-    "simpchinese" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/SimpChinese.nsh"),
-        UTF_8,
-      )),
-    )),
-    "french" => Some((
-      lang_file,
-      Some((include_str!("./templates/nsis-languages/French.nsh"), UTF_8)),
+  let lang_path = PathBuf::from(format!("{lang}.nsh"));
+  let lang_content = match lang.to_lowercase().as_str() {
+    "arabic" => Some(include_str!("./templates/nsis-languages/Arabic.nsh")),
+    "dutch" => Some(include_str!("./templates/nsis-languages/Dutch.nsh")),
+    "english" => Some(include_str!("./templates/nsis-languages/English.nsh")),
+    "japanese" => Some(include_str!("./templates/nsis-languages/Japanese.nsh")),
+    "korean" => Some(include_str!("./templates/nsis-languages/Korean.nsh")),
+    "portuguesebr" => Some(include_str!("./templates/nsis-languages/PortugueseBR.nsh")),
+    "tradchinese" => Some(include_str!("./templates/nsis-languages/TradChinese.nsh")),
+    "simpchinese" => Some(include_str!("./templates/nsis-languages/SimpChinese.nsh")),
+    "french" => Some(include_str!("./templates/nsis-languages/French.nsh")),
+    "spanish" => Some(include_str!("./templates/nsis-languages/Spanish.nsh")),
+    "spanishinternational" => Some(include_str!(
+      "./templates/nsis-languages/SpanishInternational.nsh"
     )),
-    "spanish" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Spanish.nsh"),
-        UTF_8,
-      )),
-    )),
-    "spanishinternational" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/SpanishInternational.nsh"),
-        UTF_8,
-      )),
-    )),
-    "persian" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Persian.nsh"),
-        UTF_16LE,
-      )),
-    )),
-    "turkish" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Turkish.nsh"),
-        UTF_8,
-      )),
-    )),
-    "swedish" => Some((
-      lang_file,
-      Some((
-        include_str!("./templates/nsis-languages/Swedish.nsh"),
-        UTF_8,
-      )),
-    )),
-    _ => None,
+    "persian" => Some(include_str!("./templates/nsis-languages/Persian.nsh")),
+    "turkish" => Some(include_str!("./templates/nsis-languages/Turkish.nsh")),
+    "swedish" => Some(include_str!("./templates/nsis-languages/Swedish.nsh")),
+    _ => return Ok(None),
+  };
+
+  Ok(Some((lang_path, lang_content)))
+}
+
+fn write_ut16_le_with_bom<P: AsRef<Path>>(path: P, content: &str) -> crate::Result<()> {
+  use std::fs::File;
+  use std::io::{BufWriter, Write};
+
+  let file = File::create(path)?;
+  let mut output = BufWriter::new(file);
+  output.write_all(&[0xFF, 0xFE])?; // the BOM part
+  for utf16 in content.encode_utf16() {
+    output.write_all(&utf16.to_le_bytes())?;
   }
+  Ok(())
 }

+ 1 - 1
tooling/bundler/src/bundle/windows/templates/nsis-languages/Persian.nsh

@@ -24,4 +24,4 @@ LangString webview2DownloadSuccess ${LANG_PERSIAN} "WebView2 بوت استرپر
 LangString webview2Downloading ${LANG_PERSIAN} "دانلود بوت استرپر WebView2..."
 LangString webview2InstallError ${LANG_PERSIAN} "ارور: نصب WebView2 با کد $1 شکست خورد"
 LangString webview2InstallSuccess ${LANG_PERSIAN} "WebView2 با موفقیت نصب شد"
-LangString deleteAppData ${LANG_PERSIAN} "حذف دیتا های اپلیکیشن"
+LangString deleteAppData ${LANG_PERSIAN} "حذف دیتا های اپلیکیشن"

+ 37 - 7
tooling/cli/Cargo.lock

@@ -163,17 +163,17 @@ dependencies = [
 
 [[package]]
 name = "attohttpc"
-version = "0.24.1"
+version = "0.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
+checksum = "7e57d6e7a84f33ff3316e97af3180fe7f86597a6a60161c0be70c0e45f382620"
 dependencies = [
  "http",
  "log",
  "native-tls",
- "rustls",
+ "rustls 0.21.1",
  "url",
  "webpki",
- "webpki-roots",
+ "webpki-roots 0.23.0",
 ]
 
 [[package]]
@@ -2764,6 +2764,28 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "rustls"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki",
+ "sct",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.100.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.13"
@@ -3253,7 +3275,6 @@ dependencies = [
  "bitness",
  "dirs-next",
  "dunce",
- "encoding_rs",
  "glob",
  "handlebars",
  "heck",
@@ -3762,10 +3783,10 @@ dependencies = [
  "flate2",
  "log",
  "once_cell",
- "rustls",
+ "rustls 0.20.8",
  "url",
  "webpki",
- "webpki-roots",
+ "webpki-roots 0.22.6",
 ]
 
 [[package]]
@@ -3952,6 +3973,15 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "webpki-roots"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125"
+dependencies = [
+ "rustls-webpki",
+]
+
 [[package]]
 name = "weezl"
 version = "0.1.7"