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

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

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 12.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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#ifndef Q_OS_WIN
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#ifndef Q_OS_WIN
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
323static QString binDir;
324
325static void initBinaryDir(
326#ifndef Q_OS_WIN
327 const char *_argv0
328#endif
329 )
330{
331#ifdef Q_OS_WIN
332 wchar_t module_name[MAX_PATH];
333 GetModuleFileName(0, module_name, MAX_PATH);
334 QFileInfo filePath = QString::fromWCharArray(module_name);
335 binDir = filePath.filePath();
336#else
337 QString argv0 = QFile::decodeName(QByteArray(_argv0));
338 QString absPath;
339
340 if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
341 /*
342 If argv0 starts with a slash, it is already an absolute
343 file path.
344 */
345 absPath = argv0;
346 } else if (argv0.contains(QLatin1Char('/'))) {
347 /*
348 If argv0 contains one or more slashes, it is a file path
349 relative to the current directory.
350 */
351 absPath = QDir::current().absoluteFilePath(argv0);
352 } else {
353 /*
354 Otherwise, the file path has to be determined using the
355 PATH environment variable.
356 */
357 QByteArray pEnv = qgetenv("PATH");
358 QDir currentDir = QDir::current();
359 QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":"));
360 for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
361 if ((*p).isEmpty())
362 continue;
363 QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
364 QFileInfo candidate_fi(candidate);
365 if (candidate_fi.exists() && !candidate_fi.isDir()) {
366 binDir = candidate_fi.canonicalPath();
367 return;
368 }
369 }
370 return;
371 }
372
373 QFileInfo fi(absPath);
374 if (fi.exists())
375 binDir = fi.canonicalPath();
376#endif
377}
378
379QT_BEGIN_NAMESPACE
380
381// The name is hard-coded in QLibraryInfo
382QString qmake_libraryInfoFile()
383{
384 if (binDir.isEmpty())
385 return QString();
386 return QDir(binDir).filePath(QString::fromLatin1("qt.conf"));
387}
388
389QT_END_NAMESPACE
390
391#endif // QT_BOOTSTRAPPED
Note: See TracBrowser for help on using the repository browser.