File indexing completed on 2024-10-06 03:31:32
0001 // SPDX-FileCopyrightText: 1999-2003 Hans Petter Bieker <bieker@kde.org> 0002 // SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org> 0003 // SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu> 0004 // SPDX-License-Identifier: LGPL-2.0-or-later 0005 0006 #include "languagelistmodel.h" 0007 #include <KConfig> 0008 #include <KConfigGroup> 0009 #include <KLocalizedString> 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QLocale> 0013 #include <QStandardPaths> 0014 #include <qstringliteral.h> 0015 0016 bool operator<(const LanguageListModel::Language &a, const LanguageListModel::Language &b) 0017 { 0018 return a.name < b.name; 0019 } 0020 0021 static QString nameFromEntryFile(const QString &entryFile) 0022 { 0023 const KConfig entry(entryFile, KConfig::SimpleConfig); 0024 const KConfigGroup group(&entry, QStringLiteral("KCM Locale")); 0025 return group.readEntry("Name", QString()); 0026 } 0027 0028 LanguageListModel::LanguageListModel(QObject *parent) 0029 : QAbstractListModel(parent) 0030 { 0031 const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("locale"), QStandardPaths::LocateDirectory); 0032 for (const QString &localeDir : localeDirs) { 0033 const QStringList entries = QDir(localeDir).entryList(QDir::Dirs, QDir::Name); 0034 for (const QString &languageCode : entries) { 0035 const QString entryFile = localeDir + QLatin1Char('/') + languageCode + QStringLiteral("/kf5_entry.desktop"); 0036 if (QFile::exists(entryFile)) { 0037 QString text; 0038 const QString entryFile = 0039 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + languageCode + QLatin1String("/kf5_entry.desktop")); 0040 if (QFile::exists(entryFile)) { 0041 text = nameFromEntryFile(entryFile); 0042 } 0043 0044 if (text.isEmpty()) { 0045 text = languageCode; 0046 QLocale locale(languageCode); 0047 if (locale != QLocale::c()) { 0048 text = locale.nativeLanguageName(); 0049 // For some languages the native name might be empty. 0050 // In this case use the non native language name as fallback. 0051 // See: QTBUG-51323 0052 text = text.isEmpty() ? QLocale::languageToString(locale.language()) : text; 0053 } 0054 } 0055 0056 m_availableLanguages.append(LanguageListModel::Language{languageCode, text}); 0057 } 0058 } 0059 } 0060 } 0061 0062 int LanguageListModel::rowCount(const QModelIndex &parent) const 0063 { 0064 return parent.isValid() ? 0 : m_availableLanguages.size(); 0065 } 0066 0067 QVariant LanguageListModel::data(const QModelIndex &index, int role) const 0068 { 0069 Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); 0070 0071 const auto &language = m_availableLanguages[index.row()]; 0072 0073 switch (role) { 0074 case Qt::DisplayRole: 0075 case NativeNameRole: 0076 return language.name; 0077 case LanguageCodeRole: 0078 return language.locale; 0079 case FlagRole: 0080 return flagFromName(language.locale); 0081 } 0082 return {}; 0083 } 0084 0085 QString LanguageListModel::languageName(const QString &language) const 0086 { 0087 const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("locale"), QStandardPaths::LocateDirectory); 0088 for (const QString &localeDir : localeDirs) { 0089 const QString entryFile = localeDir + QLatin1Char('/') + language + QStringLiteral("/kf5_entry.desktop"); 0090 if (QFile::exists(entryFile)) { 0091 QString text; 0092 const QString entryFile = 0093 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + language + QLatin1String("/kf5_entry.desktop")); 0094 if (QFile::exists(entryFile)) { 0095 text = nameFromEntryFile(entryFile); 0096 } 0097 0098 if (!text.isEmpty()) { 0099 return text; 0100 } 0101 } 0102 } 0103 0104 QLocale locale(language); 0105 return locale.nativeLanguageName(); 0106 } 0107 0108 QString LanguageListModel::flagFromName(const QString &language) const 0109 { 0110 QString flagCode; 0111 const QStringList split = QLocale(language).name().split(QLatin1Char('_')); 0112 if (split.size() > 1) { 0113 flagCode = split[1].toLower(); 0114 } 0115 0116 static constexpr auto surrogatePairCodePoint = 0xD83C; 0117 static constexpr auto flagCodePointStart = 0xDDE6; // https://en.wikipedia.org/wiki/Regional_indicator_symbol 0118 static constexpr auto offsetCodePointA = QLatin1Char('A').unicode(); // offset from 0, the flag code points have the same offsets 0119 static constexpr auto basePoint = flagCodePointStart - offsetCodePointA; 0120 0121 QString emoji; 0122 for (const auto &c : flagCode) { 0123 emoji.append(QChar(surrogatePairCodePoint)); 0124 emoji.append(QChar(basePoint + c.toUpper().unicode())); 0125 } 0126 0127 if (emoji.isEmpty()) { 0128 return QString(); 0129 } 0130 0131 return emoji; 0132 } 0133 0134 QHash<int, QByteArray> LanguageListModel::roleNames() const 0135 { 0136 return { 0137 {NativeNameRole, QByteArrayLiteral("nativeName")}, 0138 {LanguageCodeRole, QByteArrayLiteral("languageCode")}, 0139 {FlagRole, QByteArrayLiteral("flag")}, 0140 }; 0141 } 0142 0143 #include "moc_languagelistmodel.cpp"