QtLingo 1
QtLingo is an Application to make Qt Computer Translations easier
qonlinetranslator.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2018-2021 Hennadii Chernyshchyk <genaloner@gmail.com>
3 *
4 * This file is part of QOnlineTranslator.
5 *
6 * QOnlineTranslator is free library; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a get of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 */
20
21#include "qonlinetranslator.h"
22#include "qonlinetts.h"
23
24
28const QMap<QOnlineTranslator::Language, QString> QOnlineTranslator::s_genericLanguageCodes =
29{
30 { Auto, QStringLiteral("auto") },
31 { Afrikaans, QStringLiteral("af") },
32 { Albanian, QStringLiteral("sq") },
33 { Amharic, QStringLiteral("am") },
34 { Arabic, QStringLiteral("ar") },
35 { Armenian, QStringLiteral("hy") },
36 { Azerbaijani, QStringLiteral("az") },
37 { Bashkir, QStringLiteral("ba") },
38 { Basque, QStringLiteral("eu") },
39 { Belarusian, QStringLiteral("be") },
40 { Bengali, QStringLiteral("bn") },
41 { Bosnian, QStringLiteral("bs") },
42 { Bulgarian, QStringLiteral("bg") },
43 { Cantonese, QStringLiteral("yue") },
44 { Catalan, QStringLiteral("ca") },
45 { Cebuano, QStringLiteral("ceb") },
46 { Chichewa, QStringLiteral("ny") },
47 { Corsican, QStringLiteral("co") },
48 { Croatian, QStringLiteral("hr") },
49 { Czech, QStringLiteral("cs") },
50 { Danish, QStringLiteral("da") },
51 { Dutch, QStringLiteral("nl") },
52 { English, QStringLiteral("en") },
53 { Esperanto, QStringLiteral("eo") },
54 { Estonian, QStringLiteral("et") },
55 { Fijian, QStringLiteral("fj") },
56 { Filipino, QStringLiteral("fil") },
57 { Finnish, QStringLiteral("fi") },
58 { French, QStringLiteral("fr") },
59 { Frisian, QStringLiteral("fy") },
60 { Galician, QStringLiteral("gl") },
61 { Georgian, QStringLiteral("ka") },
62 { German, QStringLiteral("de") },
63 { Greek, QStringLiteral("el") },
64 { Gujarati, QStringLiteral("gu") },
65 { HaitianCreole, QStringLiteral("ht") },
66 { Hausa, QStringLiteral("ha") },
67 { Hawaiian, QStringLiteral("haw") },
68 { Hebrew, QStringLiteral("he") },
69 { HillMari, QStringLiteral("mrj") },
70 { Hindi, QStringLiteral("hi") },
71 { Hmong, QStringLiteral("hmn") },
72 { Hungarian, QStringLiteral("hu") },
73 { Icelandic, QStringLiteral("is") },
74 { Igbo, QStringLiteral("ig") },
75 { Indonesian, QStringLiteral("id") },
76 { Irish, QStringLiteral("ga") },
77 { Italian, QStringLiteral("it") },
78 { Japanese, QStringLiteral("ja") },
79 { Javanese, QStringLiteral("jw") },
80 { Kannada, QStringLiteral("kn") },
81 { Kazakh, QStringLiteral("kk") },
82 { Khmer, QStringLiteral("km") },
83 { Kinyarwanda, QStringLiteral("rw") },
84 { Klingon, QStringLiteral("tlh") },
85 { KlingonPlqaD, QStringLiteral("tlh-Qaak") },
86 { Korean, QStringLiteral("ko") },
87 { Kurdish, QStringLiteral("ku") },
88 { Kyrgyz, QStringLiteral("ky") },
89 { Lao, QStringLiteral("lo") },
90 { Latin, QStringLiteral("la") },
91 { Latvian, QStringLiteral("lv") },
92 { LevantineArabic, QStringLiteral("apc") },
93 { Lithuanian, QStringLiteral("lt") },
94 { Luxembourgish, QStringLiteral("lb") },
95 { Macedonian, QStringLiteral("mk") },
96 { Malagasy, QStringLiteral("mg") },
97 { Malay, QStringLiteral("ms") },
98 { Malayalam, QStringLiteral("ml") },
99 { Maltese, QStringLiteral("mt") },
100 { Maori, QStringLiteral("mi") },
101 { Marathi, QStringLiteral("mr") },
102 { Mari, QStringLiteral("mhr") },
103 { Mongolian, QStringLiteral("mn") },
104 { Myanmar, QStringLiteral("my") },
105 { Nepali, QStringLiteral("ne") },
106 { Norwegian, QStringLiteral("no") },
107 { Oriya, QStringLiteral("or") },
108 { Papiamento, QStringLiteral("pap") },
109 { Pashto, QStringLiteral("ps") },
110 { Persian, QStringLiteral("fa") },
111 { Polish, QStringLiteral("pl") },
112 { Portuguese, QStringLiteral("pt") },
113 { Punjabi, QStringLiteral("pa") },
114 { QueretaroOtomi, QStringLiteral("otq") },
115 { Romanian, QStringLiteral("ro") },
116 { Russian, QStringLiteral("ru") },
117 { Samoan, QStringLiteral("sm") },
118 { ScotsGaelic, QStringLiteral("gd") },
119 { SerbianCyrillic, QStringLiteral("sr") },
120 { SerbianLatin, QStringLiteral("sr-Latin") },
121 { Sesotho, QStringLiteral("st") },
122 { Shona, QStringLiteral("sn") },
123 { SimplifiedChinese, QStringLiteral("zh-CN") },
124 { Sindhi, QStringLiteral("sd") },
125 { Sinhala, QStringLiteral("si") },
126 { Slovak, QStringLiteral("sk") },
127 { Slovenian, QStringLiteral("sl") },
128 { Somali, QStringLiteral("so") },
129 { Spanish, QStringLiteral("es") },
130 { Sundanese, QStringLiteral("su") },
131 { Swahili, QStringLiteral("sw") },
132 { Swedish, QStringLiteral("sv") },
133 { Tagalog, QStringLiteral("tl") },
134 { Tahitian, QStringLiteral("ty") },
135 { Tajik, QStringLiteral("tg") },
136 { Tamil, QStringLiteral("ta") },
137 { Tatar, QStringLiteral("tt") },
138 { Telugu, QStringLiteral("te") },
139 { Thai, QStringLiteral("th") },
140 { Tongan, QStringLiteral("to") },
141 { TraditionalChinese, QStringLiteral("zh-TW") },
142 { Turkish, QStringLiteral("tr") },
143 { Turkmen, QStringLiteral("tk") },
144 { Udmurt, QStringLiteral("udm") },
145 { Uighur, QStringLiteral("ug") },
146 { Ukrainian, QStringLiteral("uk") },
147 { Urdu, QStringLiteral("ur") },
148 { Uzbek, QStringLiteral("uz") },
149 { Vietnamese, QStringLiteral("vi") },
150 { Welsh, QStringLiteral("cy") },
151 { Xhosa, QStringLiteral("xh") },
152 { Yiddish, QStringLiteral("yi") },
153 { Yoruba, QStringLiteral("yo") },
154 { YucatecMaya, QStringLiteral("yua") },
155 { Zulu, QStringLiteral("zu") }
156};
157
161const QMap<QOnlineTranslator::Language, QString> QOnlineTranslator::s_googleLanguageCodes =
162{
163 { Hebrew, QStringLiteral("iw") }
164};
165
169const QMap<QOnlineTranslator::Language, QString> QOnlineTranslator::s_yandexLanguageCodes =
170{
171 { SimplifiedChinese, QStringLiteral("zn") },
172 { Javanese, QStringLiteral("jv") }
173};
174
178const QMap<QOnlineTranslator::Language, QString> QOnlineTranslator::s_bingLanguageCodes =
179{
180 { Auto, QStringLiteral("auto-detect") },
181 { Bosnian, QStringLiteral("bs-Latn") },
182 { SerbianCyrillic, QStringLiteral("sr-Cyrl") },
183 { SimplifiedChinese, QStringLiteral("zh-Hans") },
184 { TraditionalChinese, QStringLiteral("zh-Hant") },
185 { Hmong, QStringLiteral("mww") }
186};
187
191QOnlineTranslator::QOnlineTranslator(QObject *parent) : QObject(parent), m_stateMachine(new QStateMachine(this)), m_networkManager(new QNetworkAccessManager(this))
192{
193 connect(m_stateMachine, &QStateMachine::finished, this, &QOnlineTranslator::finished);
194 connect(m_stateMachine, &QStateMachine::stopped, this, &QOnlineTranslator::finished);
195}
196
200void QOnlineTranslator::translate(const QString &text, Engine engine, Language translationLang, Language sourceLang, Language uiLang)
201{
202 abort();
203 resetData();
204
205 m_onlyDetectLanguage = false;
206 m_source = text;
207 m_sourceLang = sourceLang;
208 m_translationLang = translationLang == Auto ? language(QLocale()) : translationLang;
209 m_uiLang = uiLang == Auto ? language(QLocale()) : uiLang;
210
211 // Check if the source selected languages ​​are supported by the engine
212 if (!isSupportTranslation(engine, m_sourceLang))
213 {
214 resetData(ParametersError, QString("%1 %2 %3 %4").arg(tr("Selected source language"), languageName(m_sourceLang), QMetaEnum::fromType<Engine>().valueToKey(engine), tr("is not supported for")));
215 emit finished();
216 return;
217 }
218 // Check if the selected languages ​​are supported by the engine
219 if (!isSupportTranslation(engine, m_translationLang))
220 {
221 resetData(ParametersError, QString("%1 %2 %3 %4").arg(tr("Selected translation language"), languageName(m_translationLang), QMetaEnum::fromType<Engine>().valueToKey(engine), tr("is not supported for")));
222 emit finished();
223 return;
224 }
225 // Check if the selected UI languages ​​are supported by the engine
226 if (!isSupportTranslation(engine, m_uiLang))
227 {
228 resetData(ParametersError, QString("%1 %2 %3 %4").arg(tr("Selected ui language"), languageName(m_uiLang), QMetaEnum::fromType<Engine>().valueToKey(engine), tr("is not supported for")));
229 emit finished();
230 return;
231 }
232
233 switch (engine)
234 {
235 case Google: buildGoogleStateMachine(); break;
236 case Yandex: buildYandexStateMachine(); break;
237 case Bing: buildBingStateMachine(); break;
238 }
239
240 m_stateMachine->start();
241}
242
246void QOnlineTranslator::detectLanguage(const QString &text, Engine engine)
247{
248 abort();
249 resetData();
250
251 m_onlyDetectLanguage = true;
252 m_source = text;
253 m_sourceLang = Auto;
254 m_translationLang = English;
255 m_uiLang = language(QLocale());
256
257 switch (engine)
258 {
259 case Google: buildGoogleDetectStateMachine(); break;
260 case Yandex: buildYandexDetectStateMachine(); break;
261 case Bing: buildBingDetectStateMachine(); break;
262 }
263
264 m_stateMachine->start();
265}
266
271{
272 if (m_currentReply != nullptr) { m_currentReply->abort(); }
273}
274
279{
280 return m_stateMachine->isRunning();
281}
282
286QJsonDocument QOnlineTranslator::toJson() const
287{
288 QJsonObject translationOptions;
289 for (auto it = m_translationOptions.cbegin(); it != m_translationOptions.cend(); ++it)
290 {
291 QJsonArray arr;
292 for (const QOption &option : it.value())
293 {
294 arr.append(option.toJson());
295 }
296 translationOptions.insert(it.key(), arr);
297 }
298
299 QJsonObject examples;
300 for (auto it = m_examples.cbegin(); it != m_examples.cend(); ++it)
301 {
302 QJsonArray arr;
303 for (const QExample &example : it.value())
304 {
305 arr.append(example.toJson());
306 }
307 examples.insert(it.key(), arr);
308 }
309
310 QJsonObject object
311 {
312 {"examples", qMove(examples)},
313 {"source", m_source},
314 {"sourceTranscription", m_sourceTranscription},
315 {"sourceTranslit", m_sourceTranslit},
316 {"translation", m_translation},
317 {"translationOptions", qMove(translationOptions)},
318 {"translationTranslit", m_translationTranslit},
319 };
320
321 return QJsonDocument(object);
322}
323
328{
329 return m_source;
330}
331
336{
337 return m_sourceTranslit;
338}
339
344{
345 return m_sourceTranscription;
346}
347
352{
353 return languageName(m_sourceLang);
354}
355
360{
361 return m_sourceLang;
362}
363
368{
369 return m_translation;
370}
371
376{
377 return m_translationTranslit;
378}
379
384{
385 return languageName(m_translationLang);
386}
387
392{
393 return m_translationLang;
394}
395
399QMap<QString, QVector<QOption>> QOnlineTranslator::translationOptions() const
400{
401 return m_translationOptions;
402}
403
407QMap<QString, QVector<QExample>> QOnlineTranslator::examples() const
408{
409 return m_examples;
410}
411
416{
417 return m_error;
418}
419
424{
425 return m_errorString;
426}
427
432{
433 return m_sourceTranslitEnabled;
434}
435
440{
441 m_sourceTranslitEnabled = enable;
442}
443
448{
449 return m_translationTranslitEnabled;
450}
451
456{
457 m_translationTranslitEnabled = enable;
458}
459
464{
465 return m_sourceTranscriptionEnabled;
466}
467
472{
473 m_sourceTranscriptionEnabled = enable;
474}
475
480{
481 return m_translationOptionsEnabled;
482}
483
488{
489 m_translationOptionsEnabled = enable;
490}
491
496{
497 return m_examplesEnabled;
498}
499
504{
505 m_examplesEnabled = enable;
506}
507
512{
513 switch (lang)
514 {
515 case Auto: return tr("Automatically detect");
516 case Afrikaans: return tr("Afrikaans");
517 case Albanian: return tr("Albanian");
518 case Amharic: return tr("Amharic");
519 case Arabic: return tr("Arabic");
520 case Armenian: return tr("Armenian");
521 case Azerbaijani: return tr("Azeerbaijani");
522 case Basque: return tr("Basque");
523 case Bashkir: return tr("Bashkir");
524 case Belarusian: return tr("Belarusian");
525 case Bengali: return tr("Bengali");
526 case Bosnian: return tr("Bosnian");
527 case Bulgarian: return tr("Bulgarian");
528 case Catalan: return tr("Catalan");
529 case Cantonese: return tr("Cantonese");
530 case Cebuano: return tr("Cebuano");
531 case SimplifiedChinese: return tr("Chinese (Simplified)");
532 case TraditionalChinese: return tr("Chinese (Traditional)");
533 case Corsican: return tr("Corsican");
534 case Croatian: return tr("Croatian");
535 case Czech: return tr("Czech");
536 case Danish: return tr("Danish");
537 case Dutch: return tr("Dutch");
538 case English: return tr("English");
539 case Esperanto: return tr("Esperanto");
540 case Estonian: return tr("Estonian");
541 case Fijian: return tr("Fijian");
542 case Filipino: return tr("Filipino");
543 case Finnish: return tr("Finnish");
544 case French: return tr("French");
545 case Frisian: return tr("Frisian");
546 case Galician: return tr("Galician");
547 case Georgian: return tr("Georgian");
548 case German: return tr("German");
549 case Greek: return tr("Greek");
550 case Gujarati: return tr("Gujarati");
551 case HaitianCreole: return tr("Haitian Creole");
552 case Hausa: return tr("Hausa");
553 case Hawaiian: return tr("Hawaiian");
554 case Hebrew: return tr("Hebrew");
555 case HillMari: return tr("Hill Mari");
556 case Hindi: return tr("Hindi");
557 case Hmong: return tr("Hmong");
558 case Hungarian: return tr("Hungarian");
559 case Icelandic: return tr("Icelandic");
560 case Igbo: return tr("Igbo");
561 case Indonesian: return tr("Indonesian");
562 case Irish: return tr("Irish");
563 case Italian: return tr("Italian");
564 case Japanese: return tr("Japanese");
565 case Javanese: return tr("Javanese");
566 case Kannada: return tr("Kannada");
567 case Kazakh: return tr("Kazakh");
568 case Khmer: return tr("Khmer");
569 case Kinyarwanda: return tr("Kinyarwanda");
570 case Klingon: return tr("Klingon");
571 case KlingonPlqaD: return tr("Klingon (PlqaD)");
572 case Korean: return tr("Korean");
573 case Kurdish: return tr("Kurdish");
574 case Kyrgyz: return tr("Kyrgyz");
575 case Lao: return tr("Lao");
576 case Latin: return tr("Latin");
577 case Latvian: return tr("Latvian");
578 case LevantineArabic: return tr("Levantine Arabic");
579 case Lithuanian: return tr("Lithuanian");
580 case Luxembourgish: return tr("Luxembourgish");
581 case Macedonian: return tr("Macedonian");
582 case Malagasy: return tr("Malagasy");
583 case Malay: return tr("Malay");
584 case Malayalam: return tr("Malayalam");
585 case Maltese: return tr("Maltese");
586 case Maori: return tr("Maori");
587 case Marathi: return tr("Marathi");
588 case Mari: return tr("Mari");
589 case Mongolian: return tr("Mongolian");
590 case Myanmar: return tr("Myanmar");
591 case Nepali: return tr("Nepali");
592 case Norwegian: return tr("Norwegian");
593 case Oriya: return tr("Oriya");
594 case Chichewa: return tr("Chichewa");
595 case Papiamento: return tr("Papiamento");
596 case Pashto: return tr("Pashto");
597 case Persian: return tr("Persian");
598 case Polish: return tr("Polish");
599 case Portuguese: return tr("Portuguese");
600 case Punjabi: return tr("Punjabi");
601 case QueretaroOtomi: return tr("Queretaro Otomi");
602 case Romanian: return tr("Romanian");
603 case Russian: return tr("Russian");
604 case Samoan: return tr("Samoan");
605 case ScotsGaelic: return tr("Scots Gaelic");
606 case SerbianCyrillic: return tr("Serbian (Cyrillic)");
607 case SerbianLatin: return tr("Serbian (Latin)");
608 case Sesotho: return tr("Sesotho");
609 case Shona: return tr("Shona");
610 case Sindhi: return tr("Sindhi");
611 case Sinhala: return tr("Sinhala");
612 case Slovak: return tr("Slovak");
613 case Slovenian: return tr("Slovenian");
614 case Somali: return tr("Somali");
615 case Spanish: return tr("Spanish");
616 case Sundanese: return tr("Sundanese");
617 case Swahili: return tr("Swahili");
618 case Swedish: return tr("Swedish");
619 case Tagalog: return tr("Tagalog");
620 case Tahitian: return tr("Tahitian");
621 case Tajik: return tr("Tajik");
622 case Tamil: return tr("Tamil");
623 case Tatar: return tr("Tatar");
624 case Telugu: return tr("Telugu");
625 case Thai: return tr("Thai");
626 case Tongan: return tr("Tongan");
627 case Turkish: return tr("Turkish");
628 case Turkmen: return tr("Turkmen");
629 case Udmurt: return tr("Udmurt");
630 case Uighur: return tr("Uighur");
631 case Ukrainian: return tr("Ukrainian");
632 case Urdu: return tr("Urdu");
633 case Uzbek: return tr("Uzbek");
634 case Vietnamese: return tr("Vietnamese");
635 case Welsh: return tr("Welsh");
636 case Xhosa: return tr("Xhosa");
637 case Yiddish: return tr("Yiddish");
638 case Yoruba: return tr("Yoruba");
639 case YucatecMaya: return tr("Yucatec Maya");
640 case Zulu: return tr("Zulu");
641 default: return QString();
642 } // end switch (lang)
643} // end languageName(Language lang)
644
649{
650 return s_genericLanguageCodes.value(lang);
651}
652
657{
658 switch (locale.language())
659 {
660 case QLocale::Afrikaans: return Afrikaans;
661 case QLocale::Albanian: return Albanian;
662 case QLocale::Amharic: return Amharic;
663 case QLocale::Arabic: return Arabic;
664 case QLocale::Armenian: return Armenian;
665 case QLocale::Azerbaijani: return Azerbaijani;
666 case QLocale::Basque: return Basque;
667 case QLocale::Belarusian: return Belarusian;
668 case QLocale::Bengali: return Bengali;
669 case QLocale::Bosnian: return Bosnian;
670 case QLocale::Bulgarian: return Bulgarian;
671 case QLocale::Catalan: return Catalan;
672 case QLocale::Chinese: return SimplifiedChinese;
673 case QLocale::LiteraryChinese: return TraditionalChinese;
674 case QLocale::Corsican: return Corsican;
675 case QLocale::Croatian: return Croatian;
676 case QLocale::Czech: return Czech;
677 case QLocale::Danish: return Danish;
678 case QLocale::Dutch: return Dutch;
679 case QLocale::Esperanto: return Esperanto;
680 case QLocale::Estonian: return Estonian;
681 case QLocale::Finnish: return Finnish;
682 case QLocale::French: return French;
683 case QLocale::Frisian: return Frisian;
684 case QLocale::Galician: return Galician;
685 case QLocale::Georgian: return Georgian;
686 case QLocale::German: return German;
687 case QLocale::Greek: return Greek;
688 case QLocale::Gujarati: return Gujarati;
689 case QLocale::Haitian: return HaitianCreole;
690 case QLocale::Hausa: return Hausa;
691 case QLocale::Hawaiian: return Hawaiian;
692 case QLocale::Hebrew: return Hebrew;
693 case QLocale::Hindi: return Hindi;
694 case QLocale::Hungarian: return Hungarian;
695 case QLocale::Icelandic: return Icelandic;
696 case QLocale::Igbo: return Igbo;
697 case QLocale::Indonesian: return Indonesian;
698 case QLocale::Irish: return Irish;
699 case QLocale::Italian: return Italian;
700 case QLocale::Japanese: return Japanese;
701 case QLocale::Javanese: return Javanese;
702 case QLocale::Kannada: return Kannada;
703 case QLocale::Kazakh: return Kazakh;
704 case QLocale::Khmer: return Khmer;
705 case QLocale::Kinyarwanda: return Kinyarwanda;
706 case QLocale::Korean: return Korean;
707 case QLocale::Kurdish: return Kurdish;
708 case QLocale::Lao: return Lao;
709 case QLocale::Latin: return Latin;
710 case QLocale::Latvian: return Latvian;
711 case QLocale::Lithuanian: return Lithuanian;
712 case QLocale::Luxembourgish: return Luxembourgish;
713 case QLocale::Macedonian: return Macedonian;
714 case QLocale::Malagasy: return Malagasy;
715 case QLocale::Malay: return Malay;
716 case QLocale::Malayalam: return Malayalam;
717 case QLocale::Maltese: return Maltese;
718 case QLocale::Maori: return Maori;
719 case QLocale::Marathi: return Marathi;
720 case QLocale::Mongolian: return Mongolian;
721 case QLocale::Nepali: return Nepali;
722 case QLocale::NorwegianBokmal: return Norwegian;
723 case QLocale::Oriya: return Oriya;
724 case QLocale::Pashto: return Pashto;
725 case QLocale::Persian: return Persian;
726 case QLocale::Polish: return Polish;
727 case QLocale::Portuguese: return Portuguese;
728 case QLocale::Punjabi: return Punjabi;
729 case QLocale::Romanian: return Romanian;
730 case QLocale::Russian: return Russian;
731 case QLocale::Samoan: return Samoan;
732 case QLocale::Gaelic: return ScotsGaelic;
733 case QLocale::Serbian: return SerbianCyrillic;
734 case QLocale::Shona: return Shona;
735 case QLocale::Sindhi: return Sindhi;
736 case QLocale::Sinhala: return Sinhala;
737 case QLocale::Slovak: return Slovak;
738 case QLocale::Slovenian: return Slovenian;
739 case QLocale::Somali: return Somali;
740 case QLocale::Spanish: return Spanish;
741 case QLocale::Sundanese: return Sundanese;
742 case QLocale::Swahili: return Swahili;
743 case QLocale::Swedish: return Swedish;
744 case QLocale::Filipino: return Filipino;
745 case QLocale::Tajik: return Tajik;
746 case QLocale::Tamil: return Tamil;
747 case QLocale::Tatar: return Tatar;
748 case QLocale::Telugu: return Telugu;
749 case QLocale::Thai: return Thai;
750 case QLocale::Turkish: return Turkish;
751 case QLocale::Turkmen: return Turkmen;
752 case QLocale::Uighur: return Uighur;
753 case QLocale::Ukrainian: return Ukrainian;
754 case QLocale::Urdu: return Urdu;
755 case QLocale::Uzbek: return Uzbek;
756 case QLocale::Vietnamese: return Vietnamese;
757 case QLocale::Welsh: return Welsh;
758 case QLocale::Xhosa: return Xhosa;
759 case QLocale::Yiddish: return Yiddish;
760 case QLocale::Yoruba: return Yoruba;
761 case QLocale::Zulu: return Zulu;
762 default: return English;
763 }
764} // end language(const QLocale
765
771{
772 return s_genericLanguageCodes.key(langCode, NoLanguage);
773}
774
779{
780 bool isSupported = false;
781
782 switch (engine)
783 {
784 case Google:
785 switch (lang)
786 {
787 case NoLanguage:
788 case Bashkir:
789 case Cantonese:
790 case Fijian:
791 case Filipino:
792 case Georgian:
793 case HillMari:
794 case Klingon:
795 case KlingonPlqaD:
796 case LevantineArabic:
797 case Mari:
798 case Papiamento:
799 case QueretaroOtomi:
800 case SerbianLatin:
801 case Tahitian:
802 case Tongan:
803 case Udmurt:
804 case YucatecMaya:
805 isSupported = false;
806 break;
807 default:
808 isSupported = true;
809 break;
810 }
811 break;
812 case Yandex:
813 switch (lang)
814 {
815 case NoLanguage:
816 case Cantonese:
817 case Chichewa:
818 case Corsican:
819 case Fijian:
820 case Filipino:
821 case Frisian:
822 case Hausa:
823 case Hawaiian:
824 case Igbo:
825 case Kinyarwanda:
826 case Klingon:
827 case KlingonPlqaD:
828 case Kurdish:
829 case LevantineArabic:
830 case Oriya:
831 case Pashto:
832 case QueretaroOtomi:
833 case Samoan:
834 case SerbianLatin:
835 case Sesotho:
836 case Shona:
837 case Sindhi:
838 case Somali:
839 case Tahitian:
840 case Tongan:
841 case Turkmen:
842 case Uighur:
843 case Yoruba:
844 case YucatecMaya:
845 case Zulu:
846 isSupported = false;
847 break;
848 default:
849 isSupported = true;
850 break;
851 }
852 break;
853 case Bing:
854 switch (lang)
855 {
856 case NoLanguage:
857 case Albanian:
858 case Amharic:
859 case Armenian:
860 case Azerbaijani:
861 case Basque:
862 case Bashkir:
863 case Belarusian:
864 case Cebuano:
865 case Corsican:
866 case Esperanto:
867 case Frisian:
868 case Galician:
869 case Georgian:
870 case Gujarati:
871 case Hausa:
872 case Hawaiian:
873 case HillMari:
874 case Igbo:
875 case Irish:
876 case Javanese:
877 case Kannada:
878 case Kazakh:
879 case Khmer:
880 case Kinyarwanda:
881 case Kurdish:
882 case Kyrgyz:
883 case Lao:
884 case Latin:
885 case Luxembourgish:
886 case Macedonian:
887 case Malayalam:
888 case Maori:
889 case Marathi:
890 case Mari:
891 case Mongolian:
892 case Myanmar:
893 case Nepali:
894 case Oriya:
895 case Chichewa:
896 case Papiamento:
897 case Pashto:
898 case Punjabi:
899 case ScotsGaelic:
900 case Sesotho:
901 case Shona:
902 case Sindhi:
903 case Sinhala:
904 case Somali:
905 case Sundanese:
906 case Tagalog:
907 case Tajik:
908 case Tatar:
909 case Turkmen:
910 case Uighur:
911 case Udmurt:
912 case Uzbek:
913 case Xhosa:
914 case Yiddish:
915 case Yoruba:
916 case Zulu:
917 isSupported = false;
918 break;
919 default:
920 isSupported = true;
921 break;
922 }
923 }
924
925 return isSupported;
926}
927
931void QOnlineTranslator::skipGarbageText()
932{
933 m_translation.append(sender()->property(s_textProperty).toString());
934}
935
939void QOnlineTranslator::requestGoogleTranslate()
940{
941 const QString sourceText = sender()->property(s_textProperty).toString();
942
943 // Generate API url
944 QUrl url(QStringLiteral("https://translate.googleapis.com/translate_a/single"));
945
946 url.setQuery(QStringLiteral("client=gtx&ie=UTF-8&oe=UTF-8&dt=bd&dt=ex&dt=ld&dt=md&dt=rw&dt=rm&dt=ss&dt=t&dt=at&dt=qc&sl=%1&tl=%2&hl=%3&q=%4")
947 .arg(languageApiCode(Google, m_sourceLang), languageApiCode(Google, m_translationLang), languageApiCode(Google, m_uiLang), QUrl::toPercentEncoding(sourceText)));
948
949 m_currentReply = m_networkManager->get(QNetworkRequest(url));
950}
951
955void QOnlineTranslator::parseGoogleTranslate()
956{
957 m_currentReply->deleteLater();
958
959 // Check for error
960 if (m_currentReply->error() != QNetworkReply::NoError)
961 {
962 if (m_currentReply->error() == QNetworkReply::ServiceUnavailableError)
963 { resetData(ServiceError, tr("Error: Engine systems have detected suspicious traffic from your computer network. Please try your request again later.")); }
964 else
965 { resetData(NetworkError, m_currentReply->errorString()); }
966 return;
967 }
968
969 // Check availability of service
970 const QByteArray data = m_currentReply->readAll();
971 if (data.startsWith('<'))
972 {
973 resetData(ServiceError, tr("Error: Engine systems have detected suspicious traffic from your computer network. Please try your request again later."));
974 return;
975 }
976
977 // Read Json
978 const QJsonDocument jsonResponse = QJsonDocument::fromJson(data);
979 const QJsonArray jsonData = jsonResponse.array();
980
981 if (m_sourceLang == Auto)
982 {
983 // Parse language
984 m_sourceLang = language(Google, jsonData.at(2).toString());
985 if (m_sourceLang == NoLanguage)
986 {
987 resetData(ParsingError, tr("Error: Unable to parse autodetected language"));
988 return;
989 }
990 if (m_onlyDetectLanguage) { return; }
991 }
992
993 addSpaceBetweenParts(m_translation);
994 addSpaceBetweenParts(m_translationTranslit);
995 addSpaceBetweenParts(m_sourceTranslit);
996 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
997 for (const QJsonValueRef translationData : jsonData.at(0).toArray())
998 {
999 const QJsonArray translationArray = translationData.toArray();
1000 m_translation.append(translationArray.at(0).toString());
1001 if (m_translationTranslitEnabled) { m_translationTranslit.append(translationArray.at(2).toString()); }
1002 if (m_sourceTranslitEnabled) { m_sourceTranslit.append(translationArray.at(3).toString()); }
1003 }
1004
1005 if (m_source.size() >= s_googleTranslateLimit) { return; }
1006
1007 // Translation options
1008 if (m_translationOptionsEnabled)
1009 {
1010 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1011 for (const QJsonValueRef typeOfSpeechData : jsonData.at(1).toArray())
1012 {
1013 const QJsonArray typeOfSpeechDataArray = typeOfSpeechData.toArray();
1014 const QString typeOfSpeech = typeOfSpeechDataArray.at(0).toString();
1015 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1016 for (const QJsonValueRef wordData : typeOfSpeechDataArray.at(2).toArray())
1017 {
1018 const QJsonArray wordDataArray = wordData.toArray();
1019 const QString word = wordDataArray.at(0).toString();
1020 const QString gender = wordDataArray.at(4).toString();
1021 const QJsonArray translationsArray = wordDataArray.at(1).toArray();
1022 QStringList translations;
1023 translations.reserve(translationsArray.size());
1024 for (const QJsonValue &wordTranslation : translationsArray)
1025 { translations.append(wordTranslation.toString()); }
1026 m_translationOptions[typeOfSpeech].append({word, gender, translations});
1027 }
1028 }
1029 }
1030
1031 // Examples
1032 if (m_examplesEnabled)
1033 {
1034 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1035 for (const QJsonValueRef exampleData : jsonData.at(12).toArray())
1036 {
1037 const QJsonArray exampleDataArray = exampleData.toArray();
1038 const QString typeOfSpeech = exampleDataArray.at(0).toString();
1039 const QJsonArray example = exampleDataArray.at(1).toArray().first().toArray();
1040
1041 m_examples[typeOfSpeech].append({example.at(2).toString(), example.at(0).toString()});
1042 }
1043 }
1044}
1045
1049void QOnlineTranslator::requestYandexKey()
1050{
1051 const QUrl url(QStringLiteral("https://translate.yandex.com"));
1052 m_currentReply = m_networkManager->get(QNetworkRequest(url));
1053}
1054
1058void QOnlineTranslator::parseYandexKey()
1059{
1060 m_currentReply->deleteLater();
1061
1062 if (m_currentReply->error() != QNetworkReply::NoError)
1063 {
1064 resetData(NetworkError, m_currentReply->errorString());
1065 return;
1066 }
1067
1068 // Check availability of service
1069 const QByteArray webSiteData = m_currentReply->readAll();
1070 if (webSiteData.isEmpty() || webSiteData.contains("<title>Oops!</title>") || webSiteData.contains("<title>302 Found</title>"))
1071 {
1072 resetData(ServiceError, tr("Error: Engine systems have detected suspicious traffic from your computer network. Please try your request again later."));
1073 return;
1074 }
1075
1076 const QByteArray sidBeginString = "SID: '";
1077 const int sidBeginStringPos = webSiteData.indexOf(sidBeginString);
1078 if (sidBeginStringPos == -1)
1079 {
1080 resetData(ParsingError, tr("Error: Unable to find Yandex SID in web version."));
1081 return;
1082 }
1083
1084 const int sidBeginPosition = sidBeginStringPos + sidBeginString.size();
1085 const int sidEndPosition = webSiteData.indexOf('\'', sidBeginPosition);
1086 if (sidEndPosition == -1)
1087 {
1088 resetData(ParsingError, tr("Error: Unable to extract Yandex SID from web version."));
1089 return;
1090 }
1091
1092 // Yandex show reversed parts of session ID, need to decode
1093 const QString sid = webSiteData.mid(sidBeginPosition, sidEndPosition - sidBeginPosition);
1094 QStringList sidParts = sid.split('.');
1095 for (int i = 0; i < sidParts.size(); ++i) { std::reverse(sidParts[i].begin(), sidParts[i].end()); }
1096 //
1097 s_yandexKey = sidParts.join('.');
1098}
1099
1103void QOnlineTranslator::requestYandexTranslate()
1104{
1105 const QString sourceText = sender()->property(s_textProperty).toString();
1106
1107 QString lang;
1108 if (m_sourceLang == Auto) { lang = languageApiCode(Yandex, m_translationLang); }
1109 else { lang = QString("%1-%2").arg(languageApiCode(Yandex, m_sourceLang), languageApiCode(Yandex, m_translationLang)); }
1110
1111 // Generate API url
1112 QUrl url(QStringLiteral("https://translate.yandex.net/api/v1/tr.json/translate"));
1113 url.setQuery(QStringLiteral("id=%1-2-0&srv=tr-text&text=%2&lang=%3").arg(s_yandexKey, QUrl::toPercentEncoding(sourceText), lang));
1114
1115 // Setup request
1116 QNetworkRequest request;
1117 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
1118 request.setUrl(url);
1119
1120 // Make reply
1121 m_currentReply = m_networkManager->post(request, QByteArray());
1122}
1123
1127void QOnlineTranslator::parseYandexTranslate()
1128{
1129 m_currentReply->deleteLater();
1130
1131 // Check for errors
1132 if (m_currentReply->error() != QNetworkReply::NoError)
1133 {
1134 // Network errors
1135 if (m_currentReply->error() < QNetworkReply::ContentAccessDenied)
1136 {
1137 resetData(NetworkError, m_currentReply->errorString());
1138 return;
1139 }
1140
1141 // Parse data to get request error type
1142 s_yandexKey.clear();
1143 const QJsonDocument jsonResponse = QJsonDocument::fromJson(m_currentReply->readAll());
1144 resetData(ServiceError, jsonResponse.object().value(QStringLiteral("message")).toString());
1145 return;
1146 }
1147
1148 // Read Json
1149 const QJsonDocument jsonResponse = QJsonDocument::fromJson(m_currentReply->readAll());
1150 const QJsonObject jsonData = jsonResponse.object();
1151
1152 // Parse language
1153 if (m_sourceLang == Auto)
1154 {
1155 QString sourceCode = jsonData.value(QStringLiteral("lang")).toString();
1156 sourceCode = sourceCode.left(sourceCode.indexOf('-'));
1157 m_sourceLang = language(Yandex, sourceCode);
1158 if (m_sourceLang == NoLanguage)
1159 {
1160 resetData(ParsingError, tr("Error: Unable to parse autodetected language"));
1161 return;
1162 }
1163 if (m_onlyDetectLanguage) { return; }
1164 }
1165
1166 // Parse translation data
1167 m_translation += jsonData.value(QStringLiteral("text")).toArray().at(0).toString();
1168}
1169
1173void QOnlineTranslator::requestYandexSourceTranslit()
1174{
1175 requestYandexTranslit(m_sourceLang);
1176}
1177
1181void QOnlineTranslator::parseYandexSourceTranslit()
1182{
1183 parseYandexTranslit(m_sourceTranslit);
1184}
1185
1189void QOnlineTranslator::requestYandexTranslationTranslit()
1190{
1191 requestYandexTranslit(m_translationLang);
1192}
1193
1197void QOnlineTranslator::parseYandexTranslationTranslit()
1198{
1199 parseYandexTranslit(m_translationTranslit);
1200}
1201
1205void QOnlineTranslator::requestYandexDictionary()
1206{
1207 // Check if language is supported (need to check here because language may be autodetected)
1208 if (!isSupportDictionary(Yandex, m_sourceLang, m_translationLang) && !m_source.contains(' '))
1209 {
1210 auto *state = qobject_cast<QState *>(sender());
1211 state->addTransition(new QFinalState(state->parentState()));
1212 return;
1213 }
1214 // Generate API url
1215 const QString text = sender()->property(s_textProperty).toString();
1216 QUrl url(QStringLiteral("https://dictionary.yandex.net/dicservice.json/lookupMultiple"));
1217 url.setQuery(QStringLiteral("text=%1&ui=%2&dict=%3-%4").arg(QUrl::toPercentEncoding(text), languageApiCode(Yandex, m_uiLang), languageApiCode(Yandex, m_sourceLang), languageApiCode(Yandex, m_translationLang)));
1218 //
1219 m_currentReply = m_networkManager->get(QNetworkRequest(url));
1220}
1221
1225void QOnlineTranslator::parseYandexDictionary()
1226{
1227 m_currentReply->deleteLater();
1228
1229 if (m_currentReply->error() != QNetworkReply::NoError)
1230 {
1231 resetData(NetworkError, m_currentReply->errorString());
1232 return;
1233 }
1234 // Parse reply
1235 const QJsonDocument jsonResponse = QJsonDocument::fromJson(m_currentReply->readAll());
1236 const QJsonValue jsonData = jsonResponse.object().value(languageApiCode(Yandex, m_sourceLang) + '-' + languageApiCode(Yandex, m_translationLang)).toObject().value(QStringLiteral("regular"));
1237
1238 if (m_sourceTranscriptionEnabled)
1239 { m_sourceTranscription = jsonData.toArray().at(0).toObject().value(QStringLiteral("ts")).toString(); }
1240
1241 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1242 for (const QJsonValueRef typeOfSpeechData : jsonData.toArray())
1243 {
1244 QJsonObject typeOfSpeechObject = typeOfSpeechData.toObject();
1245 const QString typeOfSpeech = typeOfSpeechObject.value(QStringLiteral("pos")).toObject().value(QStringLiteral("text")).toString();
1246 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1247 for (const QJsonValueRef wordData : typeOfSpeechObject.value(QStringLiteral("tr")).toArray())
1248 {
1249 // Parse translation options
1250 const QJsonObject wordObject = wordData.toObject();
1251 const QString word = wordObject.value(QStringLiteral("text")).toString();
1252 const QString gender = wordObject.value(QStringLiteral("gen")).toObject().value(QStringLiteral("text")).toString();
1253 const QJsonArray translationsArray = wordObject.value(QStringLiteral("mean")).toArray();
1254 QStringList translations;
1255 translations.reserve(translationsArray.size());
1256 for (const QJsonValue &wordTranslation : translationsArray)
1257 { translations.append(wordTranslation.toObject().value(QStringLiteral("text")).toString()); }
1258
1259 m_translationOptions[typeOfSpeech].append({word, gender, translations});
1260
1261 // Parse examples
1262 if (m_examplesEnabled && wordObject.contains(QLatin1String("ex")))
1263 {
1264 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1265 for (const QJsonValueRef exampleData : wordObject.value(QStringLiteral("ex")).toArray())
1266 {
1267 const QJsonObject exampleObject = exampleData.toObject();
1268 const QString example = exampleObject.value(QStringLiteral("text")).toString();
1269 const QString description = exampleObject.value(QStringLiteral("tr")).toArray().first().toObject().value(QStringLiteral("text")).toString();
1270
1271 m_examples[typeOfSpeech].append({example, description});
1272 }
1273 }
1274 } // end for
1275 } // end for
1276} // end parseYandexDictionary
1277
1281void QOnlineTranslator::requestBingCredentials()
1282{
1283 const QUrl url(QStringLiteral("https://www.bing.com/translator"));
1284 m_currentReply = m_networkManager->get(QNetworkRequest(url));
1285}
1286
1290void QOnlineTranslator::parseBingCredentials()
1291{
1292 m_currentReply->deleteLater();
1293
1294 if (m_currentReply->error() != QNetworkReply::NoError)
1295 {
1296 resetData(NetworkError, m_currentReply->errorString());
1297 return;
1298 }
1299
1300 const QByteArray webSiteData = m_currentReply->readAll();
1301 const QByteArray credentialsBeginString = "var params_RichTranslateHelper = [";
1302 const int credentialsBeginPos = webSiteData.indexOf(credentialsBeginString);
1303 if (credentialsBeginPos == -1)
1304 {
1305 resetData(ParsingError, tr("Error: Unable to find Bing credentials in web version."));
1306 return;
1307 }
1308
1309 const int keyBeginPos = credentialsBeginPos + credentialsBeginString.size();
1310 const int keyEndPos = webSiteData.indexOf(',', keyBeginPos);
1311 if (keyEndPos == -1)
1312 {
1313 resetData(ParsingError, tr("Error: Unable to extract Bing key from web version."));
1314 return;
1315 }
1316 s_bingKey = webSiteData.mid(keyBeginPos, keyEndPos - keyBeginPos);
1317
1318 const int tokenBeginPos = keyEndPos + 2; // Skip two symbols instead of one because the value is enclosed in quotes
1319 const int tokenEndPos = webSiteData.indexOf('"', tokenBeginPos);
1320 if (tokenEndPos == -1)
1321 {
1322 resetData(ParsingError, tr("Error: Unable to extract Bing token from web version."));
1323 return;
1324 }
1325 s_bingToken = webSiteData.mid(tokenBeginPos, tokenEndPos - tokenBeginPos);
1326}
1327
1331void QOnlineTranslator::requestBingTranslate()
1332{
1333 const QString sourceText = sender()->property(s_textProperty).toString();
1334 // Generate POST data
1335 const QByteArray postData = QString("&text=%1&fromLang=%2&to=%3&token=%4&key=%5").arg(QUrl::toPercentEncoding(sourceText, languageApiCode(Bing, m_sourceLang).toUtf8()), languageApiCode(Bing, m_translationLang).toUtf8(), s_bingToken, s_bingKey).toLocal8Bit();
1336 // Setup request
1337 QNetworkRequest request;
1338 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
1339 request.setHeader(QNetworkRequest::UserAgentHeader, QString("%1/%2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()));
1340 request.setUrl(QStringLiteral("https://www.bing.com/ttranslatev3"));
1341
1342 // Make reply
1343 m_currentReply = m_networkManager->post(request, postData);
1344}
1345
1349void QOnlineTranslator::parseBingTranslate()
1350{
1351 m_currentReply->deleteLater();
1352
1353 // Check for errors
1354 if (m_currentReply->error() != QNetworkReply::NoError)
1355 {
1356 resetData(NetworkError, m_currentReply->errorString());
1357 return;
1358 }
1359
1360 // Parse translation data
1361 const QJsonDocument jsonResponse = QJsonDocument::fromJson(m_currentReply->readAll());
1362 const QJsonObject responseObject = jsonResponse.array().first().toObject();
1363
1364 if (m_sourceLang == Auto)
1365 {
1366 const QString langCode = responseObject.value(QStringLiteral("detectedLanguage")).toObject().value(QStringLiteral("language")).toString();
1367 m_sourceLang = language(Bing, langCode);
1368 if (m_sourceLang == NoLanguage)
1369 {
1370 resetData(ParsingError, tr("Error: Unable to parse autodetected language"));
1371 return;
1372 }
1373 if (m_onlyDetectLanguage) { return; }
1374 }
1375
1376 const QJsonObject translationsObject = responseObject.value(QStringLiteral("translations")).toArray().first().toObject();
1377 m_translation += translationsObject.value(QStringLiteral("text")).toString();
1378 m_translationTranslit += translationsObject.value(QStringLiteral("transliteration")).toObject().value(QStringLiteral("text")).toString();
1379}
1380
1384void QOnlineTranslator::requestBingDictionary()
1385{
1386 // Check if language is supported (need to check here because language may be autodetected)
1387 if (!isSupportDictionary(Bing, m_sourceLang, m_translationLang) && !m_source.contains(' '))
1388 {
1389 auto *state = qobject_cast<QState *>(sender());
1390 state->addTransition(new QFinalState(state->parentState()));
1391 return;
1392 }
1393 // Generate POST data
1394 const QByteArray postData = QString("&text=%1&from=%2&to=%3").arg(QUrl::toPercentEncoding(sender()->property(s_textProperty).toString()), languageApiCode(Bing, m_sourceLang).toUtf8(), languageApiCode(Bing, m_translationLang).toUtf8()).toLocal8Bit();
1395 //
1396 QNetworkRequest request;
1397 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
1398 request.setUrl(QStringLiteral("https://www.bing.com/tlookupv3"));
1399
1400 m_currentReply = m_networkManager->post(request, postData);
1401}
1402
1406void QOnlineTranslator::parseBingDictionary()
1407{
1408 m_currentReply->deleteLater();
1409
1410 // Check for errors
1411 if (m_currentReply->error() != QNetworkReply::NoError)
1412 {
1413 resetData(NetworkError, m_currentReply->errorString());
1414 return;
1415 }
1416
1417 const QJsonDocument jsonResponse = QJsonDocument::fromJson(m_currentReply->readAll());
1418 const QJsonObject responseObject = jsonResponse.array().first().toObject();
1419 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1420 for (const QJsonValueRef dictionaryData : responseObject.value(QStringLiteral("translations")).toArray())
1421 {
1422 const QJsonObject dictionaryObject = dictionaryData.toObject();
1423 const QString typeOfSpeech = dictionaryObject.value(QStringLiteral("posTag")).toString().toLower();
1424 const QString word = dictionaryObject.value(QStringLiteral("displayTarget")).toString().toLower();
1425 const QJsonArray translationsArray = dictionaryObject.value(QStringLiteral("backTranslations")).toArray();
1426 QStringList translations;
1427 translations.reserve(translationsArray.size());
1428 for (const QJsonValue &wordTranslation : translationsArray)
1429 { translations.append(wordTranslation.toObject().value(QStringLiteral("displayText")).toString()); }
1430
1431 m_translationOptions[typeOfSpeech].append({word, {}, translations});
1432 }
1433}
1434
1438void QOnlineTranslator::buildGoogleStateMachine()
1439{
1440 // States (Google sends translation, translit and dictionary in one request, that will be splitted into several by the translation limit)
1441 auto *translationState = new QState(m_stateMachine);
1442 auto *finalState = new QFinalState(m_stateMachine);
1443 m_stateMachine->setInitialState(translationState);
1444
1445 translationState->addTransition(translationState, &QState::finished, finalState);
1446
1447 // Setup translation state
1448 buildSplitNetworkRequest(translationState, &QOnlineTranslator::requestGoogleTranslate, &QOnlineTranslator::parseGoogleTranslate, m_source, s_googleTranslateLimit);
1449}
1450
1454void QOnlineTranslator::buildGoogleDetectStateMachine()
1455{
1456 // States
1457 auto *detectState = new QState(m_stateMachine);
1458 auto *finalState = new QFinalState(m_stateMachine);
1459 m_stateMachine->setInitialState(detectState);
1460
1461 detectState->addTransition(detectState, &QState::finished, finalState);
1462
1463 // Setup detect state
1464 const QString text = m_source.left(getSplitIndex(m_source, s_googleTranslateLimit));
1465 buildNetworkRequestState(detectState, &QOnlineTranslator::requestGoogleTranslate, &QOnlineTranslator::parseGoogleTranslate, text);
1466}
1467
1471void QOnlineTranslator::buildYandexStateMachine()
1472{
1473 // States
1474 auto *keyState = new QState(m_stateMachine); // Generate SID from web version first to access API
1475 auto *translationState = new QState(m_stateMachine);
1476 auto *sourceTranslitState = new QState(m_stateMachine);
1477 auto *translationTranslitState = new QState(m_stateMachine);
1478 auto *dictionaryState = new QState(m_stateMachine);
1479 auto *finalState = new QFinalState(m_stateMachine);
1480 m_stateMachine->setInitialState(keyState);
1481
1482 // Transitions
1483 keyState->addTransition(keyState, &QState::finished, translationState);
1484 translationState->addTransition(translationState, &QState::finished, sourceTranslitState);
1485 sourceTranslitState->addTransition(sourceTranslitState, &QState::finished, translationTranslitState);
1486 translationTranslitState->addTransition(translationTranslitState, &QState::finished, dictionaryState);
1487 dictionaryState->addTransition(dictionaryState, &QState::finished, finalState);
1488
1489 // Setup key state
1490 if (s_yandexKey.isEmpty())
1491 { buildNetworkRequestState(keyState, &QOnlineTranslator::requestYandexKey, &QOnlineTranslator::parseYandexKey); }
1492 else
1493 { keyState->setInitialState(new QFinalState(keyState)); }
1494
1495 // Setup translation state
1496 buildSplitNetworkRequest(translationState, &QOnlineTranslator::requestYandexTranslate, &QOnlineTranslator::parseYandexTranslate, m_source, s_yandexTranslateLimit);
1497
1498 // Setup source translit state
1499 if (m_sourceTranslitEnabled)
1500 { buildSplitNetworkRequest(sourceTranslitState, &QOnlineTranslator::requestYandexSourceTranslit, &QOnlineTranslator::parseYandexSourceTranslit, m_source, s_yandexTranslitLimit); }
1501 else
1502 { sourceTranslitState->setInitialState(new QFinalState(sourceTranslitState)); }
1503
1504 // Setup translation translit state
1505 if (m_translationTranslitEnabled)
1506 { buildSplitNetworkRequest(translationTranslitState, &QOnlineTranslator::requestYandexTranslationTranslit, &QOnlineTranslator::parseYandexTranslationTranslit, m_translation, s_yandexTranslitLimit); }
1507 else
1508 { translationTranslitState->setInitialState(new QFinalState(translationTranslitState)); }
1509
1510 // Setup dictionary state
1511 if (m_translationOptionsEnabled && !isContainsSpace(m_source))
1512 { buildNetworkRequestState(dictionaryState, &QOnlineTranslator::requestYandexDictionary, &QOnlineTranslator::parseYandexDictionary, m_source); }
1513 else
1514 { dictionaryState->setInitialState(new QFinalState(dictionaryState)); }
1515}
1516
1520void QOnlineTranslator::buildYandexDetectStateMachine()
1521{
1522 // States
1523 auto *keyState = new QState(m_stateMachine); // Generate SID from web version first to access API
1524 auto *detectState = new QState(m_stateMachine);
1525 auto *finalState = new QFinalState(m_stateMachine);
1526 m_stateMachine->setInitialState(keyState);
1527
1528 // Transitions
1529 keyState->addTransition(keyState, &QState::finished, detectState);
1530 detectState->addTransition(detectState, &QState::finished, finalState);
1531
1532 // Setup key state
1533 if (s_yandexKey.isEmpty())
1534 { buildNetworkRequestState(keyState, &QOnlineTranslator::requestYandexKey, &QOnlineTranslator::parseYandexKey); }
1535 else
1536 { keyState->setInitialState(new QFinalState(keyState)); }
1537
1538 // Setup detect state
1539 const QString text = m_source.left(getSplitIndex(m_source, s_yandexTranslateLimit));
1540 buildNetworkRequestState(detectState, &QOnlineTranslator::requestYandexTranslate, &QOnlineTranslator::parseYandexTranslate, text);
1541}
1542
1546void QOnlineTranslator::buildBingStateMachine()
1547{
1548 // States
1549 auto *credentialsState = new QState(m_stateMachine); // Generate credentials from web version first to access API
1550 auto *translationState = new QState(m_stateMachine);
1551 auto *dictionaryState = new QState(m_stateMachine);
1552 auto *finalState = new QFinalState(m_stateMachine);
1553 m_stateMachine->setInitialState(credentialsState);
1554
1555 // Transitions
1556 credentialsState->addTransition(credentialsState, &QState::finished, translationState);
1557 translationState->addTransition(translationState, &QState::finished, dictionaryState);
1558 dictionaryState->addTransition(dictionaryState, &QState::finished, finalState);
1559
1560 // Setup credentials state
1561 if (s_bingKey.isEmpty() || s_bingToken.isEmpty())
1562 { buildNetworkRequestState(credentialsState, &QOnlineTranslator::requestBingCredentials, &QOnlineTranslator::parseBingCredentials); }
1563 else
1564 { credentialsState->setInitialState(new QFinalState(credentialsState)); }
1565
1566 // Setup translation state
1567 buildSplitNetworkRequest(translationState, &QOnlineTranslator::requestBingTranslate, &QOnlineTranslator::parseBingTranslate, m_source, s_bingTranslateLimit);
1568
1569 // Setup dictionary state
1570 if (m_translationOptionsEnabled && !isContainsSpace(m_source))
1571 { buildNetworkRequestState(dictionaryState, &QOnlineTranslator::requestBingDictionary, &QOnlineTranslator::parseBingDictionary, m_source); }
1572 else
1573 { dictionaryState->setInitialState(new QFinalState(dictionaryState)); }
1574}
1575
1579void QOnlineTranslator::buildBingDetectStateMachine()
1580{
1581 // States
1582 auto *detectState = new QState(m_stateMachine);
1583 auto *finalState = new QFinalState(m_stateMachine);
1584 m_stateMachine->setInitialState(detectState);
1585
1586 detectState->addTransition(detectState, &QState::finished, finalState);
1587
1588 // Setup translation state
1589 const QString text = m_source.left(getSplitIndex(m_source, s_bingTranslateLimit));
1590 buildNetworkRequestState(detectState, &QOnlineTranslator::requestBingTranslate, &QOnlineTranslator::parseBingTranslate, text);
1591}
1592
1596void QOnlineTranslator::buildSplitNetworkRequest(QState *parent, void (QOnlineTranslator::*requestMethod)(), void (QOnlineTranslator::*parseMethod)(), const QString &text, int textLimit)
1597{
1598 QString unsendedText = text;
1599 auto *nextTranslationState = new QState(parent);
1600 parent->setInitialState(nextTranslationState);
1601
1602 while (!unsendedText.isEmpty())
1603 {
1604 auto *currentTranslationState = nextTranslationState;
1605 nextTranslationState = new QState(parent);
1606
1607 // Do not translate the part if it looks like garbage
1608 const int splitIndex = getSplitIndex(unsendedText, textLimit);
1609 if (splitIndex == -1)
1610 {
1611 currentTranslationState->setProperty(s_textProperty, unsendedText.left(textLimit));
1612 currentTranslationState->addTransition(nextTranslationState);
1613 connect(currentTranslationState, &QState::entered, this, &QOnlineTranslator::skipGarbageText);
1614
1615 // Remove the parsed part from the next parsing
1616 unsendedText = unsendedText.mid(textLimit);
1617 }
1618 else
1619 {
1620 buildNetworkRequestState(currentTranslationState, requestMethod, parseMethod, unsendedText.left(splitIndex));
1621 currentTranslationState->addTransition(currentTranslationState, &QState::finished, nextTranslationState);
1622
1623 // Remove the parsed part from the next parsing
1624 unsendedText = unsendedText.mid(splitIndex);
1625 }
1626 }
1627
1628 nextTranslationState->addTransition(new QFinalState(parent));
1629}
1630
1634void QOnlineTranslator::buildNetworkRequestState(QState *parent, void (QOnlineTranslator::*requestMethod)(), void (QOnlineTranslator::*parseMethod)(), const QString &text)
1635{
1636 // Network substates
1637 auto *requestingState = new QState(parent);
1638 auto *parsingState = new QState(parent);
1639
1640 parent->setInitialState(requestingState);
1641 // Substates transitions
1642 requestingState->addTransition(m_networkManager, &QNetworkAccessManager::finished, parsingState);
1643 parsingState->addTransition(new QFinalState(parent));
1644 // Setup requesting state
1645 requestingState->setProperty(s_textProperty, text);
1646 connect(requestingState, &QState::entered, this, requestMethod);
1647 // Setup parsing state
1648 connect(parsingState, &QState::entered, this, parseMethod);
1649}
1650
1654void QOnlineTranslator::requestYandexTranslit(Language language)
1655{
1656 // Check if language is supported (need to check here because language may be autodetected)
1657 if (!isSupportTranslit(Yandex, language))
1658 {
1659 auto *state = qobject_cast<QState *>(sender());
1660 state->addTransition(new QFinalState(state->parentState()));
1661 return;
1662 }
1663
1664 const QString text = sender()->property(s_textProperty).toString();
1665
1666 // Generate API url
1667 QUrl url(QStringLiteral("https://translate.yandex.net/translit/translit"));
1668 url.setQuery(QString("text=%1&lang=%2").arg(QUrl::toPercentEncoding(text), languageApiCode(Yandex, language)));
1669 //
1670 m_currentReply = m_networkManager->get(QNetworkRequest(url));
1671}
1672
1676void QOnlineTranslator::parseYandexTranslit(QString &text)
1677{
1678 m_currentReply->deleteLater();
1679
1680 if (m_currentReply->error() != QNetworkReply::NoError)
1681 {
1682 resetData(NetworkError, m_currentReply->errorString());
1683 return;
1684 }
1685
1686 const QByteArray reply = m_currentReply->readAll();
1687
1688 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
1689 text += reply.mid(1).chopped(1);
1690 #else
1691 text += reply.mid(1);
1692 text.chop(1);
1693 #endif
1694}
1695
1699void QOnlineTranslator::resetData(TranslationError error, const QString &errorString)
1700{
1701 m_error = error;
1702 m_errorString = errorString;
1703 m_translation.clear();
1704 m_translationTranslit.clear();
1705 m_sourceTranslit.clear();
1706 m_sourceTranscription.clear();
1707 m_translationOptions.clear();
1708 m_examples.clear();
1709
1710 m_stateMachine->stop();
1711 // FIXME https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md
1712 for (QAbstractState *state : m_stateMachine->findChildren<QAbstractState *>())
1713 {
1714 if (!m_stateMachine->configuration().contains(state))
1715 { state->deleteLater(); }
1716 }
1717}
1718
1722bool QOnlineTranslator::isSupportTranslit(Engine engine, Language lang)
1723{
1724 switch (engine)
1725 {
1726 case Google:
1727 isSupportTranslation(Google, lang); // Google supports transliteration for all supported languages
1728 break;
1729 case Yandex:
1730 switch (lang)
1731 {
1732 case Amharic:
1733 case Armenian:
1734 case Bengali:
1735 case SimplifiedChinese:
1736 case Georgian:
1737 case Greek:
1738 case Gujarati:
1739 case Hebrew:
1740 case Hindi:
1741 case Japanese:
1742 case Kannada:
1743 case Korean:
1744 case Malayalam:
1745 case Marathi:
1746 case Nepali:
1747 case Punjabi:
1748 case Russian:
1749 case Sinhala:
1750 case Tamil:
1751 case Telugu:
1752 case Thai:
1753 case Yiddish:
1754 return true;
1755 default:
1756 return false;
1757 }
1758 case Bing:
1759 switch (lang)
1760 {
1761 case Arabic:
1762 case Bengali:
1763 case Gujarati:
1764 case Hebrew:
1765 case Hindi:
1766 case Japanese:
1767 case Kannada:
1768 case Malayalam:
1769 case Marathi:
1770 case Punjabi:
1771 case SerbianCyrillic:
1772 case SerbianLatin:
1773 case Tamil:
1774 case Telugu:
1775 case Thai:
1776 case SimplifiedChinese:
1777 case TraditionalChinese:
1778 return true;
1779 default:
1780 return false;
1781 }
1782 }
1783
1784 return false;
1785}
1786
1790bool QOnlineTranslator::isSupportDictionary(Engine engine, Language sourceLang, Language translationLang)
1791{
1792 switch (engine)
1793 {
1794 case Google:
1795 return isSupportTranslation(Google, sourceLang) && isSupportTranslation(Google, translationLang); // Google supports dictionary for all supported languages
1796 case Yandex:
1797 switch (sourceLang)
1798 {
1799 case Belarusian:
1800 switch (translationLang)
1801 {
1802 case Belarusian:
1803 case Russian:
1804 return true;
1805 default:
1806 return false;
1807 }
1808 case Bulgarian:
1809 switch (translationLang)
1810 {
1811 case Russian:
1812 return true;
1813 default:
1814 return false;
1815 }
1816 case Czech:
1817 case Danish:
1818 case Dutch:
1819 case Estonian:
1820 case Greek:
1821 case Latvian:
1822 case Norwegian:
1823 case Portuguese:
1824 case Slovak:
1825 case Swedish:
1826 switch (translationLang)
1827 {
1828 case English:
1829 case Russian:
1830 return true;
1831 default:
1832 return false;
1833 }
1834 case German:
1835 switch (translationLang)
1836 {
1837 case German:
1838 case English:
1839 case Russian:
1840 case Turkish:
1841 return true;
1842 default:
1843 return false;
1844 }
1845 case English:
1846 switch (translationLang)
1847 {
1848 case Czech:
1849 case Danish:
1850 case German:
1851 case Greek:
1852 case English:
1853 case Spanish:
1854 case Estonian:
1855 case Finnish:
1856 case French:
1857 case Italian:
1858 case Lithuanian:
1859 case Latvian:
1860 case Dutch:
1861 case Norwegian:
1862 case Portuguese:
1863 case Russian:
1864 case Slovak:
1865 case Swedish:
1866 case Turkish:
1867 case Ukrainian:
1868 return true;
1869 default:
1870 return false;
1871 }
1872 case Spanish:
1873 switch (translationLang)
1874 {
1875 case English:
1876 case Spanish:
1877 case Russian:
1878 return true;
1879 default:
1880 return false;
1881 }
1882 case Finnish:
1883 switch (translationLang)
1884 {
1885 case English:
1886 case Russian:
1887 case Finnish:
1888 return true;
1889 default:
1890 return false;
1891 }
1892 case French:
1893 switch (translationLang)
1894 {
1895 case French:
1896 case English:
1897 case Russian:
1898 return true;
1899 default:
1900 return false;
1901 }
1902 case Hungarian:
1903 switch (translationLang)
1904 {
1905 case Hungarian:
1906 case Russian:
1907 return true;
1908 default:
1909 return false;
1910 }
1911 case Italian:
1912 switch (translationLang)
1913 {
1914 case English:
1915 case Italian:
1916 case Russian:
1917 return true;
1918 default:
1919 return false;
1920 }
1921 case Lithuanian:
1922 switch (translationLang)
1923 {
1924 case English:
1925 case Lithuanian:
1926 case Russian:
1927 return true;
1928 default:
1929 return false;
1930 }
1931 case Mari:
1932 case HillMari:
1933 case Polish:
1934 case Tatar:
1935 switch (translationLang)
1936 {
1937 case Russian:
1938 return true;
1939 default:
1940 return false;
1941 }
1942 case Russian:
1943 switch (translationLang)
1944 {
1945 case Belarusian:
1946 case Bulgarian:
1947 case Czech:
1948 case Danish:
1949 case German:
1950 case Greek:
1951 case English:
1952 case Spanish:
1953 case Estonian:
1954 case Finnish:
1955 case French:
1956 case Italian:
1957 case Lithuanian:
1958 case Latvian:
1959 case Mari:
1960 case HillMari:
1961 case Dutch:
1962 case Norwegian:
1963 case Portuguese:
1964 case Russian:
1965 case Slovak:
1966 case Swedish:
1967 case Turkish:
1968 case Tatar:
1969 case Ukrainian:
1970 return true;
1971 default:
1972 return false;
1973 }
1974 case Turkish:
1975 switch (translationLang)
1976 {
1977 case German:
1978 case English:
1979 case Russian:
1980 return true;
1981 default:
1982 return false;
1983 }
1984 case Ukrainian:
1985 switch (translationLang)
1986 {
1987 case English:
1988 case Russian:
1989 case Ukrainian:
1990 return true;
1991 default:
1992 return false;
1993 }
1994 default:
1995 return false;
1996 }
1997 case Bing:
1998 // Bing support dictionary only to or from English
1999 Language secondLang;
2000 if (sourceLang == English) { secondLang = translationLang; }
2001 else if (translationLang == English) { secondLang = sourceLang; }
2002 else { return false; }
2003
2004 switch (secondLang)
2005 {
2006 case Afrikaans:
2007 case Arabic:
2008 case Bengali:
2009 case Bosnian:
2010 case Bulgarian:
2011 case Catalan:
2012 case SimplifiedChinese:
2013 case Croatian:
2014 case Czech:
2015 case Danish:
2016 case Dutch:
2017 case Estonian:
2018 case Finnish:
2019 case French:
2020 case German:
2021 case Greek:
2022 case HaitianCreole:
2023 case Hebrew:
2024 case Hindi:
2025 case Hmong:
2026 case Hungarian:
2027 case Icelandic:
2028 case Indonesian:
2029 case Italian:
2030 case Japanese:
2031 case Swahili:
2032 case Klingon:
2033 case Korean:
2034 case Latvian:
2035 case Lithuanian:
2036 case Malay:
2037 case Maltese:
2038 case Norwegian:
2039 case Persian:
2040 case Polish:
2041 case Portuguese:
2042 case Romanian:
2043 case Russian:
2044 case SerbianLatin:
2045 case Slovak:
2046 case Slovenian:
2047 case Spanish:
2048 case Swedish:
2049 case Tamil:
2050 case Thai:
2051 case Turkish:
2052 case Ukrainian:
2053 case Urdu:
2054 case Vietnamese:
2055 case Welsh:
2056 return true;
2057 default:
2058 return false;
2059 }
2060 }
2061
2062 return false;
2063}
2064
2068QString QOnlineTranslator::languageApiCode(Engine engine, Language lang)
2069{
2070 if (!isSupportTranslation(engine, lang)) { return QString(); }
2071
2072 switch (engine)
2073 {
2074 case Google: return s_googleLanguageCodes.value(lang, s_genericLanguageCodes.value(lang));
2075 case Yandex: return s_yandexLanguageCodes.value(lang, s_genericLanguageCodes.value(lang));
2076 case Bing: return s_bingLanguageCodes.value(lang, s_genericLanguageCodes.value(lang));
2077 }
2078
2079 Q_UNREACHABLE();
2080}
2081
2085QOnlineTranslator::Language QOnlineTranslator::language(Engine engine, const QString &langCode)
2086{
2087 // Engine exceptions
2088 switch (engine)
2089 {
2090 case Google: return s_googleLanguageCodes.key(langCode, s_genericLanguageCodes.key(langCode, NoLanguage));
2091 case Yandex: return s_yandexLanguageCodes.key(langCode, s_genericLanguageCodes.key(langCode, NoLanguage));
2092 case Bing: return s_bingLanguageCodes.key(langCode, s_genericLanguageCodes.key(langCode, NoLanguage));
2093 }
2094
2095 Q_UNREACHABLE();
2096}
2097
2102int QOnlineTranslator::getSplitIndex(const QString &untranslatedText, int limit)
2103{
2104 if (untranslatedText.size() < limit) { return limit; }
2105 //
2106 int splitIndex = untranslatedText.lastIndexOf(QLatin1String(". "), limit - 1);
2107 if (splitIndex != -1) { return splitIndex + 1; }
2108 //
2109 splitIndex = untranslatedText.lastIndexOf(' ', limit - 1);
2110 if (splitIndex != -1) { return splitIndex + 1; }
2111 //
2112 splitIndex = untranslatedText.lastIndexOf('\n', limit - 1);
2113 if (splitIndex != -1) { return splitIndex + 1; }
2114 // Non-breaking space
2115 splitIndex = untranslatedText.lastIndexOf(0x00a0, limit - 1);
2116 if (splitIndex != -1) { return splitIndex + 1; }
2117 // If the text has not passed any check and is most likely garbage
2118 return limit;
2119}
2120
2124bool QOnlineTranslator::isContainsSpace(const QString &text)
2125{
2126 return std::any_of(text.cbegin(), text.cend(), [](QChar symbol)
2127 { return symbol.isSpace(); });
2128}
2129
2133void QOnlineTranslator::addSpaceBetweenParts(QString &text)
2134{
2135 if (text.isEmpty()) { return; }
2136 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
2137 if (!text.back().isSpace())
2138 #else
2139 if (!text.at(text.size() - 1).isSpace())
2140 #endif
2141 { text.append(' '); }
2142}
2143
QOnlineTranslator.
bool isSourceTranslitEnabled() const
is Source Translit Enabled
static QString languageCode(Language lang)
language Code
bool isTranslationTranslitEnabled() const
is Translation Translit Enabled
static bool isSupportTranslation(Engine engine, Language lang)
is Support Translation
void setTranslationTranslitEnabled(bool enable)
set Translation Translit Enabled
QString errorString() const
error String
void detectLanguage(const QString &text, Engine engine=Google)
detect Language
bool isTranslationOptionsEnabled() const
is Translation Options Enabled
void setSourceTranscriptionEnabled(bool enable)
set Source Transcription Enabled
Language sourceLanguage() const
source Language
QJsonDocument toJson() const
to Json
void setExamplesEnabled(bool enable)
set Examples Enabled
static QString languageName(Language lang)
language Name
QString sourceTranslit() const
source Translit
Language translationLanguage() const
translation Language
bool isExamplesEnabled() const
is Examples Enabled
static Language language(const QLocale &locale)
language
bool isSourceTranscriptionEnabled() const
is Source Transcription Enabled
QString translation() const
translation
@ ParsingError
ParsingError
@ NetworkError
NetworkError
@ ParametersError
ParametersError
@ ServiceError
ServiceError
bool isRunning() const
is Running
QString translationLanguageName() const
translation Language Name
QMap< QString, QVector< QExample > > examples() const
examples
QOnlineTranslator(QObject *parent=nullptr)
QOnlineTranslator.
QString sourceLanguageName() const
source Language Name
QString source() const
source
void setTranslationOptionsEnabled(bool enable)
set Translation Options Enabled
void translate(const QString &text, Engine engine=Google, Language translationLang=Auto, Language sourceLang=Auto, Language uiLang=Auto)
translate
void setSourceTranslitEnabled(bool enable)
set Source Translit Enabled
QMap< QString, QVector< QOption > > translationOptions() const
translation Options
QString sourceTranscription() const
source Transcription
QString translationTranslit() const
translation Translit
@ ScotsGaelic
ScotsGaelic
@ Kinyarwanda
Kinyarwanda
@ TraditionalChinese
TraditionalChinese
@ Azerbaijani
Azerbaijani
@ YucatecMaya
YucatecMaya
@ HaitianCreole
HaitianCreole
@ KlingonPlqaD
KlingonPlqaD
@ SerbianLatin
SerbianLatin
@ SerbianCyrillic
SerbianCyrillic
@ LevantineArabic
LevantineArabic
@ SimplifiedChinese
SimplifiedChinese
@ NoLanguage
NoLanguage
@ QueretaroOtomi
QueretaroOtomi
@ Luxembourgish
Luxembourgish
@ Belarusian
Belarusian
TranslationError error() const
error
QExample.
Definition: qexample.h:31
QOption.
Definition: qoption.h:32