1 /* 2 * iso-codes.cpp 3 * 4 * Copyright (C) 2017 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 5 * Copyright (C) 2017 Pino Toscano <pino@kde.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include "log.h" 19 20 #include <KLocalizedString> 21 #include <QFile> 22 #include <QLocale> 23 #include <QRegularExpression> 24 #include <QStandardPaths> 25 #include <QXmlStreamReader> 26 27 #include "iso-codes.h" 28 29 namespace IsoCodes 30 { load(QHash<QString,QString> * code_3letters,QHash<QString,QString> * code_2letters,QString file,QString main_key,QString entry_key,QString code_3_key,QString code_2_key,QString name_key)31 static void load(QHash<QString, QString> *code_3letters, 32 QHash<QString, QString> *code_2letters, 33 QString file, 34 QString main_key, 35 QString entry_key, 36 QString code_3_key, 37 QString code_2_key, 38 QString name_key) 39 { 40 if (!code_3letters->isEmpty()) 41 return; 42 43 const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, file); 44 if (fileName.isEmpty()) { 45 qCInfo(logConfig, 46 "Could not locate %s (is iso-codes installed?)", 47 qPrintable(file)); 48 return; 49 } 50 51 QFile f(fileName); 52 if (!f.open(QIODevice::ReadOnly)) { 53 qCWarning(logConfig, 54 "Could not open %s (%s)", 55 qPrintable(fileName), 56 qPrintable(f.errorString())); 57 return; 58 } 59 60 QXmlStreamReader r(&f); 61 bool inDoc = false; 62 while (!r.atEnd()) { 63 const QXmlStreamReader::TokenType t = r.readNext(); 64 QStringRef name; 65 switch (t) { 66 case QXmlStreamReader::StartElement: 67 name = r.name(); 68 if (inDoc && name == entry_key) { 69 const QXmlStreamAttributes attrs = r.attributes(); 70 const QString code3 = attrs.value(code_3_key).toString().toUpper(); 71 const QString lang = attrs.value(name_key).toString(); 72 code_3letters->insert(code3, lang); 73 if (code_2letters) { 74 const QString code2 = attrs.value(code_2_key).toString().toUpper(); 75 if (code2 != "") 76 code_2letters->insert(code2, code3); 77 } 78 } else if (name == main_key) { 79 inDoc = true; 80 } 81 break; 82 case QXmlStreamReader::EndElement: 83 name = r.name(); 84 if (inDoc && name == main_key) { 85 inDoc = false; 86 } 87 break; 88 case QXmlStreamReader::NoToken: 89 case QXmlStreamReader::Invalid: 90 case QXmlStreamReader::StartDocument: 91 case QXmlStreamReader::EndDocument: 92 case QXmlStreamReader::Characters: 93 case QXmlStreamReader::Comment: 94 case QXmlStreamReader::DTD: 95 case QXmlStreamReader::EntityReference: 96 case QXmlStreamReader::ProcessingInstruction: 97 break; 98 } 99 } 100 if (code_3letters->isEmpty()) 101 qCWarning(logConfig, 102 "Error parsing %s: no entries found.", 103 qPrintable(fileName)); 104 } 105 106 /* 107 * ISO 639-2 language codes 108 * Loaded and translated at runtime from iso-codes. 109 */ 110 static QHash<QString, QString> iso639_2_codes; 111 112 /* 113 * ISO 639-1 to ISO 639-2 language code conversion 114 * Loaded and translated at runtime from iso-codes. 115 */ 116 static QHash<QString, QString> iso639_1_codes; 117 getLanguage(const QString & iso_code,QString * language)118 bool getLanguage(const QString &iso_code, QString *language) 119 { 120 static bool first = true; 121 122 QString code = iso_code.toUpper(); 123 124 if (code == "QAA") { 125 *language = i18n("Original Language"); 126 return true; 127 } 128 129 if (first) { 130 load(&iso639_2_codes, 131 &iso639_1_codes, 132 QString("xml/iso-codes/iso_639-2.xml"), 133 QLatin1String("iso_639_entries"), 134 QLatin1String("iso_639_entry"), 135 QLatin1String("iso_639_2B_code"), 136 QLatin1String("iso_639_1_code"), 137 QLatin1String("name")); 138 first = false; 139 } 140 141 QHash<QString, QString>::ConstIterator it = iso639_2_codes.constFind(code); 142 if (it == iso639_2_codes.constEnd()) { 143 /* 144 * The ETSI EN 300 468 Annex F spec defines the 145 * original audio soundtrack code to be "QAA". Yet, 146 * TV bundle providers could use something else. 147 * 148 * At least here, my provider actually uses "ORG" 149 * instead. Currently, this is an unused ISO 639 150 * code, so we can safely accept it as well, 151 * at least as a fallback code while ISO doesn't 152 * define it. 153 */ 154 if (code == "ORG") { 155 *language = i18n("Original Language"); 156 return true; 157 } 158 return false; 159 } 160 161 if (language) { 162 *language = i18nd("iso_639-2", it.value().toUtf8().constData()); 163 } 164 return true; 165 } 166 code2Convert(const QString & code2)167 const QString code2Convert(const QString &code2) 168 { 169 static bool first = true; 170 171 QString code = code2.toUpper(); 172 173 /* Ignore any embedded Country data */ 174 code.remove(QRegularExpression("_.*")); 175 176 if (first) { 177 load(&iso639_2_codes, 178 &iso639_1_codes, 179 QString("xml/iso-codes/iso_639-2.xml"), 180 QLatin1String("iso_639_entries"), 181 QLatin1String("iso_639_entry"), 182 QLatin1String("iso_639_2B_code"), 183 QLatin1String("iso_639_1_code"), 184 QLatin1String("name")); 185 first = false; 186 } 187 188 QHash<QString, QString>::ConstIterator it = iso639_1_codes.constFind(code); 189 if (it == iso639_1_codes.constEnd()) { 190 return "QAA"; 191 } 192 return it.value().toUtf8().constData(); 193 } 194 195 /* 196 * ISO 3166-1 Alpha 3 Country codes 197 * Loaded and translated at runtime from iso-codes. 198 */ 199 static QHash<QString, QString> iso3166_1_codes, iso3166_2_codes; 200 getCountry(const QString & _code,QString * country)201 bool getCountry(const QString &_code, QString *country) 202 { 203 static bool first = true; 204 QString code; 205 206 if (first) { 207 load(&iso3166_1_codes, 208 &iso3166_2_codes, 209 QString("xml/iso-codes/iso_3166-1.xml"), 210 QLatin1String("iso_3166_entries"), 211 QLatin1String("iso_3166_entry"), 212 QLatin1String("alpha_3_code"), 213 QString("alpha_2_code"), 214 QLatin1String("name")); 215 first = false; 216 } 217 218 if (_code.size() == 2) { 219 QHash<QString, QString>::ConstIterator it = iso3166_1_codes.constFind(code); 220 if (it == iso3166_2_codes.constEnd()) 221 return false; 222 code = it.value(); 223 } else { 224 code = _code; 225 } 226 227 QHash<QString, QString>::ConstIterator it = iso3166_1_codes.constFind(code); 228 if (it == iso3166_1_codes.constEnd()) { 229 return false; 230 } 231 232 if (country) { 233 *country = i18nd("iso_3166-1", it.value().toUtf8().constData()); 234 } 235 return true; 236 } 237 } 238