Commit c7c2a3a8 authored by Akseli Lahtinen's avatar Akseli Lahtinen Committed by Harald Sitter
Browse files

Add LC_ADDRESS, LC_IDENTIFICATION, LC_NAME and LC_TELEPHONE to region settings

Now users can modify LC_ADDRESS, LC_IDENTIFICATION, LC_NAME and LC_TELEPHONE from region settings.

See LC_IDENTIFICATION example here: https://lh.2xlibre.net/locale/fi_FI/

BUG: 204525
BUG: 430801
parent 42bba62d
Pipeline #219101 passed with stage
in 14 minutes and 14 seconds
......@@ -50,3 +50,201 @@ QString Utility::paperSizeExample(const QLocale &locale)
}
return paperSizeExample;
}
// If LC_ADDRESS does not exist, we do not use these at all
// The format characters are found from here https://lh.2xlibre.net/
#ifdef LC_ADDRESS
QString Utility::addressExample(const QLocale &locale)
{
// Create an example string using POSTAL_FMT
const QStringList lang = getLangCodeFromLocale(locale);
const QHash<QChar, QString> map{
// QChar is the field descriptor and QString is the value
{'n', nameStyleExample(locale)}, // Person's Name, use LC_NAME for this
{'a', ki18nc("Care of person or organization", "c/o").toString(lang)},
{'f', ki18nc("Firm name", "Acme Corporation").toString(lang)},
{'d', ki18nc("Department name", "Development Department").toString(lang)},
{'b', ki18nc("Building name", "Dev-Building").toString(lang)},
{'s', ki18nc("Street or block name", "Main Street").toString(lang)},
{'h', ki18nc("House number", "House 1").toString(lang)},
{'N', "\n"}, // End of line
{'t', ki18nc("Whitespace field for locale address style example", " ").toString(lang)}, // Space
{'r', ki18nc("Room number", "Room 2").toString(lang)},
{'e', ki18nc("Floor number", "Floor 3").toString(lang)},
{'C', getLocaleInfo(_NL_ADDRESS_COUNTRY_POST, LC_ADDRESS, locale)}, // Country designation from the CountryKeyword
{'l', ki18nc("Local township within town or city", "Downtown").toString(lang)},
{'z', ki18nc("Zip number, postal code", "123456").toString(lang)},
{'T', ki18nc("Town or city", "City").toString(lang)},
{'S', ki18nc("State, province or prefecture", "State").toString(lang)},
{'c', getLocaleInfo(_NL_ADDRESS_COUNTRY_NAME, LC_ADDRESS, locale)}, // Country from data record
};
return resolveFieldDescriptors(map, _NL_ADDRESS_POSTAL_FMT, LC_ADDRESS, locale);
}
#endif
#ifdef LC_ADDRESS
QString Utility::nameStyleExample(const QLocale &locale)
{
const QStringList lang = getLangCodeFromLocale(locale);
const QHash<QChar, QString> map{
{'f', ki18nc("Family names", "FamilyName").toString(lang)},
{'F', ki18nc("Family names in uppercase", "FAMILYNAME").toString(lang)},
{'g', ki18nc("First given name", "FirstName").toString(lang)},
{'G', ki18nc("First given initial", "F").toString(lang)},
{'l', ki18nc("First given name with latin letters", "FirstName").toString(lang)},
{'o', ki18nc("Other shorter name", "OtherName").toString(lang)},
{'m', ki18nc("Additional given names", "AdditionalName").toString(lang)},
{'M', ki18nc("Initials for additional given names", "A").toString(lang)},
{'p', ki18nc("Profession", "Profession").toString(lang)},
{'s', ki18nc("Salutation", "Doctor").toString(lang)},
{'S', ki18nc("Abbreviated salutation", "Dr.").toString(lang)},
{'d', ki18nc("Salutation using the FDCC-sets conventions", "Dr.").toString(lang)},
{'t', ki18nc("Space or dot for locale name style example", " ").toString(lang)}, // Space or dot. Space produces better examples.
};
return resolveFieldDescriptors(map, _NL_NAME_NAME_FMT, LC_NAME, locale);
}
#endif
#ifdef LC_ADDRESS
QString Utility::phoneNumbersExample(const QLocale &locale)
{
const QHash<QChar, QString> map = {
{'a', "123"}, // Area code without nationwide prefix
{'A', "0123"}, // Area code with nationwide prefix
{'l', "1234567"}, // Local number within area code
{'e', "321"}, // Extension to local number
{'c', getLocaleInfo(_NL_TELEPHONE_INT_PREFIX, LC_TELEPHONE, locale)}, // Country code
{'C', "01"}, // Alternate carrier service code used for dialling abroad
{'t', ki18nc("Whitespace for telephone style example", " ").toString(getLangCodeFromLocale(locale))}, // Insert space
};
return resolveFieldDescriptors(map, _NL_TELEPHONE_TEL_INT_FMT, LC_TELEPHONE, locale);
}
#endif
#ifdef LC_ADDRESS
QString Utility::resolveFieldDescriptors(QHash<QChar, QString> map, int langInfoFormat, int lcFormat, const QLocale &locale)
{
QString formatString = getLocaleInfo(langInfoFormat, lcFormat, locale);
QString example = KMacroExpander::expandMacros(formatString, map);
if (example.isEmpty() || example == QLatin1String("???")) {
return i18nc("This is returned when an example test could not be made from locale information", "Could not find an example for this locale");
}
return example;
}
QString Utility::getLocaleInfo(int langInfoFormat, int lcFormat, const QLocale &locale)
{
const QString localeString = locale.name() + QLatin1String(".UTF-8");
const QByteArray localeByteArray = localeString.toUtf8();
QString localeInfo = parseLocaleFile(locale.name(), langInfoFormat);
if (localeInfo.isEmpty()) {
setlocale(lcFormat, localeByteArray);
localeInfo = QString::fromUtf8(nl_langinfo(langInfoFormat));
}
return localeInfo;
}
QString Utility::parseLocaleFile(QString localeName, int langInfoFormat)
{
QFileInfo localeFileInfo;
// Get the locale file info from the first folder where it's found
for (const auto &localeDirectory : {QStringLiteral("/usr/share/i18n/locales/")}) {
localeFileInfo = findLocaleInFolder(localeName, localeDirectory);
if (localeFileInfo.exists()) {
break;
}
}
// Parse through file and return the inquired field descriptor
if (localeFileInfo.exists()) {
QFile localeFile(localeFileInfo.filePath());
// Return if we cant open file
if (!localeFile.open(QIODevice::ReadOnly)) {
return {};
}
QTextStream textStream(&localeFile);
QString formatToFetch = getFormatToFetch(langInfoFormat);
if (formatToFetch.isEmpty()) {
return {};
}
// Read the file with regex and return the first match
const QRegularExpression rx({formatToFetch + "\\s+\"(.*)\""});
while (!textStream.atEnd()) {
QString line = textStream.readLine();
QRegularExpressionMatch match = rx.match(line);
if (match.hasMatch()) {
// Return the first (and only) match
return match.captured(1);
}
}
}
return {};
}
QFileInfo Utility::findLocaleInFolder(QString localeName, QString localeDirectory)
{
QDirIterator dirIterator(localeDirectory);
// Iterate through files in the locale directory
while (dirIterator.hasNext()) {
QString fileName = dirIterator.next();
QFileInfo fileInfo(fileName);
// Ignore directories
if (fileInfo.isDir()) {
continue;
}
// If file is found, break the loop
if (fileInfo.fileName().contains(localeName)) {
return fileInfo;
break;
}
}
return {};
}
QString Utility::getFormatToFetch(int langInfoFormat)
{
switch (langInfoFormat) {
case _NL_ADDRESS_POSTAL_FMT:
return QStringLiteral("postal_fmt");
case _NL_ADDRESS_COUNTRY_POST:
return QStringLiteral("country_post");
case _NL_ADDRESS_COUNTRY_NAME:
return QStringLiteral("country_name");
case _NL_NAME_NAME_FMT:
return QStringLiteral("name_fmt");
case _NL_TELEPHONE_TEL_INT_FMT:
return QStringLiteral("tel_int_fmt");
case _NL_TELEPHONE_INT_PREFIX:
return QStringLiteral("int_prefix");
}
return {};
}
QStringList Utility::getLangCodeFromLocale(QLocale locale)
{
// Match letters from a-z before end of string OR -
QStringList list; // ki18nc takes only stringlists for langs
const QRegularExpression rx(R"(([a-z]*)($|[\-]).*)");
QRegularExpressionMatch match = rx.match(locale.bcp47Name());
if (match.hasMatch()) {
list.append(match.captured(1)); // Return first capture
} else {
list.append("en"); // Return the default "en"
}
return list;
}
#endif
......@@ -8,11 +8,27 @@
#pragma once
#include <KLocalizedString>
#include <KMacroExpander>
#include <QDateTime>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QLocale>
#include <QRegularExpression>
#include <QTextStream>
#include <langinfo.h>
class Utility
{
#ifdef LC_ADDRESS
static QString resolveFieldDescriptors(QHash<QChar, QString> map, int langInfoFormat, int lcFormat, const QLocale &locale);
static QString getLocaleInfo(int langInfoFormat, int lcFormat, const QLocale &locale);
static QString parseLocaleFile(QString localeName, int langInfoFormat);
static QFileInfo findLocaleInFolder(QString localeName, QString localeDirectory);
static QString getFormatToFetch(int langInfoFormat);
static QStringList getLangCodeFromLocale(QLocale locale);
#endif
public:
static QString numericExample(const QLocale &locale);
static QString timeExample(const QLocale &locale);
......@@ -20,4 +36,9 @@ public:
static QString measurementExample(const QLocale &locale);
static QString monetaryExample(const QLocale &locale);
static QString paperSizeExample(const QLocale &locale);
#ifdef LC_ADDRESS
static QString addressExample(const QLocale &locale);
static QString nameStyleExample(const QLocale &locale);
static QString phoneNumbersExample(const QLocale &locale);
#endif
};
......@@ -83,6 +83,15 @@ void KCMRegionAndLang::save()
if (!settings()->isDefaultSetting(SettingType::PaperSize)) {
locales.append(settings()->paperSize());
}
if (settings()->isDefaultSetting(SettingType::Address)) {
locales.append(settings()->address());
}
if (settings()->isDefaultSetting(SettingType::NameStyle)) {
locales.append(settings()->nameStyle());
}
if (settings()->isDefaultSetting(SettingType::PhoneNumbers)) {
locales.append(settings()->phoneNumbers());
}
if (!settings()->language().isEmpty()) {
QStringList languages = settings()->language().split(QLatin1Char(':'));
for (const QString &lang : languages) {
......@@ -140,10 +149,20 @@ void KCMRegionAndLang::unset(SettingType setting)
} else if (setting == SettingType::Currency) {
entry = "LC_MONETARY";
settings()->setMonetary(settings()->defaultMonetaryValue());
} else {
} else if (setting == SettingType::PaperSize) {
entry = "LC_PAPER";
settings()->setPaperSize(settings()->defaultPaperSizeValue());
} else if (setting == SettingType::Address) {
entry = "LC_ADDRESS";
settings()->setAddress(settings()->defaultAddressValue());
} else if (setting == SettingType::NameStyle) {
entry = "LC_NAME";
settings()->setNameStyle(settings()->defaultNameStyleValue());
} else if (setting == SettingType::PhoneNumbers) {
entry = "LC_TELEPHONE";
settings()->setPhoneNumbers(settings()->defaultPhoneNumbersValue());
}
settings()->config()->group(QStringLiteral("Formats")).deleteEntry(entry);
}
......
......@@ -133,6 +133,23 @@ 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);
......
......@@ -38,6 +38,11 @@ public:
QString currencyExample() const;
QString timeExample() const;
QString paperSizeExample() const;
#ifdef LC_ADDRESS
QString addressExample() const;
QString nameStyleExample() const;
QString phoneNumbersExample() const;
#endif
QString metric() const;
// currently unused, but we need it if we want preview examples in add langauge overlay
......
......@@ -20,6 +20,7 @@
#include "localelistmodel.h"
#include "exampleutility.h"
#include "kcmregionandlang.h"
#include "settingtype.h"
#include <QTextCodec>
......@@ -103,6 +104,14 @@ QVariant LocaleListModel::data(const QModelIndex &index, int role) const
return Utility::measurementExample(data.locale);
case PaperSize:
return Utility::paperSizeExample(data.locale);
#ifdef LC_ADDRESS
case Address:
return Utility::addressExample(data.locale);
case NameStyle:
return Utility::nameStyleExample(data.locale);
case PhoneNumbers:
return Utility::phoneNumbersExample(data.locale);
#endif
}
return {};
}
......
......@@ -26,6 +26,12 @@ OptionsModel::OptionsModel(KCMRegionAndLang *parent)
{i18nc("@info:title", "Currency"), SettingType::Currency},
{i18nc("@info:title", "Measurements"), SettingType::Measurement},
{i18nc("@info:title", "Paper Size"), SettingType::PaperSize}}};
#ifdef LC_ADDRESS
m_staticNames.push_back(std::make_pair(i18nc("@info:title", "Address"), SettingType::Address));
m_staticNames.push_back(std::make_pair(i18nc("@info:title", "Name Style"), SettingType::NameStyle)),
m_staticNames.push_back(std::make_pair(i18nc("@info:title", "Phone Numbers"), SettingType::PhoneNumbers));
#endif
connect(m_settings, &RegionAndLangSettings::langChanged, this, &OptionsModel::handleLangChange);
connect(m_settings, &RegionAndLangSettings::numericChanged, this, [this] {
QLocale locale(m_settings->LC_LocaleWithLang(SettingType::Numeric));
......@@ -52,6 +58,23 @@ OptionsModel::OptionsModel(KCMRegionAndLang *parent)
m_measurementExample = Utility::measurementExample(locale);
Q_EMIT dataChanged(createIndex(5, 0), createIndex(5, 0), {Subtitle, Example});
});
#ifdef LC_ADDRESS
connect(m_settings, &RegionAndLangSettings::addressChanged, this, [this] {
QLocale locale(m_settings->LC_LocaleWithLang(SettingType::Address));
m_measurementExample = Utility::measurementExample(locale);
Q_EMIT dataChanged(createIndex(6, 0), createIndex(6, 0), {Subtitle, Example});
});
connect(m_settings, &RegionAndLangSettings::nameStyleChanged, this, [this] {
QLocale locale(m_settings->LC_LocaleWithLang(SettingType::NameStyle));
m_measurementExample = Utility::measurementExample(locale);
Q_EMIT dataChanged(createIndex(7, 0), createIndex(7, 0), {Subtitle, Example});
});
connect(m_settings, &RegionAndLangSettings::phoneNumbersChanged, this, [this] {
QLocale locale(m_settings->LC_LocaleWithLang(SettingType::PhoneNumbers));
m_measurementExample = Utility::measurementExample(locale);
Q_EMIT dataChanged(createIndex(8, 0), createIndex(8, 0), {Subtitle, Example});
});
#endif
// initialize examples
m_numberExample = Utility::numericExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::Numeric)));
......@@ -59,6 +82,11 @@ OptionsModel::OptionsModel(KCMRegionAndLang *parent)
m_measurementExample = Utility::measurementExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::Measurement)));
m_currencyExample = Utility::monetaryExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::Currency)));
m_paperSizeExample = Utility::paperSizeExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::PaperSize)));
#ifdef LC_ADDRESS
m_addressExample = Utility::addressExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::Address)));
m_nameStyleExample = Utility::nameStyleExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::NameStyle)));
m_phoneNumbersExample = Utility::phoneNumbersExample(QLocale(m_settings->LC_LocaleWithLang(SettingType::PhoneNumbers)));
#endif
}
int OptionsModel::rowCount(const QModelIndex &parent) const
......@@ -116,6 +144,23 @@ QVariant OptionsModel::data(const QModelIndex &index, int role) const
return getNativeName(m_settings->paperSize());
}
break;
#ifdef LC_ADDRESS
case Address:
if (m_settings->isDefaultSetting(SettingType::Address)) {
return getNativeName(m_settings->address());
}
break;
case NameStyle:
if (m_settings->isDefaultSetting(SettingType::NameStyle)) {
return getNativeName(m_settings->nameStyle());
}
break;
case PhoneNumbers:
if (m_settings->isDefaultSetting(SettingType::PhoneNumbers)) {
return getNativeName(m_settings->phoneNumbers());
}
break;
#endif
}
return {}; // implicit locale
}
......@@ -158,6 +203,29 @@ QVariant OptionsModel::data(const QModelIndex &index, int role) const
}
return example;
}
#ifdef LC_ADDRESS
case Address: {
QString example = m_addressExample;
if (m_settings->isDefaultSetting(SettingType::Address)) {
example += implicitFormatExampleMsg();
}
return example;
}
case NameStyle: {
QString example = m_nameStyleExample;
if (m_settings->isDefaultSetting(SettingType::NameStyle)) {
example += implicitFormatExampleMsg();
}
return example;
}
case PhoneNumbers: {
QString example = m_phoneNumbersExample;
if (m_settings->isDefaultSetting(SettingType::PhoneNumbers)) {
example += implicitFormatExampleMsg();
}
return example;
}
#endif
}
return {};
}
......@@ -196,9 +264,23 @@ void OptionsModel::handleLangChange()
Q_EMIT dataChanged(createIndex(4, 0), createIndex(4, 0), {Subtitle, Example});
}
if (m_settings->isDefaultSetting(SettingType::PaperSize)) {
m_paperSizeExample = Utility::measurementExample(lang);
m_paperSizeExample = Utility::paperSizeExample(lang);
Q_EMIT dataChanged(createIndex(5, 0), createIndex(5, 0), {Subtitle, Example});
}
#ifdef LC_ADDRESS
if (m_settings->isDefaultSetting(SettingType::Address)) {
m_addressExample = Utility::addressExample(lang);
Q_EMIT dataChanged(createIndex(6, 0), createIndex(6, 0), {Subtitle, Example});
}
if (m_settings->isDefaultSetting(SettingType::NameStyle)) {
m_nameStyleExample = Utility::nameStyleExample(lang);
Q_EMIT dataChanged(createIndex(7, 0), createIndex(7, 0), {Subtitle, Example});
}
if (m_settings->isDefaultSetting(SettingType::PhoneNumbers)) {
m_phoneNumbersExample = Utility::phoneNumbersExample(lang);
Q_EMIT dataChanged(createIndex(8, 0), createIndex(8, 0), {Subtitle, Example});
}
#endif
}
QString OptionsModel::implicitFormatExampleMsg() const
......
......@@ -38,8 +38,13 @@ private:
QString m_currencyExample;
QString m_measurementExample;
QString m_paperSizeExample;
#ifdef LC_ADDRESS
QString m_addressExample;
QString m_nameStyleExample;
QString m_phoneNumbersExample;
#endif
std::array<std::pair<QString, KCM_RegionAndLang::SettingType>, 6> m_staticNames; // title, page
std::vector<std::pair<QString, KCM_RegionAndLang::SettingType>> m_staticNames; // title, page
RegionAndLangSettings *m_settings;
};
......@@ -166,6 +166,12 @@ KCM.ScrollViewKCM {
return i18n("Measurements");
case SettingType.PaperSize:
return i18n("Paper Size");
case SettingType.Address:
return i18nc("Postal Address", "Address");
case SettingType.NameStyle:
return i18nc("Name Style", "Name");
case SettingType.PhoneNumbers:
return i18nc("Phone Numbers","Phone number");
}
console.warn("Invalid setting passed: ", setting);
return "Invalid"; // guard
......@@ -230,6 +236,15 @@ KCM.ScrollViewKCM {
case SettingType.PaperSize:
kcm.settings.paperSize = localeName;
break;
case SettingType.Address:
kcm.settings.address = localeName;
break;
case SettingType.NameStyle:
kcm.settings.nameStyle = localeName;
break;
case SettingType.PhoneNumbers:
kcm.settings.phoneNumbers = localeName;
break;
}
} else {
kcm.unset(setting);
......
......@@ -26,6 +26,12 @@ bool RegionAndLangSettings::isDefaultSetting(SettingType setting) const
return measurement() == defaultMeasurementValue();
case SettingType::PaperSize:
return paperSize() == defaultMeasurementValue();
case SettingType::Address:
return address() == defaultAddressValue();
case SettingType::NameStyle:
return nameStyle() == defaultNameStyleValue();
case SettingType::PhoneNumbers:
return phoneNumbers() == defaultPhoneNumbersValue();
}
return false;
......@@ -60,6 +66,12 @@ QString RegionAndLangSettings::LC_LocaleWithLang(SettingType setting) const
return measurement();
case SettingType::PaperSize:
return paperSize();
case SettingType::Address:
return address();
case SettingType::NameStyle:
return nameStyle();
case SettingType::PhoneNumbers:
return phoneNumbers();
case SettingType::Lang:
Q_UNREACHABLE();
}
......
......@@ -29,6 +29,15 @@
<entry key="LC_PAGE" name="paperSize" type="String">
<default code="true">i18n("Inherit Language")</default>
</entry>
<entry key="LC_ADDRESS" name="address" type="String">
<default code="true">i18n("Inherit Language")</default>
</entry>
<entry key="LC_NAME" name="nameStyle" type="String">
<default code="true">i18n("Inherit Language")</default>
</entry>
<entry key="LC_TELEPHONE" name="phoneNumbers" type="String">
<default code="true">i18n("Inherit Language")</default>
</entry>
<entry key="LC_CTYPE" name="ctype" type="String">
<default code="true">i18n("Inherit Language")</default>
</entry>
......
......@@ -12,6 +12,6 @@
namespace KCM_RegionAndLang
{
Q_NAMESPACE_EXPORT()
enum SettingType { Lang, Numeric, Time, Currency, Measurement, PaperSize };
enum SettingType { Lang, Numeric, Time, Currency, Measurement, PaperSize, Address, NameStyle, PhoneNumbers };
Q_ENUM_NS(SettingType)
} // namespace KCM_RegionAndLang
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment