[frameworks/ktexttemplate] /: Make date/time filter locale-aware
Volker Krause <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 3eecf70aab9096a29c2ee52a8cdf58bd56dd0176 by Volker Krause.
Committed on 16/08/2026 at 10:28.
Pushed by vkrause into branch 'master'.
Make date/time filter locale-aware
That's in line with what the Django template syntax does (which this is
all modeled after).
There's at least three different alternative solutions for locale-aware
date/time formatting across the PIM repos, which would become obsolete
with fixing this properly here.
M +26 -2 autotests/testfilters.cpp
M +29 -6 src/defaultfilters/datetime.cpp
https://invent.kde.org/frameworks/ktexttemplate/-/commit/3eecf70aab9096a29c2ee52a8cdf58bd56dd0176
diff --git a/autotests/testfilters.cpp b/autotests/testfilters.cpp
index 34dd67f5..7bbfa39d 100644
--- a/autotests/testfilters.cpp
+++ b/autotests/testfilters.cpp
@@ -18,9 +18,12 @@
#include "context.h"
#include "engine.h"
#include "ktexttemplate_paths.h"
+#include "qtlocalizer.h"
#include "template.h"
#include <util.h>
+using namespace Qt::Literals;
+
using Dict = QHash<QString, QVariant>;
Q_DECLARE_METATYPE(KTextTemplate::Error)
@@ -90,6 +93,8 @@ void TestFilters::initTestCase()
QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH),
appDirPath + QStringLiteral("/tests/") // For testtags.qs
});
+
+ m_engine->addDefaultLibrary(u"ktexttemplate_i18ntags"_s); // for with_locale for testing date/time filters
}
void TestFilters::cleanupTestCase()
@@ -107,6 +112,7 @@ void TestFilters::doTest()
auto t = m_engine->newTemplate(input, QLatin1String(QTest::currentDataTag()));
Context context(dict);
+ context.setLocalizer(QSharedPointer<AbstractLocalizer>(new QtLocalizer(QLocale(QLocale::English, QLocale::UnitedStates))));
auto result = t->render(&context);
@@ -332,11 +338,29 @@ void TestFilters::testDateBasedFilters_data()
dict.insert(QStringLiteral("d"), d);
QTest::newRow("date01") << "{{ d|date:\"MM\" }}" << dict << QStringLiteral("01") << NoError;
- QTest::newRow("date02") << QStringLiteral("{{ d|date }}") << dict << d.toString(QStringLiteral("MMM. d, yyyy")) << NoError;
+ QTest::newRow("date02") << QStringLiteral("{{ d|date }}") << dict << QLocale(u"en_US"_s).toString(d.date(), QLocale::LongFormat) << NoError;
+ QTest::newRow("date03") << "{{ d|date:\"MMMM\" }}" << dict << u"January"_s << NoError;
+ QTest::newRow("date04") << "{{ d|date:\"SHORT_DATE_FORMAT\" }}" << dict << u"1/1/08"_s << NoError;
+ QTest::newRow("date05") << "{{ d|date:\"SHORT_DATETIME_FORMAT\" }}" << dict << u"1/1/08 12:00\u202fAM"_s << NoError;
dict.clear();
dict.insert(QStringLiteral("d"), QStringLiteral("fail_string"));
- QTest::newRow("date03") << "{{ d|date:\"MM\" }}" << dict << QString() << NoError;
+ QTest::newRow("date06") << "{{ d|date:\"MM\" }}" << dict << QString() << NoError;
+
+ dict.clear();
+ dict.insert(u"d"_s, QDate{2026, 7, 14});
+ QTest::newRow("date07") << u"{% with_locale \"de_DE\" %}{{ d|date:\"SHORT_DATE_FORMAT\" }}{% endwith_locale %}"_s << dict << u"14.07.26"_s << NoError;
+ QTest::newRow("date08") << u"{% with_locale \"de_DE\" %}{{ d|date }}{% endwith_locale %}"_s << dict << u"Dienstag, 14. Juli 2026"_s << NoError;
+ QTest::newRow("date09") << u"{% with_locale \"de_DE\" %}{{ d|date:\"DATE_FORMAT\" }}{% endwith_locale %}"_s << dict << u"Dienstag, 14. Juli 2026"_s
+ << NoError;
+ QTest::newRow("date10") << u"{% with_locale \"de_DE\" %}{{ d|date:\"MMMM\" }}{% endwith_locale %}"_s << dict << u"Juli"_s << NoError;
+
+ dict.clear();
+ dict.insert(u"t"_s, QTime{13, 24, 56});
+ QTest::newRow("time01") << u"{{ t|time }}"_s << dict << u"1:24\u202fPM"_s << NoError;
+ QTest::newRow("time02") << u"{{ t|time:\"H'h'mm\" }}"_s << dict << u"13h24"_s << NoError;
+ QTest::newRow("time03") << u"{% with_locale \"de_DE\" %}{{ t|time }}{% endwith_locale %}"_s << dict << u"13:24"_s << NoError;
+ QTest::newRow("time04") << u"{% with_locale \"de_DE\" %}{{ t|time:\"TIME_FORMAT\" }}{% endwith_locale %}"_s << dict << u"13:24"_s << NoError;
}
void TestFilters::testStringFilters_data()
diff --git a/src/defaultfilters/datetime.cpp b/src/defaultfilters/datetime.cpp
index 6cb590fd..ea144bad 100644
--- a/src/defaultfilters/datetime.cpp
+++ b/src/defaultfilters/datetime.cpp
@@ -13,6 +13,8 @@
#include <QDateTime>
+using namespace Qt::Literals;
+
QVariant timeSince(const QDateTime &early, const QDateTime &late)
{
Q_ASSERT(early.isValid());
@@ -81,12 +83,26 @@ QVariant DateFilter::doFilter(const QVariant &input, const QVariant &argument, b
d = QDateTime::fromString(getSafeString(input), Qt::ISODateWithMs);
}
- auto argString = getSafeString(argument);
+ const QString argString = getSafeString(argument);
- if (!argString.get().isEmpty())
- return d.toString(argString);
+ // locale-specific format constants as defined in https://docs.djangoproject.com/en/6.0/ref/templates/builtins/#date
+ if (argString.isEmpty() || argString == "DATE_FORMAT"_L1) {
+ return context()->localizer()->localizeDate(d.date(), QLocale::LongFormat);
+ }
+ if (argString == "SHORT_DATE_FORMAT"_L1) {
+ return context()->localizer()->localizeDate(d.date(), QLocale::ShortFormat);
+ }
+ if (argString == "DATETIME_FORMAT"_L1) {
+ return context()->localizer()->localizeDateTime(d, QLocale::LongFormat);
+ }
+ if (argString == "SHORT_DATETIME_FORMAT"_L1) {
+ return context()->localizer()->localizeDateTime(d, QLocale::ShortFormat);
+ }
- return d.toString(QStringLiteral("MMM. d, yyyy"));
+ // custom format
+ Q_ASSERT(!argString.isEmpty());
+ QLocale l(context()->localizer()->currentLocale());
+ return l.toString(d, argString);
}
QVariant TimeFilter::doFilter(const QVariant &input, const QVariant &argument, bool autoescape) const
@@ -103,8 +119,15 @@ QVariant TimeFilter::doFilter(const QVariant &input, const QVariant &argument, b
d = QDateTime::fromString(getSafeString(input), Qt::ISODateWithMs);
}
- auto argString = getSafeString(argument);
- return d.toString(argString);
+ const QString argString = getSafeString(argument);
+
+ if (argString.isEmpty() || argString == "TIME_FORMAT"_L1) {
+ return context()->localizer()->localizeTime(d.time(), QLocale::ShortFormat);
+ }
+
+ Q_ASSERT(!argString.isEmpty());
+ QLocale l(context()->localizer()->currentLocale());
+ return l.toString(d.time(), argString);
}
QVariant TimeSinceFilter::doFilter(const QVariant &input, const QVariant &argument, bool autoescape) const