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

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

trunk: Merged in qt 4.6.1 sources.

File size: 11.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 "proreader.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
58QT_USE_NAMESPACE
59
60#ifdef QT_BOOTSTRAPPED
61static void initBinaryDir(
62#ifndef Q_OS_WIN
63 const char *argv0
64#endif
65 );
66#endif
67
68static void printOut(const QString & out)
69{
70 QTextStream stream(stdout);
71 stream << out;
72}
73
74static void printUsage()
75{
76 printOut(QCoreApplication::tr(
77 "Usage:\n"
78 " lrelease [options] project-file\n"
79 " lrelease [options] ts-files [-qm qm-file]\n\n"
80 "lrelease is part of Qt's Linguist tool chain. It can be used as a\n"
81 "stand-alone tool to convert XML-based translations files in the TS\n"
82 "format into the 'compiled' QM format used by QTranslator objects.\n\n"
83 "Options:\n"
84 " -help Display this information and exit\n"
85 " -idbased\n"
86 " Use IDs instead of source strings for message keying\n"
87 " -compress\n"
88 " Compress the QM files\n"
89 " -nounfinished\n"
90 " Do not include unfinished translations\n"
91 " -removeidentical\n"
92 " If the translated text is the same as\n"
93 " the source text, do not include the message\n"
94 " -markuntranslated <prefix>\n"
95 " If a message has no real translation, use the source text\n"
96 " prefixed with the given string instead\n"
97 " -silent\n"
98 " Do not explain what is being done\n"
99 " -version\n"
100 " Display the version of lrelease and exit\n"
101 ));
102}
103
104static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbose */)
105{
106 ConversionData cd;
107 bool ok = tor.load(tsFileName, cd, QLatin1String("auto"));
108 if (!ok) {
109 qWarning("lrelease error: %s\n", qPrintable(cd.error()));
110 } else {
111 if (!cd.errors().isEmpty())
112 printOut(cd.error());
113 }
114 return ok;
115}
116
117static bool releaseTranslator(Translator &tor, const QString &qmFileName,
118 ConversionData &cd, bool removeIdentical)
119{
120 tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
121
122 if (cd.isVerbose())
123 printOut(QCoreApplication::tr( "Updating '%1'...\n").arg(qmFileName));
124 if (removeIdentical) {
125 if (cd.isVerbose())
126 printOut(QCoreApplication::tr( "Removing translations equal to source text in '%1'...\n").arg(qmFileName));
127 tor.stripIdenticalSourceTranslations();
128 }
129
130 QFile file(qmFileName);
131 if (!file.open(QIODevice::WriteOnly)) {
132 qWarning("lrelease error: cannot create '%s': %s\n",
133 qPrintable(qmFileName), qPrintable(file.errorString()));
134 return false;
135 }
136
137 tor.normalizeTranslations(cd);
138 bool ok = tor.release(&file, cd);
139 file.close();
140
141 if (!ok) {
142 qWarning("lrelease error: cannot save '%s': %s\n",
143 qPrintable(qmFileName), qPrintable(cd.error()));
144 return false;
145 } else if (!cd.errors().isEmpty()) {
146 printOut(cd.error());
147 }
148 return true;
149}
150
151static bool releaseTsFile(const QString& tsFileName,
152 ConversionData &cd, bool removeIdentical)
153{
154 Translator tor;
155 if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
156 return false;
157
158 QString qmFileName = tsFileName;
159 foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
160 if (qmFileName.endsWith(QLatin1Char('.') + fmt.extension)) {
161 qmFileName.chop(fmt.extension.length() + 1);
162 break;
163 }
164 }
165 qmFileName += QLatin1String(".qm");
166
167 return releaseTranslator(tor, qmFileName, cd, removeIdentical);
168}
169
170int main(int argc, char **argv)
171{
172#ifdef QT_BOOTSTRAPPED
173 initBinaryDir(
174#ifndef Q_OS_WIN
175 argv[0]
176#endif
177 );
178#else
179 QCoreApplication app(argc, argv);
180 QTranslator translator;
181 if (translator.load(QLatin1String("lrelease_") + QLocale::system().name()))
182 app.installTranslator(&translator);
183#endif
184
185 ConversionData cd;
186 cd.m_verbose = true; // the default is true starting with Qt 4.2
187 bool removeIdentical = false;
188 Translator tor;
189 QStringList inputFiles;
190 QString outputFile;
191
192 for (int i = 1; i < argc; ++i) {
193 if (!strcmp(argv[i], "-compress")) {
194 cd.m_saveMode = SaveStripped;
195 continue;
196 } else if (!strcmp(argv[i], "-idbased")) {
197 cd.m_idBased = true;
198 continue;
199 } else if (!strcmp(argv[i], "-nocompress")) {
200 cd.m_saveMode = SaveEverything;
201 continue;
202 } else if (!strcmp(argv[i], "-removeidentical")) {
203 removeIdentical = true;
204 continue;
205 } else if (!strcmp(argv[i], "-nounfinished")) {
206 cd.m_ignoreUnfinished = true;
207 continue;
208 } else if (!strcmp(argv[i], "-markuntranslated")) {
209 if (i == argc - 1) {
210 printUsage();
211 return 1;
212 }
213 cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
214 } else if (!strcmp(argv[i], "-silent")) {
215 cd.m_verbose = false;
216 continue;
217 } else if (!strcmp(argv[i], "-verbose")) {
218 cd.m_verbose = true;
219 continue;
220 } else if (!strcmp(argv[i], "-version")) {
221 printOut(QCoreApplication::tr( "lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)) );
222 return 0;
223 } else if (!strcmp(argv[i], "-qm")) {
224 if (i == argc - 1) {
225 printUsage();
226 return 1;
227 }
228 outputFile = QString::fromLocal8Bit(argv[++i]);
229 } else if (!strcmp(argv[i], "-help")) {
230 printUsage();
231 return 0;
232 } else if (argv[i][0] == '-') {
233 printUsage();
234 return 1;
235 } else {
236 inputFiles << QString::fromLocal8Bit(argv[i]);
237 }
238 }
239
240 if (inputFiles.isEmpty()) {
241 printUsage();
242 return 1;
243 }
244
245 foreach (const QString &inputFile, inputFiles) {
246 if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
247 || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
248 QHash<QByteArray, QStringList> varMap;
249 bool ok = evaluateProFile(inputFile, cd.isVerbose(), &varMap);
250 if (ok) {
251 QStringList translations = varMap.value("TRANSLATIONS");
252 if (translations.isEmpty()) {
253 qWarning("lrelease warning: Met no 'TRANSLATIONS' entry in"
254 " project file '%s'\n",
255 qPrintable(inputFile));
256 } else {
257 foreach (const QString &trans, translations)
258 if (!releaseTsFile(trans, cd, removeIdentical))
259 return 1;
260 }
261 } else {
262 qWarning("error: lrelease encountered project file functionality that is currently not supported.\n"
263 "You might want to consider using TS files as input instead of a project file.\n"
264 "Try the following syntax:\n"
265 " lrelease [options] ts-files [-qm qm-file]\n");
266 }
267 } else {
268 if (outputFile.isEmpty()) {
269 if (!releaseTsFile(inputFile, cd, removeIdentical))
270 return 1;
271 } else {
272 if (!loadTsFile(tor, inputFile, cd.isVerbose()))
273 return 1;
274 }
275 }
276 }
277
278 if (!outputFile.isEmpty())
279 return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
280
281 return 0;
282}
283
284#ifdef QT_BOOTSTRAPPED
285
286#ifdef Q_OS_WIN
287# include <windows.h>
288#endif
289
290static QString binDir;
291
292static void initBinaryDir(
293#ifndef Q_OS_WIN
294 const char *_argv0
295#endif
296 )
297{
298#ifdef Q_OS_WIN
299 wchar_t module_name[MAX_PATH];
300 GetModuleFileName(0, module_name, MAX_PATH);
301 QFileInfo filePath = QString::fromWCharArray(module_name);
302 binDir = filePath.filePath();
303#else
304 QString argv0 = QFile::decodeName(QByteArray(_argv0));
305 QString absPath;
306
307 if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
308 /*
309 If argv0 starts with a slash, it is already an absolute
310 file path.
311 */
312 absPath = argv0;
313 } else if (argv0.contains(QLatin1Char('/'))) {
314 /*
315 If argv0 contains one or more slashes, it is a file path
316 relative to the current directory.
317 */
318 absPath = QDir::current().absoluteFilePath(argv0);
319 } else {
320 /*
321 Otherwise, the file path has to be determined using the
322 PATH environment variable.
323 */
324 QByteArray pEnv = qgetenv("PATH");
325 QDir currentDir = QDir::current();
326 QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":"));
327 for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
328 if ((*p).isEmpty())
329 continue;
330 QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
331 QFileInfo candidate_fi(candidate);
332 if (candidate_fi.exists() && !candidate_fi.isDir()) {
333 binDir = candidate_fi.canonicalPath();
334 return;
335 }
336 }
337 return;
338 }
339
340 QFileInfo fi(absPath);
341 if (fi.exists())
342 binDir = fi.canonicalPath();
343#endif
344}
345
346QT_BEGIN_NAMESPACE
347
348// The name is hard-coded in QLibraryInfo
349QString qmake_libraryInfoFile()
350{
351 if (binDir.isEmpty())
352 return QString();
353 return QDir(binDir).filePath(QString::fromLatin1("qt.conf"));
354}
355
356QT_END_NAMESPACE
357
358#endif // QT_BOOTSTRAPPED
Note: See TracBrowser for help on using the repository browser.