diff --git a/CMakeLists.txt b/CMakeLists.txt index b8b574ea3..0530205e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,26 @@ +include(ECMQMLModules) +ecm_find_qmlmodule(org.kde.plasma.core 2.0) + +# KI18N Translation Domain for this library. add_definitions(-DTRANSLATION_DOMAIN=\"kcmtranslations\") -set(kcm_translations_PART_SRCS kcmtranslations.cpp) +########### next target ############### -ki18n_wrap_ui(kcm_translations_PART_SRCS kcmtranslationswidget.ui) +set(kcm_translations_PART_SRCS translations.cpp translationsmodel.cpp) -add_library(kcm_translations ${kcm_translations_PART_SRCS}) +add_library(kcm_translations MODULE ${kcm_translations_PART_SRCS}) target_link_libraries(kcm_translations - Qt5::Widgets - KF5::WidgetsAddons - KF5::KCMUtils KF5::I18n + KF5::KCMUtils + KF5::QuickAddons ) +kcoreaddons_desktop_to_json(kcm_translations "kcm_translations.desktop") + ########### install files ############### -install(TARGETS kcm_translations DESTINATION ${KDE_INSTALL_PLUGINDIR}) -install(FILES translations.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) +install(TARGETS kcm_translations DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms) +install(FILES kcm_translations.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) +kpackage_install_package(package kcm_translations kcms) + diff --git a/Messages.sh b/Messages.sh index 06e3e5a3c..2312e470d 100644 --- a/Messages.sh +++ b/Messages.sh @@ -1,4 +1,2 @@ #! /usr/bin/env bash -$EXTRACTRC *.ui >> rc.cpp -$XGETTEXT -ktranslate:1,1t -ktranslate:1c,2,2t *.cpp -o $podir/kcmtranslations.pot -rm -f rc.cpp +$XGETTEXT `find . -name \*.cpp -o -name \*.qml` -o $podir/kcmtranslations.pot diff --git a/translations.desktop b/kcm_translations.desktop similarity index 100% copy from translations.desktop copy to kcm_translations.desktop diff --git a/kcmtranslations.cpp b/kcmtranslations.cpp deleted file mode 100644 index 62dd66839..000000000 --- a/kcmtranslations.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2010, 2014 John Layt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "kcmtranslations.h" - -#include "../formats/writeexports.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ui_kcmtranslationswidget.h" - -K_PLUGIN_FACTORY(KCMTranslationsFactory, registerPlugin();) - -KCMTranslations::KCMTranslations(QWidget *parent, const QVariantList &args) - : KCModule(parent, args), - m_ui(new Ui::KCMTranslationsWidget), - m_messageWidget(0) -{ - KAboutData *about = new KAboutData(QStringLiteral("KCMTranslations"), - i18n("Translations"), - QString(), - i18n("Configure Plasma translations"), - KAboutLicense::GPL); - about->addAuthor(i18n("John Layt"), i18n("Maintainer"), QStringLiteral("jlayt@kde.net")); - setAboutData(about); - - m_ui->setupUi(this); - - // Get the current config - m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations"); - - // Set up UI to respond to user changing the translation selection - connect(m_ui->m_selectTranslations, &KActionSelector::added, this, &KCMTranslations::changedTranslationsSelected); - connect(m_ui->m_selectTranslations, &KActionSelector::removed, this, &KCMTranslations::changedTranslationsAvailable); - connect(m_ui->m_selectTranslations, &KActionSelector::movedUp, this, &KCMTranslations::changedTranslationsSelected); - connect(m_ui->m_selectTranslations, &KActionSelector::movedDown, this, &KCMTranslations::changedTranslationsSelected); - - // If user clicks the Install button, trigger distro specific install routine - connect(m_ui->m_buttonTranslationsInstall, &QPushButton::clicked, this, &KCMTranslations::installTranslations); - - // Hide the Install button, this will be activated by those distros that support this feature. - m_ui->m_buttonTranslationsInstall->setHidden(true); -} - -KCMTranslations::~KCMTranslations() -{ - delete m_ui; -} - -// Reimp -// Load == User has clicked on Reset to restore saved settings -// Also gets called automatically called after constructor -void KCMTranslations::load() -{ - // Get the currently installed translations for Plasma - // TODO May want to later add all installed .po files on system? - m_installedTranslations.clear(); - - m_installedTranslations = KLocalizedString::availableDomainTranslations("systemsettings").toList(); - - // Load the current user translations - loadTranslations(); - - // Check if any of the current user translations are no longer installed - // If any missing remove them and save the settings, then tell the user - QStringList missingLanguages; - QStringList availableLanguages; - if (!m_config.isEntryImmutable(lcLanguage)) { - foreach (const QString &languageCode, m_kcmTranslations) { - if (m_installedTranslations.contains(languageCode)) { - availableLanguages.append(languageCode); - } else { - missingLanguages.append(languageCode); - } - } - m_config.writeEntry(lcLanguage, availableLanguages.join(QStringLiteral(":"))); - m_config.sync(); - m_config.config()->reparseConfiguration(); - loadTranslations(); - } - - // Then update all the widgets to use the new settings - initWidgets(); - - if (missingLanguages.count()) { - const QString txt = i18ncp("%1 is the language code", - "The translation files for the language with the code '%2' " - "could not be found. The " - "language has been removed from your configuration. " - "If you want to add it back, please install the " - "localization files for it and add the language again.", - - "The translation files for the languages with the codes " - "'%2' could not be found. These " - "languages have been removed from your configuration. " - "If you want to add them back, please install the " - "localization files for it and the languages again.", - missingLanguages.count(), - missingLanguages.join("', '")); - - m_messageWidget = new KMessageWidget(this); - m_messageWidget->setMessageType(KMessageWidget::Information); - m_messageWidget->setWordWrap(true); - m_messageWidget->setText(txt); - - m_ui->verticalLayout->insertWidget(0, m_messageWidget); - } -} - -// Reimp -// Save the new LANGUAGE setting -void KCMTranslations::save() -{ - m_config.writeEntry(lcLanguage, m_kcmTranslations.join(QStringLiteral(":")), KConfig::Persistent | KConfig::Global); - m_config.sync(); - KMessageBox::information(this, - i18n("Your changes will take effect the next time you log in."), - i18n("Applying Language Settings"), - QStringLiteral("LanguageChangesApplyOnlyToNewlyStartedPrograms")); - load(); - writeExports(); -} - -// Reimp -// Defaults == User has clicked on Defaults to load default settings -// We interpret this to mean the default LANG setting -void KCMTranslations::defaults() -{ - // Get the users LANG setting, or if empty the system LANG - KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); - - QString lang = formatsConfig.readEntry("LANG", QString()); - if (lang.isEmpty() || !m_installedTranslations.contains(lang)) { - lang = QLocale::system().name(); - } - if (!m_installedTranslations.contains(lang)) { - lang = QStringLiteral("en_US"); - } - - // Use the available lang as the default - m_kcmTranslations.clear(); - m_kcmTranslations.append(lang); - - // The update all the widgets to use the new language - initWidgets(); -} - -void KCMTranslations::loadTranslations() -{ - // Load the user translations - m_configTranslations = m_config.readEntry(lcLanguage, QString()); - m_kcmTranslations.clear(); - m_kcmTranslations = m_configTranslations.split(':', QString::SkipEmptyParts); -} - -QString KCMTranslations::quickHelp() const -{ - return i18n("

