[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;
+}
+