Skip to content

Commit 56fd120

Browse files
committed
chore: use ICU for relative dateime formatting
使用 ICU 提供相对时间格式的时间展示,避免拼接字符串造成不易于本地化 的问题. Log:
1 parent 32279ca commit 56fd120

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ include(KDEGitCommitHooks)
5454

5555
find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Core Gui Concurrent Quick WaylandClient DBus LinguistTools Sql)
5656
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui)
57+
find_package(ICU 74.2 REQUIRED COMPONENTS uc i18n io)
5758
find_package(WaylandProtocols REQUIRED)
5859
find_package(PkgConfig REQUIRED)
5960

debian/control

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Build-Depends:
2121
libdtk6core-dev,
2222
libdtk6widget-dev,
2323
libdtkcommon-dev,
24+
libicu-dev,
2425
libxcb-ewmh-dev,
2526
libxcb-res0-dev,
2627
libxcb-util-dev,

panels/notification/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ target_link_libraries(ds-notification-shared PUBLIC
3535
Qt${QT_VERSION_MAJOR}::Core
3636
Qt${QT_VERSION_MAJOR}::Sql
3737
Dtk${DTK_VERSION_MAJOR}::Core
38+
ICU::uc
39+
ICU::i18n
40+
ICU::io
3841
)
3942

4043
install(TARGETS ds-notification-shared DESTINATION "${LIB_INSTALL_DIR}")

panels/notification/center/notifyitem.cpp

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <QDateTime>
88
#include <QLoggingCategory>
99

10+
#include <unicode/reldatefmt.h> // For RelativeDateTimeFormatter
11+
#include <unicode/smpdtfmt.h> // For SimpleDateFormat
12+
1013
#include "notifyaccessor.h"
1114

1215
namespace notification {
@@ -62,12 +65,48 @@ QString AppNotifyItem::time() const
6265
return m_time;
6366
}
6467

68+
namespace
69+
{
70+
QString toQString(const icu::UnicodeString &icuString)
71+
{
72+
// Get a pointer to the internal UTF-16 buffer of the icu::UnicodeString.
73+
// The buffer is not necessarily null-terminated, so we also need the length.
74+
const UChar *ucharData = icuString.getBuffer();
75+
int32_t length = icuString.length();
76+
77+
// QString has a constructor that takes a const QChar* and a length.
78+
// UChar is typically a 16-bit unsigned integer, which is compatible with QChar.
79+
// Static_cast is used here for explicit type conversion, though often
80+
// UChar and QChar are typedefs to the same underlying type (e.g., unsigned short).
81+
return QString(reinterpret_cast<const QChar *>(ucharData), length);
82+
}
83+
84+
icu::UnicodeString fromQString(const QString &qstr)
85+
{
86+
return icu::UnicodeString(qstr.utf16(), qstr.length());
87+
}
88+
} // anonymous namespace
89+
6590
void AppNotifyItem::updateTime()
6691
{
6792
QDateTime time = QDateTime::fromMSecsSinceEpoch(m_entity.cTime());
6893
if (!time.isValid())
6994
return;
7095

96+
using namespace icu;
97+
static std::unique_ptr<RelativeDateTimeFormatter> formatter;
98+
static UErrorCode cachedStatus = U_ZERO_ERROR;
99+
if (!formatter) {
100+
cachedStatus = U_ZERO_ERROR;
101+
formatter = std::make_unique<RelativeDateTimeFormatter>(icu::Locale::getDefault(),
102+
nullptr, // Use default NumberFormat
103+
UDAT_STYLE_LONG,
104+
UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,
105+
cachedStatus);
106+
}
107+
UErrorCode status = U_ZERO_ERROR; // For any per-call ICU operations
108+
UnicodeString result;
109+
71110
QString ret;
72111
QDateTime currentTime = QDateTime::currentDateTime();
73112
auto elapsedDay = time.daysTo(currentTime);
@@ -77,21 +116,27 @@ void AppNotifyItem::updateTime()
77116
if (minute <= 0) {
78117
ret = tr("Just now");
79118
} else if (minute > 0 && minute < 60) {
80-
ret = tr("%1 minutes ago").arg(minute);
119+
formatter->format(minute, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, result, status);
120+
ret = toQString(result);
81121
} else {
82122
const auto hour = minute / 60;
83-
if (hour == 1) {
84-
ret = tr("1 hour ago");
85-
} else {
86-
ret = tr("%1 hours ago").arg(hour);
87-
}
123+
formatter->format(hour, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, result, status);
124+
ret = toQString(result);
88125
}
89126
} else if (elapsedDay >= 1 && elapsedDay < 2) {
90-
ret = tr("Yesterday ") + " " + time.toString("hh:mm");
127+
formatter->format(1, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, result, status);
128+
UnicodeString combinedString;
129+
UErrorCode timeStatus = U_ZERO_ERROR;
130+
SimpleDateFormat timeFormatter("HH:mm", icu::Locale::getDefault(), timeStatus);
131+
UnicodeString timeString;
132+
UDate udate = static_cast<UDate>(m_entity.cTime());
133+
timeFormatter.format(udate, timeString, timeStatus);
134+
formatter->combineDateAndTime(result, timeString, combinedString, status);
135+
ret = toQString(combinedString);
91136
} else if (elapsedDay >= 2 && elapsedDay < 7) {
92137
ret = time.toString("ddd hh:mm");
93138
} else {
94-
ret = time.toString("yyyy/MM/dd");
139+
ret = time.toString(QLocale::system().dateFormat(QLocale::ShortFormat));
95140
}
96141

97142
m_time = ret;

0 commit comments

Comments
 (0)