source: trunk/tools/linguist/lrelease/main.cpp

Last change on this file was 1032, checked in by Dmitry A. Kuminov, 14 years ago

OS/2: Build fix (r1030 regression).

File size: 13.1 KB
RevLine 
[2]1/****************************************************************************
2**
[846]3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
[561]4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
[2]6**
7** This file is part of the Qt Linguist of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
[561]24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
[2]27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
[561]36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
[2]38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "translator.h"
[651]43#include "profileevaluator.h"
[2]44
[561]45#ifndef QT_BOOTSTRAPPED
[2]46#include <QtCore/QCoreApplication>
[561]47#include <QtCore/QTranslator>
48#endif
[2]49#include <QtCore/QDebug>
50#include <QtCore/QDir>
51#include <QtCore/QFile>
52#include <QtCore/QFileInfo>
53#include <QtCore/QRegExp>
54#include <QtCore/QString>
55#include <QtCore/QStringList>
56#include <QtCore/QTextStream>
57
[846]58#include <iostream>
59
[561]60QT_USE_NAMESPACE
61
62#ifdef QT_BOOTSTRAPPED
63static void initBinaryDir(
[1032]64#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
[561]65 const char *argv0
66#endif
67 );
[846]68
69struct LR {
70 static inline QString tr(const char *sourceText, const char *comment = 0)
71 {
72 return QCoreApplication::translate("LRelease", sourceText, comment);
73 }
74};
75#else
76class LR {
77 Q_DECLARE_TR_FUNCTIONS(LRelease)
78};
[561]79#endif
80
[2]81static void printOut(const QString & out)
82{
83 QTextStream stream(stdout);
84 stream << out;
85}
86
87static void printUsage()
88{
[846]89 printOut(LR::tr(
[2]90 "Usage:\n"
91 " lrelease [options] project-file\n"
92 " lrelease [options] ts-files [-qm qm-file]\n\n"
93 "lrelease is part of Qt's Linguist tool chain. It can be used as a\n"
[561]94 "stand-alone tool to convert XML-based translations files in the TS\n"
95 "format into the 'compiled' QM format used by QTranslator objects.\n\n"
[2]96 "Options:\n"
97 " -help Display this information and exit\n"
[561]98 " -idbased\n"
99 " Use IDs instead of source strings for message keying\n"
[2]100 " -compress\n"
[561]101 " Compress the QM files\n"
[2]102 " -nounfinished\n"
103 " Do not include unfinished translations\n"
104 " -removeidentical\n"
105 " If the translated text is the same as\n"
106 " the source text, do not include the message\n"
[561]107 " -markuntranslated <prefix>\n"
108 " If a message has no real translation, use the source text\n"
109 " prefixed with the given string instead\n"
[2]110 " -silent\n"
[561]111 " Do not explain what is being done\n"
[2]112 " -version\n"
113 " Display the version of lrelease and exit\n"
114 ));
115}
116
117static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbose */)
118{
119 ConversionData cd;
120 bool ok = tor.load(tsFileName, cd, QLatin1String("auto"));
121 if (!ok) {
[846]122 std::cerr << qPrintable(LR::tr("lrelease error: %1").arg(cd.error()));
[2]123 } else {
124 if (!cd.errors().isEmpty())
125 printOut(cd.error());
126 }
[651]127 cd.clearErrors();
[2]128 return ok;
129}
130
131static bool releaseTranslator(Translator &tor, const QString &qmFileName,
[561]132 ConversionData &cd, bool removeIdentical)
[2]133{
[561]134 tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
[2]135
[561]136 if (cd.isVerbose())
[846]137 printOut(LR::tr("Updating '%1'...\n").arg(qmFileName));
[2]138 if (removeIdentical) {
[561]139 if (cd.isVerbose())
[846]140 printOut(LR::tr("Removing translations equal to source text in '%1'...\n").arg(qmFileName));
[2]141 tor.stripIdenticalSourceTranslations();
142 }
143
144 QFile file(qmFileName);
145 if (!file.open(QIODevice::WriteOnly)) {
[846]146 std::cerr << qPrintable(LR::tr("lrelease error: cannot create '%1': %2\n")
147 .arg(qmFileName, file.errorString()));
[2]148 return false;
149 }
150
[561]151 tor.normalizeTranslations(cd);
[2]152 bool ok = tor.release(&file, cd);
153 file.close();
154
155 if (!ok) {
[846]156 std::cerr << qPrintable(LR::tr("lrelease error: cannot save '%1': %2")
157 .arg(qmFileName, cd.error()));
[2]158 } else if (!cd.errors().isEmpty()) {
159 printOut(cd.error());
160 }
[651]161 cd.clearErrors();
162 return ok;
[2]163}
164
[561]165static bool releaseTsFile(const QString& tsFileName,
166 ConversionData &cd, bool removeIdentical)
[2]167{
168 Translator tor;
[561]169 if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
[2]170 return false;
171
172 QString qmFileName = tsFileName;
173 foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
174 if (qmFileName.endsWith(QLatin1Char('.') + fmt.extension)) {
175 qmFileName.chop(fmt.extension.length() + 1);
176 break;
177 }
178 }
179 qmFileName += QLatin1String(".qm");
180
[561]181 return releaseTranslator(tor, qmFileName, cd, removeIdentical);
[2]182}
183
184int main(int argc, char **argv)
185{
[561]186#ifdef QT_BOOTSTRAPPED
187 initBinaryDir(
[1032]188#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
[561]189 argv[0]
190#endif
191 );
192#else
[2]193 QCoreApplication app(argc, argv);
[846]194#ifndef Q_OS_WIN32
[2]195 QTranslator translator;
[846]196 QTranslator qtTranslator;
197 QString sysLocale = QLocale::system().name();
198 QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
199 if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
200 && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
[2]201 app.installTranslator(&translator);
[846]202 app.installTranslator(&qtTranslator);
203 }
204#endif // Q_OS_WIN32
205#endif // QT_BOOTSTRAPPED
[2]206
[561]207 ConversionData cd;
208 cd.m_verbose = true; // the default is true starting with Qt 4.2
[2]209 bool removeIdentical = false;
210 Translator tor;
[561]211 QStringList inputFiles;
[2]212 QString outputFile;
213
214 for (int i = 1; i < argc; ++i) {
[561]215 if (!strcmp(argv[i], "-compress")) {
216 cd.m_saveMode = SaveStripped;
[2]217 continue;
[561]218 } else if (!strcmp(argv[i], "-idbased")) {
219 cd.m_idBased = true;
[2]220 continue;
[561]221 } else if (!strcmp(argv[i], "-nocompress")) {
222 cd.m_saveMode = SaveEverything;
223 continue;
224 } else if (!strcmp(argv[i], "-removeidentical")) {
[2]225 removeIdentical = true;
226 continue;
[561]227 } else if (!strcmp(argv[i], "-nounfinished")) {
228 cd.m_ignoreUnfinished = true;
[2]229 continue;
[561]230 } else if (!strcmp(argv[i], "-markuntranslated")) {
231 if (i == argc - 1) {
232 printUsage();
233 return 1;
234 }
235 cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
236 } else if (!strcmp(argv[i], "-silent")) {
237 cd.m_verbose = false;
[2]238 continue;
[561]239 } else if (!strcmp(argv[i], "-verbose")) {
240 cd.m_verbose = true;
[2]241 continue;
[561]242 } else if (!strcmp(argv[i], "-version")) {
[846]243 printOut(LR::tr("lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)));
[2]244 return 0;
[561]245 } else if (!strcmp(argv[i], "-qm")) {
[2]246 if (i == argc - 1) {
247 printUsage();
248 return 1;
249 }
[561]250 outputFile = QString::fromLocal8Bit(argv[++i]);
251 } else if (!strcmp(argv[i], "-help")) {
[2]252 printUsage();
253 return 0;
[561]254 } else if (argv[i][0] == '-') {
[2]255 printUsage();
256 return 1;
257 } else {
[561]258 inputFiles << QString::fromLocal8Bit(argv[i]);
[2]259 }
260 }
261
[561]262 if (inputFiles.isEmpty()) {
[2]263 printUsage();
264 return 1;
265 }
266
[561]267 foreach (const QString &inputFile, inputFiles) {
268 if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
269 || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
[651]270 QFileInfo fi(inputFile);
271 ProFile pro(fi.absoluteFilePath());
272
273 ProFileEvaluator visitor;
274 visitor.setVerbose(cd.isVerbose());
275
276 if (!visitor.queryProFile(&pro)) {
[846]277 std::cerr << qPrintable(LR::tr(
278 "lrelease error: cannot read project file '%1'.\n")
279 .arg(inputFile));
[651]280 continue;
281 }
282 if (!visitor.accept(&pro)) {
[846]283 std::cerr << qPrintable(LR::tr(
284 "lrelease error: cannot process project file '%1'.\n")
285 .arg(inputFile));
[651]286 continue;
287 }
288
289 QStringList translations = visitor.values(QLatin1String("TRANSLATIONS"));
290 if (translations.isEmpty()) {
[846]291 std::cerr << qPrintable(LR::tr(
292 "lrelease warning: Met no 'TRANSLATIONS' entry in project file '%1'\n")
293 .arg(inputFile));
[2]294 } else {
[651]295 QDir proDir(fi.absolutePath());
296 foreach (const QString &trans, translations)
297 if (!releaseTsFile(QFileInfo(proDir, trans).filePath(), cd, removeIdentical))
298 return 1;
[2]299 }
300 } else {
301 if (outputFile.isEmpty()) {
[561]302 if (!releaseTsFile(inputFile, cd, removeIdentical))
[2]303 return 1;
304 } else {
[561]305 if (!loadTsFile(tor, inputFile, cd.isVerbose()))
[2]306 return 1;
307 }
308 }
309 }
310
311 if (!outputFile.isEmpty())
[561]312 return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
[2]313
314 return 0;
315}
[561]316
317#ifdef QT_BOOTSTRAPPED
318
319#ifdef Q_OS_WIN
320# include <windows.h>
321#endif
322
[1032]323#ifdef Q_OS_OS2
324# include <qt_os2.h>
325#endif
326
[561]327static QString binDir;
328
329static void initBinaryDir(
[1032]330#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
[561]331 const char *_argv0
332#endif
333 )
334{
[1030]335 // this is in sync with qmake_libraryInfoFile() from qmake/option.cpp
[561]336#ifdef Q_OS_WIN
337 wchar_t module_name[MAX_PATH];
338 GetModuleFileName(0, module_name, MAX_PATH);
339 QFileInfo filePath = QString::fromWCharArray(module_name);
340 binDir = filePath.filePath();
[1030]341#elif defined(Q_OS_OS2)
342 QFileInfo filePath;
343 static char appFileName[CCHMAXPATH] = "\0";
344 if (!appFileName[0]) {
345 PPIB ppib;
346 DosGetInfoBlocks(NULL, &ppib);
347 DosQueryModuleName(ppib->pib_hmte, sizeof(appFileName), appFileName);
348 }
349 binDir = QFileInfo(QString::fromLocal8Bit(appFileName)).filePath();
[561]350#else
351 QString argv0 = QFile::decodeName(QByteArray(_argv0));
352 QString absPath;
353
354 if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
355 /*
356 If argv0 starts with a slash, it is already an absolute
357 file path.
358 */
359 absPath = argv0;
360 } else if (argv0.contains(QLatin1Char('/'))) {
361 /*
362 If argv0 contains one or more slashes, it is a file path
363 relative to the current directory.
364 */
365 absPath = QDir::current().absoluteFilePath(argv0);
366 } else {
367 /*
368 Otherwise, the file path has to be determined using the
369 PATH environment variable.
370 */
371 QByteArray pEnv = qgetenv("PATH");
372 QDir currentDir = QDir::current();
373 QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":"));
374 for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
375 if ((*p).isEmpty())
376 continue;
377 QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
378 QFileInfo candidate_fi(candidate);
379 if (candidate_fi.exists() && !candidate_fi.isDir()) {
380 binDir = candidate_fi.canonicalPath();
381 return;
382 }
383 }
384 return;
385 }
386
387 QFileInfo fi(absPath);
388 if (fi.exists())
389 binDir = fi.canonicalPath();
390#endif
391}
392
393QT_BEGIN_NAMESPACE
394
395// The name is hard-coded in QLibraryInfo
396QString qmake_libraryInfoFile()
397{
398 if (binDir.isEmpty())
399 return QString();
400 return QDir(binDir).filePath(QString::fromLatin1("qt.conf"));
401}
402
403QT_END_NAMESPACE
404
405#endif // QT_BOOTSTRAPPED
Note: See TracBrowser for help on using the repository browser.