ts2po.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <QFile>
00012 #include <QFileInfo>
00013 #include <QDomDocument>
00014 #include <QTextStream>
00015 #include <QTextCodec>
00016 #include <QDateTime>
00017 #include <stdlib.h>
00018
00019 #include "ts2po_config.h"
00020
00021 #define TS_DOCTYPE "TS"
00022 #define TS_ELEMENT_CONTEXT "context"
00023 #define TS_ELEMENT_NAME "name"
00024 #define TS_ELEMENT_MESSAGE "message"
00025 #define TS_ELEMENT_SOURCE "source"
00026 #define TS_ELEMENT_TRANSLATION "translation"
00027 #define TS_ELEMENT_LOCATION "location"
00028 #define TS_ATTR_FILENAME "filename"
00029 #define TS_ATTR_LINE "line"
00030
00031
00032
00033 QString
00034 create_po_timestamp()
00035 {
00036 QDateTime now = QDateTime::currentDateTime().toUTC();
00037 return now.toString("yyyy-MM-dd hh:mm+0000");
00038 }
00039
00040
00041
00042 QString
00043 create_po_header(const QString &encoding)
00044 {
00045 QString header;
00046 QString tstamp = create_po_timestamp();
00047
00048 header.append("msgid \"\"\n");
00049 header.append("msgstr \"\"\n");
00050 header.append("\"Project-Id-Version: "TS2PO_PROJECT_ID"\\n\"\n");
00051 header.append("\"Report-Msgid-Bugs-To: "TS2PO_CONTACT_ADDR"\\n\"\n");
00052 header.append(QString("\"POT-Creation-Date: %1\\n\"\n").arg(tstamp));
00053 header.append("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n");
00054 header.append("\"Last-Translator: \\n\"\n");
00055 header.append("\"Language-Team: "TS2PO_LANGUAGE_TEAM"\\n\"\n");
00056 header.append("\"MIME-Version: 1.0\\n\"\n");
00057 header.append("\"Content-Type: text/plain; ");
00058 header.append(QString("charset=%1\\n\"\n").arg(encoding));
00059 header.append("\"Content-Transfer-Encoding: 8bit\\n\"\n");
00060 header.append("\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n");
00061 header.append("\"X-Generator: Vidalia ts2po "TS2PO_VERSION"\\n\"\n");
00062 header.append("\n");
00063
00064 return header;
00065 }
00066
00067
00068
00069 QString
00070 parse_filename(const QString &filePath)
00071 {
00072 QFileInfo file(filePath);
00073 return file.fileName();
00074 }
00075
00076
00077
00078
00079 int
00080 convert_context(const QDomElement &context, QString *po, QString *errorMessage)
00081 {
00082 QString msgctxt, msgid, msgstr;
00083 QString filename, line;
00084 QDomElement location, source, translation;
00085 int n = 0;
00086
00087 Q_ASSERT(po);
00088 Q_ASSERT(errorMessage);
00089
00090 QDomElement name = context.firstChildElement(TS_ELEMENT_NAME);
00091 if (name.isNull()) {
00092 *errorMessage = QString("context element with no name (line %1)")
00093 .arg(context.lineNumber());
00094 return -1;
00095 }
00096 msgctxt = name.text();
00097
00098 QDomElement msg = context.firstChildElement(TS_ELEMENT_MESSAGE);
00099 while (!msg.isNull()) {
00100
00101 source = msg.firstChildElement(TS_ELEMENT_SOURCE);
00102 if (source.isNull()) {
00103 *errorMessage = QString("message element with no source string "
00104 "(line %1)").arg(msg.lineNumber());
00105 return -1;
00106 }
00107 msgid = source.text();
00108 msgid.replace("\r", "");
00109 msgid.replace("\"", "\\\"");
00110 msgid.replace("\n", "\"\n\"");
00111
00112
00113 translation = msg.firstChildElement(TS_ELEMENT_TRANSLATION);
00114 msgstr = translation.text();
00115 msgstr.replace("\r", "");
00116 msgstr.replace("\"", "\\\"");
00117 msgstr.replace("\n", "\"\n\"");
00118
00119
00120 location = msg.firstChildElement(TS_ELEMENT_LOCATION);
00121 filename = parse_filename(location.attribute(TS_ATTR_FILENAME));
00122 line = location.attribute(TS_ATTR_LINE);
00123
00124
00125 if (!filename.isEmpty() && !line.isEmpty())
00126 (*po).append(QString("#: %1:%2\n").arg(filename).arg(line));
00127 (*po).append(QString("msgctxt \"%1\"\n").arg(msgctxt));
00128 (*po).append(QString("msgid \"%1\"\n").arg(msgid));
00129 (*po).append(QString("msgstr \"%1\"\n").arg(msgstr));
00130 (*po).append("\n");
00131
00132
00133 msg = msg.nextSiblingElement(TS_ELEMENT_MESSAGE);
00134 n++;
00135 }
00136 return n;
00137 }
00138
00139
00140
00141
00142
00143 int
00144 ts2po(const QDomDocument *ts, QString *po, const QString &encoding,
00145 QString *errorMessage)
00146 {
00147 int n_strings = 0;
00148 QString context;
00149
00150 Q_ASSERT(ts);
00151 Q_ASSERT(po);
00152 Q_ASSERT(errorMessage);
00153
00154
00155 QDomElement root = ts->documentElement();
00156 if (root.tagName() != TS_DOCTYPE)
00157 return -1;
00158
00159
00160 *po = create_po_header(encoding);
00161
00162
00163
00164 QDomElement child = root.firstChildElement(TS_ELEMENT_CONTEXT);
00165 while (!child.isNull()) {
00166 QString context;
00167
00168
00169 int n = convert_context(child, &context, errorMessage);
00170 if (n < 0)
00171 return -1;
00172
00173
00174 (*po).append(context);
00175 n_strings += n;
00176
00177
00178 child = child.nextSiblingElement(TS_ELEMENT_CONTEXT);
00179 }
00180 return n_strings;
00181 }
00182
00183
00184 void
00185 print_usage_and_exit()
00186 {
00187 QTextStream error(stderr);
00188 error << "usage: ts2po [-q] -i <infile.ts> -o <outfile.po> "
00189 "[-c <encoding>]\n";
00190 error << " -q (optional) Quiet mode (errors are still displayed)\n";
00191 error << " -i <infile.ts> Input .ts file\n";
00192 error << " -o <outfile.po> Output .po file\n";
00193 error << " -c <encoding> Text encoding (default: utf-8)\n";
00194 error.flush();
00195 exit(1);
00196 }
00197
00198 int
00199 main(int argc, char *argv[])
00200 {
00201 QTextStream error(stderr);
00202 QString errorMessage;
00203 char *infile, *outfile;
00204 QTextCodec *codec = QTextCodec::codecForName("utf-8");
00205 bool quiet = false;
00206
00207
00208 if (argc < 5 || argc > 8)
00209 print_usage_and_exit();
00210 for (int i = 1; i < argc; i++) {
00211 QString arg(argv[i]);
00212 if (!arg.compare("-q", Qt::CaseInsensitive))
00213 quiet = true;
00214 else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
00215 infile = argv[i];
00216 else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
00217 outfile = argv[i];
00218 else if (!arg.compare("-c", Qt::CaseInsensitive) && ++i < argc) {
00219 codec = QTextCodec::codecForName(argv[i]);
00220 if (!codec) {
00221 error << "Invalid text encoding specified.\n";
00222 return 1;
00223 }
00224 } else
00225 print_usage_and_exit();
00226 }
00227
00228
00229 QDomDocument ts;
00230 QFile tsFile(infile);
00231 if (!ts.setContent(&tsFile, true, &errorMessage)) {
00232 error << QString("Unable to parse '%1': %2\n").arg(infile)
00233 .arg(errorMessage);
00234 return 1;
00235 }
00236
00237
00238 QFile poFile(outfile);
00239 if (!poFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
00240 error << QString("Unable to open '%1' for writing: %2\n")
00241 .arg(outfile)
00242 .arg(tsFile.errorString());
00243 return 2;
00244 }
00245
00246
00247 QString po;
00248 int n_strings = ts2po(&ts, &po, QString(codec->name()), &errorMessage);
00249 if (n_strings < 0) {
00250 error << QString("Unable to convert '%1' to '%2': %3\n").arg(infile)
00251 .arg(outfile)
00252 .arg(errorMessage);
00253 return 3;
00254 }
00255
00256
00257 QTextStream out(&poFile);
00258 out.setCodec(codec);
00259 out << po;
00260 poFile.close();
00261
00262 if (!quiet) {
00263 QTextStream results(stdout);
00264 results << QString("Converted %1 strings from %2 to %3.\n").arg(n_strings)
00265 .arg(infile)
00266 .arg(outfile);
00267 }
00268 return 0;
00269 }
00270