[setup - the official Cygwin setup program] branch master, updated. release_2.911

Jon TURNEY via Cygwin-apps-cvs <[email protected]>
Newsgroups gmane.os.cygwin.cvs.apps
Message-ID <[email protected]>


https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=b63da9d41520afe3cf380630ed41056ed4416adc

commit b63da9d41520afe3cf380630ed41056ed4416adc
Author: Jon Turney <[email protected]>
Date:   Sat Dec 4 14:25:09 2021 +0000

    Push some dynamic dialog text into IDD_FILE_INUSE resource
    
    Can't use that approach for the label for the IDRETRY button (since it
    needs a fixed ID), so move that to string resources.
    
    Also make text placed in IDC_FILE_INUSE_MSG localizable.

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=dfd97f99febea198819697c7cb73e4e1c8140b05

commit dfd97f99febea198819697c7cb73e4e1c8140b05
Author: Jon Turney <[email protected]>
Date:   Sat Dec 4 14:04:39 2021 +0000

    Move download progress message to string resources

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=6bcfadf121e02d64503b168b899c9e6e0bba4d5c

commit 6bcfadf121e02d64503b168b899c9e6e0bba4d5c
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 23:17:09 2021 +0000

    Move confirm report strings to string resources

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=c2f99cb1bcc85613df3b7183a8400030ae78c5f3

commit c2f99cb1bcc85613df3b7183a8400030ae78c5f3
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 22:06:50 2021 +0000

    Move hash checking progress message to string resources

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=c156d59f49b79a72a3be15714e5245faf2656c3b

commit c156d59f49b79a72a3be15714e5245faf2656c3b
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 21:25:31 2021 +0000

    Use string resource for progress windows title

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=7409282c51cb071f4b4ed1dcfccc62dab3ec773c

commit 7409282c51cb071f4b4ed1dcfccc62dab3ec773c
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 20:25:15 2021 +0000

    Move local package directory browser title to string resources
    
    Also improve LoadStringW to handle a missing translation by falling back
    to untranslated string.

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=195864cef702b8c32da987ae934682343b27c4d9

commit 195864cef702b8c32da987ae934682343b27c4d9
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 20:07:13 2021 +0000

    Move root directory browser title to string resources
    
    Note: IFileDialog now recommended over SHBrowseForFolder.
    
    There's something a little wonky here: The browser is shown by
    OnMessageCmd(), so a simple click on the unfocused button isn't enough?

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=7f85e28044a2de00021f00672d1c13bd34615d65

commit 7f85e28044a2de00021f00672d1c13bd34615d65
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 18:26:53 2021 +0000

    Move localizable 'empty chooser' text to string resources

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=b08f9c71f6df2d643d67608c5eba3454b26d5a5d

commit b08f9c71f6df2d643d67608c5eba3454b26d5a5d
Author: Jon Turney <[email protected]>
Date:   Fri Dec 3 18:07:51 2021 +0000

    Don't localize logged 'exit message' set via setExitMsg()

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=477a6ff52ec52e558188033d9f3128ac6ef7ef27

commit 477a6ff52ec52e558188033d9f3128ac6ef7ef27
Author: Jon Turney <[email protected]>
Date:   Thu Sep 10 17:31:56 2020 +0100

    Don't localize mbox strings written to log
    
    Add LoadStringWEx to access string resource for a specifc locale.
    Use that in mbox when formatting string for log.

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=15befbf1b0047258896c243a22d3395cd0146a9d

commit 15befbf1b0047258896c243a22d3395cd0146a9d
Author: Jon Turney <[email protected]>
Date:   Thu Dec 2 22:28:32 2021 +0000

    Push some dynamic download/install dialog text into IDD_LOCAL_DIR DIALOG resource
    
    Move the localizable dialog text which is dynamically modified depending
    on install or download mode into the IDD_LOCAL_DIR DIALOG resource, and
    show or hide it as appropriate.


Diff:
---
 ListView.cc       | 10 +++-------
 ListView.h        |  4 ++--
 LogFile.cc        | 13 +++++--------
 String++.cc       | 10 ++++++++++
 String++.h        |  1 +
 choose.cc         |  7 ++++---
 confirm.cc        | 56 ++++++++++++++++++++++++++++++++-----------------------
 desktop.cc        | 10 +++-------
 dialog.cc         |  6 ++++++
 dialog.h          |  1 +
 geturl.cc         |  7 +++++--
 install.cc        | 29 ++++++++++++++--------------
 localdir.cc       | 29 ++++++++++++++++------------
 msg.cc            | 12 ++++++++++--
 package_source.cc |  8 ++++++--
 res.rc            | 41 ++++++++++++++++++++++++++++++++--------
 res/fr/res.rc     | 45 ++++++++++++++++++++++++++++++++++----------
 resource.h        | 25 +++++++++++++++++++++----
 root.cc           | 18 +++++++++++-------
 threebar.cc       | 16 ++++++++++++----
 threebar.h        |  3 ++-
 win32.cc          | 35 +++++++++++++++++++++++++++++++++-
 win32.h           |  2 ++
 window.cc         |  4 ++--
 window.h          |  2 +-
 25 files changed, 274 insertions(+), 120 deletions(-)

diff --git a/ListView.cc b/ListView.cc
index dc420e2c..62a37ab1 100644
--- a/ListView.cc
+++ b/ListView.cc
@@ -345,11 +345,7 @@ ListView::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
   case LVN_GETEMPTYMARKUP:
     {
       NMLVEMPTYMARKUP *pNmMarkup = (NMLVEMPTYMARKUP*) pNmHdr;
-
-      MultiByteToWideChar(CP_UTF8, 0,
-                          empty_list_text, -1,
-                          pNmMarkup->szMarkup, L_MAX_URL_LENGTH);
-
+      wcsncpy(pNmMarkup->szMarkup, empty_list_text.c_str(), L_MAX_URL_LENGTH);
       *pResult = true;
       return true;
     }
