diff --git a/autotests/kformattest.h b/autotests/kformattest.h --- a/autotests/kformattest.h +++ b/autotests/kformattest.h @@ -38,6 +38,7 @@ void formatSpelloutDuration(); void formatRelativeDate(); void formatValue(); + void formatList(); }; #endif // KFORMATTEST_H diff --git a/autotests/kformattest.cpp b/autotests/kformattest.cpp --- a/autotests/kformattest.cpp +++ b/autotests/kformattest.cpp @@ -389,4 +389,35 @@ QLocale::c().toString(testDateTime, QLocale::LongFormat)); } +void KFormatTest::formatList() +{ + KFormat format(QLocale::c()); + + QStringList testList; + QCOMPARE(format.formatList(testList), QStringLiteral("")); + QCOMPARE(format.formatList(testList, KFormat::AndListType), QStringLiteral("")); + QCOMPARE(format.formatList(testList, KFormat::OrListType), QStringLiteral("")); + QCOMPARE(format.formatList(testList, KFormat::UnitListType), QStringLiteral("")); + testList << "A"; + QCOMPARE(format.formatList(testList), QStringLiteral("A")); + QCOMPARE(format.formatList(testList, KFormat::AndListType), QStringLiteral("A")); + QCOMPARE(format.formatList(testList, KFormat::OrListType), QStringLiteral("A")); + QCOMPARE(format.formatList(testList, KFormat::UnitListType), QStringLiteral("A")); + testList << "B"; + QCOMPARE(format.formatList(testList), QStringLiteral("A and B")); + QCOMPARE(format.formatList(testList, KFormat::AndListType), QStringLiteral("A and B")); + QCOMPARE(format.formatList(testList, KFormat::OrListType), QStringLiteral("A or B")); + QCOMPARE(format.formatList(testList, KFormat::UnitListType), QStringLiteral("A and B")); + testList << "C"; + QCOMPARE(format.formatList(testList), QStringLiteral("A, B and C")); + QCOMPARE(format.formatList(testList, KFormat::AndListType), QStringLiteral("A, B and C")); + QCOMPARE(format.formatList(testList, KFormat::OrListType), QStringLiteral("A, B or C")); + QCOMPARE(format.formatList(testList, KFormat::UnitListType), QStringLiteral("A, B and C")); + testList << "D"; + QCOMPARE(format.formatList(testList), QStringLiteral("A, B, C and D")); + QCOMPARE(format.formatList(testList, KFormat::AndListType), QStringLiteral("A, B, C and D")); + QCOMPARE(format.formatList(testList, KFormat::OrListType), QStringLiteral("A, B, C or D")); + QCOMPARE(format.formatList(testList, KFormat::UnitListType), QStringLiteral("A, B, C and D")); +} + QTEST_MAIN(KFormatTest) diff --git a/src/lib/util/kformat.h b/src/lib/util/kformat.h --- a/src/lib/util/kformat.h +++ b/src/lib/util/kformat.h @@ -201,6 +201,18 @@ Q_DECLARE_FLAGS(DurationFormatOptions, DurationFormatOption) Q_FLAG(DurationFormatOption) + /** + * Format types used for formatList() function. + * + * @see formatList + * @since 5.50 + */ + enum ListFormatterType { + AndListType, ///< Berna, Matt and Susan + OrListType, ///< Berna, Matt or Susan + UnitListType ///< 1 hour, 45 minutes and 30 seconds + }; + /** * Constructs a KFormat. * @@ -400,6 +412,21 @@ int precision = 1, KFormat::UnitPrefix prefix = KFormat::UnitPrefix::AutoAdjust) const; + /** + * Returns a string formatted to a relative displaying list. + * + * e.g. given formatList(stringListOfNames) returns "Berna, Matt and Susan". + * + * @param stringList list of strings to be formatted + * @param type type of displaying list to be formatted to. + * @return converted list as a displaying list e.g. "Berna and Matt" "Matt or Berna" + * "Berna, Matt, Susan and Santiago" "1 meters and 5 centimeters". + * @see ListFormatterType + * @since 5.50 + */ + QString formatList(const QStringList &stringList, + KFormat::ListFormatterType type = KFormat::ListFormatterType::AndListType) const; + private: QSharedDataPointer d; }; diff --git a/src/lib/util/kformat.cpp b/src/lib/util/kformat.cpp --- a/src/lib/util/kformat.cpp +++ b/src/lib/util/kformat.cpp @@ -98,4 +98,10 @@ return d->formatRelativeDateTime(dateTime, format); } +QString KFormat::formatList(const QStringList &stringList, + KFormat::ListFormatterType type) const +{ + return d->formatList(stringList, type); +} + #include "moc_kformat.cpp" diff --git a/src/lib/util/kformatprivate.cpp b/src/lib/util/kformatprivate.cpp --- a/src/lib/util/kformatprivate.cpp +++ b/src/lib/util/kformatprivate.cpp @@ -575,3 +575,67 @@ return tr("%1, %2").arg(formatRelativeDate(dateTime.date(), format)) .arg(m_locale.toString(dateTime.time(), format)); } + +QString KFormatPrivate::formatList(const QStringList &stringList, KFormat::ListFormatterType type) const +{ + if (stringList.isEmpty()) { + return QString(); + } else if (stringList.size() == 1) { + return stringList.at(0); + } + + if (type <= KFormat::AndListType || type > KFormat::UnitListType) { + type = KFormat::AndListType; + } + + typedef struct + { + KFormat::ListFormatterType type; + const char* format; + } ListPositionFormat; + + static const ListPositionFormat listStartPosition[] = { + /*: @item:intext The following "list" strings are used to join different strings/names into a + readable human string. E.g. joining: 1."Berna" 2."Matt" 3."Susan" with the "And list type" results + in English: "Berna, Matt and Susan", and in "Or list type": "Berna, Matt or Susan". "Unit list type" + is used to join units like measurement, time, quantity, size, etc. "1 meters and 5 centimeters". + Taking "Berna"+"Matt"+"Susan"+"Santiago" as an example: + "Berna"+"Matt" -> "Berna, Matt" (start form, %1: Berna %2: Matt) + "Berna, Matt"+"Susan" -> "Berna, Matt, Susan" (middle form, %1: result of start form %2: Susan) + "Berna, Matt, Susan"+"Santiago" -> "Berna, matt, Susan and Santiago" + (end form, %1: result of middle form %2: Santiago) */ + { KFormat::ListFormatterType::AndListType, QT_TRANSLATE_NOOP("And list type, start form", "%1, %2") }, + { KFormat::ListFormatterType::OrListType, QT_TRANSLATE_NOOP("Or list type, start form", "%1, %2") }, + { KFormat::ListFormatterType::UnitListType, QT_TRANSLATE_NOOP("Unit list type, start form", "%1, %2") }, + }; + static const ListPositionFormat listMiddlePosition[] = { + { KFormat::ListFormatterType::AndListType, QT_TRANSLATE_NOOP("And list type, middle form", "%1, %2") }, + { KFormat::ListFormatterType::OrListType, QT_TRANSLATE_NOOP("Or list type, middle form", "%1, %2") }, + { KFormat::ListFormatterType::UnitListType, QT_TRANSLATE_NOOP("Unit list type, middle form", "%1, %2") }, + }; + static const ListPositionFormat listEndPosition[] = { + { KFormat::ListFormatterType::AndListType, QT_TRANSLATE_NOOP("And list type, end form", "%1 and %2") }, + { KFormat::ListFormatterType::OrListType, QT_TRANSLATE_NOOP("Or list type, end form", "%1 or %2") }, + { KFormat::ListFormatterType::UnitListType, QT_TRANSLATE_NOOP("Unit list type, end form", "%1 and %2") }, + }; + + auto startString = std::find_if(std::begin(listStartPosition), std::end(listStartPosition), + [type](const ListPositionFormat& e) { return e.type == type; }); + auto middleString = std::find_if(std::begin(listMiddlePosition), std::end(listMiddlePosition), + [type](const ListPositionFormat& e) { return e.type == type; }); + auto lastString = std::find_if(std::begin(listEndPosition), std::end(listEndPosition), + [type](const ListPositionFormat& e) { return e.type == type; }); + + if (stringList.size() == 2) { + return tr(lastString->format).arg(stringList.at(0), stringList.at(1)); + } + + QString result = tr(startString->format).arg(stringList.at(0), stringList.at(1)); + + for (int i = 2; i < stringList.size()-1; ++i) { + result = tr(middleString->format).arg(result, stringList.at(i)); + } + + result = tr(lastString->format).arg(result, stringList.at(stringList.size()-1)); + return result; +} diff --git a/src/lib/util/kformatprivate_p.h b/src/lib/util/kformatprivate_p.h --- a/src/lib/util/kformatprivate_p.h +++ b/src/lib/util/kformatprivate_p.h @@ -64,6 +64,8 @@ QString formatRelativeDateTime(const QDateTime &dateTime, QLocale::FormatType format) const; + QString formatList(const QStringList &stringList, + KFormat::ListFormatterType type) const; private: QLocale m_locale;