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
trparser.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 "trparser.h"
5#include "translator.h"
6#include <iostream>
7
8using namespace Qt::StringLiterals;
9
11
12static bool processTs(Translator &fetchedTor, const QString &file, ConversionData &cd)
13{
14 for (const Translator::FileFormat &fmt : std::as_const(Translator::registeredFileFormats())) {
15 if (file.endsWith(u'.' + fmt.extension, Qt::CaseInsensitive)) {
16 Translator tor;
17 if (tor.load(file, cd, fmt.extension)) {
18 for (TranslatorMessage msg : tor.messages()) {
19 msg.setType(TranslatorMessage::Unfinished);
20 msg.setTranslations(QStringList());
21 msg.setTranslatorComment(QString());
22 fetchedTor.extend(msg, cd);
23 }
24 }
25 return true;
26 }
27 }
28 return false;
29}
30
31void processSources(Translator &fetchedTor, const QStringList &sourceFiles, ConversionData &cd)
32{
33#ifdef QT_NO_QML
34 bool requireQmlSupport = false;
35#endif
36 QStringList sourceFilesCpp;
37 for (const auto &sourceFile : sourceFiles) {
38 if (sourceFile.endsWith(".java"_L1, Qt::CaseInsensitive))
39 loadJava(fetchedTor, sourceFile, cd);
40 else if (sourceFile.endsWith(".ui"_L1, Qt::CaseInsensitive)
41 || sourceFile.endsWith(".jui"_L1, Qt::CaseInsensitive))
42 loadUI(fetchedTor, sourceFile, cd);
43#ifndef QT_NO_QML
44 else if (sourceFile.endsWith(".js"_L1, Qt::CaseInsensitive)
45 || sourceFile.endsWith(".qs"_L1, Qt::CaseInsensitive)) {
46 loadQScript(fetchedTor, sourceFile, cd);
47 } else if (sourceFile.endsWith(".mjs"_L1, Qt::CaseInsensitive)) {
48 loadJSModule(fetchedTor, sourceFile, cd);
49 } else if (sourceFile.endsWith(".qml"_L1, Qt::CaseInsensitive))
50 loadQml(fetchedTor, sourceFile, cd);
51#else
52 else if (sourceFile.endsWith(".qml"_L1, Qt::CaseInsensitive)
53 || sourceFile.endsWith(".js"_L1, Qt::CaseInsensitive)
54 || sourceFile.endsWith(".mjs"_L1, Qt::CaseInsensitive)
55 || sourceFile.endsWith(".qs"_L1, Qt::CaseInsensitive))
56 requireQmlSupport = true;
57#endif // QT_NO_QML
58 else if (sourceFile.endsWith(u".py", Qt::CaseInsensitive))
59 loadPython(fetchedTor, sourceFile, cd);
60 else if (!processTs(fetchedTor, sourceFile, cd))
61 sourceFilesCpp << sourceFile;
62 }
63
64#ifdef QT_NO_QML
65 if (requireQmlSupport) {
66 std::cout << qPrintable("missing qml/javascript support\n"
67 "Some files have been ignored.\n"_L1);
68 }
69#endif
70
71 loadCPP(fetchedTor, sourceFilesCpp, cd);
72
73 if (!cd.error().isEmpty())
74 std::cerr << qPrintable(cd.error());
75}
76
77QString transcode(const QString &str)
78{
79 static const char tab[] = "abfnrtv";
80 static const char backTab[] = "\a\b\f\n\r\t\v";
81 // This function has to convert back to bytes, as C's \0* sequences work at that level.
82 const QByteArray in = str.toUtf8();
83 QByteArray out;
84
85 out.reserve(in.size());
86 for (qsizetype i = 0; i < in.size();) {
87 uchar c = in[i++];
88 if (c == '\\') {
89 if (i >= in.size())
90 break;
91 c = in[i++];
92
93 if (c == '\n')
94 continue;
95
96 if (c == 'x' || c == 'u' || c == 'U') {
97 qsizetype maxSize = 2; // \x
98 if (c == 'u')
99 maxSize = 4;
100 else if (c == 'U')
101 maxSize = 8;
102 maxSize = std::min(in.size(), i + maxSize);
103
104 const bool unicode = (c != 'x');
105 QByteArray hex;
106 while (i < maxSize && isxdigit((c = in[i]))) {
107 hex += c;
108 i++;
109 }
110 if (unicode)
111 out += QString(QChar(hex.toUInt(nullptr, 16))).toUtf8();
112 else
113 out += hex.toUInt(nullptr, 16);
114 } else if (c >= '0' && c < '8') {
115 QByteArray oct;
116 int n = 0;
117 oct += c;
118 while (n < 2 && i < in.size() && (c = in[i]) >= '0' && c < '8') {
119 i++;
120 n++;
121 oct += c;
122 }
123 out += oct.toUInt(0, 8);
124 } else {
125 const char *p = strchr(tab, c);
126 out += !p ? c : backTab[p - tab];
127 }
128 } else {
129 out += c;
130 }
131 }
132 return QString::fromUtf8(out.constData(), out.size());
133}
134
135// Can't have an array of QStaticStringData<N> for different N, so
136// use QString, which requires constructor calls. Doesn't matter
137// much, since this is in an app, not a lib:
139// MSVC can't handle the lambda in this array if QStringLiteral expands
140// to a lambda. In that case, use a QString instead.
141#if defined(Q_CC_MSVC) && defined(Q_COMPILER_LAMBDA)
142# define STRINGLITERAL(F) QLatin1String(#F),
143#else
144# define STRINGLITERAL(F) QStringLiteral(#F),
145#endif
146 LUPDATE_FOR_EACH_TR_FUNCTION(STRINGLITERAL)
147#undef STRINGLITERAL
148};
149
150Q_STATIC_ASSERT((TrFunctionAliasManager::NumTrFunctions
151 == sizeof defaultTrFunctionNames / sizeof *defaultTrFunctionNames));
152
153TrFunctionAliasManager trFunctionAliasManager;
154
156{
157 QStringList result;
158 result.reserve(TrFunctionAliasManager::NumTrFunctions);
159 for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
160 result.push_back(defaultTrFunctionNames[i]);
161 return result;
162}
163
164int trFunctionByDefaultName(const QString &trFunctionName)
165{
166 for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
167 if (trFunctionName == defaultTrFunctionNames[i])
168 return i;
169 return -1;
170}
171
172static void printErr(const QString &out)
173{
174 std::cerr << qPrintable(out);
175}
176
177bool parseTrFunctionAliasString(const QString &aliasString)
178{
179 for (const QString &pair : aliasString.split(u',', Qt::SkipEmptyParts)) {
180 const int equalSign = pair.indexOf(u'=');
181 if (equalSign < 0) {
182 printErr("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n"_L1.arg(
183 pair));
184 return false;
185 }
186 const bool plusEqual = equalSign > 0 && pair[equalSign - 1] == u'+';
187 const int trFunctionEnd = plusEqual ? equalSign - 1 : equalSign;
188 const QString trFunctionName = pair.left(trFunctionEnd).trimmed();
189 const QString alias = pair.mid(equalSign + 1).trimmed();
190 const int trFunction = trFunctionByDefaultName(trFunctionName);
191 if (trFunction < 0) {
192 printErr("Unknown tr-function '%1' in -tr-function-alias option.\n"
193 "Available tr-functions are: %2\n"_L1.arg(trFunctionName,
194 availableFunctions().join(u',')));
195 return false;
196 }
197 if (alias.isEmpty()) {
198 printErr("Empty alias for tr-function '%1' in -tr-function-alias option.\n"_L1.arg(
199 trFunctionName));
200 return false;
201 }
202 trFunctionAliasManager.modifyAlias(trFunction, alias,
203 plusEqual ? TrFunctionAliasManager::AddAlias
204 : TrFunctionAliasManager::SetAlias);
205 }
206 return true;
207}
208
209TrFunctionAliasManager::TrFunctionAliasManager() : m_trFunctionAliases()
210{
211 for (int i = 0; i < NumTrFunctions; ++i)
212 m_trFunctionAliases[i].push_back(defaultTrFunctionNames[i]);
213}
214
215TrFunctionAliasManager::~TrFunctionAliasManager() { }
216
217int TrFunctionAliasManager::trFunctionByName(const QString &trFunctionName) const
218{
219 ensureTrFunctionHashUpdated();
220 // this function needs to be fast
221 const auto it = m_nameToTrFunctionMap.constFind(trFunctionName);
222 return it == m_nameToTrFunctionMap.cend() ? -1 : *it;
223}
224
225void TrFunctionAliasManager::modifyAlias(int trFunction, const QString &alias, Operation op)
226{
227 QList<QString> &list = m_trFunctionAliases[trFunction];
228 if (op == SetAlias)
229 list.clear();
230 list.push_back(alias);
231 m_nameToTrFunctionMap.clear();
232}
233
234void TrFunctionAliasManager::ensureTrFunctionHashUpdated() const
235{
236 if (!m_nameToTrFunctionMap.empty())
237 return;
238
239 NameToTrFunctionMap nameToTrFunctionMap;
240 for (int i = 0; i < NumTrFunctions; ++i)
241 for (const QString &alias : m_trFunctionAliases[i])
242 nameToTrFunctionMap[alias] = TrFunction(i);
243 // commit:
244 m_nameToTrFunctionMap.swap(nameToTrFunctionMap);
245}
246
247const TrFunctionAliasManager::NameToTrFunctionMap &
248TrFunctionAliasManager::nameToTrFunctionMap() const
249{
250 ensureTrFunctionHashUpdated();
251 return m_nameToTrFunctionMap;
252}
253
254QStringList TrFunctionAliasManager::availableFunctionsWithAliases() const
255{
256 QStringList result;
257 result.reserve(NumTrFunctions);
258 for (int i = 0; i < NumTrFunctions; ++i)
259 result.push_back(defaultTrFunctionNames[i] + " (="_L1 + m_trFunctionAliases[i].join(u'=')
260 + u')');
261 return result;
262}
263
264QStringList TrFunctionAliasManager::listAliases() const
265{
266 QStringList result;
267 result.reserve(NumTrFunctions);
268 for (int i = 0; i < NumTrFunctions; ++i) {
269 for (int ii = 1; ii < m_trFunctionAliases[i].size(); ii++) {
270 // ii = 0 is the default name. Not listed here
271 result.push_back(m_trFunctionAliases[i][ii]);
272 }
273 }
274 return result;
275}
276
277QT_END_NAMESPACE
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
int trFunctionByDefaultName(const QString &trFunctionName)
Definition trparser.cpp:164
QStringList availableFunctions()
Definition trparser.cpp:155
static const QString defaultTrFunctionNames[]
Definition trparser.cpp:138
TrFunctionAliasManager trFunctionAliasManager
Definition trparser.cpp:153
static QT_BEGIN_NAMESPACE bool processTs(Translator &fetchedTor, const QString &file, ConversionData &cd)
Definition trparser.cpp:12
static void printErr(const QString &out)
Definition trparser.cpp:172
bool parseTrFunctionAliasString(const QString &aliasString)
Definition trparser.cpp:177
void processSources(Translator &fetchedTor, const QStringList &sourceFiles, ConversionData &cd)
Definition trparser.cpp:31
QString transcode(const QString &str)
Definition trparser.cpp:77
#define LUPDATE_FOR_EACH_TR_FUNCTION(UNARY_MACRO)
Definition trparser.h:51