Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
5#include "lupdate.h"
6#if QT_CONFIG(clangcpp)
7#include "cpp_clang.h"
8#endif
9
10#include <profileutils.h>
11#include <projectdescriptionreader.h>
12#include <qrcreader.h>
13#include <runqttool.h>
14#include <translator.h>
15
16#include <QtCore/QCoreApplication>
17#include <QtCore/QDir>
18#include <QtCore/QFile>
19#include <QtCore/QFileInfo>
20#include <QtCore/QLibraryInfo>
21#include <QtCore/QRegularExpression>
22#include <QtCore/QString>
23#include <QtCore/QStringList>
24#include <QtCore/QTranslator>
25
26#include <iostream>
27
28using namespace Qt::StringLiterals;
29
30bool useClangToParseCpp = false;
31QString commandLineCompilationDatabaseDir; // for the path to the json file passed as a command line argument.
32 // Has priority over what is in the .pro file and passed to the project.
34
35// Can't have an array of QStaticStringData<N> for different N, so
36// use QString, which requires constructor calls. Doesn't matter
37// much, since this is in an app, not a lib:
39// MSVC can't handle the lambda in this array if QStringLiteral expands
40// to a lambda. In that case, use a QString instead.
41#if defined(Q_CC_MSVC) && defined(Q_COMPILER_LAMBDA)
42#define STRINGLITERAL(F) QLatin1String(#F),
43#else
44#define STRINGLITERAL(F) QStringLiteral(#F),
45#endif
46 LUPDATE_FOR_EACH_TR_FUNCTION(STRINGLITERAL)
47#undef STRINGLITERAL
48};
49Q_STATIC_ASSERT((TrFunctionAliasManager::NumTrFunctions == sizeof defaultTrFunctionNames / sizeof *defaultTrFunctionNames));
50
51static int trFunctionByDefaultName(const QString &trFunctionName)
52{
53 for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
54 if (trFunctionName == defaultTrFunctionNames[i])
55 return i;
56 return -1;
57}
58
61{
62 for (int i = 0; i < NumTrFunctions; ++i)
63 m_trFunctionAliases[i].push_back(defaultTrFunctionNames[i]);
64}
65
67
68int TrFunctionAliasManager::trFunctionByName(const QString &trFunctionName) const
69{
70 ensureTrFunctionHashUpdated();
71 // this function needs to be fast
72 const auto it = m_nameToTrFunctionMap.constFind(trFunctionName);
73 return it == m_nameToTrFunctionMap.cend() ? -1 : *it;
74}
75
76void TrFunctionAliasManager::modifyAlias(int trFunction, const QString &alias, Operation op)
77{
78 QList<QString> &list = m_trFunctionAliases[trFunction];
79 if (op == SetAlias)
80 list.clear();
81 list.push_back(alias);
82 m_nameToTrFunctionMap.clear();
83}
84
85void TrFunctionAliasManager::ensureTrFunctionHashUpdated() const
86{
87 if (!m_nameToTrFunctionMap.empty())
88 return;
89
90 NameToTrFunctionMap nameToTrFunctionMap;
91 for (int i = 0; i < NumTrFunctions; ++i)
92 for (const QString &alias : m_trFunctionAliases[i])
93 nameToTrFunctionMap[alias] = TrFunction(i);
94 // commit:
95 m_nameToTrFunctionMap.swap(nameToTrFunctionMap);
96}
97
99{
100 ensureTrFunctionHashUpdated();
101 return m_nameToTrFunctionMap;
102}
103
105{
106 QStringList result;
108 for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
109 result.push_back(defaultTrFunctionNames[i]);
110 return result;
111}
112
114{
115 QStringList result;
116 result.reserve(NumTrFunctions);
117 for (int i = 0; i < NumTrFunctions; ++i)
118 result.push_back(defaultTrFunctionNames[i] +
119 QLatin1String(" (=") +
120 m_trFunctionAliases[i].join(QLatin1Char('=')) +
121 QLatin1Char(')'));
122 return result;
123}
124
126{
127 QStringList result;
128 result.reserve(NumTrFunctions);
129 for (int i = 0; i < NumTrFunctions; ++i) {
130 for (int ii = 1; ii < m_trFunctionAliases[i].size() ; ii++) {
131 // ii = 0 is the default name. Not listed here
132 result.push_back(m_trFunctionAliases[i][ii]);
133 }
134 }
135 return result;
136}
137
139
140QString ParserTool::transcode(const QString &str)
141{
142 static const char tab[] = "abfnrtv";
143 static const char backTab[] = "\a\b\f\n\r\t\v";
144 // This function has to convert back to bytes, as C's \0* sequences work at that level.
145 const QByteArray in = str.toUtf8();
146 QByteArray out;
147
148 out.reserve(in.size());
149 for (int i = 0; i < in.size();) {
150 uchar c = in[i++];
151 if (c == '\\') {
152 if (i >= in.size())
153 break;
154 c = in[i++];
155
156 if (c == '\n')
157 continue;
158
159 if (c == 'x' || c == 'u' || c == 'U') {
160 const bool unicode = (c != 'x');
161 QByteArray hex;
162 while (i < in.size() && isxdigit((c = in[i]))) {
163 hex += c;
164 i++;
165 }
166 if (unicode)
167 out += QString(QChar(hex.toUInt(nullptr, 16))).toUtf8();
168 else
169 out += hex.toUInt(nullptr, 16);
170 } else if (c >= '0' && c < '8') {
171 QByteArray oct;
172 int n = 0;
173 oct += c;
174 while (n < 2 && i < in.size() && (c = in[i]) >= '0' && c < '8') {
175 i++;
176 n++;
177 oct += c;
178 }
179 out += oct.toUInt(0, 8);
180 } else {
181 const char *p = strchr(tab, c);
182 out += !p ? c : backTab[p - tab];
183 }
184 } else {
185 out += c;
186 }
187 }
188 return QString::fromUtf8(out.constData(), out.size());
189}
190
192
193static void printOut(const QString & out)
194{
195 std::cout << qPrintable(out);
196}
197
198static void printErr(const QString & out)
199{
200 std::cerr << qPrintable(out);
201}
202
203static void printWarning(UpdateOptions options,
204 const QString &msg,
205 const QString &warningMsg = {},
206 const QString &errorMsg = {})
207{
208 QString text = msg;
209 if (options & Werror) {
210 text.prepend("lupdate error: "_L1);
211 if (!errorMsg.isEmpty())
212 text.append(" "_L1).append(errorMsg);
213 } else {
214 text.prepend("lupdate warning: "_L1);
215 if (!warningMsg.isEmpty())
216 text.append(" "_L1).append(warningMsg);
217 }
218
219 printErr(text);
220}
221
222static void recursiveFileInfoList(const QDir &dir,
223 const QSet<QString> &nameFilters, QDir::Filters filter,
224 QFileInfoList *fileinfolist)
225{
226 for (const QFileInfo &fi : dir.entryInfoList(filter))
227 if (fi.isDir())
228 recursiveFileInfoList(QDir(fi.absoluteFilePath()), nameFilters, filter, fileinfolist);
229 else if (nameFilters.contains(fi.suffix()))
230 fileinfolist->append(fi);
231}
232
233static void printUsage()
234{
235 printOut(QStringLiteral(
236 "Usage:\n"
237 " lupdate [options] [project-file]...\n"
238 " lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
239 "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n"
240 "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n"
241 "Extracted messages are stored in textual translation source files (typically\n"
242 "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n"
243 "Passing .pro files to lupdate is deprecated.\n"
244 "Please use the lupdate-pro tool instead.\n\n"
245 "Options:\n"
246 " -help Display this information and exit.\n"
247 " -no-obsolete\n"
248 " Drop all obsolete and vanished strings.\n"
249 " -extensions <ext>[,<ext>]...\n"
250 " Process files with the given extensions only.\n"
251 " The extension list must be separated with commas, not with whitespace.\n"
252 " Default: '%1'.\n"
253 " -pluralonly\n"
254 " Only include plural form messages.\n"
255 " -silent\n"
256 " Do not explain what is being done.\n"
257 " -no-sort\n"
258 " Do not sort contexts in TS files.\n"
259 " -no-recursive\n"
260 " Do not recursively scan directories.\n"
261 " -recursive\n"
262 " Recursively scan directories (default).\n"
263 " -warnings-are-errors\n"
264 " Treat warnings as errors.\n"
265 " -I <includepath> or -I<includepath>\n"
266 " Additional location to look for include files.\n"
267 " May be specified multiple times.\n"
268 " -locations {absolute|relative|none}\n"
269 " Specify/override how source code references are saved in TS files.\n"
270 " absolute: Source file path is relative to target file. Absolute line\n"
271 " number is stored.\n"
272 " relative: Source file path is relative to target file. Line number is\n"
273 " relative to other entries in the same source file.\n"
274 " none: no information about source location is stored.\n"
275 " Guessed from existing TS files if not specified.\n"
276 " Default is absolute for new files.\n"
277 " -no-ui-lines\n"
278 " Do not record line numbers in references to UI files.\n"
279 " -disable-heuristic {sametext|similartext}\n"
280 " Disable the named merge heuristic. Can be specified multiple times.\n"
281 " -project <filename>\n"
282 " Name of a file containing the project's description in JSON format.\n"
283 " Such a file may be generated from a .pro file using the lprodump tool.\n"
284 " -pro <filename>\n"
285 " Name of a .pro file. Useful for files with .pro file syntax but\n"
286 " different file suffix. Projects are recursed into and merged.\n"
287 " This option is deprecated. Use the lupdate-pro tool instead.\n"
288 " -pro-out <directory>\n"
289 " Virtual output directory for processing subsequent .pro files.\n"
290 " -pro-debug\n"
291 " Trace processing .pro files. Specify twice for more verbosity.\n"
292 " -source-language <language>[_<region>]\n"
293 " Specify the language of the source strings for new files.\n"
294 " Defaults to POSIX if not specified.\n"
295 " -target-language <language>[_<region>]\n"
296 " Specify the language of the translations for new files.\n"
297 " Guessed from the file name if not specified.\n"
298 " -tr-function-alias <function>{+=,=}<alias>[,<function>{+=,=}<alias>]...\n"
299 " With +=, recognize <alias> as an alternative spelling of <function>.\n"
300 " With =, recognize <alias> as the only spelling of <function>.\n"
301 " Available <function>s (with their currently defined aliases) are:\n"
302 " %2\n"
303 " -ts <ts-file>...\n"
304 " Specify the output file(s). This will override the TRANSLATIONS.\n"
305 " -version\n"
306 " Display the version of lupdate and exit.\n"
307 " -clang-parser [compilation-database-dir]\n"
308 " Use clang to parse cpp files. Otherwise a custom parser is used.\n"
309 " This option needs a clang compilation database (compile_commands.json)\n"
310 " for the files that needs to be parsed.\n"
311 " The path to the directory containing this file can be specified on the \n"
312 " command line, directly after the -clang-parser option, or in the .pro file\n"
313 " by setting the variable LUPDATE_COMPILE_COMMANDS_PATH.\n"
314 " A directory specified on the command line takes precedence.\n"
315 " If no path is given, the compilation database will be searched\n"
316 " in all parent paths of the first input file.\n"
317 " -project-roots <directory>...\n"
318 " Specify one or more project root directories.\n"
319 " Only files below a project root are considered for translation when using\n"
320 " the -clang-parser option.\n"
321 " @lst-file\n"
322 " Read additional file names (one per line) or includepaths (one per\n"
323 " line, and prefixed with -I) from lst-file.\n"
324 ).arg(m_defaultExtensions,
325 trFunctionAliasManager.availableFunctionsWithAliases()
326 .join(QLatin1String("\n "))));
327}
328
329static bool handleTrFunctionAliases(const QString &arg)
330{
331 for (const QString &pair : arg.split(QLatin1Char(','), Qt::SkipEmptyParts)) {
332 const int equalSign = pair.indexOf(QLatin1Char('='));
333 if (equalSign < 0) {
334 printErr(QStringLiteral("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n").arg(pair));
335 return false;
336 }
337 const bool plusEqual = equalSign > 0 && pair[equalSign-1] == QLatin1Char('+');
338 const int trFunctionEnd = plusEqual ? equalSign-1 : equalSign;
339 const QString trFunctionName = pair.left(trFunctionEnd).trimmed();
340 const QString alias = pair.mid(equalSign+1).trimmed();
341 const int trFunction = trFunctionByDefaultName(trFunctionName);
342 if (trFunction < 0) {
343 printErr(QStringLiteral("Unknown tr-function '%1' in -tr-function-alias option.\n"
344 "Available tr-functions are: %2")
345 .arg(trFunctionName, availableFunctions().join(QLatin1Char(','))));
346 return false;
347 }
348 if (alias.isEmpty()) {
349 printErr(QStringLiteral("Empty alias for tr-function '%1' in -tr-function-alias option.\n")
350 .arg(trFunctionName));
351 return false;
352 }
353 trFunctionAliasManager.modifyAlias(trFunction, alias,
354 plusEqual ? TrFunctionAliasManager::AddAlias : TrFunctionAliasManager::SetAlias);
355 }
356 return true;
357}
358
359static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
360 const QStringList &alienFiles,
361 const QString &sourceLanguage, const QString &targetLanguage,
362 UpdateOptions options, bool *fail)
363{
364 for (int i = 0; i < fetchedTor.messageCount(); i++) {
365 const TranslatorMessage &msg = fetchedTor.constMessage(i);
366 if (!msg.id().isEmpty() && msg.sourceText().isEmpty()) {
367 printWarning(options,
368 "Message with id '%1' has no source.\n"_L1.arg(msg.id()));
369 if (options & Werror)
370 return;
371 }
372 }
373 QList<Translator> aliens;
374 for (const QString &fileName : alienFiles) {
375 ConversionData cd;
376 Translator tor;
377 if (!tor.load(fileName, cd, QLatin1String("auto"))) {
378 printErr(cd.error());
379 *fail = true;
380 continue;
381 }
382 tor.resolveDuplicates();
383 aliens << tor;
384 }
385 QDir dir;
386 QString err;
387 for (const QString &fileName : tsFileNames) {
388 QString fn = dir.relativeFilePath(fileName);
389 ConversionData cd;
390 Translator tor;
391 cd.m_sortContexts = !(options & NoSort);
392 if (QFile(fileName).exists()) {
393 if (!tor.load(fileName, cd, QLatin1String("auto"))) {
394 printErr(cd.error());
395 *fail = true;
396 continue;
397 }
398 tor.resolveDuplicates();
399 cd.clearErrors();
400 if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode()) {
401 printWarning(options,
402 "Specified target language '%1' disagrees with"
403 " existing file's language '%2'.\n"_L1
404 .arg(targetLanguage, tor.languageCode()),
405 u"Ignoring.\n"_s);
406 if (options & Werror)
407 return;
408 }
409 if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode()) {
410 printWarning(options,
411 "Specified source language '%1' disagrees with"
412 " existing file's language '%2'.\n"_L1
413 .arg(sourceLanguage, tor.sourceLanguageCode()),
414 u"Ignoring.\n"_s);
415 if (options & Werror)
416 return;
417 }
418 // If there is translation in the file, the language should be recognized
419 // (when the language is not recognized, plural translations are lost)
420 if (tor.translationsExist()) {
421 QLocale::Language l;
422 QLocale::Territory c;
423 tor.languageAndTerritory(tor.languageCode(), &l, &c);
424 QStringList forms;
425 if (!getNumerusInfo(l, c, 0, &forms, 0)) {
426 printErr(QStringLiteral("File %1 won't be updated: it contains translation but the"
427 " target language is not recognized\n").arg(fileName));
428 continue;
429 }
430 }
431 } else {
432 if (!targetLanguage.isEmpty())
433 tor.setLanguageCode(targetLanguage);
434 else
435 tor.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName));
436 if (!sourceLanguage.isEmpty())
437 tor.setSourceLanguageCode(sourceLanguage);
438 }
439 tor.makeFileNamesAbsolute(QFileInfo(fileName).absoluteDir());
440 if (options & NoLocations)
441 tor.setLocationsType(Translator::NoLocations);
442 else if (options & RelativeLocations)
443 tor.setLocationsType(Translator::RelativeLocations);
444 else if (options & AbsoluteLocations)
445 tor.setLocationsType(Translator::AbsoluteLocations);
446 if (options & Verbose)
447 printOut(QStringLiteral("Updating '%1'...\n").arg(fn));
448
449 UpdateOptions theseOptions = options;
450 if (tor.locationsType() == Translator::NoLocations) // Could be set from file
451 theseOptions |= NoLocations;
452 Translator out = merge(tor, fetchedTor, aliens, theseOptions, err);
453
454 if ((options & Verbose) && !err.isEmpty()) {
455 printOut(err);
456 err.clear();
457 }
458 if (options & PluralOnly) {
459 if (options & Verbose)
460 printOut(QStringLiteral("Stripping non plural forms in '%1'...\n").arg(fn));
461 out.stripNonPluralForms();
462 }
463 if (options & NoObsolete)
464 out.stripObsoleteMessages();
465 out.stripEmptyContexts();
466
467 out.normalizeTranslations(cd);
468 if (!cd.errors().isEmpty()) {
469 printErr(cd.error());
470 cd.clearErrors();
471 }
472 if (!out.save(fileName, cd, QLatin1String("auto"))) {
473 printErr(cd.error());
474 *fail = true;
475 }
476 }
477}
478
479static bool readFileContent(const QString &filePath, QByteArray *content, QString *errorString)
480{
481 QFile file(filePath);
482 if (!file.open(QIODevice::ReadOnly)) {
483 *errorString = file.errorString();
484 return false;
485 }
486 *content = file.readAll();
487 return true;
488}
489
490static bool readFileContent(const QString &filePath, QString *content, QString *errorString)
491{
492 QByteArray ba;
493 if (!readFileContent(filePath, &ba, errorString))
494 return false;
495 *content = QString::fromLocal8Bit(ba);
496 return true;
497}
498
499static void removeExcludedSources(Projects &projects)
500{
501 for (Project &project : projects) {
502 for (const QRegularExpression &rx : project.excluded) {
503 for (auto it = project.sources.begin(); it != project.sources.end(); ) {
504 if (rx.match(*it).hasMatch())
505 it = project.sources.erase(it);
506 else
507 ++it;
508 }
509 }
510 removeExcludedSources(project.subProjects);
511 }
512}
513
514static QStringList getResources(const QString &resourceFile)
515{
516 if (!QFile::exists(resourceFile))
517 return QStringList();
518 QString content;
519 QString errStr;
520 if (!readFileContent(resourceFile, &content, &errStr)) {
521 printErr(QStringLiteral("lupdate error: Can not read %1: %2\n").arg(resourceFile, errStr));
522 return QStringList();
523 }
524 ReadQrcResult rqr = readQrcFile(resourceFile, content);
525 if (rqr.hasError()) {
526 printErr(QStringLiteral("lupdate error: %1:%2: %3\n")
527 .arg(resourceFile, QString::number(rqr.line), rqr.errorString));
528 }
529 return rqr.files;
530}
531
532// Remove .qrc files from the project and return them as absolute paths.
534{
535 auto it = project.sources.begin();
536 QStringList qrcFiles;
537 while (it != project.sources.end()) {
538 QFileInfo fi(*it);
539 QString fn = QDir::cleanPath(fi.absoluteFilePath());
540 if (fn.endsWith(QLatin1String(".qrc"), Qt::CaseInsensitive)) {
541 qrcFiles += fn;
542 it = project.sources.erase(it);
543 } else {
544 ++it;
545 }
546 }
547 return qrcFiles;
548}
549
550// Replace all .qrc files in the project with their content.
551static void expandQrcFiles(Project &project)
552{
553 for (const QString &qrcFile : extractQrcFiles(project))
554 project.sources << getResources(qrcFile);
555}
556
557static bool processTs(Translator &fetchedTor, const QString &file, ConversionData &cd)
558{
559 for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
560 if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
561 Translator tor;
562 if (tor.load(file, cd, fmt.extension)) {
563 for (TranslatorMessage msg : tor.messages()) {
564 msg.setType(TranslatorMessage::Unfinished);
565 msg.setTranslations(QStringList());
566 msg.setTranslatorComment(QString());
567 fetchedTor.extend(msg, cd);
568 }
569 }
570 return true;
571 }
572 }
573 return false;
574}
575
576static void processSources(Translator &fetchedTor, const QStringList &sourceFiles,
577 ConversionData &cd, UpdateOptions options, bool *fail)
578{
579#ifdef QT_NO_QML
580 bool requireQmlSupport = false;
581#endif
582 QStringList sourceFilesCpp;
583 for (const auto &sourceFile : sourceFiles) {
584 if (sourceFile.endsWith(QLatin1String(".java"), Qt::CaseInsensitive))
585 loadJava(fetchedTor, sourceFile, cd);
586 else if (sourceFile.endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)
587 || sourceFile.endsWith(QLatin1String(".jui"), Qt::CaseInsensitive))
588 loadUI(fetchedTor, sourceFile, cd);
589#ifndef QT_NO_QML
590 else if (sourceFile.endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
591 || sourceFile.endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) {
592 loadQScript(fetchedTor, sourceFile, cd);
593 } else if (sourceFile.endsWith(QLatin1String(".mjs"), Qt::CaseInsensitive)) {
594 loadJSModule(fetchedTor, sourceFile, cd);
595 } else if (sourceFile.endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
596 loadQml(fetchedTor, sourceFile, cd);
597#else
598 else if (sourceFile.endsWith(QLatin1String(".qml"), Qt::CaseInsensitive)
599 || sourceFile.endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
600 || sourceFile.endsWith(QLatin1String(".mjs"), Qt::CaseInsensitive)
601 || sourceFile.endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
602 requireQmlSupport = true;
603#endif // QT_NO_QML
604 else if (sourceFile.endsWith(u".py", Qt::CaseInsensitive))
605 loadPython(fetchedTor, sourceFile, cd);
606 else if (!processTs(fetchedTor, sourceFile, cd))
607 sourceFilesCpp << sourceFile;
608 }
609
610#ifdef QT_NO_QML
611 if (requireQmlSupport) {
612 printWarning(options, u"missing qml/javascript support\n"_s,
613 u"Some files have been ignored.\n"_s);
614 if (options & Werror)
615 return;
616 }
617#else
618 Q_UNUSED(options)
619#endif
620
621 if (useClangToParseCpp) {
622#if QT_CONFIG(clangcpp)
623 ClangCppParser::loadCPP(fetchedTor, sourceFilesCpp, cd, fail);
624#else
625 *fail = true;
626 printErr(QStringLiteral("lupdate error: lupdate was built without clang support."));
627#endif
628 }
629 else
630 loadCPP(fetchedTor, sourceFilesCpp, cd);
631
632 if (!cd.error().isEmpty())
633 printErr(cd.error());
634}
635
636static QSet<QString> projectRoots(const QString &projectFile, const QStringList &sourceFiles)
637{
638 const QString proPath = QFileInfo(projectFile).path();
639 QSet<QString> sourceDirs;
640 sourceDirs.insert(proPath + QLatin1Char('/'));
641 for (const QString &sf : sourceFiles)
642 sourceDirs.insert(sf.left(sf.lastIndexOf(QLatin1Char('/')) + 1));
643 QStringList rootList = sourceDirs.values();
644 rootList.sort();
645 for (int prev = 0, curr = 1; curr < rootList.size(); )
646 if (rootList.at(curr).startsWith(rootList.at(prev)))
647 rootList.removeAt(curr);
648 else
649 prev = curr++;
650 return QSet<QString>(rootList.cbegin(), rootList.cend());
651}
652
654{
655public:
656 ProjectProcessor(const QString &sourceLanguage,
657 const QString &targetLanguage)
660 {
661 }
662
663 void processProjects(bool topLevel, UpdateOptions options, const Projects &projects,
664 bool nestComplain, Translator *parentTor, bool *fail) const
665 {
666 for (const Project &prj : projects)
667 processProject(options, prj, topLevel, nestComplain, parentTor, fail);
668 }
669
670private:
671
672 void processProject(UpdateOptions options, const Project &prj, bool topLevel,
673 bool nestComplain, Translator *parentTor, bool *fail) const
674 {
675
676 QString codecForSource = prj.codec.toLower();
677 if (!codecForSource.isEmpty()) {
678 if (codecForSource == QLatin1String("utf-16")
679 || codecForSource == QLatin1String("utf16")) {
680 options |= SourceIsUtf16;
681 } else if (codecForSource == QLatin1String("utf-8")
682 || codecForSource == QLatin1String("utf8")) {
683 options &= ~SourceIsUtf16;
684 } else {
685 printWarning(
686 options,
687 "Codec for source '%1' is invalid.\n"_L1.arg(codecForSource),
688 u"Falling back to UTF-8.\n"_s);
689 if (options & Werror)
690 return;
691 options &= ~SourceIsUtf16;
692 }
693 }
694
695 const QString projectFile = prj.filePath;
696 const QStringList sources = prj.sources;
698 cd.m_noUiLines = options & NoUiLines;
699 cd.m_projectRoots = projectRoots(projectFile, sources);
700 QStringList projectRootDirs;
701 for (auto dir : cd.m_projectRoots)
702 projectRootDirs.append(dir);
703 cd.m_rootDirs = projectRootDirs;
704 cd.m_includePath = prj.includePaths;
705 cd.m_excludes = prj.excluded;
706 cd.m_sourceIsUtf16 = options & SourceIsUtf16;
708 cd.m_compilationDatabaseDir = prj.compileCommands;
709 else
710 cd.m_compilationDatabaseDir = commandLineCompilationDatabaseDir;
711
712 QStringList tsFiles;
713 if (prj.translations) {
714 tsFiles = *prj.translations;
715 if (parentTor) {
716 if (topLevel) {
717 printWarning(options, u"Existing top level."_s,
718 "TS files from command line will "
719 "override TRANSLATIONS in %1.\n"_L1.arg(projectFile),
720 u"Terminating the operation.\n"_s);
721 if (options & Werror)
722 return;
723 goto noTrans;
724 } else if (nestComplain) {
725 printWarning(options,
726 "TS files from command line "
727 "prevent recursing into %1.\n"_L1.arg(projectFile));
728 return;
729 }
730 }
731 if (tsFiles.isEmpty()) {
732 // This might mean either a buggy PRO file or an intentional detach -
733 // we can't know without seeing the actual RHS of the assignment ...
734 // Just assume correctness and be silent.
735 return;
736 }
737 Translator tor;
738 processProjects(false, options, prj.subProjects, false, &tor, fail);
739 processSources(tor, sources, cd, options, fail);
740 updateTsFiles(tor, tsFiles, QStringList(), m_sourceLanguage, m_targetLanguage,
741 options, fail);
742 return;
743 }
744
745
746 noTrans:
747
748 if (!parentTor) {
749 if (topLevel) {
750 printWarning(options, u"no TS files specified."_s,
751 "Only diagnostics will be produced for %1.\n"_L1.arg(projectFile),
752 u"Terminating the operation.\n"_s);
753 if (options & Werror)
754 return;
755 }
756 Translator tor;
757 processProjects(false, options, prj.subProjects, nestComplain, &tor, fail);
758 processSources(tor, sources, cd, options, fail);
759 } else {
760 processProjects(false, options, prj.subProjects, nestComplain, parentTor, fail);
761 processSources(*parentTor, sources, cd, options, fail);
762 }
763 }
764
765 QString m_sourceLanguage;
766 QString m_targetLanguage;
767};
768
769int main(int argc, char **argv)
770{
771 QCoreApplication app(argc, argv);
772#ifndef QT_BOOTSTRAPPED
773#ifndef Q_OS_WIN32
774 QTranslator translator;
775 QTranslator qtTranslator;
776 QString sysLocale = QLocale::system().name();
777 QString resourceDir = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
778 if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
779 && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
780 app.installTranslator(&translator);
781 app.installTranslator(&qtTranslator);
782 }
783#endif // Q_OS_WIN32
784#endif
785
786 m_defaultExtensions = QLatin1String("java,jui,ui,c,c++,cc,cpp,cxx,ch,h,"_L1
787 "h++,hh,hpp,hxx,js,mjs,qs,qml,qrc"_L1);
788
789 QStringList args = app.arguments();
790 QStringList tsFileNames;
791 QStringList proFiles;
792 QString projectDescriptionFile;
793 QString outDir = QDir::currentPath();
794 QMultiHash<QString, QString> allCSources;
795 QSet<QString> projectRoots;
796 QStringList sourceFiles;
797 QStringList resourceFiles;
798 QStringList includePath;
799 QStringList alienFiles;
800 QString targetLanguage;
801 QString sourceLanguage;
802
803 UpdateOptions options =
804 Verbose | // verbose is on by default starting with Qt 4.2
805 HeuristicSameText | HeuristicSimilarText;
806 int numFiles = 0;
807 bool metTsFlag = false;
808 bool metXTsFlag = false;
809 bool recursiveScan = true;
810
811 bool fail = false;
812
813 QString extensions = m_defaultExtensions;
814 QSet<QString> extensionsNameFilters;
815
816 for (int i = 1; i < args.size(); ++i) {
817 QString arg = args.at(i);
818 if (arg == QLatin1String("-help")
819 || arg == QLatin1String("--help")
820 || arg == QLatin1String("-h")) {
822 return 0;
823 } else if (arg == QLatin1String("-list-languages")) {
824 printOut(getNumerusInfoString());
825 return 0;
826 } else if (arg == QLatin1String("-pluralonly")) {
827 options |= PluralOnly;
828 continue;
829 } else if (arg == QLatin1String("-noobsolete")
830 || arg == QLatin1String("-no-obsolete")) {
831 options |= NoObsolete;
832 continue;
833 } else if (arg == QLatin1String("-silent")) {
834 options &= ~Verbose;
835 continue;
836 } else if (arg == QLatin1String("-pro-debug")) {
837 continue;
838 } else if (arg == QLatin1String("-project")) {
839 ++i;
840 if (i == argc) {
841 printErr(u"The option -project requires a parameter.\n"_s);
842 return 1;
843 }
844 if (!projectDescriptionFile.isEmpty()) {
845 printErr(u"The option -project must appear only once.\n"_s);
846 return 1;
847 }
848 projectDescriptionFile = args[i];
849 numFiles++;
850 continue;
851 } else if (arg == QLatin1String("-target-language")) {
852 ++i;
853 if (i == argc) {
854 printErr(u"The option -target-language requires a parameter.\n"_s);
855 return 1;
856 }
857 targetLanguage = args[i];
858 continue;
859 } else if (arg == QLatin1String("-source-language")) {
860 ++i;
861 if (i == argc) {
862 printErr(u"The option -source-language requires a parameter.\n"_s);
863 return 1;
864 }
865 sourceLanguage = args[i];
866 continue;
867 } else if (arg == QLatin1String("-disable-heuristic")) {
868 ++i;
869 if (i == argc) {
870 printErr(u"The option -disable-heuristic requires a parameter.\n"_s);
871 return 1;
872 }
873 arg = args[i];
874 if (arg == QLatin1String("sametext")) {
875 options &= ~HeuristicSameText;
876 } else if (arg == QLatin1String("similartext")) {
877 options &= ~HeuristicSimilarText;
878 } else {
879 printErr(u"Invalid heuristic name passed to -disable-heuristic.\n"_s);
880 return 1;
881 }
882 continue;
883 } else if (arg == QLatin1String("-locations")) {
884 ++i;
885 if (i == argc) {
886 printErr(u"The option -locations requires a parameter.\n"_s);
887 return 1;
888 }
889 if (args[i] == QLatin1String("none")) {
890 options |= NoLocations;
891 } else if (args[i] == QLatin1String("relative")) {
892 options |= RelativeLocations;
893 } else if (args[i] == QLatin1String("absolute")) {
894 options |= AbsoluteLocations;
895 } else {
896 printErr(u"Invalid parameter passed to -locations.\n"_s);
897 return 1;
898 }
899 continue;
900 } else if (arg == QLatin1String("-no-ui-lines")) {
901 options |= NoUiLines;
902 continue;
903 } else if (arg == QLatin1String("-verbose")) {
904 options |= Verbose;
905 continue;
906 } else if (arg == QLatin1String("-warnings-are-errors")) {
907 options |= Werror;
908 continue;
909 } else if (arg == QLatin1String("-no-recursive")) {
910 recursiveScan = false;
911 continue;
912 } else if (arg == QLatin1String("-recursive")) {
913 recursiveScan = true;
914 continue;
915 } else if (arg == QLatin1String("-no-sort")
916 || arg == QLatin1String("-nosort")) {
917 options |= NoSort;
918 continue;
919 } else if (arg == QLatin1String("-version")) {
920 printOut(QStringLiteral("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
921 return 0;
922 } else if (arg == QLatin1String("-ts")) {
923 metTsFlag = true;
924 metXTsFlag = false;
925 continue;
926 } else if (arg == QLatin1String("-xts")) {
927 metTsFlag = false;
928 metXTsFlag = true;
929 continue;
930 } else if (arg == QLatin1String("-extensions")) {
931 ++i;
932 if (i == argc) {
933 printErr(u"The -extensions option should be followed by an extension list.\n"_s);
934 return 1;
935 }
936 extensions = args[i];
937 continue;
938 } else if (arg == QLatin1String("-tr-function-alias")) {
939 ++i;
940 if (i == argc) {
941 printErr(u"The -tr-function-alias option should be followed by a list of function=alias mappings.\n"_s);
942 return 1;
943 }
944 if (!handleTrFunctionAliases(args[i]))
945 return 1;
946 continue;
947 } else if (arg == QLatin1String("-pro")) {
948 ++i;
949 if (i == argc) {
950 printErr(u"The -pro option should be followed by a filename of .pro file.\n"_s);
951 return 1;
952 }
953 QString file = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
954 proFiles += file;
955 numFiles++;
956 continue;
957 } else if (arg == QLatin1String("-pro-out")) {
958 ++i;
959 if (i == argc) {
960 printErr(u"The -pro-out option should be followed by a directory name.\n"_s);
961 return 1;
962 }
963 outDir = QDir::cleanPath(QFileInfo(args[i]).absoluteFilePath());
964 continue;
965 } else if (arg.startsWith(QLatin1String("-I"))) {
966 if (arg.size() == 2) {
967 ++i;
968 if (i == argc) {
969 printErr(u"The -I option should be followed by a path.\n"_s);
970 return 1;
971 }
972 includePath += args[i];
973 } else {
974 includePath += args[i].mid(2);
975 }
976 continue;
977 }
978#if QT_CONFIG(clangcpp)
979 else if (arg == QLatin1String("-clang-parser")) {
980 useClangToParseCpp = true;
981 // the option after -clang-parser is optional
982 if ((i + 1) != argc && !args[i + 1].startsWith(QLatin1String("-"))) {
983 i++;
984 commandLineCompilationDatabaseDir = args[i];
985 }
986 continue;
987 } else if (arg == QLatin1String("-project-roots")) {
988 while ((i + 1) != argc && !args[i + 1].startsWith(QLatin1String("-"))) {
989 i++;
990 rootDirs << args[i];
991 }
992 rootDirs.removeDuplicates();
993 continue;
994 }
995#endif
996 else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
997 printErr(QStringLiteral("Unrecognized option '%1'.\n").arg(arg));
998 return 1;
999 }
1000
1001 QStringList files;
1002 if (arg.startsWith(QLatin1String("@"))) {
1003 QFile lstFile(arg.mid(1));
1004 if (!lstFile.open(QIODevice::ReadOnly)) {
1005 printErr(QStringLiteral("lupdate error: List file '%1' is not readable.\n")
1006 .arg(lstFile.fileName()));
1007 return 1;
1008 }
1009 while (!lstFile.atEnd()) {
1010 QString lineContent = QString::fromLocal8Bit(lstFile.readLine().trimmed());
1011
1012 if (lineContent.startsWith(QLatin1String("-I"))) {
1013 if (lineContent.size() == 2) {
1014 printErr(u"The -I option should be followed by a path.\n"_s);
1015 return 1;
1016 }
1017 includePath += lineContent.mid(2);
1018 } else {
1019 files << lineContent;
1020 }
1021 }
1022 } else {
1023 files << arg;
1024 }
1025 if (metTsFlag) {
1026 for (const QString &file : std::as_const(files)) {
1027 bool found = false;
1028 for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
1029 if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
1030 QFileInfo fi(file);
1031 if (!fi.exists() || fi.isWritable()) {
1032 tsFileNames.append(QFileInfo(file).absoluteFilePath());
1033 } else {
1034 printWarning(options,
1035 "For some reason, '%1' is not writable.\n"_L1
1036 .arg(file));
1037 if (options & Werror)
1038 return 1;
1039 }
1040 found = true;
1041 break;
1042 }
1043 }
1044 if (!found) {
1045 printErr(QStringLiteral("lupdate error: File '%1' has no recognized extension.\n")
1046 .arg(file));
1047 return 1;
1048 }
1049 }
1050 numFiles++;
1051 } else if (metXTsFlag) {
1052 alienFiles += files;
1053 } else {
1054 for (const QString &file : std::as_const(files)) {
1055 QFileInfo fi(file);
1056 if (!fi.exists()) {
1057 printErr(QStringLiteral("lupdate error: File '%1' does not exist.\n").arg(file));
1058 return 1;
1059 }
1060 if (isProOrPriFile(file)) {
1061 QString cleanFile = QDir::cleanPath(fi.absoluteFilePath());
1062 proFiles << cleanFile;
1063 } else if (fi.isDir()) {
1064 if (options & Verbose)
1065 printOut(QStringLiteral("Scanning directory '%1'...\n").arg(file));
1066 QDir dir = QDir(fi.filePath());
1067 projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
1068 if (extensionsNameFilters.isEmpty()) {
1069 for (QString ext : extensions.split(QLatin1Char(','))) {
1070 ext = ext.trimmed();
1071 if (ext.startsWith(QLatin1Char('.')))
1072 ext.remove(0, 1);
1073 extensionsNameFilters.insert(ext);
1074 }
1075 }
1076 QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
1077 if (recursiveScan)
1078 filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
1079 QFileInfoList fileinfolist;
1080 recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
1081 int scanRootLen = dir.absolutePath().size();
1082 for (const QFileInfo &fi : std::as_const(fileinfolist)) {
1083 QString fn = QDir::cleanPath(fi.absoluteFilePath());
1084 if (fn.endsWith(QLatin1String(".qrc"), Qt::CaseInsensitive)) {
1085 resourceFiles << fn;
1086 } else {
1087 sourceFiles << fn;
1088
1089 if (!fn.endsWith(QLatin1String(".java"))
1090 && !fn.endsWith(QLatin1String(".jui"))
1091 && !fn.endsWith(QLatin1String(".ui"))
1092 && !fn.endsWith(QLatin1String(".js"))
1093 && !fn.endsWith(QLatin1String(".mjs"))
1094 && !fn.endsWith(QLatin1String(".qs"))
1095 && !fn.endsWith(QLatin1String(".qml"))) {
1096 int offset = 0;
1097 int depth = 0;
1098 do {
1099 offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
1100 QString ffn = fn.mid(offset + 1);
1101 allCSources.insert(ffn, fn);
1102 } while (++depth < 3 && offset > scanRootLen);
1103 }
1104 }
1105 }
1106 } else {
1107 QString fn = QDir::cleanPath(fi.absoluteFilePath());
1108 if (fn.endsWith(QLatin1String(".qrc"), Qt::CaseInsensitive))
1109 resourceFiles << fn;
1110 else
1111 sourceFiles << fn;
1112 projectRoots.insert(fi.absolutePath() + QLatin1Char('/'));
1113 }
1114 }
1115 numFiles++;
1116 }
1117 } // for args
1118
1119 if (numFiles == 0) {
1121 return 1;
1122 }
1123
1124 if (!targetLanguage.isEmpty() && tsFileNames.size() != 1) {
1125 printWarning(options,
1126 u"-target-language usually only makes sense with exactly one TS file.\n"_s);
1127 if (options & Werror)
1128 return 1;
1129 }
1130
1131 if (proFiles.isEmpty() && resourceFiles.isEmpty() && sourceFiles.size() == 1
1132 && QFileInfo(sourceFiles.first()).fileName() == u"CMakeLists.txt"_s) {
1133 printErr(u"lupdate error: Passing a CMakeLists.txt as project file is not supported.\n"_s
1134 u"Please use the 'qt_add_lupdate' CMake command and build the "_s
1135 u"'update_translations' target.\n"_s);
1136 return 1;
1137 }
1138
1139 QString errorString;
1140 if (!proFiles.isEmpty()) {
1141 runInternalQtTool(u"lupdate-pro"_s, app.arguments().mid(1));
1142 return 0;
1143 }
1144
1145 Projects projectDescription;
1146 if (!projectDescriptionFile.isEmpty()) {
1147 projectDescription = readProjectDescription(projectDescriptionFile, &errorString);
1148 if (!errorString.isEmpty()) {
1149 printErr(QStringLiteral("lupdate error: %1\n").arg(errorString));
1150 return 1;
1151 }
1152 if (projectDescription.empty()) {
1153 printErr(QStringLiteral("lupdate error:"
1154 " Could not find project descriptions in %1.\n")
1155 .arg(projectDescriptionFile));
1156 return 1;
1157 }
1158 removeExcludedSources(projectDescription);
1159 for (Project &project : projectDescription)
1160 expandQrcFiles(project);
1161 }
1162
1163 if (projectDescription.empty()) {
1164 if (tsFileNames.isEmpty()) {
1165 printWarning(options, u"no TS files specified."_s,
1166 u"Only diagnostics will be produced.\n"_s,
1167 u"Terminating the operation.\n"_s);
1168 if (options & Werror)
1169 return 1;
1170 }
1171
1172 Translator fetchedTor;
1173 ConversionData cd;
1174 cd.m_noUiLines = options & NoUiLines;
1175 cd.m_sourceIsUtf16 = options & SourceIsUtf16;
1176 cd.m_projectRoots = projectRoots;
1177 cd.m_includePath = includePath;
1178 cd.m_allCSources = allCSources;
1179 cd.m_compilationDatabaseDir = commandLineCompilationDatabaseDir;
1180 cd.m_rootDirs = rootDirs;
1181 for (const QString &resource : std::as_const(resourceFiles))
1182 sourceFiles << getResources(resource);
1183 processSources(fetchedTor, sourceFiles, cd, options, &fail);
1184 updateTsFiles(fetchedTor, tsFileNames, alienFiles,
1185 sourceLanguage, targetLanguage, options, &fail);
1186 } else {
1187 if (!sourceFiles.isEmpty() || !resourceFiles.isEmpty() || !includePath.isEmpty()) {
1188 printErr(QStringLiteral("lupdate error:"
1189 " Both project and source files / include paths specified.\n"));
1190 return 1;
1191 }
1192 QString errorString;
1193 ProjectProcessor projectProcessor(sourceLanguage, targetLanguage);
1194 if (!tsFileNames.isEmpty()) {
1195 Translator fetchedTor;
1196 projectProcessor.processProjects(true, options, projectDescription, true, &fetchedTor,
1197 &fail);
1198 if (!fail) {
1199 updateTsFiles(fetchedTor, tsFileNames, alienFiles,
1200 sourceLanguage, targetLanguage, options, &fail);
1201 }
1202 } else {
1203 projectProcessor.processProjects(true, options, projectDescription, false, nullptr,
1204 &fail);
1205 }
1206 }
1207 return fail ? 1 : 0;
1208}
int main(int argc, char *argv[])
[2]
Definition buffer.cpp:77
bool m_sourceIsUtf16
Definition translator.h:52
void processProjects(bool topLevel, UpdateOptions options, const Projects &projects, bool nestComplain, Translator *parentTor, bool *fail) const
Definition main.cpp:663
ProjectProcessor(const QString &sourceLanguage, const QString &targetLanguage)
Definition main.cpp:656
bool hasError() const
Definition qrcreader.h:17
QStringList availableFunctionsWithAliases() const
Definition main.cpp:113
QStringList listAliases() const
Definition main.cpp:125
int trFunctionByName(const QString &trFunctionName) const
Definition main.cpp:68
void modifyAlias(int trFunction, const QString &alias, Operation op)
Definition main.cpp:76
const NameToTrFunctionMap & nameToTrFunctionMap() const
Definition main.cpp:98
int messageCount() const
Definition translator.h:146
const TranslatorMessage & constMessage(int i) const
Definition translator.h:149
#define LUPDATE_FOR_EACH_TR_FUNCTION(UNARY_MACRO)
Definition lupdate.h:56
@ NoLocations
Definition lupdate.h:32
@ Verbose
Definition lupdate.h:24
@ NoObsolete
Definition lupdate.h:25
@ PluralOnly
Definition lupdate.h:26
@ Werror
Definition lupdate.h:35
@ NoSort
Definition lupdate.h:27
@ HeuristicSimilarText
Definition lupdate.h:29
@ SourceIsUtf16
Definition lupdate.h:34
@ HeuristicSameText
Definition lupdate.h:28
@ AbsoluteLocations
Definition lupdate.h:30
@ RelativeLocations
Definition lupdate.h:31
@ NoUiLines
Definition lupdate.h:33
std::vector< Project > Projects
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
static void printUsage()
Definition main.cpp:60
static void printOut(const QString &out)
Definition main.cpp:18
static void printErr(const QString &out)
Definition main.cpp:23
static QStringList getResources(const QString &resourceFile)
Definition main.cpp:514
static void processSources(Translator &fetchedTor, const QStringList &sourceFiles, ConversionData &cd, UpdateOptions options, bool *fail)
Definition main.cpp:576
static QString m_defaultExtensions
Definition main.cpp:191
static void printWarning(UpdateOptions options, const QString &msg, const QString &warningMsg={}, const QString &errorMsg={})
Definition main.cpp:203
QStringList rootDirs
Definition main.cpp:33
static QStringList availableFunctions()
Definition main.cpp:104
static int trFunctionByDefaultName(const QString &trFunctionName)
Definition main.cpp:51
static void removeExcludedSources(Projects &projects)
Definition main.cpp:499
static const QString defaultTrFunctionNames[]
Definition main.cpp:38
static void expandQrcFiles(Project &project)
Definition main.cpp:551
bool useClangToParseCpp
Definition main.cpp:30
static void recursiveFileInfoList(const QDir &dir, const QSet< QString > &nameFilters, QDir::Filters filter, QFileInfoList *fileinfolist)
Definition main.cpp:222
TrFunctionAliasManager trFunctionAliasManager
Definition main.cpp:138
static bool readFileContent(const QString &filePath, QByteArray *content, QString *errorString)
Definition main.cpp:479
QString commandLineCompilationDatabaseDir
Definition main.cpp:31
static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames, const QStringList &alienFiles, const QString &sourceLanguage, const QString &targetLanguage, UpdateOptions options, bool *fail)
Definition main.cpp:359
static bool processTs(Translator &fetchedTor, const QString &file, ConversionData &cd)
Definition main.cpp:557
static QSet< QString > projectRoots(const QString &projectFile, const QStringList &sourceFiles)
Definition main.cpp:636
static bool handleTrFunctionAliases(const QString &arg)
Definition main.cpp:329
static QStringList extractQrcFiles(Project &project)
Definition main.cpp:533