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 | |
40 | QT_BEGIN_NAMESPACE |
41 | |
42 | class UiReader : public XmlParser |
43 | { |
44 | public: |
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 | |
57 | private: |
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 ; |
73 | QString ; |
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 | |
83 | bool 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 | |
106 | bool 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 | |
130 | bool UiReader::characters(const QStringRef &ch) |
131 | { |
132 | m_accum += ch.toString(); |
133 | return true; |
134 | } |
135 | |
136 | bool 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 | |
146 | void 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 | |
164 | void 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 | |
180 | bool 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 | |
199 | QT_END_NAMESPACE |
200 | |