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
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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**
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.
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**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "translator.h"
43#include "profileevaluator.h"
44
45#ifndef QT_BOOTSTRAPPED
46#include <QtCore/QCoreApplication>
47#include <QtCore/QTranslator>
48#endif
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
58#include <iostream>
59
60QT_USE_NAMESPACE
61
62#ifdef QT_BOOTSTRAPPED
63static void initBinaryDir(
64#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
65 const char *argv0
66#endif
67 );
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};
79#endif
80
81static void printOut(const QString & out)
82{
83 QTextStream stream(stdout);
84 stream << out;
85}
86
87static void printUsage()
88{
89 printOut(LR::tr(
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"
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"
96 "Options:\n"
97 " -help Display this information and exit\n"
98 " -idbased\n"
99 " Use IDs instead of source strings for message keying\n"
100 " -compress\n"
101 " Compress the QM files\n"
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"
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"
110 " -silent\n"
111 " Do not explain what is being done\n"
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) {
122 std::cerr << qPrintable(LR::tr("lrelease error: %1").arg(cd.error()));
123 } else {
124 if (!cd.errors().isEmpty())
125 printOut(cd.error());
126 }
127 cd.clearErrors();
128 return ok;
129}
130
131static bool releaseTranslator(Translator &tor, const QString &qmFileName,
132 ConversionData &cd, bool removeIdentical)
133{
134 tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
135
136 if (cd.isVerbose())
137 printOut(LR::tr("Updating '%1'...\n").arg(qmFileName));
138 if (removeIdentical) {
139 if (cd.isVerbose())
140 printOut(LR::tr("Removing translations equal to source text in '%1'...\n").arg(qmFileName));
141 tor.stripIdenticalSourceTranslations();
142 }
143
144 QFile file(qmFileName);
145 if (!file.open(QIODevice::WriteOnly)) {
146 std::cerr << qPrintable(LR::tr("lrelease error: cannot create '%1': %2\n")
147 .arg(qmFileName, file.errorString()));
148 return false;
149 }
150
151 tor.normalizeTranslations(cd);
152 bool ok = tor.release(&file, cd);
153 file.close();
154
155 if (!ok) {
156 std::cerr << qPrintable(LR::tr("lrelease error: cannot save '%1': %2")
157 .arg(qmFileName, cd.error()));
158 } else if (!cd.errors().isEmpty()) {
159 printOut(cd.error());
160 }
161 cd.clearErrors();
162 return ok;
163}
164
165static bool releaseTsFile(const QString& tsFileName,
166 ConversionData &cd, bool removeIdentical)
167{
168 Translator tor;
169 if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
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
181 return releaseTranslator(tor, qmFileName, cd, removeIdentical);
182}
183
184int main(int argc, char **argv)
185{
186#ifdef QT_BOOTSTRAPPED
187 initBinaryDir(
188#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
189 argv[0]
190#endif
191 );
192#else
193 QCoreApplication app(argc, argv);
194#ifndef Q_OS_WIN32
195 QTranslator translator;
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)) {
201 app.installTranslator(&translator);
202 app.installTranslator(&qtTranslator);
203 }
204#endif // Q_OS_WIN32
205#endif // QT_BOOTSTRAPPED
206
207 ConversionData cd;
208 cd.m_verbose = true; // the default is true starting with Qt 4.2
209 bool removeIdentical = false;
210 Translator tor;
211 QStringList inputFiles;
212 QString outputFile;
213
214 for (int i = 1; i < argc; ++i) {
215 if (!strcmp(argv[i], "-compress")) {
216 cd.m_saveMode = SaveStripped;
217 continue;
218 } else if (!strcmp(argv[i], "-idbased")) {
219 cd.m_idBased = true;
220 continue;
221 } else if (!strcmp(argv[i], "-nocompress")) {
222 cd.m_saveMode = SaveEverything;
223 continue;
224 } else if (!strcmp(argv[i], "-removeidentical")) {
225 removeIdentical = true;
226 continue;
227 } else if (!strcmp(argv[i], "-nounfinished")) {
228 cd.m_ignoreUnfinished = true;
229 continue;
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;
238 continue;
239 } else if (!strcmp(argv[i], "-verbose")) {
240 cd.m_verbose = true;
241 continue;
242 } else if (!strcmp(argv[i], "-version")) {
243 printOut(LR::tr("lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)));
244 return 0;
245 } else if (!strcmp(argv[i], "-qm")) {
246 if (i == argc - 1) {
247 printUsage();
248 return 1;
249 }
250 outputFile = QString::fromLocal8Bit(argv[++i]);
251 } else if (!strcmp(argv[i], "-help")) {
252 printUsage();
253 return 0;
254 } else if (argv[i][0] == '-') {
255 printUsage();
256 return 1;
257 } else {
258 inputFiles << QString::fromLocal8Bit(argv[i]);
259 }
260 }
261
262 if (inputFiles.isEmpty()) {
263 printUsage();
264 return 1;
265 }
266
267 foreach (const QString &inputFile, inputFiles) {
268 if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
269 || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
270 QFileInfo fi(inputFile);
271 ProFile pro(fi.absoluteFilePath());
272
273 ProFileEvaluator visitor;
274 visitor.setVerbose(cd.isVerbose());
275
276 if (!visitor.queryProFile(&pro)) {
277 std::cerr << qPrintable(LR::tr(
278 "lrelease error: cannot read project file '%1'.\n")
279 .arg(inputFile));
280 continue;
281 }
282 if (!visitor.accept(&pro)) {
283 std::cerr << qPrintable(LR::tr(
284 "lrelease error: cannot process project file '%1'.\n")
285 .arg(inputFile));
286 continue;
287 }
288
289 QStringList translations = visitor.values(QLatin1String("TRANSLATIONS"));
290 if (translations.isEmpty()) {
291 std::cerr << qPrintable(LR::tr(
292 "lrelease warning: Met no 'TRANSLATIONS' entry in project file '%1'\n")
293 .arg(inputFile));
294 } else {
295 QDir proDir(fi.absolutePath());
296 foreach (const QString &trans, translations)
297 if (!releaseTsFile(QFileInfo(proDir, trans).filePath(), cd, removeIdentical))
298 return 1;
299 }
300 } else {
301 if (outputFile.isEmpty()) {
302 if (!releaseTsFile(inputFile, cd, removeIdentical))
303 return 1;
304 } else {
305 if (!loadTsFile(tor, inputFile, cd.isVerbose()))
306 return 1;
307 }
308 }
309 }
310
311 if (!outputFile.isEmpty())
312 return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
313
314 return 0;
315}
316
317#ifdef QT_BOOTSTRAPPED
318
319#ifdef Q_OS_WIN
320# include <windows.h>
321#endif
322
323#ifdef Q_OS_OS2
324# include <qt_os2.h>
325#endif
326
327static QString binDir;
328
329static void initBinaryDir(
330#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
331 const char *_argv0
332#endif
333 )
334{
335 // this is in sync with qmake_libraryInfoFile() from qmake/option.cpp
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();
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();
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.