1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "lupdate.h"
30
31#include <translator.h>
32#include <xmlparser.h>
33
34#include <QtCore/QCoreApplication>
35#include <QtCore/QDebug>
36#include <QtCore/QFile>
37#include <QtCore/QString>
38#include <QtCore/QXmlStreamReader>
39
40QT_BEGIN_NAMESPACE
41
42class UiReader : public XmlParser
43{
44public:
45 UiReader(Translator &translator, ConversionData &cd, QXmlStreamReader &reader)
46 : XmlParser(reader),
47 m_translator(translator),
48 m_cd(cd),
49 m_lineNumber(-1),
50 m_isTrString(false),
51 m_insideStringList(false),
52 m_idBasedTranslations(false)
53 {
54 }
55 ~UiReader() override = default;
56
57private:
58 bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
59 const QStringRef &qName, const QXmlStreamAttributes &atts) override;
60 bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
61 const QStringRef &qName) override;
62 bool characters(const QStringRef &ch) override;
63 bool fatalError(qint64 line, qint64 column, const QString &message) override;
64
65 void flush();
66 void readTranslationAttributes(const QXmlStreamAttributes &atts);
67
68 Translator &m_translator;
69 ConversionData &m_cd;
70 QString m_context;
71 QString m_source;
72 QString m_comment;
73 QString m_extracomment;
74 QString m_id;
75
76 QString m_accum;
77 int m_lineNumber;
78 bool m_isTrString;
79 bool m_insideStringList;
80 bool m_idBasedTranslations;
81};
82
83bool UiReader::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
84 const QStringRef &qName, const QXmlStreamAttributes &atts)
85{
86 Q_UNUSED(namespaceURI);
87 Q_UNUSED(localName);
88
89 if (qName == QLatin1String("string")) {
90 flush();
91 if (!m_insideStringList)
92 readTranslationAttributes(atts);
93 } else if (qName == QLatin1String("stringlist")) {
94 flush();
95 m_insideStringList = true;
96 readTranslationAttributes(atts);
97 } else if (qName == QLatin1String("ui")) { // UI "header"
98 const auto attr = QStringLiteral("idbasedtr");
99 m_idBasedTranslations =
100 atts.hasAttribute(qualifiedName: attr) && atts.value(qualifiedName: attr) == QLatin1String("true");
101 }
102 m_accum.clear();
103 return true;
104}
105
106bool UiReader::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
107 const QStringRef &qName)
108{
109 Q_UNUSED(namespaceURI);
110 Q_UNUSED(localName);
111
112 m_accum.replace(before: QLatin1String("\r\n"), after: QLatin1String("\n"));
113
114 if (qName == QLatin1String("class")) { // UI "header"
115 if (m_context.isEmpty())
116 m_context = m_accum;
117 } else if (qName == QLatin1String("string") && m_isTrString) {
118 m_source = m_accum;
119 } else if (qName == QLatin1String("comment")) { // FIXME: what's that?
120 m_comment = m_accum;
121 flush();
122 } else if (qName == QLatin1String("stringlist")) {
123 m_insideStringList = false;
124 } else {
125 flush();
126 }
127 return true;
128}
129
130bool UiReader::characters(const QStringRef &ch)
131{
132 m_accum += ch.toString();
133 return true;
134}
135
136bool UiReader::fatalError(qint64 line, qint64 column, const QString &message)
137{
138 QString msg = LU::tr(sourceText: "XML error: Parse error at line %1, column %2 (%3).")
139 .arg(a: line)
140 .arg(a: column)
141 .arg(a: message);
142 m_cd.appendError(error: msg);
143 return false;
144}
145
146void UiReader::flush()
147{
148 if (!m_context.isEmpty() && !m_source.isEmpty()) {
149 TranslatorMessage msg(m_context, m_source,
150 m_comment, QString(), m_cd.m_sourceFileName,
151 m_lineNumber, QStringList());
152 msg.setExtraComment(m_extracomment);
153 msg.setId(m_id);
154 m_translator.extend(msg, cd&: m_cd);
155 }
156 m_source.clear();
157 if (!m_insideStringList) {
158 m_comment.clear();
159 m_extracomment.clear();
160 m_id.clear();
161 }
162}
163
164void UiReader::readTranslationAttributes(const QXmlStreamAttributes &atts)
165{
166 const auto notr = atts.value(QStringLiteral("notr"));
167 if (notr.isEmpty() || notr != QStringLiteral("true")) {
168 m_isTrString = true;
169 m_comment = atts.value(QStringLiteral("comment")).toString();
170 m_extracomment = atts.value(QStringLiteral("extracomment")).toString();
171 if (m_idBasedTranslations)
172 m_id = atts.value(QStringLiteral("id")).toString();
173 if (!m_cd.m_noUiLines)
174 m_lineNumber = static_cast<int>(reader.lineNumber());
175 } else {
176 m_isTrString = false;
177 }
178}
179
180bool loadUI(Translator &translator, const QString &filename, ConversionData &cd)
181{
182 cd.m_sourceFileName = filename;
183 QFile file(filename);
184 if (!file.open(flags: QIODevice::ReadOnly)) {
185 cd.appendError(error: LU::tr(sourceText: "Cannot open %1: %2").arg(args: filename, args: file.errorString()));
186 return false;
187 }
188
189 QXmlStreamReader reader(&file);
190 reader.setNamespaceProcessing(false);
191
192 UiReader uiReader(translator, cd, reader);
193 bool result = uiReader.parse();
194 if (!result)
195 cd.appendError(error: LU::tr(sourceText: "Parse error in UI file"));
196 return result;
197}
198
199QT_END_NAMESPACE
200

source code of qttools/src/linguist/lupdate/ui.cpp