Translations

\n" - "

Here you can set your preferred language for translating the " - "user interface of your applications. You can choose a single " - "language, or a list of languages to be applied in sequence. Only " - "language translations that are installed on your system will " - "be listed as available. If your language is not listed then " - "you will need to install it first.

"); -} - -void KCMTranslations::initWidgets() -{ - initTranslations(); - initTranslationsInstall(); - // Init the KCM "Apply" button - emit changed(m_kcmTranslations.join(QStringLiteral(":")) != m_configTranslations); -} - -void KCMTranslations::initTranslations() -{ - m_ui->m_selectTranslations->blockSignals(true); - - m_ui->m_selectTranslations->setAvailableLabel(i18n("Available Languages:")); - QString availableHelp = i18n("This is the list of installed KDE Plasma language " - "translations not currently being used.
To use a language " - "translation move it to the 'Preferred Languages' list in " - "the order of preference.
If no suitable languages are " - "listed, then you may need to install more language packages " - "using your usual installation method."); - m_ui->m_selectTranslations->availableListWidget()->setToolTip(availableHelp); - m_ui->m_selectTranslations->availableListWidget()->setWhatsThis(availableHelp); - - m_ui->m_selectTranslations->setSelectedLabel(i18n("Preferred Languages:")); - QString selectedHelp = i18n("This is the list of installed KDE Plasma language " - "translations currently being used, listed in order of " - "preference.
If a translation is not available for the " - "first language in the list, the next language will be used.
" - "If no other translations are available then US English will " - "be used."); - m_ui->m_selectTranslations->selectedListWidget()->setToolTip(selectedHelp); - m_ui->m_selectTranslations->selectedListWidget()->setWhatsThis(selectedHelp); - - // Clear the selector before reloading - m_ui->m_selectTranslations->availableListWidget()->clear(); - m_ui->m_selectTranslations->selectedListWidget()->clear(); - - // Load each user selected language into the selected list - foreach (const QString &languageCode, m_kcmTranslations) { - QListWidgetItem *listItem = new QListWidgetItem(m_ui->m_selectTranslations->selectedListWidget()); - // TODO This gives the name in the language itself, not in current language, need new QLocale api for that - QString label = QLocale(languageCode).nativeLanguageName(); - if (label.isEmpty()) { - label = languageCode; - } - listItem->setText(label); - listItem->setData(Qt::UserRole, languageCode); - } - - // Load all the available languages the user hasn't selected into the available list - foreach (const QString &languageCode, m_installedTranslations) { - if (!m_kcmTranslations.contains(languageCode)) { - QListWidgetItem *listItem = new QListWidgetItem(m_ui->m_selectTranslations->availableListWidget()); - // TODO This gives the name in the language itself, not in current language, need new QLocale api for that - const QLocale l(languageCode); - QString label = l.nativeLanguageName(); - if (label.isEmpty()) { - label = languageCode; - } else if (languageCode.contains(QStringLiteral("@"))) { - label = i18nc("%1 is language name, %2 is language code name", "%1 (%2)", label, languageCode); - } else if (l.name() != languageCode) { - if (m_installedTranslations.contains(l.name())) { - // KDE languageCode got translated by QLocale to a locale code we also have on the list - // Currently only this happens with pt that gets trasnated to pt_BR - if (languageCode == QLatin1String("pt")) { - label = QLocale(QStringLiteral("pt_PT")).nativeLanguageName(); - } else { - qWarning() << "Language code morphed into another existing language code, please report" << languageCode << l.name(); - label = i18nc("%1 is language name, %2 is language code name", "%1 (%2)", label, languageCode); - } - } - } - listItem->setText(label); - listItem->setData(Qt::UserRole, languageCode); - } - } - m_ui->m_selectTranslations->availableListWidget()->sortItems(); - - // Default to selecting the first Selected language, - // otherwise the first Available language, - // otherwise no languages so disable all buttons - if (m_ui->m_selectTranslations->selectedListWidget()->count() > 0) { - m_ui->m_selectTranslations->selectedListWidget()->setCurrentRow(0); - } else if (m_ui->m_selectTranslations->availableListWidget()->count() > 0) { - m_ui->m_selectTranslations->availableListWidget()->setCurrentRow(0); - } - - // If the setting is locked down by Kiosk, then don't let the user make any changes - if (m_config.isEntryImmutable(lcLanguage)) { - m_ui->m_selectTranslations->setEnabled(false); - } - - m_ui->m_selectTranslations->blockSignals(false); -} - -void KCMTranslations::changedTranslationsAvailable(QListWidgetItem *item) -{ - Q_UNUSED(item); - m_ui->m_selectTranslations->availableListWidget()->sortItems(); - int row = m_ui->m_selectTranslations->availableListWidget()->currentRow(); - changedTranslations(); - m_ui->m_selectTranslations->availableListWidget()->setCurrentRow(row); -} - -void KCMTranslations::changedTranslationsSelected(QListWidgetItem *item) -{ - Q_UNUSED(item); - int row = m_ui->m_selectTranslations->selectedListWidget()->currentRow(); - changedTranslations(); - m_ui->m_selectTranslations->selectedListWidget()->setCurrentRow(row); -} - -void KCMTranslations::changedTranslations() -{ - // Read the list of all Selected translations from the selector widget - m_kcmTranslations.clear(); - for (int i = 0; i < m_ui->m_selectTranslations->selectedListWidget()->count(); ++i) { - m_kcmTranslations.append(m_ui->m_selectTranslations->selectedListWidget()->item(i)->data(Qt::UserRole).toString()); - } - initWidgets(); -} - -void KCMTranslations::initTranslationsInstall() -{ - m_ui->m_buttonTranslationsInstall->blockSignals(true); - m_ui->m_buttonTranslationsInstall->setText(i18n("Install more languages")); - QString helpText = i18n("

