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

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