File indexing completed on 2024-05-19 05:49:21
0001 /* 0002 Copyright (C) 2014-2021 Harald Sitter <apachelogger@kubuntu.org> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public License as 0006 published by the Free Software Foundation; either version 2 of 0007 the License or (at your option) version 3 or any later version 0008 accepted by the membership of KDE e.V. (or its successor approved 0009 by the membership of KDE e.V.), which shall act as a proxy 0010 defined in Section 14 of version 3 of the license. 0011 0012 This program is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 GNU General Public License for more details. 0016 0017 You should have received a copy of the GNU General Public License 0018 along with this program. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "l10nevent.h" 0022 0023 #include <QDebug> 0024 0025 #include <KConfigGroup> 0026 #include <KToolInvocation> 0027 #include <KSharedConfig> 0028 0029 #include <Kubuntu/l10n_language.h> 0030 #include <Kubuntu/l10n_languagecollection.h> 0031 0032 L10nEvent::L10nEvent(QObject *parent) 0033 : Event(parent, "L10n") 0034 , m_languageCollection(nullptr) 0035 { 0036 // We only want this notification once for now. 0037 // NOTE: might be viable to watch the config and apt lock to 0038 // issue a notification when the user installs software that 0039 // requires additional language support packages. 0040 show(); // noop if nothing needs showing 0041 } 0042 0043 L10nEvent::~L10nEvent() 0044 { 0045 } 0046 0047 void L10nEvent::show() 0048 { 0049 if (isHidden()) { 0050 return; 0051 } 0052 if (!m_languageCollection) { 0053 m_languageCollection = new Kubuntu::LanguageCollection(this); 0054 connect(m_languageCollection, SIGNAL(updated()), 0055 this, SLOT(showOnLanguageCollectionUpdated())); 0056 } 0057 if (!m_languageCollection->isUpdated()) { 0058 m_languageCollection->update(); 0059 return; 0060 } 0061 showOnLanguageCollectionUpdated(); 0062 } 0063 0064 void L10nEvent::showOnLanguageCollectionUpdated() 0065 { 0066 const KSharedConfig::Ptr userConfig = KSharedConfig::openConfig("kdeglobals", KConfig::IncludeGlobals); 0067 const KConfigGroup userSettings = KConfigGroup(userConfig, "Locale"); 0068 const QString languageConfigString = userSettings.readEntry("Language", QString()); 0069 const QStringList kdeLanguageList = languageConfigString.split(QLatin1Char(':'), 0070 QString::SkipEmptyParts); 0071 qDebug() << "KDE Languages:" << kdeLanguageList; 0072 0073 m_missingPackages.clear(); 0074 0075 // languages() at the time of writing has no caching capability, so make sure 0076 // that it is not called more than necessary. 0077 const QSet<Kubuntu::Language *> languages = m_languageCollection->languages(); 0078 0079 // Check if we can find the kde languages and if they are complete. 0080 foreach (const QString &kdeLanguage, kdeLanguageList) { 0081 foreach (Kubuntu::Language *language, languages) { 0082 if (kdeLanguage == language->kdeLanguageCode()) { 0083 qDebug() << "matched" << kdeLanguage; 0084 checkForMissingPackages(language); 0085 } 0086 } 0087 } 0088 0089 // Additionally check the system locale. 0090 // This is necessary because 0091 // - after installation there won't be a KCM config, but a possibly 0092 // incomplete language support. 0093 // - if the user mangles his locale manually or through other tools 0094 // we still want him to complete his language support. 0095 const QStringList matchables = systemLocaleMatchables(); 0096 qDebug() << "System Language Matchables:" << matchables; 0097 bool matched = false; 0098 foreach (const QString &matchable, matchables) { 0099 foreach (Kubuntu::Language *language, languages) { 0100 if (matchable == language->kdeLanguageCode()) { 0101 qDebug() << "matched" << matchable; 0102 checkForMissingPackages(language); 0103 // If we had a match we abort as we only want the most generic 0104 // match. 0105 // e.g. we want 'ca@valencia' but not 'ca'. 0106 matched = true; 0107 } 0108 } 0109 if (matched) { 0110 continue; 0111 } 0112 } 0113 0114 m_missingPackages.removeDuplicates(); 0115 0116 if (m_missingPackages.isEmpty()) { 0117 return; 0118 } 0119 0120 QString icon = QString("preferences-desktop-locale"); 0121 QString text(i18nc("Notification when additional packages are required for complete system localization", 0122 "Language support is incomplete, additional packages are required")); 0123 QStringList actions; 0124 actions << i18nc("Installs additional localization packages", "Install"); 0125 actions << i18nc("Button to dismiss this notification once", "Ignore for now"); 0126 actions << i18nc("Button to make this notification never show up again", 0127 "Never show again"); 0128 Event::show(icon, text, actions); 0129 } 0130 0131 void L10nEvent::run() 0132 { 0133 qDebug() << m_missingPackages; 0134 if (!m_missingPackages.isEmpty()) { 0135 QStringList args; 0136 args.append("--install"); 0137 args.append(m_missingPackages); 0138 KToolInvocation::kdeinitExec("qapt-batch", args); 0139 } 0140 Event::run(); 0141 } 0142 0143 bool L10nEvent::checkForMissingPackages(Kubuntu::Language *language) 0144 { 0145 // Not cached, so cache here. 0146 const bool isSupportComplete = language->isSupportComplete(); 0147 qDebug() << " completeness:" << isSupportComplete; 0148 if (!isSupportComplete) { 0149 m_missingPackages.append(language->missingPackages()); 0150 return true; 0151 } 0152 return false; 0153 } 0154 0155 QStringList L10nEvent::systemLocaleMatchables() const 0156 { 0157 const QString systemLocale = qgetenv("LANG"); 0158 0159 // Valid locales may be C or LANGUAGE followed by country and/or encoding 0160 // and/or variant. Leading to the following regex: 0161 // Please note that the regex is a bit blown up because greedy matching 0162 // requires us to except the seperator characters to get the intended result. 0163 // v language exactly once 0164 // . v country one match at the most 0165 // . . v encoding one match at the most 0166 // . . . v variant one match at the most 0167 QRegExp localeRegex("([^_\\.\\@]+)(_([^\\.\\@]+))?(\\.([^\\@]+))?(\\@(.+))?"); 0168 if (localeRegex.indexIn(systemLocale) == -1) { 0169 return QStringList(); 0170 } 0171 qDebug() << localeRegex.capturedTexts(); 0172 0173 const QString language = localeRegex.capturedTexts().at(1); 0174 const QString country = localeRegex.capturedTexts().at(3); 0175 // Not useful for language resolution: 0176 // const QString encoding = localeRegex.capturedTexts().at(5); 0177 const QString variant = localeRegex.capturedTexts().at(7); 0178 0179 QStringList matchables; 0180 matchables.reserve(4); // We can only ever match 4. 0181 0182 if (!language.isEmpty()) { // Language cannot really be empty, but oh well. 0183 matchables.prepend(language); 0184 } 0185 if (!language.isEmpty() && !country.isEmpty()) { 0186 matchables.prepend(QString::fromLatin1("%1_%2").arg(language, country)); 0187 } 0188 if (!language.isEmpty() && !variant.isEmpty()) { 0189 matchables.prepend(QString::fromLatin1("%1@%2").arg(language, variant)); 0190 } 0191 if (!language.isEmpty() && !country.isEmpty() && !variant.isEmpty()) { 0192 matchables.prepend(QString::fromLatin1("%1_%2@%3").arg(language, country, variant)); 0193 } 0194 return matchables; 0195 }