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// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "translator.h"
5
6#include <profileutils.h>
7#include <projectdescriptionreader.h>
8#include <runqttool.h>
9
10#ifndef QT_BOOTSTRAPPED
11#include <QtCore/QCoreApplication>
12#include <QtCore/QTranslator>
13#endif
14#include <QtCore/QDebug>
15#include <QtCore/QDir>
16#include <QtCore/QFile>
17#include <QtCore/QFileInfo>
18#include <QtCore/QString>
19#include <QtCore/QStringList>
20#include <QtCore/QTextStream>
21#include <QtCore/QLibraryInfo>
22
23QT_USE_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27static void printOut(const QString & out)
28{
29 QTextStream stream(stdout);
30 stream << out;
31}
32
33static void printErr(const QString & out)
34{
35 QTextStream stream(stderr);
36 stream << out;
37}
38
39static void printUsage()
40{
41 printOut(uR"(Usage:
42 lrelease [options] -project project-file
43 lrelease [options] ts-files [-qm qm-file]
45lrelease is part of Qt's Linguist tool chain. It can be used as a
46stand-alone tool to convert XML-based translations files in the TS
47format into the 'compiled' QM format used by QTranslator objects.
48
49Passing .pro files to lrelease is deprecated.
50Please use the lrelease-pro tool instead, or use qmake's lrelease.prf
51feature.
52
53Options:
54 -help Display this information and exit
55 -idbased
56 Deprecated. The flag is not required anymore and will be removed
57 in a future version. It was used to enable ID based translation.
58 -compress
59 Compress the QM files
60 -nounfinished
61 Do not include unfinished translations
62 -fail-on-unfinished
63 Generate an error if unfinished translations are found
64 -removeidentical
65 If the translated text is the same as
66 the source text, do not include the message
67 -markuntranslated <prefix>
68 If a message has no real translation, use the source text
69 prefixed with the given string instead
70 -project <filename>
71 Name of a file containing the project's description in JSON format.
72 Such a file may be generated from a .pro file using the lprodump tool.
73 -silent
74 Do not explain what is being done
75 -verbose
76 Explain what is being done (default)
77 -version
78 Display the version of lrelease and exit
79)"_s);
80}
81
82static bool loadTsFile(Translator &tor, const QString &tsFileName)
83{
85 bool ok = tor.load(tsFileName, cd, "auto"_L1);
86 if (!ok) {
87 printErr("lrelease error: %1"_L1.arg(cd.error()));
88 } else {
89 if (!cd.errors().isEmpty())
90 printOut(cd.error());
91 }
93 return ok;
94}
95
96static bool releaseTranslator(Translator &tor, const QString &qmFileName, ConversionData &cd,
97 bool removeIdentical, bool failOnUnfinished)
98{
99 if (failOnUnfinished && tor.unfinishedTranslationsExist()) {
100 printErr("lrelease error: cannot create '%1': existing unfinished translation(s) "
101 "found (-fail-on-unfinished)"_L1.arg(qmFileName));
102 return false;
103 }
104
105 tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
106
107 if (cd.isVerbose())
108 printOut("Updating '%1'...\n"_L1.arg(qmFileName));
109 if (removeIdentical) {
110 if (cd.isVerbose())
111 printOut("Removing translations equal to source text in '%1'...\n"_L1.arg(qmFileName));
113 }
114
115 QFile file(qmFileName);
116 if (!file.open(QIODevice::WriteOnly)) {
117 printErr("lrelease error: cannot create '%1': %2\n"_L1.arg(qmFileName, file.errorString()));
118 return false;
119 }
120
122 bool ok = saveQM(tor, file, cd);
123 file.close();
124
125 if (!ok) {
126 printErr("lrelease error: cannot save '%1': %2"_L1.arg(qmFileName, cd.error()));
127 } else if (!cd.errors().isEmpty()) {
128 printOut(cd.error());
129 }
131 return ok;
132}
133
134static bool releaseTsFile(const QString &tsFileName, ConversionData &cd, bool removeIdentical,
135 bool failOnUnfinished)
136{
137 Translator tor;
138 if (!loadTsFile(tor, tsFileName))
139 return false;
140
141 QString qmFileName = tsFileName;
142 for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
143 if (qmFileName.endsWith(u'.' + fmt.extension)) {
144 qmFileName.chop(fmt.extension.size() + 1);
145 break;
146 }
147 }
148 qmFileName += ".qm"_L1;
149
150 return releaseTranslator(tor, qmFileName, cd, removeIdentical, failOnUnfinished);
151}
152
153static QStringList translationsFromProjects(const Projects &projects, bool topLevel);
154
155static QStringList translationsFromProject(const Project &project, bool topLevel)
156{
157 QStringList result;
158 if (project.translations)
159 result = *project.translations;
160 result << translationsFromProjects(project.subProjects, false);
161 if (topLevel && result.isEmpty()) {
162 printErr("lrelease warning: Met no 'TRANSLATIONS' entry in project file '%1'\n"_L1.arg(
163 project.filePath));
164 }
165 return result;
166}
167
168static QStringList translationsFromProjects(const Projects &projects, bool topLevel = true)
169{
170 QStringList result;
171 for (const Project &p : projects)
172 result << translationsFromProject(p, topLevel);
173 return result;
174}
175
176int main(int argc, char **argv)
177{
178 QCoreApplication app(argc, argv);
179
181 cd.m_verbose = true; // the default is true starting with Qt 4.2
182 bool removeIdentical = false;
183 bool failOnUnfinished = false;
184 Translator tor;
185 QStringList inputFiles;
186 QString outputFile;
187 QString projectDescriptionFile;
188
189 for (int i = 1; i < argc; ++i) {
190 const char *arg = argv[i];
191 if (!strcmp(arg, "-compress")) {
193 continue;
194 } else if (!strcmp(arg, "-idbased")) {
195 printOut("The flag -idbased is deprecated and not required anymore."
196 "It will be removed in a future version"_L1);
197 continue;
198 } else if (!strcmp(arg, "-nocompress")) {
200 continue;
201 } else if (!strcmp(arg, "-removeidentical")) {
202 removeIdentical = true;
203 continue;
204 } else if (!strcmp(arg, "-nounfinished")) {
205 cd.m_ignoreUnfinished = true;
206 continue;
207 } else if (!strcmp(arg, "-fail-on-unfinished")) {
208 failOnUnfinished = true;
209 continue;
210 } else if (!strcmp(arg, "-markuntranslated")) {
211 if (i == argc - 1) {
213 return 1;
214 }
215 cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
216 } else if (!strcmp(arg, "-project")) {
217 if (i == argc - 1) {
218 printErr("The option -project requires a parameter.\n"_L1);
219 return 1;
220 }
221 if (!projectDescriptionFile.isEmpty()) {
222 printErr("The option -project must appear only once.\n"_L1);
223 return 1;
224 }
225 projectDescriptionFile = QString::fromLocal8Bit(argv[++i]);
226 } else if (!strcmp(arg, "-silent")) {
227 cd.m_verbose = false;
228 continue;
229 } else if (!strcmp(arg, "-verbose")) {
230 cd.m_verbose = true;
231 continue;
232 } else if (!strcmp(arg, "-version")) {
233 printOut("lrelease version %1\n"_L1.arg(QLatin1StringView(QT_VERSION_STR)));
234 return 0;
235 } else if (!strcmp(arg, "-qm")) {
236 if (i == argc - 1) {
238 return 1;
239 }
240 outputFile = QString::fromLocal8Bit(argv[++i]);
241 } else if (!strcmp(arg, "-help")) {
243 return 0;
244 } else if (arg[0] == '-') {
246 return 1;
247 } else {
248 inputFiles << QString::fromLocal8Bit(arg);
249 }
250 }
251
252 if (inputFiles.isEmpty() && projectDescriptionFile.isEmpty()) {
254 return 1;
255 }
256
257 QString errorString;
258 if (!extractProFiles(&inputFiles).isEmpty()) {
259 runInternalQtTool("lrelease-pro"_L1, app.arguments().mid(1));
260 return 0;
261 }
262
263 if (!projectDescriptionFile.isEmpty()) {
264 if (!inputFiles.isEmpty()) {
265 printErr(QLatin1String(
266 "lrelease error: Do not specify TS files if -project is given.\n"));
267 return 1;
268 }
269 Projects projectDescription = readProjectDescription(projectDescriptionFile, &errorString);
270 if (!errorString.isEmpty()) {
271 printErr("lrelease error: %1\n"_L1.arg(errorString));
272 return 1;
273 }
274 inputFiles = translationsFromProjects(projectDescription);
275 }
276
277 for (const QString &inputFile : std::as_const(inputFiles)) {
278 if (outputFile.isEmpty()) {
279 if (!releaseTsFile(inputFile, cd, removeIdentical, failOnUnfinished))
280 return 1;
281 } else {
282 if (!loadTsFile(tor, inputFile))
283 return 1;
284 }
285 }
286
287 if (!outputFile.isEmpty())
288 return releaseTranslator(tor, outputFile, cd, removeIdentical, failOnUnfinished) ? 0 : 1;
289
290 return 0;
291}
void clearErrors()
Definition translator.h:40
bool isVerbose() const
Definition translator.h:32
TranslatorSaveMode m_saveMode
Definition translator.h:62
bool m_ignoreUnfinished
Definition translator.h:58
bool unfinishedTranslationsExist() const
void stripIdenticalSourceTranslations()
Duplicates resolveDuplicates()
void normalizeTranslations(ConversionData &cd)
std::vector< Project > Projects
static void printOut(const QString &out)
Definition main.cpp:21
static void printErr(const QString &out)
Definition main.cpp:15
static void printUsage()
Definition main.cpp:60
int main(int argc, char *argv[])
[ctor_close]
@ SaveStripped
@ SaveEverything