po2ts.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <QHash>
00012 #include <QFile>
00013 #include <QDomDocument>
00014 #include <QTextStream>
00015 #include <QTextCodec>
00016 #include <stdlib.h>
00017
00018 #define TS_DOCTYPE "TS"
00019 #define TS_ELEMENT_ROOT "TS"
00020 #define TS_ELEMENT_CONTEXT "context"
00021 #define TS_ELEMENT_NAME "name"
00022 #define TS_ELEMENT_MESSAGE "message"
00023 #define TS_ELEMENT_SOURCE "source"
00024 #define TS_ELEMENT_TRANSLATION "translation"
00025 #define TS_ATTR_TRANSLATION_TYPE "type"
00026 #define TS_ATTR_VERSION "version"
00027
00028
00029
00030 QDomElement
00031 new_context_element(QDomDocument *ts, const QString &contextName)
00032 {
00033 QDomElement context, name;
00034
00035
00036 name = ts->createElement(TS_ELEMENT_NAME);
00037 name.appendChild(ts->createTextNode(contextName));
00038
00039
00040 context = ts->createElement(TS_ELEMENT_CONTEXT);
00041 context.appendChild(name);
00042 return context;
00043 }
00044
00045
00046
00047 QDomElement
00048 new_message_element(QDomDocument *ts,
00049 const QString &msgid, const QString &msgstr)
00050 {
00051 QDomElement message, source, translation;
00052
00053
00054 source = ts->createElement(TS_ELEMENT_SOURCE);
00055 source.appendChild(ts->createTextNode(msgid));
00056
00057
00058 translation = ts->createElement(TS_ELEMENT_TRANSLATION);
00059 if (!msgstr.isEmpty())
00060 translation.appendChild(ts->createTextNode(msgstr));
00061 else
00062 translation.setAttribute(TS_ATTR_TRANSLATION_TYPE, "unfinished");
00063
00064
00065
00066 message = ts->createElement(TS_ELEMENT_MESSAGE);
00067 message.appendChild(source);
00068 message.appendChild(translation);
00069
00070 return message;
00071 }
00072
00073
00074
00075 QDomDocument
00076 new_ts_document()
00077 {
00078 QDomDocument ts(TS_DOCTYPE);
00079
00080 QDomElement root = ts.createElement(TS_ELEMENT_ROOT);
00081 root.setAttribute(TS_ATTR_VERSION, "1.1");
00082 ts.appendChild(root);
00083
00084 return ts;
00085 }
00086
00087
00088
00089 QString
00090 parse_message_context(const QString &str)
00091 {
00092 QString out = str.trimmed();
00093 out = out.replace("\"", "");
00094 return out;
00095 }
00096
00097
00098
00099 QString
00100 parse_message_context_lame(const QString &str)
00101 {
00102 if (str.contains("#"))
00103 return str.section("#", 0, 0);
00104 return QString();
00105 }
00106
00107
00108
00109
00110 QString
00111 parse_message_string(const QString &msg)
00112 {
00113 QString out = msg.trimmed();
00114
00115 out.replace("\"\n\"", "");
00116 if (out.startsWith("\""))
00117 out = out.remove(0, 1);
00118 if (out.endsWith("\""))
00119 out.chop(1);
00120 out.replace("\\\"", "\"");
00121 return out;
00122 }
00123
00124
00125 QString
00126 read_next_line(QTextStream *stream)
00127 {
00128 stream->skipWhiteSpace();
00129 return stream->readLine().append("\n");
00130 }
00131
00132
00133
00134
00135 void
00136 skip_po_header(QTextStream *po)
00137 {
00138 QString line;
00139
00140 po->skipWhiteSpace();
00141
00142 line = po->readLine();
00143 while (!po->atEnd() && !line.isEmpty())
00144 line = po->readLine();
00145 }
00146
00147
00148
00149
00150
00151 int
00152 po2ts(QTextStream *po, QDomDocument *ts, QString *errorMessage)
00153 {
00154 QString line;
00155 QString msgctxt, msgid, msgstr;
00156 QHash<QString,QDomElement> contextElements;
00157 QDomElement contextElement, msgElement, transElement;
00158 int n_strings = 0;
00159
00160 Q_ASSERT(po);
00161 Q_ASSERT(ts);
00162 Q_ASSERT(errorMessage);
00163
00164 *ts = new_ts_document();
00165
00166 skip_po_header(po);
00167 line = read_next_line(po);
00168 while (!po->atEnd()) {
00169
00170 while (line.startsWith("#")) {
00171 if (line.startsWith("#:")) {
00172
00173 msgctxt = line.section(" ", 1);
00174 msgctxt = parse_message_context_lame(msgctxt);
00175 }
00176 line = read_next_line(po);
00177 }
00178
00179
00180
00181 if (line.startsWith("msgctxt ")) {
00182 msgctxt = line.section(" ", 1);
00183 msgctxt = parse_message_context(msgctxt);
00184 line = read_next_line(po);
00185 }
00186
00187
00188 if (!line.startsWith("msgid ")) {
00189 *errorMessage = "expected 'msgid' line";
00190 return -1;
00191 }
00192 msgid = line.section(" ", 1);
00193
00194 line = read_next_line(po);
00195 while (line.startsWith("\"")) {
00196 msgid.append(line);
00197 line = read_next_line(po);
00198 }
00199 msgid = parse_message_string(msgid);
00200
00201
00202 if (!line.startsWith("msgstr ")) {
00203 *errorMessage = "expected 'msgstr' line";
00204 return -1;
00205 }
00206 msgstr = line.section(" ", 1);
00207
00208 line = read_next_line(po);
00209 while (line.startsWith("\"")) {
00210 msgstr.append(line);
00211 line = read_next_line(po);
00212 }
00213 msgstr = parse_message_string(msgstr);
00214
00215
00216 if (contextElements.contains(msgctxt)) {
00217 contextElement = contextElements.value(msgctxt);
00218 } else {
00219 contextElement = new_context_element(ts, msgctxt);
00220 ts->documentElement().appendChild(contextElement);
00221 contextElements.insert(msgctxt, contextElement);
00222 }
00223 contextElement.appendChild(new_message_element(ts, msgid, msgstr));
00224
00225 n_strings++;
00226 }
00227 return n_strings;
00228 }
00229
00230
00231 void
00232 print_usage_and_exit()
00233 {
00234 QTextStream error(stderr);
00235 error << "usage: po2ts [-q] -i <infile.po> -o <outfile.ts> "
00236 "[-c <encoding>]\n";
00237 error << " -q (optional) Quiet mode (errors are still displayed)\n";
00238 error << " -i <infile.po> Input .po file\n";
00239 error << " -o <outfile.ts> Output .ts file\n";
00240 error << " -c <encoding> Text encoding (default: utf-8)\n";
00241 error.flush();
00242 exit(1);
00243 }
00244
00245 int
00246 main(int argc, char *argv[])
00247 {
00248 QTextStream error(stderr);
00249 QString errorMessage;
00250 char *infile, *outfile;
00251 QTextCodec *codec = QTextCodec::codecForName("utf-8");
00252 bool quiet = false;
00253
00254
00255 if (argc < 5 || argc > 8)
00256 print_usage_and_exit();
00257 for (int i = 1; i < argc; i++) {
00258 QString arg(argv[i]);
00259 if (!arg.compare("-q", Qt::CaseInsensitive))
00260 quiet = true;
00261 else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
00262 infile = argv[i];
00263 else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
00264 outfile = argv[i];
00265 else if (!arg.compare("-c", Qt::CaseInsensitive) && ++i < argc) {
00266 codec = QTextCodec::codecForName(argv[i]);
00267 if (!codec) {
00268 error << "Invalid text encoding specified\n";
00269 return 1;
00270 }
00271 } else
00272 print_usage_and_exit();
00273 }
00274
00275
00276 QFile poFile(infile);
00277 if (!poFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
00278 error << QString("Unable to open '%1' for reading: %2\n").arg(infile)
00279 .arg(poFile.errorString());
00280 return 2;
00281 }
00282
00283 QDomDocument ts;
00284 QTextStream po(&poFile);
00285 po.setCodec(codec);
00286 int n_strings = po2ts(&po, &ts, &errorMessage);
00287 if (n_strings < 0) {
00288 error << QString("Unable to convert '%1': %2\n").arg(infile)
00289 .arg(errorMessage);
00290 return 3;
00291 }
00292
00293
00294 QFile tsFile(outfile);
00295 if (!tsFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
00296 error << QString("Unable to open '%1' for writing: %2\n").arg(outfile)
00297 .arg(tsFile.errorString());
00298 return 4;
00299 }
00300
00301
00302 QTextStream out(&tsFile);
00303 out.setCodec(codec);
00304 out << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n")
00305 .arg(QString(codec->name()));
00306 out << ts.toString(4);
00307
00308 if (!quiet) {
00309 QTextStream results(stdout);
00310 results << QString("Converted %1 strings from %2 to %3.\n").arg(n_strings)
00311 .arg(infile)
00312 .arg(outfile);
00313 }
00314 return 0;
00315 }
00316