[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r2889: Add an nsh2po tool, for realsies this time. (in vidalia/trunk/src/tools: . nsh2po)
Author: edmanm
Date: 2008-07-16 22:07:03 -0400 (Wed, 16 Jul 2008)
New Revision: 2889
Added:
vidalia/trunk/src/tools/nsh2po/
vidalia/trunk/src/tools/nsh2po/CMakeLists.txt
vidalia/trunk/src/tools/nsh2po/nsh2po.cpp
vidalia/trunk/src/tools/nsh2po/nsh2po_config.h.in
Modified:
vidalia/trunk/src/tools/CMakeLists.txt
Log:
Add an nsh2po tool, for realsies this time.
Modified: vidalia/trunk/src/tools/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/tools/CMakeLists.txt 2008-07-17 01:56:54 UTC (rev 2888)
+++ vidalia/trunk/src/tools/CMakeLists.txt 2008-07-17 02:07:03 UTC (rev 2889)
@@ -13,4 +13,5 @@
add_subdirectory(ts2po)
add_subdirectory(po2ts)
add_subdirectory(po2nsh)
+add_subdirectory(nsh2po EXCLUDE_FROM_ALL)
Added: vidalia/trunk/src/tools/nsh2po/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/tools/nsh2po/CMakeLists.txt (rev 0)
+++ vidalia/trunk/src/tools/nsh2po/CMakeLists.txt 2008-07-17 02:07:03 UTC (rev 2889)
@@ -0,0 +1,44 @@
+##
+## $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.
+##
+
+## Define this version of nsh2po
+set(VERSION "0.1")
+
+## Include the source and binary directories so it can find out configured
+## header file
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+## nsh2po source files
+set(nsh2po_SRCS
+ nsh2po.cpp
+)
+
+## Set some of the properties used in the .po header
+set(NSH2PO_PROJECT_ID "Vidalia")
+set(NSH2PO_CONTACT_ADDR "translations@xxxxxxxxxxxxxxxxxxx")
+set(NSH2PO_LANGUAGE_TEAM "translations@xxxxxxxxxxxxxxxxxxx")
+
+## Create and populate config.h
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/nsh2po_config.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/nsh2po_config.h
+)
+
+## Create the nsh2po executable
+add_executable(nsh2po ${nsh2po_SRCS})
+
+## Link the executable with the appropriate Qt libraries
+target_link_libraries(nsh2po ${QT_LIBRARIES})
+
Property changes on: vidalia/trunk/src/tools/nsh2po/CMakeLists.txt
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: vidalia/trunk/src/tools/nsh2po/nsh2po.cpp
===================================================================
--- vidalia/trunk/src/tools/nsh2po/nsh2po.cpp (rev 0)
+++ vidalia/trunk/src/tools/nsh2po/nsh2po.cpp 2008-07-17 02:07:03 UTC (rev 2889)
@@ -0,0 +1,352 @@
+/*
+** 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 <QHash>
+#include <QFile>
+#include <QTextStream>
+#include <QTextCodec>
+#include <QDateTime>
+#include <QStringList>
+#include <stdlib.h>
+
+#include "nsh2po_config.h"
+
+
+/** 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>. */
+QString
+parse_message_string(const QString &msg)
+{
+ QString out = msg.trimmed();
+
+ if (out.startsWith("\""))
+ out = out.remove(0, 1);
+ if (out.endsWith("\""))
+ out.chop(1);
+ out.replace("\\\"", "\"");
+ out.replace("\\r\\n", "\\n");
+ return out;
+}
+
+/** Parse the NSIS-formatted LangString message from <b>msg</b>. */
+QString
+parse_nsh_langstring(const QString &msg)
+{
+ QString out = msg.trimmed();
+
+ if (out.startsWith("\""))
+ out = out.remove(0, 1);
+ if (out.endsWith("\""))
+ out.chop(1);
+ out.replace("$\\n", "\\n");
+ out.replace("$\\r", "");
+ out.replace("\\r", "");
+ return out;
+}
+
+/** Return the current time (in UTC) in the format YYYY-MM-DD HH:MM+0000. */
+QString
+create_po_timestamp()
+{
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+ return now.toString("yyyy-MM-dd hh:mm+0000");
+}
+
+/** Return a header to be placed at the top of the .po file. */
+QString
+create_po_header(const QString &charset)
+{
+ QString header;
+ QString tstamp = create_po_timestamp();
+
+ header.append("msgid \"\"\n");
+ header.append("msgstr \"\"\n");
+ header.append("\"Project-Id-Version: "NSH2PO_PROJECT_ID"\\n\"\n");
+ header.append("\"Report-Msgid-Bugs-To: "NSH2PO_CONTACT_ADDR"\\n\"\n");
+ header.append(QString("\"POT-Creation-Date: %1\\n\"\n").arg(tstamp));
+ header.append("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n");
+ header.append("\"Last-Translator: \\n\"\n");
+ header.append("\"Language-Team: "NSH2PO_LANGUAGE_TEAM"\\n\"\n");
+ header.append("\"MIME-Version: 1.0\\n\"\n");
+ header.append(QString("\"Content-Type: text/plain; "
+ "charset=%1\\n\"\n").arg(charset));
+ header.append("\"Content-Transfer-Encoding: 8bit\\n\"\n");
+ header.append("\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n");
+ header.append("\"X-Generator: Vidalia nsh2po "NSH2PO_VERSION"\\n\"\n");
+ header.append("\n");
+
+ return header;
+}
+
+/** Read and return the next non-empty line from <b>stream</b>. */
+QString
+read_next_line(QTextStream *stream)
+{
+ Q_ASSERT(stream);
+ stream->skipWhiteSpace();
+ return stream->readLine();
+}
+
+/** Skip past the header portion of the POT 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_pot_header(QTextStream *pot)
+{
+ QString line;
+ /* Skip any leading whitespace before the header */
+ pot->skipWhiteSpace();
+ /* Read to the first empty line */
+ line = pot->readLine();
+ while (!pot->atEnd() && !line.isEmpty())
+ line = pot->readLine();
+}
+
+/** Parse a PO template file for (context,source string) pairs, which are
+ * be stored in <b>out</b> using <i>msgctxt</i> as the key and <i>msgid</i>
+ * as the value. Return true on success, or false on failure and set
+ * <b>errmsg</b>. */
+bool
+parse_po_template(QTextStream *pot, QHash<QString,QString> *out,
+ QString *errmsg)
+{
+ QString line, msgctxt, msgid;
+
+ skip_pot_header(pot);
+ line = read_next_line(pot);
+ while (!pot->atEnd()) {
+ if (!line.startsWith("#:") && !line.startsWith("msgctxt")) {
+ /* Skip to the start of the next message entry */
+ line = read_next_line(pot);
+ continue;
+ }
+
+ 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(pot);
+ continue;
+ }
+
+ if (line.startsWith("msgctxt ")) {
+ /* A context specified on a "msgctxt" line takes precedence over a
+ * context specified using the overload "#:" notation. */
+ msgctxt = line.section(" ", 1);
+ msgctxt = parse_message_context(msgctxt);
+ line = read_next_line(pot);
+ }
+
+ if (!line.startsWith("msgid ")) {
+ *errmsg = "expected 'msgid' line";
+ return false;
+ }
+ msgid = line.section(" ", 1);
+
+ line = read_next_line(pot);
+ while (line.startsWith("\"")) {
+ /* This msgid line had multiple parts to it */
+ msgid.append(line);
+ line = read_next_line(pot);
+ }
+ msgid = parse_message_string(msgid);
+
+ out->insert(msgctxt, msgid);
+ }
+
+ return true;
+}
+
+/** Read an NSIS-formatted file containing LangString entries from <b>nsh</b>.
+ * If a LangString entry has a corresponding entry in <b>pot</b>, then the
+ * message entry is PO-formatted and appended to <b>po</b>. Return true on
+ * success, or false on failure and <b>errmsg</b> will be set. */
+int
+nsh2po(QTextStream *nsh, const QString &charset,
+ const QHash<QString,QString> &pot, QString *po, QString *errmsg)
+{
+ QString line, msgctxt, msgid, msgstr;
+ QStringList parts;
+ QHash<QString,QString> langStrings;
+ int idx, n_strings;
+
+ *po = create_po_header(charset);
+
+ /* Parse the translated strings from the NSH file */
+ while (!nsh->atEnd()) {
+ line = read_next_line(nsh);
+ if (!line.startsWith("LangString "))
+ continue;
+
+ parts = line.split(" ");
+ if (parts.size() > 3)
+ msgctxt = parts.at(1);
+ else
+ continue; /* Not properly formatted */
+
+ idx = line.indexOf("\"");
+ if (idx > 0)
+ msgstr = parse_nsh_langstring(line.mid(idx));
+ langStrings.insert(msgctxt, msgstr);
+ }
+
+ /* Format the PO file based on the template. */
+ n_strings = 0;
+ foreach (msgctxt, pot) {
+ msgid = pot.value(msgctxt);
+ if (langStrings.contains(msgctxt)) {
+ msgstr = langStrings.value(msgctxt);
+ n_strings++;
+ } else {
+ msgstr = msgid;
+ }
+
+ po->append(QString("msgctxt \"%1\"\n").arg(msgctxt));
+ po->append(QString("msgid \"%1\"\n").arg(msgid));
+ po->append(QString("msgstr \"%1\"\n").arg(msgstr));
+ po->append("\n");
+ }
+ return n_strings;
+}
+
+/** Write <b>po</b> to <b>poFileName</b> using <b>codec</b>. Return true on
+ * success. On failure, return false and set <b>errmsg</b> to the reason for
+ * failure. */
+bool
+write_po_output(const char *poFileName, const QString &po, QTextCodec *codec,
+ QString *errmsg)
+{
+ QFile poFile(poFileName);
+ if (!poFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ *errmsg = QString("Unable to open '%1' for writing.").arg(poFileName);
+ return false;
+ }
+
+ QTextStream out(&poFile);
+ out.setCodec(codec);
+ out << po;
+ return true;
+}
+
+/** Display application usage and exit. */
+void
+print_usage_and_exit()
+{
+ QTextStream error(stderr);
+ error << "usage: nsh2po [-q] -t <template.pot> -i <infile.nsh> "
+ "-o <outfile.po> [-c <encoding>]\n";
+ error << " -q (optional) Quiet mode (errors are still displayed)\n";
+ error << " -t <template.pot> PO template file\n";
+ error << " -i <infile.ts> Input .ts file\n";
+ error << " -o <outfile.po> Output .po 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 po, errorMessage;
+ char *outFileName;
+ QFile potFile, nshFile;
+ QTextStream pot, nsh;
+ QTextCodec *codec = QTextCodec::codecForName("utf-8");
+ bool quiet = false;
+
+ /* Check for the correct number of input parameters. */
+ if (argc < 7 || argc > 10)
+ 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("-t", Qt::CaseInsensitive) && ++i < argc) {
+ /* Open the input PO template file */
+ potFile.setFileName(argv[i]);
+ if (!potFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ error << QString("Couldn't open '%1' for reading: ").arg(argv[i])
+ << potFile.errorString();
+ return 1;
+ }
+ pot.setDevice(&potFile);
+ } else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc) {
+ /* Open the input NSH file */
+ nshFile.setFileName(argv[i]);
+ if (!nshFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ error << QString("Couldn't open '%1' for reading: ").arg(argv[i])
+ << nshFile.errorString();
+ return 1;
+ }
+ nsh.setDevice(&nshFile);
+ } else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc) {
+ outFileName = argv[i];
+ } else if (!arg.compare("-c", Qt::CaseInsensitive) && ++i < argc) {
+ /* Set the text encoding used for input and output */
+ codec = QTextCodec::codecForName(argv[i]);
+ if (!codec) {
+ error << "Invalid text encoding specified.\n";
+ return 1;
+ }
+ } else
+ print_usage_and_exit();
+ }
+ pot.setCodec(codec);
+ nsh.setCodec(codec);
+
+ /* Parse the template for the source strings */
+ QHash<QString,QString> poTemplate;
+ if (!parse_po_template(&pot, &poTemplate, &errorMessage)) {
+ error << QString("Failed to parse PO template: %1\n").arg(errorMessage);
+ return 1;
+ }
+
+ /* Parse the nsh for the translated strings */
+ int n_strings = nsh2po(&nsh, QString(codec->name()), poTemplate,
+ &po, &errorMessage);
+ if (n_strings < 0) {
+ error << QString("Conversion failed: %1\n").arg(errorMessage);
+ return 2;
+ }
+
+ /* Write the formatted PO output */
+ if (!write_po_output(outFileName, po, codec, &errorMessage)) {
+ error << QString("Failed to write PO output: %1\n").arg(errorMessage);
+ return 3;
+ }
+
+ if (!quiet) {
+ QTextStream out(stdout);
+ out << QString("Wrote %1 strings to '%2'.\n").arg(n_strings)
+ .arg(outFileName);
+ }
+ return 0;
+}
+
Property changes on: vidalia/trunk/src/tools/nsh2po/nsh2po.cpp
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: vidalia/trunk/src/tools/nsh2po/nsh2po_config.h.in
===================================================================
--- vidalia/trunk/src/tools/nsh2po/nsh2po_config.h.in (rev 0)
+++ vidalia/trunk/src/tools/nsh2po/nsh2po_config.h.in 2008-07-17 02:07:03 UTC (rev 2889)
@@ -0,0 +1,23 @@
+/*
+** 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.
+*/
+
+#ifndef _NSH2PO_CONFIG_H
+#define _NSH2PO_CONFIG_H
+
+#define NSH2PO_VERSION "@VERSION@"
+
+#define NSH2PO_PROJECT_ID "@NSH2PO_PROJECT_ID@"
+
+#define NSH2PO_CONTACT_ADDR "@NSH2PO_CONTACT_ADDR@"
+
+#define NSH2PO_LANGUAGE_TEAM "@NSH2PO_LANGUAGE_TEAM@"
+
+#endif
+
Property changes on: vidalia/trunk/src/tools/nsh2po/nsh2po_config.h.in
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native