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 <QtCore/QByteArray>
7#include <QtCore/QDebug>
8#include <QtCore/QTextStream>
9
10#include <QtCore/QXmlStreamReader>
11
12QT_BEGIN_NAMESPACE
13
14class QPHReader : public QXmlStreamReader
15{
16public:
17 QPHReader(QIODevice &dev)
18 : QXmlStreamReader(&dev)
19 {}
20
21 // the "real thing"
22 bool read(Translator &translator);
23
24private:
25 bool isWhiteSpace() const
26 {
27 return isCharacters() && text().toString().trimmed().isEmpty();
28 }
29
30 enum DataField { NoField, SourceField, TargetField, DefinitionField };
31 DataField m_currentField;
32 QString m_currentSource;
33 QString m_currentTarget;
34 QString m_currentDefinition;
35};
36
37bool QPHReader::read(Translator &translator)
38{
39 m_currentField = NoField;
40 while (!atEnd()) {
41 readNext();
42 if (isStartElement()) {
43 if (name() == QLatin1String("source")) {
44 m_currentField = SourceField;
45 } else if (name() == QLatin1String("target")) {
46 m_currentField = TargetField;
47 } else if (name() == QLatin1String("definition")) {
48 m_currentField = DefinitionField;
49 } else {
50 m_currentField = NoField;
51 if (name() == QLatin1String("QPH")) {
52 QXmlStreamAttributes atts = attributes();
53 translator.setLanguageCode(atts.value(qualifiedName: QLatin1String("language")).toString());
54 translator.setSourceLanguageCode(atts.value(qualifiedName: QLatin1String("sourcelanguage")).toString());
55 }
56 }
57 } else if (isWhiteSpace()) {
58 // ignore these
59 } else if (isCharacters()) {
60 if (m_currentField == SourceField)
61 m_currentSource += text();
62 else if (m_currentField == TargetField)
63 m_currentTarget += text();
64 else if (m_currentField == DefinitionField)
65 m_currentDefinition += text();
66 } else if (isEndElement() && name() == QLatin1String("phrase")) {
67 m_currentTarget.replace(before: QChar(Translator::TextVariantSeparator),
68 after: QChar(Translator::BinaryVariantSeparator));
69 TranslatorMessage msg;
70 msg.setSourceText(m_currentSource);
71 msg.setTranslation(m_currentTarget);
72 msg.setComment(m_currentDefinition);
73 translator.append(msg);
74 m_currentSource.clear();
75 m_currentTarget.clear();
76 m_currentDefinition.clear();
77 }
78 }
79 return true;
80}
81
82static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &)
83{
84 translator.setLocationsType(Translator::NoLocations);
85 QPHReader reader(dev);
86 return reader.read(translator);
87}
88
89static QString qphProtect(const QString &str)
90{
91 QString result;
92 result.reserve(asize: str.size() * 12 / 10);
93 for (int i = 0; i != str.size(); ++i) {
94 uint c = str.at(i).unicode();
95 switch (c) {
96 case '\"':
97 result += QLatin1String("&quot;");
98 break;
99 case '&':
100 result += QLatin1String("&amp;");
101 break;
102 case '>':
103 result += QLatin1String("&gt;");
104 break;
105 case '<':
106 result += QLatin1String("&lt;");
107 break;
108 case '\'':
109 result += QLatin1String("&apos;");
110 break;
111 default:
112 if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
113 result += QString(QLatin1String("&#%1;")).arg(a: c);
114 else // this also covers surrogates
115 result += QChar(c);
116 }
117 }
118 return result;
119}
120
121static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &)
122{
123 QTextStream t(&dev);
124 t << "<!DOCTYPE QPH>\n<QPH";
125 QString languageCode = translator.languageCode();
126 if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
127 t << " language=\"" << languageCode << "\"";
128 languageCode = translator.sourceLanguageCode();
129 if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
130 t << " sourcelanguage=\"" << languageCode << "\"";
131 t << ">\n";
132 for (const TranslatorMessage &msg : translator.messages()) {
133 t << "<phrase>\n";
134 t << " <source>" << qphProtect(str: msg.sourceText()) << "</source>\n";
135 QString str = msg.translations().join(sep: QLatin1Char('@'));
136 str.replace(before: QChar(Translator::BinaryVariantSeparator),
137 after: QChar(Translator::TextVariantSeparator));
138 t << " <target>" << qphProtect(str)
139 << "</target>\n";
140 if (!msg.comment().isEmpty())
141 t << " <definition>" << qphProtect(str: msg.comment()) << "</definition>\n";
142 t << "</phrase>\n";
143 }
144 t << "</QPH>\n";
145 return true;
146}
147
148int initQPH()
149{
150 Translator::FileFormat format;
151
152 format.extension = QLatin1String("qph");
153 format.untranslatedDescription = QT_TRANSLATE_NOOP("FMT", "Qt Linguist 'Phrase Book'");
154 format.fileType = Translator::FileFormat::TranslationSource;
155 format.priority = 0;
156 format.loader = &loadQPH;
157 format.saver = &saveQPH;
158 Translator::registerFileFormat(format);
159
160 return 1;
161}
162
163Q_CONSTRUCTOR_FUNCTION(initQPH)
164
165QT_END_NAMESPACE
166

source code of qttools/src/linguist/shared/qph.cpp