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(const 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(const 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.contains(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;
0213     int literalStringStart = 0;
0214     int unicodeStart = 0;
0215     QString result;
0216     result.reserve(string.size());
0217     bool replacing = false;
0218 
0219     /*
0220      * T<U00FC>rkiye
0221      * ||     ||-State 1
0222      * ||     |- State 3, we check if the code block is valid, then added the converted char
0223      * ||        to the result, reset literal string start to the position after itself
0224      * ||- State 2, we add the literal string before it to the result, set 'unicodeStart' to the position after 'U'
0225      * |- State 1, literal string state
0226      * */
0227     while (i < string.size()) {
0228         if (replacing && i > unicodeStart && string[i] == QLatin1Char('>')) {
0229             bool ok = false;
0230             QStringView section(string);
0231             // convert base 16 string to utf-16 value
0232             auto unicodePoint = section.mid(unicodeStart, i - unicodeStart).toInt(&ok, 16);
0233             if (ok && QChar::isPrint(unicodePoint)) {
0234                 result.append(QChar(unicodePoint));
0235                 literalStringStart = i + 1;
0236             }
0237             replacing = false;
0238         } else if (string[i] == QLatin1Char('<') && i + 1 < string.size() && string[i + 1] == QLatin1Char('U')) {
0239             // append literal string before unicode block
0240             result.append(QStringView(string).mid(literalStringStart, i - literalStringStart));
0241             replacing = true;
0242             // <U00FF>, we ignore 'U'
0243             unicodeStart = i + 2;
0244         }
0245         i++;
0246     }
0247     result.append(QStringView(string).mid(literalStringStart, i - literalStringStart));
0248     return result;
0249 }
0250 
0251 QFileInfo Utility::findLocaleInFolder(const QString &localeName, const QString &localeDirectory)
0252 {
0253     QDirIterator dirIterator(localeDirectory);
0254     // Iterate through files in the locale directory
0255     while (dirIterator.hasNext()) {
0256         QString fileName = dirIterator.next();
0257         QFileInfo fileInfo(fileName);
0258 
0259         // Ignore directories
0260         if (fileInfo.isDir()) {
0261             continue;
0262         }
0263 
0264         // If file is found, break the loop
0265         if (fileInfo.fileName().startsWith(localeName)) {
0266             return fileInfo;
0267             break;
0268         }
0269     }
0270     return {};
0271 }
0272 
0273 QString Utility::getFormatToFetch(int langInfoFormat)
0274 {
0275     switch (langInfoFormat) {
0276     case _NL_ADDRESS_POSTAL_FMT:
0277         return QStringLiteral("postal_fmt");
0278     case _NL_ADDRESS_COUNTRY_POST:
0279         return QStringLiteral("country_post");
0280     case _NL_ADDRESS_COUNTRY_NAME:
0281         return QStringLiteral("country_name");
0282     case _NL_NAME_NAME_FMT:
0283         return QStringLiteral("name_fmt");
0284     case _NL_TELEPHONE_TEL_INT_FMT:
0285         return QStringLiteral("tel_int_fmt");
0286     case _NL_TELEPHONE_INT_PREFIX:
0287         return QStringLiteral("int_prefix");
0288     }
0289     return {};
0290 }
0291 
0292 QStringList Utility::getLangCodeFromLocale(const QLocale &locale)
0293 {
0294     QStringList languages;
0295     for (QString language : locale.uiLanguages()) {
0296         language.replace(QLatin1Char('-'), QLatin1Char('_'));
0297         languages += language;
0298     }
0299     // `uiLanguages` don't offer the minimal version for some locale, such as fr_DZ.
0300     if (auto pos = languages.last().indexOf(QLatin1Char('_')); pos >= 0) {
0301         languages += languages.last().left(pos);
0302     }
0303     return languages;
0304 }
0305 #endif