You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

390 lines
11 KiB

/*
languagelistmodel.h
SPDX-FileCopyrightText: 2021 Han Young <hanyoung@protonmail.com>
SPDX-FileCopyrightText: 2019 Kevin Ottens <kevin.ottens@enioka.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "languagelistmodel.h"
#include "exampleutility.h"
#include "kcm_regionandlang_debug.h"
#include "kcmregionandlang.h"
#include "regionandlangsettings.h"
using namespace KCM_RegionAndLang;
LanguageListModel::LanguageListModel(QObject *parent)
: QAbstractListModel(parent)
, m_selectedLanguageModel(new SelectedLanguageModel(this))
{
connect(this, &LanguageListModel::isPreviewExampleChanged, this, &LanguageListModel::exampleChanged);
connect(m_selectedLanguageModel, &SelectedLanguageModel::exampleChanged, this, &LanguageListModel::exampleChanged);
m_availableLanguages = KLocalizedString::availableDomainTranslations("plasmashell").values();
m_availableLanguages.sort();
m_availableLanguages.push_front(QStringLiteral("C"));
}
int LanguageListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_availableLanguages.size();
}
QVariant LanguageListModel::data(const QModelIndex &index, int role) const
{
const auto row = index.row();
if (row < 0 || row >= m_availableLanguages.size()) {
return {};
}
switch (role) {
case NativeName:
return languageCodeToName(m_availableLanguages.at(row));
case LanguageCode:
return m_availableLanguages.at(row);
case Flag: {
QString flagCode;
const QStringList split = QLocale(m_availableLanguages.at(row)).name().split(QLatin1Char('_'));
if (split.size() > 1) {
flagCode = split[1].toLower();
}
return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/locale/countries/%1/flag.png").arg(flagCode));
}
}
Q_UNREACHABLE();
return {};
}
QHash<int, QByteArray> LanguageListModel::roleNames() const
{
return {{NativeName, QByteArrayLiteral("nativeName")}, {LanguageCode, QByteArrayLiteral("languageCode")}, {Flag, QByteArrayLiteral("flag")}};
}
QString LanguageListModel::languageCodeToName(const QString &languageCode)
{
const QLocale locale(languageCode);
const QString languageName = locale.nativeLanguageName();
if (languageName.isEmpty()) {
return languageCode;
}
if (languageCode.contains(QLatin1Char('@'))) {
return i18nc("%1 is language name, %2 is language code name", "%1 (%2)", languageName, languageCode);
}
// 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 == QStringLiteral("pt_BR")) {
return i18nc("%1 is português in system locale name, Brazil is to distinguish European português and Brazilian português", "%1 (Brazil)", languageName);
}
return languageName;
}
int LanguageListModel::currentIndex() const
{
return m_index;
}
void LanguageListModel::setCurrentIndex(int index)
{
if (index == m_index || index < 0 || index >= m_availableLanguages.size()) {
return;
}
m_index = index;
Q_EMIT exampleChanged();
}
QString LanguageListModel::exampleHelper(const std::function<QString(const QLocale &)> &func) const
{
if (!m_settings) {
return {};
}
QString result = func(QLocale(m_settings->langWithFallback()));
if (m_isPreviewExample) {
if (m_index < 0) {
result = func(QLocale(m_settings->langWithFallback()));
} else {
result = func(QLocale(m_availableLanguages[m_index]));
}
}
return result;
}
QString LanguageListModel::numberExample() const
{
return exampleHelper(Utility::numericExample);
}
QString LanguageListModel::currencyExample() const
{
return exampleHelper(Utility::monetaryExample);
}
QString LanguageListModel::timeExample() const
{
return exampleHelper(Utility::timeExample);
}
QString LanguageListModel::paperSizeExample() const
{
return exampleHelper(Utility::paperSizeExample);
}
#ifdef LC_ADDRESS
QString LanguageListModel::addressExample() const
{
return exampleHelper(Utility::addressExample);
}
QString LanguageListModel::nameStyleExample() const
{
return exampleHelper(Utility::nameStyleExample);
}
QString LanguageListModel::phoneNumbersExample() const
{
return exampleHelper(Utility::phoneNumbersExample);
}
#endif
QString LanguageListModel::metric() const
{
return exampleHelper(Utility::measurementExample);
}
void LanguageListModel::setRegionAndLangSettings(QObject *settings)
{
if (auto *regionandlangsettings = dynamic_cast<RegionAndLangSettings *>(settings)) {
m_settings = regionandlangsettings;
m_selectedLanguageModel->setRegionAndLangSettings(regionandlangsettings);
Q_EMIT exampleChanged();
}
}
bool LanguageListModel::isPreviewExample() const
{
return m_isPreviewExample;
}
void LanguageListModel::setIsPreviewExample(bool preview)
{
m_isPreviewExample = preview;
}
void SelectedLanguageModel::setRegionAndLangSettings(RegionAndLangSettings *settings)
{
m_settings = settings;
beginResetModel();
if (m_settings->language().isEmpty() && m_settings->isDefaultSetting(SettingType::Lang)) {
// no language but have lang
m_selectedLanguages = {m_settings->lang()};
m_selectedLanguages.first().remove(QStringLiteral(".UTF-8"));
} else if (!m_settings->language().isEmpty()) {
// have language, ignore lang
m_selectedLanguages = m_settings->language().split(QLatin1Char(':'));
} else {
// have nothing, figure out from env
QString lang = envLang();
QString language = envLanguage();
if (!language.isEmpty()) {
QStringList langlist = language.split(QLatin1Char(':'));
for (QString &lang : langlist) {
lang = lang.split(QLatin1Char('.'))[0];
}
m_selectedLanguages = langlist;
} else if (!lang.isEmpty()) {
lang.remove(QStringLiteral(".UTF-8"));
m_selectedLanguages = {lang};
}
m_hasImplicitLang = true;
Q_EMIT hasImplicitLangChanged();
}
endResetModel();
// check for invalid lang
if (const QString lang = KCMRegionAndLang::toGlibcLocale(m_selectedLanguages.front()); lang.isEmpty()) {
m_unsupportedLanguage = m_selectedLanguages.front();
Q_EMIT unsupportedLanguageChanged();
} else if (!m_unsupportedLanguage.isEmpty()) {
m_unsupportedLanguage.clear();
Q_EMIT unsupportedLanguageChanged();
}
}
SelectedLanguageModel *LanguageListModel::selectedLanguageModel() const
{
return m_selectedLanguageModel;
}
int SelectedLanguageModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_selectedLanguages.size();
}
QVariant SelectedLanguageModel::data(const QModelIndex &index, int role) const
{
Q_UNUSED(role)
const auto row = index.row();
if (row < 0 || row > m_selectedLanguages.size()) {
return {};
}
// "add Language" Item
if (row == m_selectedLanguages.size()) {
return {};
}
return LanguageListModel::languageCodeToName(m_selectedLanguages.at(row));
}
bool SelectedLanguageModel::shouldWarnMultipleLang() const
{
if (m_selectedLanguages.size() >= 2) {
if (m_selectedLanguages.front() == QStringLiteral("en_US")) {
return true;
}
}
return false;
}
void SelectedLanguageModel::move(int from, int to)
{
if (from == to || from < 0 || from >= m_selectedLanguages.size() || to < 0 || to >= m_selectedLanguages.size()) {
return;
}
if (m_hasImplicitLang) {
m_hasImplicitLang = false;
Q_EMIT hasImplicitLangChanged();
}
beginResetModel();
m_selectedLanguages.move(from, to);
endResetModel();
saveLanguages();
Q_EMIT shouldWarnMultipleLangChanged();
Q_EMIT exampleChanged();
}
void SelectedLanguageModel::remove(int index)
{
if (index < 0 || index >= m_selectedLanguages.size()) {
return;
}
beginRemoveRows(QModelIndex(), index, index);
m_selectedLanguages.removeAt(index);
endRemoveRows();
saveLanguages();
Q_EMIT shouldWarnMultipleLangChanged();
Q_EMIT exampleChanged();
}
void SelectedLanguageModel::addLanguage(const QString &lang)
{
if (lang.isEmpty() || m_selectedLanguages.indexOf(lang) != -1) {
return;
}
// fix Kirigami.SwipeListItem doesn't connect to Actions' visible property.
// Reset model enforce a refresh of delegate
beginResetModel();
if (m_hasImplicitLang) {
m_hasImplicitLang = false;
Q_EMIT hasImplicitLangChanged();
}
m_selectedLanguages.push_back(lang);
endResetModel();
saveLanguages();
Q_EMIT shouldWarnMultipleLangChanged();
Q_EMIT exampleChanged();
}
void SelectedLanguageModel::replaceLanguage(int index, const QString &lang)
{
if (index < 0 || index >= m_selectedLanguages.size() || lang.isEmpty()) {
return;
}
int existLangIndex = m_selectedLanguages.indexOf(lang);
// return if no change, but allow change implicit lang to explicit
if (existLangIndex == index && !m_hasImplicitLang) {
return;
}
beginResetModel();
m_selectedLanguages[index] = lang;
if (!m_hasImplicitLang) {
// delete duplicate lang
if (existLangIndex != -1) {
m_selectedLanguages.removeAt(existLangIndex);
}
} else {
m_hasImplicitLang = false;
Q_EMIT hasImplicitLangChanged();
}
endResetModel();
saveLanguages();
Q_EMIT shouldWarnMultipleLangChanged();
Q_EMIT exampleChanged();
}
void SelectedLanguageModel::saveLanguages()
{
// implicit lang means no change
if (!m_settings || m_hasImplicitLang) {
return;
}
if (m_selectedLanguages.empty()) {
m_settings->setLang(m_settings->defaultLangValue());
m_settings->config()->group(QStringLiteral("Formats")).deleteEntry("lang");
m_settings->config()->group(QStringLiteral("Translations")).deleteEntry("language");
} else {
QString lang = KCMRegionAndLang::toGlibcLocale(m_selectedLanguages.front());
if (lang.isEmpty()) {
m_unsupportedLanguage = m_selectedLanguages.front();
Q_EMIT unsupportedLanguageChanged();
} else {
if (!m_unsupportedLanguage.isEmpty()) {
m_unsupportedLanguage.clear();
Q_EMIT unsupportedLanguageChanged();
}
// don't set LANG in non glibc systems, because in non glibc systems KCMRegionAndLang::toGlibcLocale return the language itself
if (KCMRegionAndLang::isGlibc()) {
m_settings->setLang(lang);
}
}
QString languages;
for (auto i = m_selectedLanguages.cbegin(); i != m_selectedLanguages.cend(); i++) {
languages.push_back(*i);
// no ':' at end
if (i + 1 != m_selectedLanguages.cend()) {
languages.push_back(QLatin1Char(':'));
}
}
m_settings->setLanguage(languages);
}
}
bool SelectedLanguageModel::hasImplicitLang() const
{
return m_hasImplicitLang;
}
const QString &SelectedLanguageModel::unsupportedLanguage() const
{
return m_unsupportedLanguage;
}
QString SelectedLanguageModel::envLang() const
{
return qEnvironmentVariable("LANG");
}
QString SelectedLanguageModel::envLanguage() const
{
return qEnvironmentVariable("LANGUAGE");
}