@@ -645,9 +641,9 @@ ListView::empty(void)
 }
 
 void
-ListView::setEmptyText(const char *text)
+ListView::setEmptyText(unsigned int text)
 {
-  empty_list_text = text;
+  empty_list_text = LoadStringW(text);
 }
 
 int
diff --git a/ListView.h b/ListView.h
index 8c43fcc7..95dd9ee6 100644
--- a/ListView.h
+++ b/ListView.h
@@ -73,7 +73,7 @@ class ListView
   void resizeColumns(void);
 
   void setContents(ListViewContents *contents, bool tree = false);
-  void setEmptyText(const char *text);
+  void setEmptyText(unsigned int text);
 
   bool OnNotify (NMHDR *pNmHdr, LRESULT *pResult);
 
@@ -87,7 +87,7 @@ class ListView
 
   ListViewContents *contents;
   HeaderList headers;
-  const char *empty_list_text;
+  std::wstring empty_list_text;
   int iRow_track;
   int iCol_track;
 
diff --git a/LogFile.cc b/LogFile.cc
index 0bcf6073..0a83159f 100644
--- a/LogFile.cc
+++ b/LogFile.cc
@@ -125,17 +125,14 @@ LogFile::exit (int exit_code, bool show_end_install_msg)
   if (been_here)
     ::exit (exit_code);
   been_here = 1;
-  
+
   if (exit_msg)
     {
-      char buf[1000], fmt[1000];
-      if (LoadString (hinstance, exit_msg, fmt, sizeof (fmt)) > 0)
-        {
-          snprintf (buf, 1000, fmt, backslash(getFileName(LOG_BABBLE)).c_str());
-          Log (LOG_PLAIN) << "note: " << buf << endLog;
-        }
+      std::wstring fmt = LoadStringWEx(exit_msg, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
+      std::wstring buf = format(fmt, backslash(getFileName(LOG_BABBLE)).c_str());
+      Log (LOG_PLAIN) << "note: " << wstring_to_string(buf) << endLog;
     }
-  
+
   /* ... in that it skips the boring log messages.  Exit code -1 is used when
      just printing the help output and when we're self-elevating. */
   if (show_end_install_msg)
diff --git a/String++.cc b/String++.cc
index de2b48f9..a5649759 100644
--- a/String++.cc
+++ b/String++.cc
@@ -152,3 +152,13 @@ vformat(const std::wstring &fmt, va_list ap)
 
   return str;
 }
+
+std::wstring
+format(const std::wstring &fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  std::wstring res = vformat(fmt, ap);
+  va_end(ap);
+  return res;
+}
diff --git a/String++.h b/String++.h
index 0851f4e8..af32611c 100644
--- a/String++.h
+++ b/String++.h
@@ -47,5 +47,6 @@ std::string wstring_to_string(const std::wstring &w);
 
 // produce a std::string using formatting like sprintf
 std::wstring vformat(const std::wstring &fmt, va_list ap);
+std::wstring format(const std::wstring &fmt, ...);
 
 #endif /* SETUP_STRING___H */
diff --git a/choose.cc b/choose.cc
index cc3fa07c..1b18bbd0 100644
--- a/choose.cc
+++ b/choose.cc
@@ -335,11 +335,12 @@ ChooserPage::OnActivate()
 
   packagedb::categoriesType::iterator it = db.categories.find("All");
   if (it == db.categories.end ())
-    listview->setEmptyText("No packages found.");
+    listview->setEmptyText(IDS_CHOOSER_EMPTY_NO_PACKAGES);
+
   if (source == IDC_SOURCE_DOWNLOAD)
-    listview->setEmptyText("Nothing to download.");
+    listview->setEmptyText(IDS_CHOOSER_EMPTY_DOWNLOAD);
   else
-    listview->setEmptyText("Nothing to install or update.");
+    listview->setEmptyText(IDS_CHOOSER_EMPTY_INSTALL);
 
   chooser->build_category_tree();
   chooser->init_headers();
