00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <QHash>
00012 #include <QFile>
00013 #include <QTextStream>
00014 #include <QTextCodec>
00015 #include <QDateTime>
00016 #include <QStringList>
00017 #include <stdlib.h>
00018
00019 #include "nsh2po_config.h"
00020
00021
00022
00023
00024 QString
00025 parse_message_context(const QString &str)
00026 {
00027 QString out = str.trimmed();
00028 out = out.replace("\"", "");
00029 return out;
00030 }
00031
00032
00033
00034 QString
00035 parse_message_context_lame(const QString &str)
00036 {
00037 if (str.contains("#"))
00038 return str.section("#", 0, 0);
00039 return QString();
00040 }
00041
00042
00043 QString
00044 parse_message_string(const QString &msg)
00045 {
00046 QString out = msg.trimmed();
00047
00048 if (out.startsWith("\""))
00049 out = out.remove(0, 1);
00050 if (out.endsWith("\""))
00051 out.chop(1);
00052 out.replace("\\\"", "\"");
00053 out.replace("\\r\\n", "\\n");
00054 return out;
00055 }
00056
00057
00058 QString
00059 parse_nsh_langstring(const QString &msg)
00060 {
00061 QString out = msg.trimmed();
00062
00063 if (out.startsWith("\""))
00064 out = out.remove(0, 1);
00065 if (out.endsWith("\""))
00066 out.chop(1);
00067 out.replace("$\\n", "\\n");
00068 out.replace("$\\r", "");
00069 out.replace("\\r", "");
00070 return out;
00071 }
00072
00073
00074 QString
00075 create_po_timestamp()
00076 {
00077 QDateTime now = QDateTime::currentDateTime().toUTC();
00078 return now.toString("yyyy-MM-dd hh:mm+0000");
00079 }
00080
00081
00082 QString
00083 create_po_header(const QString &charset)
00084 {
00085 QString header;
00086 QString tstamp = create_po_timestamp();
00087
00088 header.append("msgid \"\"\n");
00089 header.append("msgstr \"\"\n");
00090 header.append("\"Project-Id-Version: "NSH2PO_PROJECT_ID"\\n\"\n");
00091 header.append("\"Report-Msgid-Bugs-To: "NSH2PO_CONTACT_ADDR"\\n\"\n");
00092 header.append(QString("\"POT-Creation-Date: %1\\n\"\n").arg(tstamp));
00093 header.append("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n");
00094 header.append("\"Last-Translator: \\n\"\n");
00095 header.append("\"Language-Team: "NSH2PO_LANGUAGE_TEAM"\\n\"\n");
00096 header.append("\"MIME-Version: 1.0\\n\"\n");
00097 header.append(QString("\"Content-Type: text/plain; "
00098 "charset=%1\\n\"\n").arg(charset));
00099 header.append("\"Content-Transfer-Encoding: 8bit\\n\"\n");
00100 header.append("\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n");
00101 header.append("\"X-Generator: Vidalia nsh2po "NSH2PO_VERSION"\\n\"\n");
00102 header.append("\n");
00103
00104 return header;
00105 }
00106
00107
00108 QString
00109 read_next_line(QTextStream *stream)
00110 {
00111 Q_ASSERT(stream);
00112 stream->skipWhiteSpace();
00113 return stream->readLine();
00114 }
00115
00116
00117
00118
00119 void
00120 skip_pot_header(QTextStream *pot)
00121 {
00122 QString line;
00123
00124 pot->skipWhiteSpace();
00125
00126 line = pot->readLine();
00127 while (!pot->atEnd() && !line.isEmpty())
00128 line = pot->readLine();
00129 }
00130
00131
00132
00133
00134
00135 bool
00136 parse_po_template(QTextStream *pot, QHash<QString,QString> *out,
00137 QString *errmsg)
00138 {
00139 QString line, msgctxt, msgid;
00140
00141 skip_pot_header(pot);
00142 line = read_next_line(pot);
00143 while (!pot->atEnd()) {
00144 if (!line.startsWith("#:") && !line.startsWith("msgctxt")) {
00145
00146 line = read_next_line(pot);
00147 continue;
00148 }
00149
00150 if (line.startsWith("#:")) {
00151
00152 msgctxt = line.section(" ", 1);
00153 msgctxt = parse_message_context_lame(msgctxt);
00154 line = read_next_line(pot);
00155 continue;
00156 }
00157
00158 if (line.startsWith("msgctxt ")) {
00159
00160
00161 msgctxt = line.section(" ", 1);
00162 msgctxt = parse_message_context(msgctxt);
00163 line = read_next_line(pot);
00164 }
00165
00166 if (!line.startsWith("msgid ")) {
00167 *errmsg = "expected 'msgid' line";
00168 return false;
00169 }
00170 msgid = line.section(" ", 1);
00171
00172 line = read_next_line(pot);
00173 while (line.startsWith("\"")) {
00174
00175 msgid.append(line);
00176 line = read_next_line(pot);
00177 }
00178 msgid = parse_message_string(msgid);
00179
00180 out->insert(msgctxt, msgid);
00181 }
00182
00183 return true;
00184 }
00185
00186
00187
00188
00189
00190 int
00191 nsh2po(QTextStream *nsh, const QString &charset,
00192 const QHash<QString,QString> &pot, QString *po, QString *errmsg)
00193 {
00194 QString line, msgctxt, msgid, msgstr;
00195 QStringList parts;
00196 QHash<QString,QString> langStrings;
00197 int idx, n_strings;
00198
00199 *po = create_po_header(charset);
00200
00201
00202 while (!nsh->atEnd()) {
00203 line = read_next_line(nsh);
00204 if (!line.startsWith("LangString "))
00205 continue;
00206
00207 parts = line.split(" ");
00208 if (parts.size() > 3)
00209 msgctxt = parts.at(1);
00210 else
00211 continue;
00212
00213 idx = line.indexOf("\"");
00214 if (idx > 0)
00215 msgstr = parse_nsh_langstring(line.mid(idx));
00216 langStrings.insert(msgctxt, msgstr);
00217 }
00218
00219
00220 n_strings = 0;
00221 foreach (QString msgctxt, pot.keys()) {
00222 msgid = pot.value(msgctxt);
00223 if (langStrings.contains(msgctxt)) {
00224 msgstr = langStrings.value(msgctxt);
00225 n_strings++;
00226 } else {
00227 msgstr = msgid;
00228 }
00229
00230 po->append(QString("msgctxt \"%1\"\n").arg(msgctxt));
00231 po->append(QString("msgid \"%1\"\n").arg(msgid));
00232 po->append(QString("msgstr \"%1\"\n").arg(msgstr));
00233 po->append("\n");
00234 }
00235 return n_strings;
00236 }
00237
00238
00239
00240
00241 bool
00242 write_po_output(const char *poFileName, const QString &po, QTextCodec *codec,
00243 QString *errmsg)
00244 {
00245 QFile poFile(poFileName);
00246 if (!poFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
00247 *errmsg = QString("Unable to open '%1' for writing.").arg(poFileName);
00248 return false;
00249 }
00250
00251 QTextStream out(&poFile);
00252 out.setCodec(codec);
00253 out << po;
00254 return true;
00255 }
00256
00257
00258 void
00259 print_usage_and_exit()
00260 {
00261 QTextStream error(stderr);
00262 error << "usage: nsh2po [-q] -t <template.pot> -i <infile.nsh> "
00263 "-o <outfile.po> [-c <encoding>]\n";
00264 error << " -q (optional) Quiet mode (errors are still displayed)\n";
00265 error << " -t <template.pot> PO template file\n";
00266 error << " -i <infile.ts> Input .ts file\n";
00267 error << " -o <outfile.po> Output .po file\n";
00268 error << " -c <encoding> Text encoding (default: utf-8)\n";
00269 error.flush();
00270 exit(1);
00271 }
00272
00273 int
00274 main(int argc, char *argv[])
00275 {
00276 QTextStream error(stderr);
00277 QString po, errorMessage;
00278 char *outFileName;
00279 QFile potFile, nshFile;
00280 QTextStream pot, nsh;
00281 QTextCodec *codec = QTextCodec::codecForName("utf-8");
00282 bool quiet = false;
00283
00284
00285 if (argc < 7 || argc > 10)
00286 print_usage_and_exit();
00287 for (int i = 1; i < argc; i++) {
00288 QString arg(argv[i]);
00289 if (!arg.compare("-q", Qt::CaseInsensitive)) {
00290 quiet = true;
00291 } else if (!arg.compare("-t", Qt::CaseInsensitive) && ++i < argc) {
00292
00293 potFile.setFileName(argv[i]);
00294 if (!potFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
00295 error << QString("Couldn't open '%1' for reading: ").arg(argv[i])
00296 << potFile.errorString();
00297 return 1;
00298 }
00299 pot.setDevice(&potFile);
00300 } else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc) {
00301
00302 nshFile.setFileName(argv[i]);
00303 if (!nshFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
00304 error << QString("Couldn't open '%1' for reading: ").arg(argv[i])
00305 << nshFile.errorString();
00306 return 1;
00307 }
00308 nsh.setDevice(&nshFile);
00309 } else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc) {
00310 outFileName = argv[i];
00311 } else if (!arg.compare("-c", Qt::CaseInsensitive) && ++i < argc) {
00312
00313 codec = QTextCodec::codecForName(argv[i]);
00314 if (!codec) {
00315 error << "Invalid text encoding specified.\n";
00316 return 1;
00317 }
00318 } else
00319 print_usage_and_exit();
00320 }
00321 pot.setCodec(codec);
00322 nsh.setCodec(codec);
00323
00324
00325 QHash<QString,QString> poTemplate;
00326 if (!parse_po_template(&pot, &poTemplate, &errorMessage)) {
00327 error << QString("Failed to parse PO template: %1\n").arg(errorMessage);
00328 return 1;
00329 }
00330
00331
00332 int n_strings = nsh2po(&nsh, QString(codec->name()), poTemplate,
00333 &po, &errorMessage);
00334 if (n_strings < 0) {
00335 error << QString("Conversion failed: %1\n").arg(errorMessage);
00336 return 2;
00337 }
00338
00339
00340 if (!write_po_output(outFileName, po, codec, &errorMessage)) {
00341 error << QString("Failed to write PO output: %1\n").arg(errorMessage);
00342 return 3;
00343 }
00344
00345 if (!quiet) {
00346 QTextStream out(stdout);
00347 out << QString("Wrote %1 strings to '%2'.\n").arg(n_strings)
00348 .arg(outFileName);
00349 }
00350 return 0;
00351 }
00352