Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "translator.h"
++ #include "releasehelper.h"
++
++ #include <linguistproject/profileutils.h>
++ #include <linguistproject/projectdescriptionreader.h>
++ #include <linguistproject/projsongenerator.h>
++
++ #ifndef QT_BOOTSTRAPPED
++ #include <QtCore/QCoreApplication>
++ #include <QtCore/QTranslator>
++ #endif
++ #include <QtCore/QDebug>
++ #include <QtCore/QDir>
++ #include <QtCore/QFile>
++ #include <QtCore/QFileInfo>
++ #include <QtCore/QString>
++ #include <QtCore/QStringList>
++ #include <QtCore/QTextStream>
++ #include <QtCore/QLibraryInfo>
++
++ QT_USE_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ static void printUsage()
++ {
++ printOut(uR"(Usage:
++ lrelease [options] -project project-file
++ lrelease [options] ts-files [-qm qm-file]
++
++ lrelease is part of Qt's Linguist tool chain. It can be used as a
++ stand-alone tool to convert XML-based translations files in the TS
++ format into the 'compiled' QM format used by QTranslator objects.
++
++ Passing .pro files to lrelease is deprecated.
++ Please use the lrelease-pro tool instead, or use qmake's lrelease.prf
++ feature.
++
++ Options:
++ -help Display this information and exit
++ -idbased
++ Deprecated. The flag is not required anymore and will be removed
++ in a future version. It was used to enable ID based translation.
++ -compress
++ Compress the QM files
++ -nounfinished
++ Do not include unfinished translations
++ -fail-on-unfinished
++ Generate an error if unfinished translations are found
++ -fail-on-invalid
++ Fail if translations failing the following checks are found:
++ validity check of accelerators
++ validity check of surrounding whitespaces
++ validity check of ending punctuation
++ validity check of place markers
++ To get more details refer to Qt Linguist help
++ -removeidentical
++ If the translated text is the same as
++ the source text, do not include the message
++ -markuntranslated <prefix>
++ If a message has no real translation, use the source text
++ prefixed with the given string instead
++ -project <filename>
++ Name of a file containing the project's description in JSON format.
++ Such a file may be generated from a .pro file using lupdate-pro -dump-json.
++ -silent
++ Do not explain what is being done
++ -verbose
++ Explain what is being done (default)
++ -version
++ Display the version of lrelease and exit
++ )"_s);
++ }
++
++ int main(int argc, char **argv)
++ {
++ QCoreApplication app(argc, argv);
++
++ ConversionData cd;
++ cd.m_verbose = true; // the default is true starting with Qt 4.2
++ ParamFlags params;
++ Translator tor;
++ QStringList inputFiles;
++ QString outputFile;
++ QString projectDescriptionFile;
++
++ for (int i = 1; i < argc; ++i) {
++ const char *arg = argv[i];
++ if (!strcmp(arg, "-compress")) {
++ cd.m_saveMode = SaveStripped;
++ continue;
++ } else if (!strcmp(arg, "-idbased")) {
++ printOut("The flag -idbased is deprecated and not required anymore."
++ "It will be removed in a future version"_L1);
++ continue;
++ } else if (!strcmp(arg, "-nocompress")) {
++ cd.m_saveMode = SaveEverything;
++ continue;
++ } else if (!strcmp(arg, "-removeidentical")) {
++ params.removeIdentical = true;
++ continue;
++ } else if (!strcmp(arg, "-nounfinished")) {
++ cd.m_ignoreUnfinished = true;
++ continue;
++ } else if (!strcmp(arg, "-fail-on-unfinished")) {
++ params.failOnUnfinished = true;
++ continue;
++ } else if (!strcmp(arg, "-fail-on-invalid")) {
++ params.failOnInvalid = true;
++ continue;
++ } else if (!strcmp(arg, "-markuntranslated")) {
++ if (i == argc - 1) {
++ printUsage();
++ return 1;
++ }
++ cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
++ } else if (!strcmp(arg, "-project")) {
++ if (i == argc - 1) {
++ printErr("The option -project requires a parameter.\n"_L1);
++ return 1;
++ }
++ if (!projectDescriptionFile.isEmpty()) {
++ printErr("The option -project must appear only once.\n"_L1);
++ return 1;
++ }
++ projectDescriptionFile = QString::fromLocal8Bit(argv[++i]);
++ } else if (!strcmp(arg, "-silent")) {
++ cd.m_verbose = false;
++ continue;
++ } else if (!strcmp(arg, "-verbose")) {
++ cd.m_verbose = true;
++ continue;
++ } else if (!strcmp(arg, "-version")) {
++ printOut("lrelease version %1\n"_L1.arg(QLatin1StringView(QT_VERSION_STR)));
++ return 0;
++ } else if (!strcmp(arg, "-qm")) {
++ if (i == argc - 1) {
++ printUsage();
++ return 1;
++ }
++ outputFile = QString::fromLocal8Bit(argv[++i]);
++ } else if (!strcmp(arg, "-help")) {
++ printUsage();
++ return 0;
++ } else if (arg[0] == '-') {
++ printUsage();
++ return 1;
++ } else {
++ inputFiles << QString::fromLocal8Bit(arg);
++ }
++ }
++
++ if (inputFiles.isEmpty() && projectDescriptionFile.isEmpty()) {
++ printUsage();
++ return 1;
++ }
++
++ QString errorString;
++ Projects projectDescription;
++ const QStringList proFiles = extractProFiles(&inputFiles);
++
++ if (!proFiles.isEmpty()) {
++ QStringList translationsVariables = { u"TRANSLATIONS"_s, u"EXTRA_TRANSLATIONS"_s };
++ QHash<QString, QString> outDirMap;
++ QString outDir = QDir::currentPath();
++ QStringList cleanProFiles;
++ cleanProFiles.reserve(proFiles.size());
++ for (const QString &proFile : std::as_const(proFiles)) {
++ const QString cleanFile = QDir::cleanPath(QFileInfo(proFile).absoluteFilePath());
++ cleanProFiles << cleanFile;
++ outDirMap[cleanFile] = outDir;
++ }
++
++ projectDescription = generateProjects(cleanProFiles, translationsVariables, outDirMap, 0,
++ true, &errorString);
++ if (!errorString.isEmpty()) {
++ printErr("lrelease error: %1\n"_L1.arg(errorString));
++ return 1;
++ }
++ if (projectDescription.empty()) {
++ printErr(u"lrelease error: No projects found in .pro files\n"_s);
++ return 1;
++ }
++ inputFiles = translationsFromProjects(projectDescription);
++ } else if (!projectDescriptionFile.isEmpty()) {
++ if (!inputFiles.isEmpty()) {
++ printErr(QLatin1String(
++ "lrelease error: Do not specify TS files if -project is given.\n"));
++ return 1;
++ }
++ projectDescription = projectDescriptionFromFile(projectDescriptionFile, &errorString);
++ if (!errorString.isEmpty()) {
++ printErr("lrelease error: %1\n"_L1.arg(errorString));
++ return 1;
++ }
++ inputFiles = translationsFromProjects(projectDescription);
++ }
++
++ for (const QString &inputFile : std::as_const(inputFiles)) {
++ if (outputFile.isEmpty()) {
++ if (!releaseTsFile(inputFile, cd, params))
++ return 1;
++ } else {
++ if (!loadTsFile(tor, inputFile))
++ return 1;
++ }
++ }
++
++ if (!outputFile.isEmpty())
++ return releaseTranslator(tor, outputFile, cd, params) ? 0 : 1;
++
++ return 0;
++ }