diff --git a/confirm.cc b/confirm.cc
index d0a84206..55ab1c13 100644
--- a/confirm.cc
+++ b/confirm.cc
@@ -55,7 +55,7 @@ void
 ConfirmPage::OnActivate()
 {
   // generate a report on actions we're going to take
-  std::string s = "";
+  std::wstring s = L"";
 
   packagedb db;
   const SolverTransactionList & trans = db.solution.transactions ();
@@ -63,34 +63,36 @@ ConfirmPage::OnActivate()
   // first list things we will erase
   if (source != IDC_SOURCE_DOWNLOAD)
     {
-      std::vector<std::string> erase;
+      std::vector<std::wstring> erase;
       for (SolverTransactionList::const_iterator i = trans.begin ();
            i != trans.end (); i++)
         {
           if (i->type == SolverTransaction::transErase)
             {
-              std::string line;
               packageversion pv = i->version;
               packagemeta *pkg = db.findBinary (PackageSpecification (pv.Name ()));
 
-              line += "Uninstall ";
-              line += i->version.Name();
-              line += " ";
-              line += i->version.Canonical_version();
+              std::wstring action = LoadStringW(IDS_CONFIRM_UNINSTALL);
+              std::wstring line = format(L"%ls %s %s", action.c_str(),
+                                         i->version.Name().c_str(),
+                                         i->version.Canonical_version().c_str());
               if (pkg && pkg->desired)
-                line += " (automatically added)";
-              line += "\r\n";
+                {
+                  line += L" ";
+                  line += LoadStringW(IDS_CONFIRM_AUTO_ADD);
+                }
+              line += L"\r\n";
               erase.push_back (line);
             }
         }
       sort (erase.begin(), erase.end());
-      for (std::vector<std::string>::const_iterator i = erase.begin ();
+      for (std::vector<std::wstring>::const_iterator i = erase.begin ();
            i != erase.end(); i++)
         s += *i;
     }
 
   // then list things downloaded or installed
-  std::vector<std::string> install;
+  std::vector<std::wstring> install;
   for (SolverTransactionList::const_iterator i = trans.begin ();
        i != trans.end (); i++)
     {
@@ -99,32 +101,40 @@ ConfirmPage::OnActivate()
 
       if (i->type == SolverTransaction::transInstall)
           {
-            std::string line;
+            std::wstring action;
             if (source != IDC_SOURCE_DOWNLOAD)
-              line += "Install ";
+              action = LoadStringW(IDS_CONFIRM_INSTALL);
             else
-              line += "Download ";
-            line += i->version.Name();
-            line += " ";
-            line += i->version.Canonical_version();
+              action = LoadStringW(IDS_CONFIRM_DOWNLOAD);
+
+            std::wstring line = format(L"%ls %s %s", action.c_str(),
+                                       i->version.Name().c_str(),
+                                       i->version.Canonical_version().c_str());
+
             if (i->version.Type() == package_source)
-              line += " (source)";
+              {
+                line += L" ";
+                line += LoadStringW(IDS_CONFIRM_SOURCE);
+              }
             else if (pkg && pkg->desired != pv)
-              line += " (automatically added)";
-            line += "\r\n";
+              {
+                line += L" ";
+                line += LoadStringW(IDS_CONFIRM_AUTO_ADD);
+              }
+            line += L"\r\n";
             install.push_back (line);
           }
     }
   sort (install.begin(), install.end());
-  for (std::vector<std::string>::const_iterator i = install.begin ();
+  for (std::vector<std::wstring>::const_iterator i = install.begin ();
        i != install.end(); i++)
     s += *i;
 
   // be explicit about doing nothing
   if (s.empty())
-    s += "No changes";
+    s += LoadStringW(IDS_CONFIRM_NOTHING);
 
-  SetDlgItemText (GetHWND (), IDC_CONFIRM_EDIT, s.c_str ());
+  SetDlgItemTextW (GetHWND (), IDC_CONFIRM_EDIT, s.c_str ());
 
   // move focus to 'next' button, so enter doesn't get eaten by edit control
   HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
diff --git a/desktop.cc b/desktop.cc
index 4f692b8a..aa1f9019 100644
--- a/desktop.cc
+++ b/desktop.cc
@@ -236,13 +236,9 @@ check_if_enable_next (HWND h)
 static void
 set_status (HWND h)
 {
-  char buf[1000], fmt[1000];
-  if (LoadString (hinstance, Logger ().getExitMsg (), fmt, sizeof (fmt)) > 0)
-    {
-      snprintf (buf, 1000, fmt,
-      		backslash (Logger ().getFileName (LOG_BABBLE)).c_str ());
-      eset (h, IDC_STATUS, buf);
-    }
+  std::wstring fmt = LoadStringW(Logger ().getExitMsg ());
+  std::wstring buf = format(fmt, backslash (Logger ().getFileName (LOG_BABBLE)).c_str ());
+  eset (h, IDC_STATUS, buf);
 }
 
 static void
diff --git a/dialog.cc b/dialog.cc
index dd12fc44..2ca57df8 100644
--- a/dialog.cc
+++ b/dialog.cc
@@ -71,6 +71,12 @@ eset (HWND h, int id, const std::string aString)
   SetDlgItemText (h, id, aString.c_str());
 }
 
+void
+eset (HWND h, int id, const std::wstring &aString)
+{
+  SetDlgItemTextW (h, id, aString.c_str());
+}
+
 void
 eset (HWND h, int id, int val)
 {
diff --git a/dialog.h b/dialog.h
index 47212e3e..63c98ee6 100644
--- a/dialog.h
+++ b/dialog.h
@@ -59,6 +59,7 @@ int eget (HWND h, int id);
 
 void eset (HWND h, int id, const char *var);
 void eset (HWND h, int id, const std::string);
+void eset (HWND h, int id, const std::wstring &);
 void eset (HWND h, int id, int var);
 
 /* RadioButtons.  ids is a null-terminated list of IDs.  Get
diff --git a/geturl.cc b/geturl.cc
index 321259e8..e82cb16b 100644
--- a/geturl.cc
+++ b/geturl.cc
@@ -63,8 +63,11 @@ init_dialog (const std::string &url, int length)
   std::string::size_type divide = url.find_last_of('/');
   max_bytes = length;
   Progress.SetText1(IDS_PROGRESS_DOWNLOADING);
-  Progress.SetText2((url.substr(divide + 1) + " from "
-                     + url.substr(0, divide)).c_str());
+  std::wstring fmt = LoadStringW(IDS_PROGRESS_DOWNLOADING_FROM);
+  std::wstring s = format(fmt,
+                          url.substr(divide + 1).c_str(),
+                          url.substr(0, divide).c_str());
+  Progress.SetText2(s.c_str());
   Progress.SetText3(IDS_PROGRESS_CONNECTING);
   Progress.SetBar1(0);
   start_tics = GetTickCount ();
diff --git a/install.cc b/install.cc
index 9bb08203..beb15c74 100644
--- a/install.cc
+++ b/install.cc
@@ -268,7 +268,7 @@ Installer::replaceOnRebootSucceeded (const std::string& fn, bool &rebootneeded)
 
 typedef struct
 {
-  const char *msg;
+  const wchar_t *msg;
   const char *processlist;
   int iteration;
 } FileInuseDlgData;
@@ -282,29 +282,30 @@ FileInuseDlgProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
       {
         FileInuseDlgData *dlg_data = (FileInuseDlgData *)lParam;
 
-        SetDlgItemText (hwndDlg, IDC_FILE_INUSE_MSG, dlg_data->msg);
+        SetDlgItemTextW (hwndDlg, IDC_FILE_INUSE_MSG, dlg_data->msg);
         SetDlgItemText (hwndDlg, IDC_FILE_INUSE_EDIT, dlg_data->processlist);
 
         switch (dlg_data->iteration)
           {
           case 0:
-            break; // show the dialog the way it is in the resource
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_SHOW);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_HIDE);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_HIDE);
+            break;
 
           case 1:
-            SetDlgItemText (hwndDlg, IDRETRY, "&Kill Processes");
-            SetDlgItemText (hwndDlg, IDC_FILE_INUSE_HELP,
-                            "Select 'Retry' to retry, "
-                            "Select 'Kill' to kill processes and retry, or "
-                            "select 'Continue' to go on anyway (the file will be updated after a reboot).");
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_HIDE);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_SHOW);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_HIDE);
+            SetDlgItemTextW (hwndDlg, IDRETRY, LoadStringW(IDS_FILE_INUSE_KILL).c_str());
             break;
 
           default:
           case 2:
-            SetDlgItemText (hwndDlg, IDRETRY, "&Kill Processes");
-            SetDlgItemText (hwndDlg, IDC_FILE_INUSE_HELP,
-                            "Select 'Retry' to retry, "
-                            "select 'Kill' to forcibly kill all processes and retry, or "
-                            "select 'Continue' to go on anyway (the file will be updated after a reboot).");
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_HIDE);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_HIDE);
+            ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_SHOW);
+            SetDlgItemTextW (hwndDlg, IDRETRY, LoadStringW(IDS_FILE_INUSE_KILL).c_str());
           }
       }
       return TRUE; // automatically set focus, please
@@ -608,7 +609,7 @@ Installer::_installOne (packagemeta &pkgm,
                             // listed processes, or just ignore the problem and schedule the file to be
                             // replaced after a reboot
                             FileInuseDlgData dlg_data;
-                            std::string msg = "Unable to extract /" + fn;
+                            std::wstring msg = LoadStringW(IDS_FILE_INUSE_MSG) + L" /" + string_to_wstring(fn);
                             dlg_data.msg = msg.c_str ();
                             dlg_data.processlist = plm.c_str ();
                             dlg_data.iteration = iteration;
diff --git a/localdir.cc b/localdir.cc
index 7a01e117..7130a17c 100644
--- a/localdir.cc
+++ b/localdir.cc
@@ -94,16 +94,16 @@ check_if_enable_next (HWND h)
 static void
 load_dialog (HWND h)
 {
-  char descText[1000];
   if (source != IDC_SOURCE_LOCALDIR)
     {
-      LoadString (hinstance, IDS_LOCAL_DIR_DOWNLOAD, descText, sizeof (descText));
+      ShowWindow (GetDlgItem(h, IDC_LOCAL_DIR_DOWNLOAD_DESC), SW_SHOW);
+      ShowWindow (GetDlgItem(h, IDC_LOCAL_DIR_INSTALL_DESC), SW_HIDE);
     }
   else
     {
-      LoadString (hinstance, IDS_LOCAL_DIR_INSTALL, descText, sizeof (descText));
+      ShowWindow (GetDlgItem(h, IDC_LOCAL_DIR_DOWNLOAD_DESC), SW_HIDE);
+      ShowWindow (GetDlgItem(h, IDC_LOCAL_DIR_INSTALL_DESC), SW_SHOW);
     }
-  eset (h, IDC_LOCAL_DIR_DESC, descText);
   eset (h, IDC_LOCAL_DIR, local_dir);
   check_if_enable_next (h);
 }
@@ -176,22 +176,27 @@ browse_cb (HWND h, UINT msg, LPARAM lp, LPARAM data)
 static void
 browse (HWND h)
 {
-  BROWSEINFO bi;
-  /* SHGetPathFromIDList doesn't handle path length > MAX_PATH. */
-  CHAR name[MAX_PATH];
-  LPITEMIDLIST pidl;
+  std::wstring title = LoadStringW((source != IDC_SOURCE_LOCALDIR) ?
+                                   IDS_LOCALDIR_BROWSE_DOWNLOAD_TITLE :
+                                   IDS_LOCALDIR_BROWSE_PACKAGE_TITLE);
+
+  wchar_t wname[MAX_PATH];
+  BROWSEINFOW bi;
   memset (&bi, 0, sizeof (bi));
   bi.hwndOwner = h;
-  bi.pszDisplayName = name;
-  bi.lpszTitle = (source != IDC_SOURCE_LOCALDIR) ? "Select download directory"
-					    : "Select local package directory";
+  bi.pszDisplayName = wname;
+  bi.lpszTitle = title.c_str();
   bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE
 	      | ((source != IDC_SOURCE_LOCALDIR) ? (BIF_EDITBOX | BIF_VALIDATE)
 						 : 0);
   bi.lpfn = browse_cb;
-  pidl = SHBrowseForFolder (&bi);
+
+  /* SHGetPathFromIDList doesn't handle path length > MAX_PATH. */
+  LPITEMIDLIST pidl;
+  pidl = SHBrowseForFolderW (&bi);
   if (pidl)
     {
+      CHAR name[MAX_PATH];
       if (SHGetPathFromIDList (pidl, name))
 	eset (h, IDC_LOCAL_DIR, name);
     }
diff --git a/msg.cc b/msg.cc
index 5badcc3c..00eaf5aa 100644
--- a/msg.cc
+++ b/msg.cc
@@ -129,7 +129,7 @@ LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
 int
 mbox(HWND owner, unsigned int format_id, int mb_type, ...)
 {
-  std::wstring fmt = LoadStringW(format_id);
+  std::wstring fmt = LoadStringWEx(format_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
   if (fmt.empty())
     fmt = L"Internal error: format string resource not found";
 
@@ -138,12 +138,20 @@ mbox(HWND owner, unsigned int format_id, int mb_type, ...)
   std::wstring buf = vformat(fmt, args);
   va_end(args);
 
-  // write to log as UTF8
+  // write unlocalized to log as UTF8
   Log (LOG_PLAIN) << "mbox " << ": " << wstring_to_string(buf) << endLog;
 
   if (unattended_mode)
     return unattended_result(mb_type);
 
+  fmt = LoadStringW(format_id);
+  if (fmt.empty())
+    fmt = L"Internal error: format string resource not found";
+
+  va_start(args, mb_type);
+  buf = vformat(fmt, args);
+  va_end(args);
+
   bool retry_continue = (mb_type & MB_TYPEMASK) == MB_RETRYCONTINUE;
   if (retry_continue) {
     mb_type &= ~MB_TYPEMASK;
diff --git a/package_source.cc b/package_source.cc
index 9ae08cb7..2b5909bf 100644
--- a/package_source.cc
+++ b/package_source.cc
@@ -113,7 +113,9 @@ packagesource::check_sha512 (const std::string fullname) const
 
   Log (LOG_BABBLE) << "Checking SHA512 for " << fullname << endLog;
 
-  Progress.SetText1 (("Checking SHA512 for " + shortname).c_str ());
+  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
+  std::wstring s = format(fmt, "SHA512", shortname.c_str());
+  Progress.SetText1 (s.c_str());
   Progress.SetText4 (IDS_PROGRESS_PROGRESS);
   Progress.SetBar1 (0);
 
@@ -162,7 +164,9 @@ packagesource::check_md5 (const std::string fullname) const
 
   Log (LOG_BABBLE) << "Checking MD5 for " << fullname << endLog;
 
-  Progress.SetText1 (("Checking MD5 for " + shortname).c_str ());
+  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
+  std::wstring s = format(fmt, "MD5", shortname);
+  Progress.SetText1 (s.c_str());
   Progress.SetText4 (IDS_PROGRESS_PROGRESS);
   Progress.SetBar1 (0);
 
diff --git a/res.rc b/res.rc
index febf357a..83d83393 100644
--- a/res.rc
+++ b/res.rc
@@ -90,7 +90,13 @@ FONT 8, "MS Shell Dlg"
 BEGIN
     LTEXT           "Select Local Package Directory",IDC_STATIC_HEADER_TITLE,
                     7,0,258,8,NOT WS_GROUP
-    LTEXT           "",IDC_LOCAL_DIR_DESC,21,9,248,16,NOT WS_GROUP
+    LTEXT           "Select a directory where you want Setup to store "
+                    "the installation files it downloads.  The directory will be "
+                    "created if it does not already exist.",
+                    IDC_LOCAL_DIR_DOWNLOAD_DESC,21,9,248,16,NOT WS_GROUP
+    LTEXT           "Select a directory where Setup should look for "
+                    "downloaded installation files.",
+                    IDC_LOCAL_DIR_INSTALL_DESC,21,9,248,16,NOT WS_GROUP
     ICON            IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
     CONTROL         "",IDC_HEADSEPARATOR,"Static",SS_BLACKFRAME | SS_SUNKEN,0,28,
                     SETUP_STANDARD_DIALOG_W,1
@@ -493,7 +499,7 @@ CAPTION "In-use file detected"
 FONT 8, "MS Shell Dlg"
 BEGIN
     ICON            IDI_WARNING,IDC_HEADICON,10,10
-    LTEXT           "Unable to extract %s",
+    LTEXT           "Unable to extract",
                     IDC_FILE_INUSE_MSG,33,10,234,8,SS_PATHELLIPSIS
     LTEXT           "The file is in use by the following processes:",
                     IDC_STATIC,33,28,234,8
@@ -503,7 +509,15 @@ BEGIN
     LTEXT           "Select 'Retry' to retry, "
                     "select 'Stop' to stop processes and retry, or "
                     "select 'Continue' to go on anyway (the file will be updated after a reboot).",
-                    IDC_FILE_INUSE_HELP,33,80,234,24,NOT WS_GROUP
+                    IDC_FILE_INUSE_HELP_0,33,80,234,24,NOT WS_GROUP
+    LTEXT           "Select 'Retry' to retry, "
+                    "select 'Kill' to kill processes and retry, or "
+                    "select 'Continue' to go on anyway (the file will be updated after a reboot).",
+                    IDC_FILE_INUSE_HELP_1,33,80,234,24,NOT WS_GROUP
+    LTEXT           "Select 'Retry' to retry, "
+                    "select 'Kill' to forcibly kill all processes and retry, or "
+                    "select 'Continue' to go on anyway (the file will be updated after a reboot).",
+                    IDC_FILE_INUSE_HELP_2,33,80,234,24,NOT WS_GROUP
     PUSHBUTTON      "&Retry",IDIGNORE,45,112,55,15
     DEFPUSHBUTTON   "&Stop Processes",IDRETRY,111,112,55,15
     PUSHBUTTON      "&Continue",IDCONTINUE,177,112,55,15
@@ -603,11 +617,6 @@ BEGIN
     IDS_SIG_INVALID    "Mirror Error:  Setup.ini signature %s from %s failed to verify.\nPossible corrupt mirror?  Setup.ini rejected."
     IDS_CRYPTO_ERROR   "Internal Error:  gcrypt library error %d %s"
     IDS_SEARCH_TOOLTIP "Search for this string in package names."
-    IDS_LOCAL_DIR_DOWNLOAD "Select a directory where you want Setup to store "
-       "the installation files it downloads.  The directory will be "
-       "created if it does not already exist."
-    IDS_LOCAL_DIR_INSTALL "Select a directory where Setup should look for "
-       "downloaded installation files."
     IDS_MAYBE_MKDIR    "Directory %s does not exist, would you like me to create it?"
     IDS_CANT_MKDIR     "Couldn't create directory %s, sorry.  (Is drive full or read-only?)"
     IDS_NO_LOCALDIR    "Local package directory %s not found.\nYou can still remove installed\npackages, but there "
@@ -688,6 +697,22 @@ BEGIN
           "Make sure your network settings are correct and try again."
     IDS_CONFIRM_EXIT "Are you sure you want to exit setup? Any current download or installation will be aborted."
     IDS_CONTINUE "Continue"
+    IDS_CHOOSER_EMPTY_NO_PACKAGES "No packages found."
+    IDS_CHOOSER_EMPTY_DOWNLOAD "Nothing to download."
+    IDS_CHOOSER_EMPTY_INSTALL "Nothing to install or update."
+    IDS_ROOT_BROWSE_TITLE "Select an installation root directory"
+    IDS_LOCALDIR_BROWSE_DOWNLOAD_TITLE "Select download directory"
+    IDS_LOCALDIR_BROWSE_PACKAGE_TITLE "Select local package directory"
+    IDS_PROGRESS_CHECKING_HASH "Checking %s for %s"
+    IDS_PROGRESS_DOWNLOADING_FROM "%s from %s"
+    IDS_CONFIRM_UNINSTALL "Uninstall"
+    IDS_CONFIRM_INSTALL "Install"
+    IDS_CONFIRM_DOWNLOAD "Download"
+    IDS_CONFIRM_NOTHING "No changes"
+    IDS_CONFIRM_AUTO_ADD "(automatically added)"
+    IDS_CONFIRM_SOURCE "(source)"
+    IDS_FILE_INUSE_KILL "&Kill Processes"
+    IDS_FILE_INUSE_MSG "Unable to extract"
 END
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/res/fr/res.rc b/res/fr/res.rc
index 0b365e2f..0b4fad15 100644
--- a/res/fr/res.rc
+++ b/res/fr/res.rc
@@ -64,7 +64,12 @@ FONT 8, "MS Shell Dlg"
 BEGIN
     LTEXT           "Sélection du dossier local des paquets",IDC_STATIC_HEADER_TITLE,
                     7,0,258,8,NOT WS_GROUP
-    LTEXT           "",IDC_LOCAL_DIR_DESC,21,9,248,16,NOT WS_GROUP
+    LTEXT           "Choisir un dossier pour enregistrer les fichiers "
+                    "téléchargés. Ce dossier sera créé s'il n'existe pas.",
+                    IDC_LOCAL_DIR_DOWNLOAD_DESC,21,9,248,16,NOT WS_GROUP
+    LTEXT           "Choisir un dossier où l'assistant ira chercher les "
+                    "fichiers téléchargés.",
+                    IDC_LOCAL_DIR_INSTALL_DESC,21,9,248,16,NOT WS_GROUP
     ICON            IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
     CONTROL         "",IDC_HEADSEPARATOR,"Static",SS_BLACKFRAME | SS_SUNKEN,0,28,
                     SETUP_STANDARD_DIALOG_W,1
@@ -438,17 +443,25 @@ CAPTION "Détection d'un fichier en cours d'utilisation"
 FONT 8, "MS Shell Dlg"
 BEGIN
     ICON            IDI_WARNING,IDC_HEADICON,10,10
-    LTEXT           "Impossible d'extraire %s",
+    LTEXT           "Impossible d'extraire",
                     IDC_FILE_INUSE_MSG,33,10,234,8,SS_PATHELLIPSIS
     LTEXT           "Ce fichier est en cours d'utilisation par : ",
                     IDC_STATIC,33,28,234,8
     EDITTEXT        IDC_FILE_INUSE_EDIT,33,40,234,32,WS_VSCROLL |
                     ES_LEFT | ES_MULTILINE | ES_READONLY |
                     ES_AUTOVSCROLL | NOT WS_TABSTOP
-    LTEXT           "Choisir 'Recommencer' pour recommencer, "
-                    "Choisir 'Stop' pour arrêter les process et recommencer, ou "
-                    "Choisir 'Continuer' pour procéder (un reboot sera nécessaire).",
-                    IDC_FILE_INUSE_HELP,33,80,234,24,NOT WS_GROUP
+    LTEXT           "Choisir «Recommencer» pour recommencer, "
+                    "choisir «Stop» pour arrêter les process et recommencer, ou "
+                    "choisir «Continuer» pour procéder (un reboot sera nécessaire).",
+                    IDC_FILE_INUSE_HELP_0,33,80,234,24,NOT WS_GROUP
+    LTEXT           "Choisir «Recommencer» pour ré-essayer, "
+                    "choisir «Tuer» pour tuer les processus et ré-essayer, ou "
+                    "choisir «Continuer» pour poursuivre (le fichier sera mis à jour après un redémarrage).",
+                    IDC_FILE_INUSE_HELP_1,33,80,234,24,NOT WS_GROUP
+    LTEXT           "Choisir «Recommencer» pour ré-essayer, "
+                    "choisir «Tuer» pour forcer la fin des processus et ré-essayer, ou "
+                    "choisir «Continuer» pour poursuivre (le fichier sera mis à jour après un redémarrage).",
+                    IDC_FILE_INUSE_HELP_2,33,80,234,24,NOT WS_GROUP
     PUSHBUTTON      "&Recommencer",IDIGNORE,45,112,55,15
     DEFPUSHBUTTON   "&Stop",IDRETRY,111,112,55,15
     PUSHBUTTON      "&Continuer",IDCONTINUE,177,112,55,15
@@ -518,10 +531,6 @@ BEGIN
     IDS_SIG_INVALID    "Erreur pour le miroir :  la signature de Setup.ini %s de %s est impossible à vérifier.\nLe miroir est peut-être corrompu ?  Setup.ini rejeté."
     IDS_CRYPTO_ERROR   "Erreur interne :  erreur %d %s de la librairie gcrypt"
     IDS_SEARCH_TOOLTIP "Chercher cette valeur dans les noms des paquets."
-    IDS_LOCAL_DIR_DOWNLOAD "Choisir un dossier pour enregistrer les fichiers "
-       "téléchargés. Ce dossier sera créé s'il n'existe pas."
-    IDS_LOCAL_DIR_INSTALL "Choisir un dossier où l'assistant ira chercher les "
-       "fichiers téléchargés."
     IDS_MAYBE_MKDIR    "Dossier %s absent, voulez-vous le créer ?"
     IDS_CANT_MKDIR     "Impossible de créer le dossier %s, désolé.  (Disque plein ou uniquement en lecture ?)"
     IDS_NO_LOCALDIR    "Dossier local des paquets %s absent.\nVoulez-vous efface les paquets, \n"
@@ -602,4 +611,20 @@ BEGIN
         "Vérifiez que vos paramètres réseau sont corrects et réessayez."
     IDS_CONFIRM_EXIT "Voulez-vous vraiment quitter l'assistant ? Les téléchargements ou installations en cours seront annulés."
     IDS_CONTINUE "Continuer"
+    IDS_CHOOSER_EMPTY_NO_PACKAGES "Pas de paquet trouvé."
+    IDS_CHOOSER_EMPTY_DOWNLOAD "Rien à télécharger."
+    IDS_CHOOSER_EMPTY_INSTALL "Rien à installer ou mettre à jour."
+    IDS_ROOT_BROWSE_TITLE "Choisissez le dossier racine de l'installation"
+    // IDS_LOCALDIR_BROWSE_DOWNLOAD_TITLE "XXX: missing translation"
+    // IDS_LOCALDIR_BROWSE_PACKAGE_TITLE  "XXX: missing translation"
+    IDS_PROGRESS_CHECKING_HASH "Vérification %s pour %s"
+    IDS_PROGRESS_DOWNLOADING_FROM "%s depuis %s"
+    IDS_CONFIRM_UNINSTALL "Désinstalle"
+    IDS_CONFIRM_INSTALL "Installe"
+    IDS_CONFIRM_DOWNLOAD "Télécharge"
+    IDS_CONFIRM_NOTHING "Pas de changement"
+    IDS_CONFIRM_AUTO_ADD "(ajouté automatiquement)"
+    IDS_CONFIRM_SOURCE "(source)"
+    IDS_FILE_INUSE_KILL "&Tuer les processus"
+    IDS_FILE_INUSE_MSG "Incapable d'extraire"
 END
diff --git a/resource.h b/resource.h
index 7b46a2ab..0a10f648 100644
--- a/resource.h
+++ b/resource.h
@@ -30,8 +30,6 @@
 #define IDS_SIG_INVALID                   131
 #define IDS_CRYPTO_ERROR                  132
 #define IDS_SEARCH_TOOLTIP                133
-#define IDS_LOCAL_DIR_DOWNLOAD            134
-#define IDS_LOCAL_DIR_INSTALL             135
 #define IDS_MAYBE_MKDIR                   136
 #define IDS_CANT_MKDIR                    137
 #define IDS_NO_LOCALDIR                   138
@@ -89,6 +87,22 @@
 #define IDS_CONFIRM_EXIT                  190
 #define IDS_EXTRACTION_INUSE              191
 #define IDS_CONTINUE                      192
+#define IDS_CHOOSER_EMPTY_NO_PACKAGES     193
+#define IDS_CHOOSER_EMPTY_DOWNLOAD        194
+#define IDS_CHOOSER_EMPTY_INSTALL         195
+#define IDS_ROOT_BROWSE_TITLE             196
+#define IDS_LOCALDIR_BROWSE_DOWNLOAD_TITLE 197
+#define IDS_LOCALDIR_BROWSE_PACKAGE_TITLE  198
+#define IDS_PROGRESS_CHECKING_HASH        199
+#define IDS_CONFIRM_UNINSTALL            1200
+#define IDS_CONFIRM_INSTALL              1201
+#define IDS_CONFIRM_DOWNLOAD             1202
+#define IDS_CONFIRM_NOTHING              1203
+#define IDS_CONFIRM_AUTO_ADD             1204
+#define IDS_CONFIRM_SOURCE               1205
+#define IDS_PROGRESS_DOWNLOADING_FROM    1206
+#define IDS_FILE_INUSE_KILL              1207
+#define IDS_FILE_INUSE_MSG               1208
 
 // Dialogs
 
@@ -211,11 +225,9 @@
 #define IDC_CHOOSE_SEARCH_EDIT            585
 #define IDC_CHOOSE_SEARCH_LABEL           586
 #define IDC_CHOOSE_CLEAR_SEARCH           587
-#define IDC_LOCAL_DIR_DESC                588
 #define IDC_POSTINSTALL_EDIT              589
 #define IDC_FILE_INUSE_EDIT               590
 #define IDC_FILE_INUSE_MSG                591
-#define IDC_FILE_INUSE_HELP               592
 #define IDC_DOWNLOAD_EDIT                 594
 #define IDC_CHOOSE_DO_SEARCH              595
 #define IDC_CHOOSE_SYNC                   596
@@ -225,3 +237,8 @@
 #define IDC_DESKTOP_HEADER_TITLE_DOWNLOAD 600
 #define IDC_DESKTOP_HEADER_INSTALL        601
 #define IDC_DESKTOP_HEADER_TITLE_INSTALL  602
+#define IDC_LOCAL_DIR_DOWNLOAD_DESC       603
+#define IDC_LOCAL_DIR_INSTALL_DESC        604
+#define IDC_FILE_INUSE_HELP_0             605
+#define IDC_FILE_INUSE_HELP_1             606
+#define IDC_FILE_INUSE_HELP_2             607
diff --git a/root.cc b/root.cc
index 6fc25b9e..ddff3f0f 100644
--- a/root.cc
+++ b/root.cc
@@ -98,19 +98,23 @@ browse_cb (HWND h, UINT msg, LPARAM lp, LPARAM data)
 static void
 browse (HWND h)
 {
-  BROWSEINFO bi;
-  /* SHGetPathFromIDList doesn't handle path length > MAX_PATH. */
-  CHAR name[MAX_PATH];
-  LPITEMIDLIST pidl;
+  std::wstring title = LoadStringW(IDS_ROOT_BROWSE_TITLE);
+
+  wchar_t wname[MAX_PATH];
+  BROWSEINFOW bi;
   memset (&bi, 0, sizeof (bi));
   bi.hwndOwner = h;
-  bi.pszDisplayName = name;
-  bi.lpszTitle = "Select an installation root directory";
+  bi.pszDisplayName = wname;
+  bi.lpszTitle = title.c_str();
   bi.ulFlags = BIF_RETURNONLYFSDIRS;
   bi.lpfn = browse_cb;
-  pidl = SHBrowseForFolder (&bi);
+
+  /* SHGetPathFromIDList doesn't handle path length > MAX_PATH. */
+  LPITEMIDLIST pidl;
+  pidl = SHBrowseForFolderW (&bi);
   if (pidl)
     {
+      CHAR name[MAX_PATH];
       if (SHGetPathFromIDList (pidl, name))
 	eset (h, IDC_ROOT_DIR, name);
     }
diff --git a/threebar.cc b/threebar.cc
index 739ca524..ff4c5cb3 100644
--- a/threebar.cc
+++ b/threebar.cc
@@ -76,9 +76,15 @@ ThreeBarProgressPage::OnInit ()
 }
 
 void
-ThreeBarProgressPage::SetText1 (const TCHAR * t)
+ThreeBarProgressPage::SetText1 (const wchar_t * t)
 {
-  ::SetWindowText (ins_action, t);
+  ::SetWindowTextW (ins_action, t);
+}
+
+void
+ThreeBarProgressPage::SetText2 (const wchar_t * t)
+{
+  ::SetWindowTextW (ins_pkgname, t);
 }
 
 void
@@ -135,8 +141,10 @@ ThreeBarProgressPage::SetBar2 (long long progress, long long max)
 {
   int percent = (int) (100.0 * ((double) progress) / (double) max);
   SendMessage (ins_iprogress, PBM_SETPOS, (WPARAM) percent, 0);
-  std::string s = stringify(percent);
-  s += "% - Cygwin Setup";
+
+  // also update window title to show progress
+  std::wstring caption = LoadStringW(IDS_MBOX_CAPTION);
+  std::wstring s = format(L"%d%% - %ls", percent, caption.c_str());
   GetOwner ()->SetWindowText (s.c_str());
 }
 
diff --git a/threebar.h b/threebar.h
index 8f03f6bc..c361eed8 100644
--- a/threebar.h
+++ b/threebar.h
@@ -69,7 +69,8 @@ public:
     return -1;
   };
 
-  void SetText1 (const TCHAR * t);
+  void SetText1 (const wchar_t * t);
+  void SetText2 (const wchar_t * t);
   void SetText2 (const TCHAR * t);
   void SetText3 (const TCHAR * t);
   void SetText4 (const TCHAR * t);
diff --git a/win32.cc b/win32.cc
index b6e7c947..35a28c6b 100644
--- a/win32.cc
+++ b/win32.cc
@@ -462,6 +462,38 @@ WowNativeMachine ()
 #endif
 }
 
+const std::wstring
+LoadStringWEx(UINT uID, UINT langId)
+{
+  HINSTANCE hInstance = GetModuleHandle(NULL);
+
+  // Convert the string ID into a bundle number
+  LPCSTR bundle = MAKEINTRESOURCE(uID / 16 + 1);
+  HRSRC hRes = ::FindResourceEx(hInstance, RT_STRING, bundle, langId);
+  if (hRes)
+    {
+      HGLOBAL h = ::LoadResource(hInstance, hRes);
+      if (h)
+        {
+          HGLOBAL hGlob = ::LockResource(h);
+
+          // walk string bundle
+          wchar_t *buf = (wchar_t *)hGlob;
+          for (unsigned int i = 0; i < (uID & 15); i++)
+            {
+              buf += 1 + (UINT)*buf;
+            }
+
+          int len = *buf;
+          return std::wstring(buf + 1, len);
+        }
+    }
+  // N.B.: Due to the way string bundles are encoded, there's no difference
+  // between an absent string resource whose bundle is present, and a string
+  // resource containing the null string.
+  return L"";
+}
+
 const std::wstring
 LoadStringW(unsigned int uID)
 {
@@ -471,7 +503,8 @@ LoadStringW(unsigned int uID)
   if (len > 0)
     return std::wstring(buf, len);
 
-  return L"";
+  // if empty or absent, fallback to the untranslated string
+  return LoadStringWEx(uID, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
 }
 
 bool
diff --git a/win32.h b/win32.h
index 05f931c8..dcaa2511 100644
--- a/win32.h
+++ b/win32.h
@@ -195,6 +195,8 @@ SetDlgItemRect (HWND h, int item, LPRECT r)
 }
 
 const std::wstring LoadStringW(unsigned int uID);
+const std::wstring LoadStringWEx(UINT uID, UINT langId);
+
 bool is_developer_mode(void);
 
 #endif /* SETUP_WIN32_H */
diff --git a/window.cc b/window.cc
index c4006d61..f13dafc4 100644
--- a/window.cc
+++ b/window.cc
@@ -347,9 +347,9 @@ Window::SetDlgItemFont (int id, const TCHAR * fontname, int Pointsize,
 }
 
 void
-Window::SetWindowText (const std::string& s)
+Window::SetWindowText (const std::wstring& s)
 {
-  ::SetWindowText (WindowHandle, s.c_str ());
+  ::SetWindowTextW (WindowHandle, s.c_str ());
 }
 
 RECT
diff --git a/window.h b/window.h
index d8b712b8..1dfb2a9f 100644
--- a/window.h
+++ b/window.h
@@ -156,7 +156,7 @@ public:
   bool MoveWindow(const RECTWrapper &r, bool Repaint = true);
 
   // Set the title of the window.
-  void SetWindowText (const std::string& s);
+  void SetWindowText (const std::wstring& s);
 
   RECT ScreenToClient(const RECT &r) const;
   void ActivateTooltips ();
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.