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