diff --git a/src/modules/locale/CMakeLists.txt b/src/modules/locale/CMakeLists.txt index bad6042a6..b631f77f7 100644 --- a/src/modules/locale/CMakeLists.txt +++ b/src/modules/locale/CMakeLists.txt @@ -22,6 +22,7 @@ calamares_add_plugin(locale Config.cpp LCLocaleDialog.cpp LocaleConfiguration.cpp + LocaleNames.cpp LocalePage.cpp LocaleViewStep.cpp SetTimezoneJob.cpp @@ -39,7 +40,7 @@ calamares_add_plugin(locale calamares_add_test( localetest - SOURCES Tests.cpp Config.cpp LocaleConfiguration.cpp SetTimezoneJob.cpp timezonewidget/TimeZoneImage.cpp + SOURCES Tests.cpp Config.cpp LocaleConfiguration.cpp LocaleNames.cpp SetTimezoneJob.cpp timezonewidget/TimeZoneImage.cpp DEFINITIONS SOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}/images" DEBUG_TIMEZONES=1 LIBRARIES Qt5::Gui ) diff --git a/src/modules/locale/LocaleNames.cpp b/src/modules/locale/LocaleNames.cpp new file mode 100644 index 000000000..148d21472 --- /dev/null +++ b/src/modules/locale/LocaleNames.cpp @@ -0,0 +1,72 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "LocaleNames.h" + +#include "utils/Logger.h" + +#include + +LocaleNameParts +LocaleNameParts::fromName( const QString& name ) +{ + auto requireAndRemoveLeadingChar = []( QChar c, QString s ) + { + if ( s.startsWith( c ) ) + { + return s.remove( 0, 1 ); + } + else + { + return QString(); + } + }; + + auto parts = QRegularExpression( "^([a-zA-Z]+)(_[a-zA-Z]+)?(\\.[-a-zA-Z0-9]+)?(@[a-zA-Z]+)?" ).match( name ); + const QString calamaresLanguage = parts.captured( 1 ); + const QString calamaresCountry = requireAndRemoveLeadingChar( '_', parts.captured( 2 ) ); + const QString calamaresEncoding = requireAndRemoveLeadingChar( '.', parts.captured( 3 ) ); + const QString calamaresRegion = requireAndRemoveLeadingChar( '@', parts.captured( 4 ) ); + + if ( calamaresLanguage.isEmpty() ) + { + return LocaleNameParts {}; + } + else + { + return LocaleNameParts { calamaresLanguage, calamaresCountry, calamaresRegion, calamaresEncoding }; + } +} + +QString +LocaleNameParts::name() const +{ + // We don't want QStringView to a temporary; force conversion + auto insertLeadingChar = []( QChar c, QString s ) -> QString + { + if ( s.isEmpty() ) + { + return QString(); + } + else + { + return c + s; + } + }; + + if ( !isValid() ) + { + return QString(); + } + else + { + return language + insertLeadingChar( '_', country ) + insertLeadingChar( '.', encoding ) + + insertLeadingChar( '@', region ); + } +} diff --git a/src/modules/locale/LocaleNames.h b/src/modules/locale/LocaleNames.h new file mode 100644 index 000000000..976ee20b3 --- /dev/null +++ b/src/modules/locale/LocaleNames.h @@ -0,0 +1,35 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LOCALENAMES_H +#define LOCALENAMES_H + +#include + +/** @brief parts of a locale-name (e.g. "ar_LY.UTF-8", split apart) + * + * These are created from lines in `/usr/share/i18n/SUPPORTED`, + * which lists all the locales supported by the system (there + * are also other sources of the same). + * + */ +struct LocaleNameParts +{ + QString language; // e.g. "ar" + QString country; // e.g. "LY" (may be empty) + QString region; // e.g. "@valencia" (may be empty) + QString encoding; // e.g. "UTF-8" (may be empty) + + bool isValid() const { return !language.isEmpty(); } + QString name() const; + + static LocaleNameParts fromName( const QString& name ); +}; + +#endif diff --git a/src/modules/locale/Tests.cpp b/src/modules/locale/Tests.cpp index 1a6887dc1..85ec34026 100644 --- a/src/modules/locale/Tests.cpp +++ b/src/modules/locale/Tests.cpp @@ -9,6 +9,7 @@ #include "Config.h" #include "LocaleConfiguration.h" +#include "LocaleNames.h" #include "timezonewidget/TimeZoneImage.h" #include "Settings.h" @@ -49,8 +50,11 @@ private Q_SLOTS: void testLanguageDetection(); void testLanguageDetectionValencia(); - // Check realistic language mapping for issue 2008 + // Check that the test-data is available and ok void testKDENeonLanguageData(); + void testLocaleNameParts(); + + // Check realistic language mapping for issue 2008 void testLanguageMappingNeon_data(); void testLanguageMappingNeon(); void testLanguageMappingFreeBSD_data(); @@ -392,6 +396,10 @@ splitTestFileIntoLines( const QString& filename ) void LocaleTests::testKDENeonLanguageData() { + if ( !m_KDEneonLocales.isEmpty() ) + { + return; + } const QStringList neonLocales = splitTestFileIntoLines( QStringLiteral( "locale-data-neon" ) ); cDebug() << "Loaded KDE neon locales test data" << neonLocales.front() << "to" << neonLocales.back(); QCOMPARE( neonLocales.length(), 318 ); // wc -l tells me 318 lines @@ -412,7 +420,7 @@ LocaleTests::MappingData() // Tired of writing QString or QStringLiteral all the time. auto l = []( const char* p ) { return QString::fromUtf8( p ); }; - auto u = [](){ return QString(); }; + auto u = []() { return QString(); }; // The KDEneon columns include the .UTF-8 from the source data // The FreeBSD columns may have u() to indicate "same as KDEneon", @@ -442,12 +450,14 @@ LocaleTests::MappingData() } -void LocaleTests::testLanguageMappingNeon_data() +void +LocaleTests::testLanguageMappingNeon_data() { MappingData(); } -void LocaleTests::testLanguageMappingFreeBSD_data() +void +LocaleTests::testLanguageMappingFreeBSD_data() { MappingData(); } @@ -455,6 +465,7 @@ void LocaleTests::testLanguageMappingFreeBSD_data() void LocaleTests::testLanguageMappingNeon() { + testKDENeonLanguageData(); QVERIFY( !m_KDEneonLocales.isEmpty() ); QFETCH( QString, selectedLanguage ); @@ -471,6 +482,7 @@ LocaleTests::testLanguageMappingNeon() void LocaleTests::testLanguageMappingFreeBSD() { + testKDENeonLanguageData(); QVERIFY( !m_FreeBSDLocales.isEmpty() ); QFETCH( QString, selectedLanguage ); @@ -485,6 +497,45 @@ LocaleTests::testLanguageMappingFreeBSD() QCOMPARE( bsd.language(), expected ); } +void +LocaleTests::testLocaleNameParts() +{ + testKDENeonLanguageData(); + QVERIFY( !m_FreeBSDLocales.isEmpty() ); + QVERIFY( !m_KDEneonLocales.isEmpty() ); + + // Example constant locales + { + auto c_parts = LocaleNameParts::fromName( QStringLiteral( "nl_NL.UTF-8" ) ); + QCOMPARE( c_parts.language, QStringLiteral( "nl" ) ); + QCOMPARE( c_parts.country, QStringLiteral( "NL" ) ); + QCOMPARE( c_parts.encoding, QStringLiteral( "UTF-8" ) ); + QVERIFY( c_parts.region.isEmpty() ); + } + { + auto c_parts = LocaleNameParts::fromName( QStringLiteral( "C.UTF-8" ) ); + QCOMPARE( c_parts.language, QStringLiteral( "C" ) ); + QVERIFY( c_parts.country.isEmpty() ); + QCOMPARE( c_parts.encoding, QStringLiteral( "UTF-8" ) ); + QVERIFY( c_parts.region.isEmpty() ); + } + + // Check all the loaded test locales + for ( const auto& s : m_FreeBSDLocales ) + { + auto parts = LocaleNameParts::fromName( s ); + QVERIFY( parts.isValid() ); + QCOMPARE( parts.name(), s ); + } + + for ( const auto& s : m_KDEneonLocales ) + { + auto parts = LocaleNameParts::fromName( s ); + QVERIFY( parts.isValid() ); + QCOMPARE( parts.name(), s ); + } +} + #include "utils/moc-warnings.h"