Click here to install more languages

"); - m_ui->m_buttonTranslationsInstall->setToolTip(helpText); - m_ui->m_buttonTranslationsInstall->setWhatsThis(helpText); - m_ui->m_buttonTranslationsInstall->blockSignals(false); -} - -void KCMTranslations::installTranslations() -{ - // User has clicked Install Languages button, trigger distro specific install routine -} - -#include "kcmtranslations.moc" diff --git a/kcmtranslations.h b/kcmtranslations.h deleted file mode 100644 index 9082a98b9..000000000 --- a/kcmtranslations.h +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of the KDE's Plasma workspace - * Copyright 2014 John Layt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCMTRANSLATIONS_H -#define KCMTRANSLATIONS_H - - -#include -#include -#include - -class QListWidgetItem; -class KMessageWidget; - -namespace Ui -{ -class KCMTranslationsWidget; -} - -/** - * @short A KCM to configure KDE Gui Translations - * - * This module is for changing the User's Gui Translations settings. - */ - -class KCMTranslations : public KCModule -{ - Q_OBJECT - -public: - KCMTranslations(QWidget *parent, const QVariantList &); - ~KCMTranslations() override; - - void load() override; - void save() override; - void defaults() override; - QString quickHelp() const override; - -private Q_SLOTS: - - void changedTranslationsAvailable(QListWidgetItem *item); - void changedTranslationsSelected(QListWidgetItem *item); - - void installTranslations(); - -private: - - void loadTranslations(); - void changedTranslations(); - - void initWidgets(); - void initTranslations(); - void initTranslationsInstall(); - - // The list of translations currently set in the KCM - QStringList m_kcmTranslations; - // The currently saved list of user translations, used to check if value changed - QString m_configTranslations; - // The currently installed translations, used to check if users translations are valid - QStringList m_installedTranslations; - - KConfigGroup m_config; - - Ui::KCMTranslationsWidget *m_ui; - KMessageWidget *m_messageWidget; -}; - -#endif //KCMTRANSLATIONS_H diff --git a/kcmtranslationswidget.ui b/kcmtranslationswidget.ui deleted file mode 100644 index fe19c80a8..000000000 --- a/kcmtranslationswidget.ui +++ /dev/null @@ -1,87 +0,0 @@ - - - KCMTranslationsWidget - - - - 0 - 0 - 687 - 333 - - - - - 0 - 0 - - - - - - - - - - 0 - 0 - - - - Available &Translations: - - - Preferred Trans&lations: - - - - - - - false - - - Install more translations - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - KActionSelector - QWidget -
kactionselector.h
-
-
- - -
diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml new file mode 100644 index 000000000..1edf4d599 --- /dev/null +++ b/package/contents/ui/main.qml @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 Marco Martin + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.3 as QtControls +import org.kde.kirigami 2.4 as Kirigami +import org.kde.plasma.core 2.1 as PlasmaCore +import org.kde.kcm 1.2 + +ScrollViewKCM { + id: root + + ConfigModule.quickHelp: i18n("Language") + + PlasmaCore.SortFilterModel { + id: availableLanguagesModel + + sourceModel: kcm.translationsModel + + filterRole: "IsSelected" + filterCallback: function(source_row, value) { return !value; } + + sortRole: "display" + } + + Kirigami.OverlaySheet { + id: addLanguagesSheet + + parent: root.parent + + topPadding: 0 + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + + header: Kirigami.Heading { text: i18nc("@title:window", "Add Languages") } + + property var selectedLanguages: [] + + onSheetOpenChanged: selectedLanguages = [] + + ListView { + implicitWidth: 18 * Kirigami.Units.gridUnit + + model: availableLanguagesModel + + delegate: Kirigami.BasicListItem { + property string languageCode: model.LanguageCode + + reserveSpaceForIcon: false + + label: model.display + + checkable: true + onCheckedChanged: { + if (checked) { + addLanguagesSheet.selectedLanguages.push(index); + + // There's no property change notification for pushing to an array + // in a var prop, so we can't bind selectedLanguages.length to + // addLanguagesButton.enabled. + addLanguagesButton.enabled = true; + } else { + addLanguagesSheet.selectedLanguages = addLanguagesSheet.selectedLanguages.filter(function(item) { return item !== index }); + + // There's no property change notification for pushing to an array + // in a var prop, so we can't bind selectedLanguages.length to + // addLanguagesButton.enabled. + if (!addLanguagesSheet.selectedLanguages.length) { + addLanguagesButton.enabled = false; + } + } + } + } + } + + footer: RowLayout { + QtControls.Button { + id: addLanguagesButton + + Layout.alignment: Qt.AlignHCenter + + text: i18nc("@action:button", "Add") + + enabled: false + + onClicked: { + var langs = []; + addLanguagesSheet.selectedLanguages.sort().forEach(function(index) { + langs.push(availableLanguagesModel.get(index).LanguageCode); + }); + + kcm.translationsModel.selectedLanguages = kcm.translationsModel.selectedLanguages.concat(langs); + + addLanguagesSheet.sheetOpen = false; + } + } + } + } + + header: ColumnLayout { + id: messagesLayout + + spacing: Kirigami.Units.largeSpacing + + Kirigami.InlineMessage { + Layout.fillWidth: true + + type: Kirigami.MessageType.Information + + text: i18nc("@info", "There are no languages available on this system.") + + visible: !availableLanguagesModel.count + } + + Kirigami.InlineMessage { + Layout.fillWidth: true + + type: kcm.everSaved ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information + + text: (kcm.everSaved ? i18nc("@info", "Your changes will take effect the next time you log in.") + : i18nc("@info", "There are currently no preferred languages configured.")) + + visible: !languagesList.count || kcm.everSaved + } + + Kirigami.InlineMessage { + Layout.fillWidth: true + + type: Kirigami.MessageType.Error + + text: i18ncp("@info %2 is the language code", + "The translation files for the language with the code '%2' could not be found. The language will be removed from your configuration. If you want to add it back, please install the localization files for it and add the language again.", + "The translation files for the languages with the codes '%2' could not be found. These languages will be removed from your configuration. If you want to add them back, please install the localization files for it and the languages again.", + kcm.translationsModel.missingLanguages.length, + kcm.translationsModel.missingLanguages.join("', '")) + + visible: kcm.translationsModel.missingLanguages.length + } + + QtControls.Label { + Layout.fillWidth: true + + visible: languagesList.count + + text: i18n("The language at the top of this list is is the one you want to see and use most often.") + } + } + + view: ListView { + id: languagesList + + model: PlasmaCore.SortFilterModel { + sourceModel: kcm.translationsModel + + filterRole: "IsSelected" + filterCallback: function(source_row, value) { return value; } + + sortRole: "SelectedPriority" + } + + delegate: Kirigami.SwipeListItem { + id: listItem + + width: ListView.view.width + + contentItem: RowLayout { + width: implicitWidth + height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) + + anchors.verticalCenter: parent.verticalCenter + + Kirigami.Icon { + visible: model.IsMissing + + Layout.alignment: Qt.AlignVCenter + + width: Kirigami.Units.iconSizes.smallMedium + height: width + + source: "error" + color: Kirigami.Theme.negativeTextColor + } + + QtControls.Label { + Layout.fillWidth: true + + Layout.alignment: Qt.AlignVCenter + + text: (index == 0) ? i18nc("@item:inlistbox 1 = Language name", "%1 (Default)", model.display) : model.display + + color: (model.IsMissing ? Kirigami.Theme.negativeTextColor + : (listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) + ? listItem.activeTextColor : listItem.textColor)) + } + } + + actions: [ + Kirigami.Action { + enabled: !model.IsMissing && index > 0 + iconName: "go-top" + tooltip: i18nc("@info:tooltip", "Promote to default") + onTriggered: kcm.translationsModel.moveSelectedLanguage(index, 0) + }, + Kirigami.Action { + enabled: !model.IsMissing && index > 0 + iconName: "go-up" + tooltip: i18nc("@info:tooltip", "Move up") + onTriggered: kcm.translationsModel.moveSelectedLanguage(index, index - 1) + }, + Kirigami.Action { + enabled: !model.IsMissing && index < (languagesList.count - 1) + iconName: "go-down" + tooltip: i18nc("@info:tooltip", "Move down") + onTriggered: kcm.translationsModel.moveSelectedLanguage(index, index + 1) + }, + Kirigami.Action { + enabled: !model.IsMissing + iconName: "list-remove" + tooltip: i18nc("@info:tooltip", "Remove") + onTriggered: kcm.translationsModel.removeSelectedLanguage(model.LanguageCode) + }] + } + } + + footer: RowLayout { + id: footerLayout + + QtControls.Button { + Layout.alignment: Qt.AlignRight + + enabled: availableLanguagesModel.count + + text: i18nc("@action:button", "Add languages...") + + onClicked: addLanguagesSheet.sheetOpen = !addLanguagesSheet.sheetOpen + + checkable: true + checked: addLanguagesSheet.sheetOpen + } + } +} + diff --git a/translations.desktop b/package/metadata.desktop similarity index 73% rename from translations.desktop rename to package/metadata.desktop index 9937b6d3e..6e28ec4d5 100644 --- a/translations.desktop +++ b/package/metadata.desktop @@ -1,55 +1,50 @@ [Desktop Entry] -Exec=kcmshell5 translations -Icon=preferences-desktop-locale -Type=Service -X-KDE-ServiceTypes=KCModule -X-DocPath=kcontrol/translations/index.html - -X-KDE-Library=kcm_translations -X-KDE-ParentApp=kcontrol - -X-KDE-System-Settings-Parent-Category=regionalsettings -X-KDE-Weight=40 - Name=Language Name[ca]=Idioma Name[ca@valencia]=Idioma Name[cs]=Jazyk Name[da]=Sprog Name[de]=Sprache Name[el]=Γλώσσα Name[en_GB]=Language Name[es]=Idioma Name[et]=Keel Name[eu]=Hizkuntza Name[fi]=Kieli Name[fr]=Langage Name[gl]=Idioma Name[he]=שפה Name[hu]=Nyelv Name[id]=Bahasa Name[it]=Lingua Name[ja]=言語 Name[ko]=언어 Name[lt]=Kalba Name[nl]=Taal Name[nn]=Språk Name[pa]=ਭਾਸ਼ਾ Name[pl]=Język Name[pt]=Língua Name[pt_BR]=Idioma Name[ru]=Язык Name[sk]=Jazyk Name[sl]=Jezik Name[sr]=Језик Name[sr@ijekavian]=Језик Name[sr@ijekavianlatin]=Jezik Name[sr@latin]=Jezik Name[sv]=Språk Name[tr]=Dil Name[uk]=Мова Name[x-test]=xxLanguagexx Name[zh_CN]=语言 Name[zh_TW]=語言 -Categories=Qt;KDE;X-KDE-settings-translations; +Icon=preferences-desktop-locale +Type=Service +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=kcm_translations +X-KDE-ServiceTypes=Plasma/Generic +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml diff --git a/translations.cpp b/translations.cpp new file mode 100644 index 000000000..bc0312e59 --- /dev/null +++ b/translations.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 John Layt + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "translations.h" +#include "translationsmodel.h" + +#include "../formats/writeexports.h" + +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(TranslationsFactory, "kcm_translations.json", registerPlugin();) + +Translations::Translations(QObject *parent, const QVariantList &args) + : KQuickAddons::ConfigModule(parent, args) + , m_translationsModel(new TranslationsModel(this)) + , m_everSaved(false) +{ + KAboutData *about = new KAboutData("kcm_translations", + i18n("Configure Plasma translations"), + "2.0", QString(), KAboutLicense::LGPL); + setAboutData(about); + + setButtons(Apply | Default); + + m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations"); + + connect(m_translationsModel, &TranslationsModel::selectedLanguagesChanged, + this, &Translations::selectedLanguagesChanged); + connect(m_translationsModel, &TranslationsModel::missingLanguagesChanged, + this, &Translations::missingLanguagesChanged); +} + +Translations::~Translations() +{ +} + +QAbstractItemModel* Translations::translationsModel() const +{ + return m_translationsModel; +} + +bool Translations::everSaved() const +{ + return m_everSaved; +} + +void Translations::load() +{ + m_configuredLanguages = m_config.readEntry(lcLanguage, + QString()).split(':', QString::SkipEmptyParts); + + m_translationsModel->setSelectedLanguages(m_configuredLanguages); +} + +void Translations::save() +{ + m_everSaved = true; + emit everSavedChanged(); + + m_configuredLanguages = m_translationsModel->selectedLanguages(); + + for (const QString& lang : m_translationsModel->missingLanguages()) { + m_configuredLanguages.removeOne(lang); + } + + m_config.writeEntry(lcLanguage, m_configuredLanguages.join(QStringLiteral(":")), KConfig::Persistent); + m_config.sync(); + + writeExports(); + + m_translationsModel->setSelectedLanguages(m_configuredLanguages); +} + +void Translations::defaults() +{ + KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); + + QString lang = formatsConfig.readEntry("LANG", QString()); + + if (lang.isEmpty() + || !KLocalizedString::availableDomainTranslations("systemsettings").contains(lang)) { + lang = QLocale::system().name(); + } + + if (!KLocalizedString::availableDomainTranslations("systemsettings").contains(lang)) { + lang = QStringLiteral("en_US"); + } + + QStringList languages; + languages << lang; + + m_translationsModel->setSelectedLanguages(languages); +} + +void Translations::selectedLanguagesChanged() +{ + setNeedsSave(m_configuredLanguages != m_translationsModel->selectedLanguages()); +} + +void Translations::missingLanguagesChanged() +{ + if (m_translationsModel->missingLanguages().count()) { + setNeedsSave(true); + } +} + +#include "translations.moc" diff --git a/translations.h b/translations.h new file mode 100644 index 000000000..16b892243 --- /dev/null +++ b/translations.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 John Layt + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TRANSLATIONS_H +#define TRANSLATIONS_H + +#include + +#include + +class TranslationsModel; + +class QAbstractListModel; + +class Translations : public KQuickAddons::ConfigModule +{ + Q_OBJECT + + Q_PROPERTY(QAbstractItemModel* translationsModel READ translationsModel CONSTANT) + Q_PROPERTY(bool everSaved READ everSaved NOTIFY everSavedChanged) + + public: + explicit Translations(QObject* parent = 0, const QVariantList &list = QVariantList()); + ~Translations() override; + + QAbstractItemModel* translationsModel() const; + + bool everSaved() const; + + public Q_SLOTS: + void load(); + void save(); + void defaults(); + + Q_SIGNALS: + void everSavedChanged() const; + + private Q_SLOTS: + void selectedLanguagesChanged(); + void missingLanguagesChanged(); + + private: + TranslationsModel *m_translationsModel; + + KConfigGroup m_config; + QStringList m_configuredLanguages; + + bool m_everSaved; +}; + +#endif diff --git a/translationsmodel.cpp b/translationsmodel.cpp new file mode 100644 index 000000000..40337b76d --- /dev/null +++ b/translationsmodel.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2014 John Layt + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "translationsmodel.h" + +#include + +#include +#include +#include + +TranslationsModel::TranslationsModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_installedLanguages = KLocalizedString::availableDomainTranslations("systemsettings"); + m_languages = m_installedLanguages.toList(); +} + +TranslationsModel::~TranslationsModel() +{ +} + +QHash TranslationsModel::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + + QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); + + for (int i = 0; i < e.keyCount(); ++i) { + roles.insert(e.value(i), e.key(i)); + } + + return roles; +} + +QVariant TranslationsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= m_languages.count()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return languageCodeToName(m_languages.at(index.row())); + } else if (role == LanguageCode) { + return m_languages.at(index.row()); + } else if (role == IsSelected) { + return m_selectedLanguages.contains(m_languages.at(index.row())); + } else if (role == SelectedPriority) { + return m_selectedLanguages.indexOf(m_languages.at(index.row())); + } else if (role == IsMissing) { + return m_missingLanguages.contains(m_languages.at(index.row())); + } + + return QVariant(); +} + +int TranslationsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_languages.count(); +} + +QStringList TranslationsModel::selectedLanguages() const +{ + return m_selectedLanguages; +} + + +void TranslationsModel::setSelectedLanguages(const QStringList &languages) +{ + if (m_selectedLanguages != languages) { + QStringList missingLanguages; + + for (const QString& lang : languages) { + if (!m_installedLanguages.contains(lang)) { + missingLanguages << lang; + } + } + + missingLanguages.sort(); + + if (missingLanguages != m_missingLanguages) { + m_missingLanguages = missingLanguages; + emit missingLanguagesChanged(); + } + + beginResetModel(); + + m_selectedLanguages = languages; + m_languages = (m_installedLanguages | QSet::fromList(m_selectedLanguages)).toList(); + + endResetModel(); + + emit selectedLanguagesChanged(); + } +} + +QStringList TranslationsModel::missingLanguages() const +{ + return m_missingLanguages; +} + +void TranslationsModel::moveSelectedLanguage(int from, int to) +{ + if (from >= m_selectedLanguages.count() || to >= m_selectedLanguages.count()) { + return; + } + + if (from == to) { + return; + } + + m_selectedLanguages.move(from, to); + + emit selectedLanguagesChanged(); + + auto idx = index(m_languages.indexOf(m_selectedLanguages.at(from)), 0); + + if (idx.isValid()) { + emit dataChanged(idx, idx, QVector{SelectedPriority}); + } + + idx = index(m_languages.indexOf(m_selectedLanguages.at(to)), 0); + + if (idx.isValid()) { + emit dataChanged(idx, idx, QVector{SelectedPriority}); + } +} + +void TranslationsModel::removeSelectedLanguage(const QString &languageCode) +{ + m_selectedLanguages.removeOne(languageCode); + + emit selectedLanguagesChanged(); + + auto idx = index(m_languages.indexOf(languageCode), 0); + + if (idx.isValid()) { + emit dataChanged(idx, idx, QVector{IsSelected, SelectedPriority}); + } +} + +QString TranslationsModel::languageCodeToName(const QString& languageCode) const +{ + const QLocale locale(languageCode); + const QString &languageName = locale.nativeLanguageName(); + + if (languageName.isEmpty()) { + return languageCode; + } + + if (languageCode.contains(QStringLiteral("@"))) { + return i18nc("%1 is language name, %2 is language code name", "%1 (%2)", languageName, languageCode); + } + + if (locale.name() != languageCode && m_installedLanguages.contains(locale.name())) { + // KDE languageCode got translated by QLocale to a locale code we also have on + // the list. Currently this only happens with pt that gets translated to pt_BR. + if (languageCode == QLatin1String("pt")) { + return QLocale(QStringLiteral("pt_PT")).nativeLanguageName(); + } else { + qWarning() << "Language code morphed into another existing language code, please report!" << languageCode << locale.name(); + return i18nc("%1 is language name, %2 is language code name", "%1 (%2)", languageName, languageCode); + } + } + + return languageName; +} diff --git a/translationsmodel.h b/translationsmodel.h new file mode 100644 index 000000000..6ef02a5fe --- /dev/null +++ b/translationsmodel.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 John Layt + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TRANSLATIONSMODEL_H +#define TRANSLATIONSMODEL_H + +#include + +#include + +#include + +class TranslationsModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QStringList selectedLanguages READ selectedLanguages WRITE setSelectedLanguages NOTIFY selectedLanguagesChanged) + Q_PROPERTY(QStringList missingLanguages READ missingLanguages NOTIFY missingLanguagesChanged) + + public: + enum AdditionalRoles { + LanguageCode = Qt::UserRole + 1, + IsSelected, + SelectedPriority, + IsMissing + }; + Q_ENUM(AdditionalRoles) + + explicit TranslationsModel(QObject *parent = nullptr); + ~TranslationsModel() override; + + QHash roleNames() const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QStringList selectedLanguages() const; + void setSelectedLanguages(const QStringList &languages); + + QStringList missingLanguages() const; + + public Q_SLOTS: + void moveSelectedLanguage(int from, int to); + void removeSelectedLanguage(const QString &languageCode); + + Q_SIGNALS: + void selectedLanguagesChanged() const; + void missingLanguagesChanged() const; + + private: + QString languageCodeToName(const QString& languageCode) const; + + KConfigGroup m_config; + + QStringList m_languages; + + QSet m_installedLanguages; + + QStringList m_selectedLanguages; + QStringList m_missingLanguages; +}; + +#endif