Warning, file /plasma/plasma-workspace/kcms/region_language/exampleutility.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 exampleutility.cpp 0003 SPDX-FileCopyrightText: 2021 Han Young <hanyoung@protonmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "exampleutility.h" 0009 0010 QString Utility::monetaryExample(const QLocale &locale) 0011 { 0012 return locale.toCurrencyString(24.00); 0013 } 0014 0015 QString Utility::timeExample(const QLocale &locale) 0016 { 0017 return locale.toString(QDateTime::currentDateTime()) + QLatin1Char('\n') + locale.toString(QDateTime::currentDateTime(), QLocale::ShortFormat); 0018 } 0019 0020 QString Utility::shortTimeExample(const QLocale &locale) 0021 { 0022 return locale.toString(QDateTime::currentDateTime(), QLocale::LongFormat); 0023 } 0024 0025 QString Utility::measurementExample(const QLocale &locale) 0026 { 0027 QString measurementExample; 0028 if (locale.measurementSystem() == QLocale::ImperialUKSystem) { 0029 measurementExample = i18nc("Measurement combobox", "Imperial UK"); 0030 } else if (locale.measurementSystem() == QLocale::ImperialUSSystem || locale.measurementSystem() == QLocale::ImperialSystem) { 0031 measurementExample = i18nc("Measurement combobox", "Imperial US"); 0032 } else { 0033 measurementExample = i18nc("Measurement combobox", "Metric"); 0034 } 0035 return measurementExample; 0036 } 0037 0038 QString Utility::numericExample(const QLocale &locale) 0039 { 0040 return locale.toString(1000.01); 0041 } 0042 0043 QString Utility::paperSizeExample(const QLocale &locale) 0044 { 0045 QString paperSizeExample; 0046 if (locale.measurementSystem() == QLocale::ImperialUSSystem || locale.measurementSystem() == QLocale::ImperialSystem) { 0047 paperSizeExample = i18nc("PaperSize combobox", "Letter"); 0048 } else { 0049 paperSizeExample = i18nc("PaperSize combobox", "A4"); 0050 } 0051 return paperSizeExample; 0052 } 0053 0054 // If LC_ADDRESS does not exist, we do not use these at all 0055 // The format characters are found from here https://lh.2xlibre.net/ 0056 #ifdef LC_ADDRESS 0057 QString Utility::addressExample(const QLocale &locale) 0058 { 0059 // Create an example string using POSTAL_FMT 0060 const QStringList lang = getLangCodeFromLocale(locale); 0061 const QHash<QChar, QString> map{ 0062 // QChar is the field descriptor and QString is the value 0063 {'n', nameStyleExample(locale)}, // Person's Name, use LC_NAME for this 0064 {'a', ki18nc("Care of person or organization", "c/o").toString(lang)}, 0065 {'f', ki18nc("Firm name", "Acme Corporation").toString(lang)}, 0066 {'d', ki18nc("Department name", "Development Department").toString(lang)}, 0067 {'b', ki18nc("Building name", "Dev-Building").toString(lang)}, 0068 {'s', ki18nc("Street or block name", "Main Street").toString(lang)}, 0069 {'h', ki18nc("House number", "House 1").toString(lang)}, 0070 {'N', "\n"}, // End of line 0071 {'t', ki18nc("Whitespace field for locale address style example", " ").toString(lang)}, // Space 0072 {'r', ki18nc("Room number", "Room 2").toString(lang)}, 0073 {'e', ki18nc("Floor number", "Floor 3").toString(lang)}, 0074 {'C', getLocaleInfo(_NL_ADDRESS_COUNTRY_POST, LC_ADDRESS, locale)}, // Country designation from the CountryKeyword 0075 {'l', ki18nc("Local township within town or city", "Downtown").toString(lang)}, 0076 {'z', ki18nc("Zip number, postal code", "123456").toString(lang)}, 0077 {'T', ki18nc("Town or city", "City").toString(lang)}, 0078 {'S', ki18nc("State, province or prefecture", "State").toString(lang)}, 0079 {'c', getLocaleInfo(_NL_ADDRESS_COUNTRY_NAME, LC_ADDRESS, locale)}, // Country from data record 0080 }; 0081 0082 return resolveFieldDescriptors(map, _NL_ADDRESS_POSTAL_FMT, LC_ADDRESS, locale); 0083 } 0084 #endif 0085 0086 #ifdef LC_ADDRESS 0087 QString Utility::nameStyleExample(const QLocale &locale) 0088 { 0089 const QStringList lang = getLangCodeFromLocale(locale); 0090 const QHash<QChar, QString> map{ 0091 {'f', ki18nc("Family names", "FamilyName").toString(lang)}, 0092 {'F', ki18nc("Family names in uppercase", "FAMILYNAME").toString(lang)}, 0093 {'g', ki18nc("First given name", "FirstName").toString(lang)}, 0094 {'G', ki18nc("First given initial", "F").toString(lang)}, 0095 {'l', ki18nc("First given name with latin letters", "FirstName").toString(lang)}, 0096 {'o', ki18nc("Other shorter name", "OtherName").toString(lang)}, 0097 {'m', ki18nc("Additional given names", "AdditionalName").toString(lang)}, 0098 {'M', ki18nc("Initials for additional given names", "A").toString(lang)}, 0099 {'p', ki18nc("Profession", "Profession").toString(lang)}, 0100 {'s', ki18nc("Salutation", "Doctor").toString(lang)}, 0101 {'S', ki18nc("Abbreviated salutation", "Dr.").toString(lang)}, 0102 {'d', ki18nc("Salutation using the FDCC-sets conventions", "Dr.").toString(lang)}, 0103 {'t', ki18nc("Space or dot for locale name style example", " ").toString(lang)}, // Space or dot. Space produces better examples. 0104 }; 0105 return resolveFieldDescriptors(map, _NL_NAME_NAME_FMT, LC_NAME, locale); 0106 } 0107 #endif 0108 0109 #ifdef LC_ADDRESS 0110 QString Utility::phoneNumbersExample(const QLocale &locale) 0111 { 0112 const QHash<QChar, QString> map = { 0113 {'a', "123"}, // Area code without nationwide prefix 0114 {'A', "0123"}, // Area code with nationwide prefix 0115 {'l', "1234567"}, // Local number within area code 0116 {'e', "321"}, // Extension to local number 0117 {'c', getLocaleInfo(_NL_TELEPHONE_INT_PREFIX, LC_TELEPHONE, locale)}, // Country code 0118 {'C', "01"}, // Alternate carrier service code used for dialling abroad 0119 {'t', ki18nc("Whitespace for telephone style example", " ").toString(getLangCodeFromLocale(locale))}, // Insert space 0120 }; 0121 0122 return resolveFieldDescriptors(map, _NL_TELEPHONE_TEL_INT_FMT, LC_TELEPHONE, locale); 0123 } 0124 #endif 0125 0126 #ifdef LC_ADDRESS 0127 QString Utility::resolveFieldDescriptors(QHash<QChar, QString> map, int langInfoFormat, int lcFormat, const QLocale &locale) 0128 { 0129 QString formatString = getLocaleInfo(langInfoFormat, lcFormat, locale); 0130 QString example = KMacroExpander::expandMacros(formatString, map); 0131 0132 if (example.isEmpty() || example == QLatin1String("???")) { 0133 return i18nc("This is returned when an example test could not be made from locale information", "Could not find an example for this locale"); 0134 } 0135 return example; 0136 } 0137 0138 QString Utility::getLocaleInfo(int langInfoFormat, int lcFormat, const QLocale &locale) 0139 { 0140 QString localeInfo = parseLocaleFile(locale.name(), langInfoFormat); 0141 0142 if (localeInfo.isEmpty()) { 0143 const QString localeString = locale.name() + QLatin1String(".UTF-8"); 0144 const QByteArray localeByteArray = localeString.toUtf8(); 0145 0146 if (setlocale(lcFormat, localeByteArray)) { 0147 localeInfo = QString::fromUtf8(nl_langinfo(langInfoFormat)); 0148 } 0149 } 0150 0151 return localeInfo; 0152 } 0153 0154 QString Utility::parseLocaleFile(QString localeName, int langInfoFormat) 0155 { 0156 static std::unordered_map<QString, QString> resultCache; 0157 0158 const QString formatToFetch = getFormatToFetch(langInfoFormat); 0159 const auto cacheKey = formatToFetch + QStringLiteral("###") + localeName; 0160 if (resultCache.count(cacheKey)) { 0161 return resultCache[cacheKey]; 0162 } 0163 0164 // insert an empty value here, so we ensure one file only parsed once 0165 resultCache.insert({cacheKey, QString()}); 0166 0167 QFileInfo localeFileInfo; 0168 0169 // Get the locale file info from the first folder where it's found 0170 for (const auto &localeDirectory : {QStringLiteral("/usr/share/i18n/locales/")}) { 0171 localeFileInfo = findLocaleInFolder(localeName, localeDirectory); 0172 if (localeFileInfo.exists()) { 0173 break; 0174 } 0175 } 0176 0177 // Parse through file and return the inquired field descriptor 0178 if (localeFileInfo.exists()) { 0179 QFile localeFile(localeFileInfo.filePath()); 0180 // Return if we cant open file 0181 if (!localeFile.open(QIODevice::ReadOnly)) { 0182 return {}; 0183 } 0184 0185 QTextStream textStream(&localeFile); 0186 0187 if (formatToFetch.isEmpty()) { 0188 return {}; 0189 } 0190 // Read the file with regex and return the first match 0191 0192 const QRegularExpression rx({formatToFetch + "\\s+\"(.*)\""}); 0193 0194 while (!textStream.atEnd()) { 0195 QString line = textStream.readLine(); 0196 QRegularExpressionMatch match = rx.match(line); 0197 if (match.hasMatch()) { 0198 // Return the first (and only) match 0199 const QString result = replaceASCIIUnicodeSymbol(match.captured(1)); 0200 resultCache[cacheKey] = result; 0201 return result; 0202 } 0203 } 0204 } 0205 0206 return {}; 0207 } 0208 0209 // Glibc store unicode char as ASCII symbol, 'T<U00FC>rkiye' for 'Türkiye' 0210 QString Utility::replaceASCIIUnicodeSymbol(const QString &string) 0211 { 0212 int i = 0, literalStringStart = 0, unicodeStart = 0; 0213 QString result; 0214 result.reserve(string.size()); 0215 bool replacing = false; 0216 0217 /* 0218 * T<U00FC>rkiye 0219 * || ||-State 1 0220 * || |- State 3, we check if the code block is valid, then added the converted char 0221 * || to the result, reset literal string start to the position after itself 0222 * ||- State 2, we add the literal string before it to the result, set 'unicodeStart' to the position after 'U' 0223 * |- State 1, literal string state 0224 * */ 0225 while (i < string.size()) { 0226 if (replacing && i > unicodeStart && string[i] == QLatin1Char('>')) { 0227 bool ok = false; 0228 QStringView section(string); 0229 // convert base 16 string to utf-16 value 0230 auto unicodePoint = section.mid(unicodeStart, i - unicodeStart).toInt(&ok, 16); 0231 if (ok && QChar::isPrint(unicodePoint)) { 0232 result.append(QChar(unicodePoint)); 0233 literalStringStart = i + 1; 0234 } 0235 replacing = false; 0236 } else if (string[i] == QLatin1Char('<') && i + 1 < string.size() && string[i + 1] == QLatin1Char('U')) { 0237 // append literal string before unicode block 0238 result.append(QStringView(string).mid(literalStringStart, i - literalStringStart)); 0239 replacing = true; 0240 // <U00FF>, we ignore 'U' 0241 unicodeStart = i + 2; 0242 } 0243 i++; 0244 } 0245 result.append(QStringView(string).mid(literalStringStart, i - literalStringStart)); 0246 return result; 0247 } 0248 0249 QFileInfo Utility::findLocaleInFolder(QString localeName, QString localeDirectory) 0250 { 0251 QDirIterator dirIterator(localeDirectory); 0252 // Iterate through files in the locale directory 0253 while (dirIterator.hasNext()) { 0254 QString fileName = dirIterator.next(); 0255 QFileInfo fileInfo(fileName); 0256 0257 // Ignore directories 0258 if (fileInfo.isDir()) { 0259 continue; 0260 } 0261 0262 // If file is found, break the loop 0263 if (fileInfo.fileName().startsWith(localeName)) { 0264 return fileInfo; 0265 break; 0266 } 0267 } 0268 return {}; 0269 } 0270 0271 QString Utility::getFormatToFetch(int langInfoFormat) 0272 { 0273 switch (langInfoFormat) { 0274 case _NL_ADDRESS_POSTAL_FMT: 0275 return QStringLiteral("postal_fmt"); 0276 case _NL_ADDRESS_COUNTRY_POST: 0277 return QStringLiteral("country_post"); 0278 case _NL_ADDRESS_COUNTRY_NAME: 0279 return QStringLiteral("country_name"); 0280 case _NL_NAME_NAME_FMT: 0281 return QStringLiteral("name_fmt"); 0282 case _NL_TELEPHONE_TEL_INT_FMT: 0283 return QStringLiteral("tel_int_fmt"); 0284 case _NL_TELEPHONE_INT_PREFIX: 0285 return QStringLiteral("int_prefix"); 0286 } 0287 return {}; 0288 } 0289 0290 QStringList Utility::getLangCodeFromLocale(QLocale locale) 0291 { 0292 QStringList languages; 0293 for (QString language : locale.uiLanguages()) { 0294 language.replace(QLatin1Char('-'), QLatin1Char('_')); 0295 languages += language; 0296 } 0297 // `uiLanguages` don't offer the minimal version for some locale, such as fr_DZ. 0298 if (int pos = languages.last().indexOf(QLatin1Char('_')); pos >= 0) { 0299 languages += languages.last().left(pos); 0300 } 0301 return languages; 0302 } 0303 #endif