[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r3596: Add support for WiX localization with po2wxl tool. (in vidalia/trunk: cmake pkg/win32 src/tools src/tools/po2wxl)



Author: coderman
Date: 2009-02-27 16:08:55 -0500 (Fri, 27 Feb 2009)
New Revision: 3596

Added:
   vidalia/trunk/src/tools/po2wxl/
   vidalia/trunk/src/tools/po2wxl/CMakeLists.txt
   vidalia/trunk/src/tools/po2wxl/po2wxl.cpp
Modified:
   vidalia/trunk/cmake/VidaliaMacros.cmake
   vidalia/trunk/pkg/win32/CMakeLists.txt
   vidalia/trunk/src/tools/CMakeLists.txt
Log:
Add support for WiX localization with po2wxl tool.

Modified: vidalia/trunk/cmake/VidaliaMacros.cmake
===================================================================
--- vidalia/trunk/cmake/VidaliaMacros.cmake	2009-02-26 00:41:47 UTC (rev 3595)
+++ vidalia/trunk/cmake/VidaliaMacros.cmake	2009-02-27 21:08:55 UTC (rev 3596)
@@ -122,6 +122,28 @@
 endmacro(VIDALIA_ADD_NSH)
 
 
+## Wraps the input .po file specified by PO in a custom command to convert it
+## to WiX's localization XML document in a .wxl file. The culture name is
+## specified by CULTURE and codepage by CHARSET.
+## Note that CHARSET is not currently used to set codepage of String elements
+## but this may be necessary if Wix is not smart enough to do this right.
+macro(VIDALIA_ADD_WXL OUTFILES PO CULTURE CHARSET)
+  get_filename_component(po ${PO} ABSOLUTE)
+  get_filename_component(outfile ${PO} NAME_WE)
+
+  ## Create the .po -> .wxl conversion step
+  set(wxl ${CMAKE_CURRENT_BINARY_DIR}/${outfile}.wxl)
+  add_custom_command(OUTPUT ${wxl}
+    COMMAND ${VIDALIA_PO2WXL_EXECUTABLE}
+    ARGS -q -n ${CULTURE} -i ${po} -o ${wxl}
+    MAIN_DEPENDENCY ${po}
+    DEPENDS ${VIDALIA_PO2WXL_EXECUTABLE}
+    COMMENT "Generating ${outfile}.wxl"
+  )
+  set(${OUTFILES} ${${OUTFILES}} ${wxl})
+endmacro(VIDALIA_ADD_WXL)
+
+
 if (WIN32)
   ## Wraps the supplied .rc files in windres commands
   macro(WIN32_WRAP_RC outfiles)

Modified: vidalia/trunk/pkg/win32/CMakeLists.txt
===================================================================
--- vidalia/trunk/pkg/win32/CMakeLists.txt	2009-02-26 00:41:47 UTC (rev 3595)
+++ vidalia/trunk/pkg/win32/CMakeLists.txt	2009-02-27 21:08:55 UTC (rev 3596)
@@ -100,6 +100,44 @@
 
 add_custom_target(i18n-win32-installer ALL DEPENDS ${vidalia_NSH})
 
+## Convert the .po files to WiX .wxl files at build time
+vidalia_add_wxl(vidalia_WXL
+  cs/vidalia_cs.po  cs    ISO-8859-2)
+vidalia_add_wxl(vidalia_WXL
+  de/vidalia_de.po  de    ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  en/vidalia_en.po  en   ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  es/vidalia_es.po  es   ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  fa/vidalia_fa.po  fa     ISO-8859-6)
+vidalia_add_wxl(vidalia_WXL
+  fi/vidalia_fi.po  fi   ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  fr/vidalia_fr.po  fr    ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  he/vidalia_he.po  he    ISO-8859-8)
+vidalia_add_wxl(vidalia_WXL
+  it/vidalia_it.po  it   ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  nl/vidalia_nl.po  nl     ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  pl/vidalia_pl.po  pl    ISO-8859-2)
+vidalia_add_wxl(vidalia_WXL
+  pt/vidalia_pt.po  pt  ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  ro/vidalia_ro.po  ro  ISO-8859-2)
+vidalia_add_wxl(vidalia_WXL
+  ru/vidalia_ru.po  ru   1251)
+vidalia_add_wxl(vidalia_WXL
+  sv/vidalia_sv.po  sv   ISO-8859-1)
+vidalia_add_wxl(vidalia_WXL
+  zh_CN/vidalia_zh_CN.po  zh-CN   936)
+vidalia_add_wxl(vidalia_WXL
+  zh_TW/vidalia_ZH_TW.po  zh-TW   950)
+  
+add_custom_target(wix-i18n-localizations ALL DEPENDS ${vidalia_WXL})
+
 add_custom_target(win32-installer
   COMMAND ${WIX_CANDLE_EXECUTABLE} 
       ${CMAKE_CURRENT_BINARY_DIR}/vidalia.wxs 

Modified: vidalia/trunk/src/tools/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/tools/CMakeLists.txt	2009-02-26 00:41:47 UTC (rev 3595)
+++ vidalia/trunk/src/tools/CMakeLists.txt	2009-02-27 21:08:55 UTC (rev 3596)
@@ -16,5 +16,6 @@
 if (WIN32)
   add_subdirectory(po2nsh)
   add_subdirectory(nsh2po EXCLUDE_FROM_ALL)
+  add_subdirectory(po2wxl)
 endif(WIN32)
 

Added: vidalia/trunk/src/tools/po2wxl/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/tools/po2wxl/CMakeLists.txt	                        (rev 0)
+++ vidalia/trunk/src/tools/po2wxl/CMakeLists.txt	2009-02-27 21:08:55 UTC (rev 3596)
@@ -0,0 +1,28 @@
+##
+##  $Id$
+## 
+##  This file is part of Vidalia, and is subject to the license terms in the
+##  LICENSE file, found in the top level directory of this distribution. If 
+##  you did not receive the LICENSE file with this file, you may obtain it
+##  from the Vidalia source package distributed by the Vidalia Project at
+##  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+##  may be copied, modified, propagated, or distributed except according to
+##  the terms described in the LICENSE file.
+##
+
+## po2wxl source files
+set(po2wxl_SRCS
+  po2wxl.cpp
+)
+
+## Create the po2wxl executable
+add_executable(po2wxl ${po2wxl_SRCS})
+
+## Link the executable with the appropriate Qt libraries
+target_link_libraries(po2wxl ${QT_LIBRARIES})
+
+## Remember the location of po2wxl so we can use it in custom commands
+get_target_property(PO2WXL_EXECUTABLE po2wxl LOCATION)
+set(VIDALIA_PO2WXL_EXECUTABLE ${PO2WXL_EXECUTABLE}
+    CACHE STRING "Location of Vidalia's po2wxl converter." FORCE)
+

Added: vidalia/trunk/src/tools/po2wxl/po2wxl.cpp
===================================================================
--- vidalia/trunk/src/tools/po2wxl/po2wxl.cpp	                        (rev 0)
+++ vidalia/trunk/src/tools/po2wxl/po2wxl.cpp	2009-02-27 21:08:55 UTC (rev 3596)
@@ -0,0 +1,291 @@
+/*
+**  $Id$
+**
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+#include <QFile>
+#include <QDomDocument>
+#include <QTextStream>
+#include <QTextCodec>
+#include <stdlib.h>
+
+#define WXL_NAMESPACE                  "http://schemas.microsoft.com/wix/2006/localization";
+#define WXL_ELEMENT_ROOT               "WixLocalization"
+#define WXL_ELEMENT_MESSAGE            "String"
+#define WXL_ATTR_MESSAGE_ID            "Id"
+#define WXL_ATTR_TRANSLATION_TYPE      "Culture"
+
+/** Create a new message string element using the source string <b>msgid</b>
+ * and the translation <b>msgstr</b> and assign identifier attribute. */
+QDomElement
+new_message_element(QDomDocument *wxl, const QString &strid,
+                    const QString &msgid, const QString &msgstr)
+{
+  QDomElement message;
+
+  message = wxl->createElement(WXL_ELEMENT_MESSAGE);
+  message.setAttribute(WXL_ATTR_MESSAGE_ID, strid);
+  if (!msgstr.isEmpty())
+    message.appendChild(wxl->createTextNode(msgstr));
+  else
+    message.appendChild(wxl->createTextNode(msgid));
+
+  return message;
+}
+
+/** Create a new WXL document of the appropriate doctype and root
+ * element with the Microsoft style culture name for locale. */
+QDomDocument
+new_wxl_document(const QString &culture)
+{
+  QDomDocument wxl;
+  
+  QDomElement root = wxl.createElementNS(WXL_NAMESPACE, WXL_ELEMENT_ROOT);
+  root.setAttribute(WXL_ATTR_TRANSLATION_TYPE, culture);
+  wxl.appendChild(root);
+
+  return wxl;
+}
+
+/** Parse the context name from <b>str</b>, where the context name is of the
+ * form DQUOTE ContextName DQUOTE. */
+QString
+parse_message_context(const QString &str)
+{
+  QString out = str.trimmed();
+  out = out.replace("\"", "");
+  return out;
+}
+
+/** Parse the context name from <b>str</b>, where <b>str</b> is of the
+ * form ContextName#Number. This is the format used by translate-toolkit. */
+QString
+parse_message_context_lame(const QString &str)
+{
+  if (str.contains("#"))
+    return str.section("#", 0, 0);
+  return QString();
+}
+
+/** Parse the PO-formatted message string from <b>msg</b>. If <b>msg</b> is a
+ * multiline string, the extra double quotes will be replaced with newlines
+ * appropriately. */
+QString
+parse_message_string(const QString &msg)
+{
+  QString out = msg.trimmed(); 
+  
+  out.replace("\"\n\"", "\n");
+  if (out.startsWith("\""))
+    out = out.remove(0, 1);
+  if (out.endsWith("\""))
+    out.chop(1);
+  out.replace("\\\"", "\"");
+
+  /* convert NSIS style vars to Properties; avoid QRegExp if possible. */
+  int lind, rind;
+  while ( ((lind = out.indexOf("${")) >= 0) &&
+          ((rind = out.indexOf("}", lind)) > lind) ) {
+    out.replace(lind, 2, "[");
+    out.replace(--rind, 1, "]");
+  }
+  return out;
+}
+
+/** Read and return the next non-empty line from <b>stream</b>. */
+QString
+read_next_line(QTextStream *stream)
+{
+  stream->skipWhiteSpace();
+  return stream->readLine().append("\n");
+}
+
+/** Skip past the header portion of the PO file and any leading whitespace. 
+ * The next line read from <b>po</b> will be the first non-header line in the
+ * document. */
+void
+skip_po_header(QTextStream *po)
+{
+  QString line;
+  /* Skip any leading whitespace before the header */
+  po->skipWhiteSpace();
+  /* Read to the first empty line */
+  line = po->readLine();
+  while (!po->atEnd() && !line.isEmpty())
+    line = po->readLine();
+}
+
+/** Convert <b>po</b> from the PO format to a WXL-formatted XML document.
+ * <b>wxl</b> will be set to the resulting WXL document. Return the number of
+ * converted strings on success, or -1 on error and <b>errorMessage</b> will
+ * be set. */
+int
+po2wxl(const QString& culture, QTextStream *po, QDomDocument *wxl,
+  QString *errorMessage)
+{
+  QString line;
+  QString msgctxt, msgid, msgstr;
+  QDomElement msgElement;
+  int n_strings = 0;
+
+  Q_ASSERT(po);
+  Q_ASSERT(wxl);
+  Q_ASSERT(errorMessage);
+
+  *wxl = new_wxl_document(culture);
+  
+  skip_po_header(po);
+  line = read_next_line(po);
+  while (!po->atEnd()) {
+    /* Ignore all "#" lines except "#:" */
+    while (line.startsWith("#")) {
+      if (line.startsWith("#:")) {
+        /* Context was specified with the stupid overloaded "#:" syntax.*/
+        msgctxt = line.section(" ", 1);
+        msgctxt = parse_message_context_lame(msgctxt);
+      }
+      line = read_next_line(po);
+    }
+
+    /* A context specified on a "msgctxt" line takes precedence over a context
+     * specified using the overload "#:" notation. */
+    if (line.startsWith("msgctxt ")) {    
+      msgctxt = line.section(" ", 1);
+      msgctxt = parse_message_context(msgctxt);
+      line = read_next_line(po);
+    }
+    
+    /* Parse the (possibly multiline) message source string */
+    if (!line.startsWith("msgid ")) {
+      *errorMessage = "expected 'msgid' line";
+      return -1;
+    }
+    msgid = line.section(" ", 1);
+    
+    line = read_next_line(po);
+    while (line.startsWith("\"")) {
+      msgid.append(line);
+      line = read_next_line(po);
+    }
+    msgid = parse_message_string(msgid);
+
+    /* Parse the (possibly multiline) translated string */
+    if (!line.startsWith("msgstr ")) {
+      *errorMessage = "expected 'msgstr' line";
+      return -1;
+    }
+    msgstr = line.section(" ", 1);
+    
+    line = read_next_line(po);
+    while (line.startsWith("\"")) {
+      msgstr.append(line);
+      line = read_next_line(po);
+    }
+    msgstr = parse_message_string(msgstr);
+
+    /* Add the message and translation to the .wxl document */
+    wxl->documentElement().appendChild(
+      new_message_element(wxl, msgctxt, msgid, msgstr)); 
+    
+    n_strings++;
+  }
+  return n_strings;
+}
+
+/** Display application usage and exit. */
+void
+print_usage_and_exit()
+{
+  QTextStream error(stderr);
+  error << "usage: po2wxl [-q] -n <culture name> -i <infile.po> -o <outfile.wxl> "
+           "[-c <encoding>]\n";
+  error << "  -q (optional)    Quiet mode (errors are still displayed)\n";
+  error << "  -n <culture>     Culture name for translation\n";
+  error << "  -i <infile.po>   Input .po file\n";
+  error << "  -o <outfile.wxl> Output .wxl file\n";
+  error << "  -c <encoding>    Text encoding (default: utf-8)\n";
+  error.flush();
+  exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+  QTextStream error(stderr);
+  QString culture, errorMessage;
+  char *infile, *outfile;
+  QTextCodec *codec = QTextCodec::codecForName("utf-8");
+  bool quiet = false;
+
+  /* Check for the correct number of input parameters. */
+  if (argc < 5 || argc > 9)
+    print_usage_and_exit();
+  for (int i = 1; i < argc; i++) {
+    QString arg(argv[i]);
+    if (!arg.compare("-q", Qt::CaseInsensitive))
+      quiet = true;
+    else if (!arg.compare("-n", Qt::CaseInsensitive) && ++i < argc)
+      culture = argv[i];
+    else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
+      infile = argv[i];
+    else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
+      outfile = argv[i];
+    else if (!arg.compare("-c", Qt::CaseInsensitive) && ++i < argc) {
+      codec = QTextCodec::codecForName(argv[i]);
+      if (!codec) {
+        error << "Invalid text encoding specified\n";
+        return 1;
+      }
+    } else
+      print_usage_and_exit(); 
+  }
+
+  /* Open the input PO file for reading. */
+  QFile poFile(infile);
+  if (!poFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+    error << QString("Unable to open '%1' for reading: %2\n").arg(infile)
+                                                .arg(poFile.errorString());
+    return 2;
+  }
+
+  QDomDocument wxl;
+  QTextStream po(&poFile);
+  po.setCodec(codec);
+  int n_strings = po2wxl(culture, &po, &wxl, &errorMessage);
+  if (n_strings < 0) {
+    error << QString("Unable to convert '%1': %2\n").arg(infile)
+                                                    .arg(errorMessage);
+    return 3;
+  }
+
+  /* Open the WXL file for writing. */
+  QFile wxlFile(outfile);
+  if (!wxlFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+    error << QString("Unable to open '%1' for writing: %2\n").arg(outfile)
+                                                .arg(wxlFile.errorString());
+    return 4;
+  }
+
+  /* Write the .wxl output. */
+  QTextStream out(&wxlFile);
+  out.setCodec(codec);
+  out << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n")
+                                                  .arg(QString(codec->name()));
+  out << wxl.toString(4);
+
+  if (!quiet) {
+    QTextStream results(stdout);
+    results << QString("Converted %1 strings from %2 to %3.\n").arg(n_strings)
+                                                               .arg(infile)
+                                                               .arg(outfile);
+  }
+  return 0;
+}
+