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) 2025 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 <QCoreApplication>
5#include <QCommandLineParser>
6#include <QString>
7
8#include "translator.h"
9#include "validator.h"
10
11QT_USE_NAMESPACE
12
13using namespace Qt::StringLiterals;
14
15static void printErr(const QString &out)
16{
17 QTextStream stream(stderr);
18 stream << out;
19}
20
21static void printOut(const QString &out)
22{
23 QTextStream stream(stdout);
24 stream << out;
25}
26
27// These descriptions intentionally mirror the (translated) warning texts in
28// WarningModel::addWarning() in the Linguist GUI. The GUI needs translatable
29// strings while this console tool does not, so the two sets are kept separate.
30static QString checkDescription(Validator::ErrorType type)
31{
32 switch (type) {
33 case Validator::SuperfluousAccelerator:
34 return u"Accelerator possibly superfluous in translation"_s;
35 case Validator::MissingAccelerator:
36 return u"Accelerator possibly missing in translation"_s;
37 case Validator::SurroundingWhitespaceDiffers:
38 return u"Translation does not have the same surrounding whitespace as the source text"_s;
39 case Validator::PunctuationDiffers:
40 return u"Translation does not end with the same punctuation as the source text"_s;
41 case Validator::PlaceMarkersDiffer:
42 return u"Translation does not refer to the same place markers as the source text"_s;
43 case Validator::NumerusMarkerMissing:
44 return u"Translation does not contain the necessary %n/%Ln place marker"_s;
45 case Validator::IgnoredPhrasebook:
46 return u"A phrase book suggestion was ignored"_s;
47 }
48 return u"Unknown error"_s;
49}
50
51int main(int argc, char **argv)
52{
53 QCoreApplication app(argc, argv);
54 QCoreApplication::setApplicationName(u"lcheck"_s);
55 QCoreApplication::setApplicationVersion(QLatin1StringView(QT_VERSION_STR));
56
57 QCommandLineParser parser;
58 parser.setApplicationDescription(
59 u"lcheck is part of Qt's Linguist tool chain. It can be used as a\n"
60 "stand-alone tool to perform batch checks on the translations of\n"
61 "TS files. By default, lcheck performs the following checks and\n"
62 "fails if at least one check fails:\n"
63 " Validity check of accelerators:\n"
64 " Whether the number of ampersands in the source\n"
65 " and translation text is the same.\n"
66 " Validity check of surrounding whitespaces:\n"
67 " Whether the source and translation texts have the\n"
68 " same surrounding whitespaces.\n"
69 " Validity check of ending punctuation:\n"
70 " Whether the source and translation texts have the\n"
71 " same ending punctuation.\n"
72 " Validity check of place markers:\n"
73 " Whether %1, %2, ... are used consistently in the\n"
74 " source text and translation text.\n"
75 "To get more details regarding the checks refer to Qt Linguist help.\n"
76 "Each check can be disabled using the arguments as explained below."_s);
77 parser.addHelpOption();
78 parser.addVersionOption();
79
80 QCommandLineOption noAcceleratorOption(u"no-accelerator"_s, u"Disable the accelerator check"_s);
81 parser.addOption(noAcceleratorOption);
82
83 QCommandLineOption noPunctuationOption(u"no-punctuation"_s, u"Disable the punctuation check"_s);
84 parser.addOption(noPunctuationOption);
85
86 QCommandLineOption noPlaceMarkerOption(u"no-place-marker"_s,
87 u"Disable the place marker check"_s);
88 parser.addOption(noPlaceMarkerOption);
89
90 QCommandLineOption noWhitespacesOption(u"no-whitespaces"_s,
91 u"Disable the check for surrounding white spaces"_s);
92 parser.addOption(noWhitespacesOption);
93
94 QCommandLineOption checkFinishedOption(
95 u"check-finished"_s,
96 u"Enable check for translations marked as finished.\n"
97 "By default, the finished translations are not checked."_s);
98 parser.addOption(checkFinishedOption);
99
100 QCommandLineOption outputOption(QStringList() << u"o"_s << u"output"_s,
101 u"The output file to generate the report to. If\n"
102 "nothing is specified, the report is written to\n"
103 "the standard error stream."_s,
104 u"file"_s);
105 parser.addOption(outputOption);
106
107 parser.addPositionalArgument(u"ts-file"_s, u"TS file to check"_s);
108
109 parser.process(app);
110
111 const QStringList args = parser.positionalArguments();
112 if (args.isEmpty()) {
113 parser.showHelp(1);
114 }
115
116 const QString tsFile = args.first();
117
118 Validator::Checks checks;
119 checks.accelerator = !parser.isSet(noAcceleratorOption);
120 checks.punctuation = !parser.isSet(noPunctuationOption);
121 checks.placeMarker = !parser.isSet(noPlaceMarkerOption);
122 checks.surroundingWhiteSpace = !parser.isSet(noWhitespacesOption);
123
124 bool checkFinished = parser.isSet(checkFinishedOption);
125 std::optional<QString> output;
126 if (parser.isSet(outputOption))
127 output = parser.value(outputOption);
128
129 Translator tor;
131 bool ok = tor.load(tsFile, cd, "auto"_L1);
132 if (!ok) {
133 printErr("lcheck error: %1"_L1.arg(cd.error()));
134 return 1;
135 }
136
137 if (!cd.errors().isEmpty())
138 ok = false;
139
140 QLocale::Language sourceLang;
141 QLocale::Language targetLang;
142 QLocale::Territory targetTerritory;
143 QList<bool> countRefNeeds;
144
145 tor.languageAndTerritory(tor.sourceLanguageCode(), &sourceLang, nullptr);
146 tor.languageAndTerritory(tor.languageCode(), &targetLang, &targetTerritory);
147
148 if (checks.placeMarker && !getCountNeed(targetLang, targetTerritory, countRefNeeds, nullptr)) {
149 printErr("Could not get numerus info");
150 ok = false;
151 }
152
153 QTextStream stream(stderr);
154 QFile f;
155 if (output) {
156 f.setFileName(*output);
157 if (f.open(QIODevice::WriteOnly | QIODevice::Text))
158 stream.setDevice(&f);
159 else {
160 printErr("Could not open the output file %1 for writing."_L1.arg(*output));
161 return 1;
162 }
163 }
164
165 int errorCount = 0;
166 for (const TranslatorMessage &msg : tor.messages()) {
167 if (!msg.isTranslated() || (!checkFinished && msg.type() == TranslatorMessage::Finished))
168 continue;
169
170 Validator validator = Validator::fromSource(msg.sourceText(), checks, sourceLang, {});
171 const QList<Validator::Error> errors =
172 validator.validate(msg.translations(), msg, targetLang, countRefNeeds);
173 if (errors.isEmpty())
174 continue;
175
176 QString location = msg.fileName();
177 if (!location.isEmpty()) {
178 if (msg.lineNumber() >= 0)
179 location += ":"_L1 + QString::number(msg.lineNumber());
180 location += ": "_L1;
181 }
182
183 for (const Validator::Error &error : errors) {
184 stream << location << checkDescription(error.type) << u'\n';
185 if (!msg.context().isEmpty())
186 stream << " Context: "_L1 << msg.context() << u'\n';
187 stream << " Source: "_L1 << msg.sourceText() << u'\n';
188 stream << " Translation: "_L1 << error.message << u'\n';
189 ++errorCount;
190 }
191 }
192
193 if (errorCount > 0)
194 ok = false;
195
196 printOut(u"Finished batch checks. Found %1 validation error(s).\n"_s.arg(errorCount));
197
198 return ok ? 0 : 1;
199}
Definition qlist.h:82
static void printOut(const QString &out)
Definition main.cpp:21
static QString checkDescription(Validator::ErrorType type)
Definition main.cpp:30
static void printErr(const QString &out)
Definition main.cpp:15
int main(int argc, char *argv[])
[ctor_close]