mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 20:35:36 +00:00
fix: add i18n options for past tense, future tense time (#3245)
This commit is contained in:
@@ -614,6 +614,7 @@ void gtr_text_buffer_set_text(Glib::RefPtr<Gtk::TextBuffer> const& b, Glib::ustr
|
||||
|
||||
void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
||||
{
|
||||
auto const now = time(nullptr);
|
||||
Glib::ustring str;
|
||||
Glib::ustring const mixed = _("Mixed");
|
||||
Glib::ustring const no_torrent = _("No Torrents Selected");
|
||||
@@ -805,7 +806,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
||||
}
|
||||
else
|
||||
{
|
||||
str = tr_strltime(time(nullptr) - baseline);
|
||||
str = tr_format_time_relative(now, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +835,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
||||
}
|
||||
else
|
||||
{
|
||||
str = tr_strltime(baseline);
|
||||
str = tr_format_time_relative(now, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,19 +1070,13 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
||||
{
|
||||
str = _("Never");
|
||||
}
|
||||
else if ((now - latest) < 5)
|
||||
{
|
||||
str = _("Active now");
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t const period = time(nullptr) - latest;
|
||||
|
||||
if (period < 5)
|
||||
{
|
||||
str = _("Active now");
|
||||
}
|
||||
else
|
||||
{
|
||||
// e.g. 5 minutes ago
|
||||
str = fmt::format(_("{time_span} ago"), fmt::arg("time_span", tr_strltime(period)));
|
||||
}
|
||||
str = tr_format_time_relative(now, latest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1911,56 +1906,45 @@ auto constexpr SuccessMarkupEnd = "</span>"sv;
|
||||
|
||||
std::array<std::string_view, 3> const text_dir_mark = { ""sv, "\u200E"sv, "\u200F"sv };
|
||||
|
||||
// if it's been longer than a minute, don't bother showing the seconds
|
||||
Glib::ustring tr_strltime_rounded(time_t t)
|
||||
{
|
||||
if (t > 60)
|
||||
{
|
||||
t -= (t % 60);
|
||||
}
|
||||
|
||||
return tr_strltime(t);
|
||||
}
|
||||
|
||||
void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
|
||||
{
|
||||
if (tracker.hasAnnounced && tracker.announceState != TR_TRACKER_INACTIVE)
|
||||
{
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
auto const timebuf = tr_strltime_rounded(now - tracker.lastAnnounceTime);
|
||||
auto const time_span_ago = tr_format_time_relative(now, tracker.lastAnnounceTime);
|
||||
|
||||
if (tracker.lastAnnounceSucceeded)
|
||||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the peer text
|
||||
ngettext(
|
||||
"Got a list of {markup_begin}{peer_count} peer{markup_end} {time_span} ago",
|
||||
"Got a list of {markup_begin}{peer_count} peers{markup_end} {time_span} ago",
|
||||
"Got a list of {markup_begin}{peer_count} peer{markup_end} {time_span_ago}",
|
||||
"Got a list of {markup_begin}{peer_count} peers{markup_end} {time_span_ago}",
|
||||
tracker.lastAnnouncePeerCount),
|
||||
fmt::arg("markup_begin", SuccessMarkupBegin),
|
||||
fmt::arg("peer_count", tracker.lastAnnouncePeerCount),
|
||||
fmt::arg("markup_end", SuccessMarkupEnd),
|
||||
fmt::arg("time_span", timebuf));
|
||||
fmt::arg("time_span_ago", time_span_ago));
|
||||
}
|
||||
else if (tracker.lastAnnounceTimedOut)
|
||||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the time_span
|
||||
_("Peer list request {markup_begin}timed out {time_span} ago{markup_end}; will retry"),
|
||||
_("Peer list request {markup_begin}timed out {time_span_ago}{markup_end}; will retry"),
|
||||
fmt::arg("markup_begin", TimeoutMarkupBegin),
|
||||
fmt::arg("time_span", timebuf),
|
||||
fmt::arg("time_span_ago", time_span_ago),
|
||||
fmt::arg("markup_end", TimeoutMarkupEnd));
|
||||
}
|
||||
else
|
||||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the error
|
||||
_("Got an error '{markup_begin}{error}{markup_end}' {time_span} ago"),
|
||||
_("Got an error '{markup_begin}{error}{markup_end}' {time_span_ago}"),
|
||||
fmt::arg("markup_begin", ErrMarkupBegin),
|
||||
fmt::arg("error", Glib::Markup::escape_text(tracker.lastAnnounceResult)),
|
||||
fmt::arg("markup_end", ErrMarkupEnd),
|
||||
fmt::arg("time_span", timebuf));
|
||||
fmt::arg("time_span_ago", time_span_ago));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1976,8 +1960,8 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
gstr << fmt::format(
|
||||
_("Asking for more peers in {time_span}"),
|
||||
fmt::arg("time_span", tr_strltime_rounded(tracker.nextAnnounceTime - now)));
|
||||
_("Asking for more peers {time_span_from_now}"),
|
||||
fmt::arg("time_span_from_now", tr_format_time_relative(now, tracker.nextAnnounceTime)));
|
||||
break;
|
||||
|
||||
case TR_TRACKER_QUEUED:
|
||||
@@ -1990,10 +1974,10 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the time_span
|
||||
_("Asking for more peers now… {markup_begin}{time_span}{markup_end}"),
|
||||
// {markup_begin} and {markup_end} should surround time_span_ago
|
||||
_("Asked for more peers {markup_begin}{time_span_ago}{markup_end}"),
|
||||
fmt::arg("markup_begin", "<small>"),
|
||||
fmt::arg("time_span", tr_strltime_rounded(now - tracker.lastAnnounceStartTime)),
|
||||
fmt::arg("time_span_ago", tr_format_time_relative(now, tracker.lastAnnounceStartTime)),
|
||||
fmt::arg("markup_end", "</small>"));
|
||||
break;
|
||||
|
||||
@@ -2008,18 +1992,18 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
||||
{
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
auto const timebuf = tr_strltime_rounded(now - tracker.lastScrapeTime);
|
||||
auto const time_span_ago = tr_format_time_relative(now, tracker.lastScrapeTime);
|
||||
|
||||
if (tracker.lastScrapeSucceeded)
|
||||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the seeder/leecher text
|
||||
_("Tracker had {markup_begin}{seeder_count} {seeder_or_seeders} and {leecher_count} {leecher_or_leechers}{markup_end} {time_span} ago"),
|
||||
_("Tracker had {markup_begin}{seeder_count} {seeder_or_seeders} and {leecher_count} {leecher_or_leechers}{markup_end} {time_span_ago}"),
|
||||
fmt::arg("seeder_count", tracker.seederCount),
|
||||
fmt::arg("seeder_or_seeders", ngettext("seeder", "seeders", tracker.seederCount)),
|
||||
fmt::arg("leecher_count", tracker.leecherCount),
|
||||
fmt::arg("leecher_or_leechers", ngettext("leecher", "leechers", tracker.leecherCount)),
|
||||
fmt::arg("time_span", timebuf),
|
||||
fmt::arg("time_span_ago", time_span_ago),
|
||||
fmt::arg("markup_begin", SuccessMarkupBegin),
|
||||
fmt::arg("markup_end", SuccessMarkupEnd));
|
||||
}
|
||||
@@ -2027,9 +2011,9 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
||||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the error text
|
||||
_("Got a scrape error '{markup_begin}{error}{markup_end}' {time_span} ago"),
|
||||
_("Got a scrape error '{markup_begin}{error}{markup_end}' {time_span_ago}"),
|
||||
fmt::arg("error", Glib::Markup::escape_text(tracker.lastScrapeResult)),
|
||||
fmt::arg("time_span", timebuf),
|
||||
fmt::arg("time_span_ago", time_span_ago),
|
||||
fmt::arg("markup_begin", ErrMarkupBegin),
|
||||
fmt::arg("markup_end", ErrMarkupEnd));
|
||||
}
|
||||
@@ -2044,8 +2028,8 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
gstr << fmt::format(
|
||||
_("Asking for peer counts in {time_span}"),
|
||||
fmt::arg("time_span", tr_strltime_rounded(tracker.nextScrapeTime - now)));
|
||||
_("Asking for peer counts in {time_span_from_now}"),
|
||||
fmt::arg("time_span_from_now", tr_format_time_relative(now, tracker.nextScrapeTime)));
|
||||
break;
|
||||
|
||||
case TR_TRACKER_QUEUED:
|
||||
@@ -2058,9 +2042,9 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
||||
gstr << '\n';
|
||||
gstr << text_dir_mark[direction];
|
||||
gstr << fmt::format(
|
||||
_("Asking for peer counts now… {markup_begin}{time_span}{markup_end}"),
|
||||
_("Asked for peer counts {markup_begin}{time_span_ago}{markup_end}"),
|
||||
fmt::arg("markup_begin", "<small>"),
|
||||
fmt::arg("time_span", tr_strltime_rounded(now - tracker.lastScrapeStartTime)),
|
||||
fmt::arg("time_span_ago", tr_format_time_relative(now, tracker.lastScrapeStartTime)),
|
||||
fmt::arg("markup_end", "</small>"));
|
||||
break;
|
||||
|
||||
|
||||
@@ -79,13 +79,13 @@ bool StatsDialog::Impl::updateStats()
|
||||
|
||||
setLabel(one_up_lb_, tr_strlsize(one.uploadedBytes));
|
||||
setLabel(one_down_lb_, tr_strlsize(one.downloadedBytes));
|
||||
setLabel(one_time_lb_, tr_strltime(one.secondsActive));
|
||||
setLabel(one_time_lb_, tr_format_time(one.secondsActive));
|
||||
setLabelFromRatio(one_ratio_lb_, one.ratio);
|
||||
|
||||
setLabel(all_sessions_lb_, startedTimesText(all.sessionCount));
|
||||
setLabel(all_up_lb_, tr_strlsize(all.uploadedBytes));
|
||||
setLabel(all_down_lb_, tr_strlsize(all.downloadedBytes));
|
||||
setLabel(all_time_lb_, tr_strltime(all.secondsActive));
|
||||
setLabel(all_time_lb_, tr_format_time(all.secondsActive));
|
||||
setLabelFromRatio(all_ratio_lb_, all.ratio);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -110,7 +110,7 @@ auto getProgressString(tr_torrent const* tor, uint64_t total_size, tr_stat const
|
||||
}
|
||||
else
|
||||
{
|
||||
gstr += fmt::format(_("{time_span} remaining"), fmt::arg("time_span", tr_strltime(eta)));
|
||||
gstr += tr_format_time_left(eta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
135
gtk/Utils.cc
135
gtk/Utils.cc
@@ -86,37 +86,136 @@ Glib::ustring tr_strlsize(guint64 bytes)
|
||||
return bytes == 0 ? Q_("None") : tr_formatter_size_B(bytes);
|
||||
}
|
||||
|
||||
Glib::ustring tr_strltime(time_t seconds)
|
||||
namespace
|
||||
{
|
||||
if (seconds < 0)
|
||||
|
||||
std::string tr_format_future_time(time_t seconds)
|
||||
{
|
||||
if (auto const days_from_now = seconds / 86400U; days_from_now > 0U)
|
||||
{
|
||||
seconds = 0;
|
||||
return fmt::format(
|
||||
ngettext("{days_from_now:L} day from now", "{days_from_now:L} days from now", days_from_now),
|
||||
fmt::arg("days_from_now", days_from_now));
|
||||
}
|
||||
|
||||
auto const days = (int)(seconds / 86400);
|
||||
auto const d = fmt::format(ngettext("{days:L} day", "{days:L} days", days), fmt::arg("days", days));
|
||||
int const hours = (seconds % 86400) / 3600;
|
||||
auto const h = fmt::format(ngettext("{hours} hour", "{hours} hours", hours), fmt::arg("hours", hours));
|
||||
if (days != 0)
|
||||
if (auto const hours_from_now = (seconds % 86400U) / 3600U; hours_from_now > 0U)
|
||||
{
|
||||
return (days >= 4 || hours == 0) ? d : fmt::format(FMT_STRING("{:s}, {:s}"), d, h);
|
||||
return fmt::format(
|
||||
ngettext("{hours_from_now:L} hour from now", "{hours_from_now:L} hours from now", hours_from_now),
|
||||
fmt::arg("hours_from_now", hours_from_now));
|
||||
}
|
||||
|
||||
int const minutes = (seconds % 3600) / 60;
|
||||
auto const m = fmt::format(ngettext("{minutes} minute", "{minutes} minutes", minutes), fmt::arg("minutes", minutes));
|
||||
if (hours != 0)
|
||||
if (auto const minutes_from_now = (seconds % 3600U) / 60U; minutes_from_now > 0U)
|
||||
{
|
||||
return (hours >= 4 || minutes == 0) ? h : fmt::format(FMT_STRING("{:s}, {:s}"), h, m);
|
||||
return fmt::format(
|
||||
ngettext("{minutes_from_now:L} minute from now", "{minutes_from_now:L} minutes from now", minutes_from_now),
|
||||
fmt::arg("minutes_from_now", minutes_from_now));
|
||||
}
|
||||
|
||||
seconds = (seconds % 3600) % 60;
|
||||
auto const s = fmt::format(ngettext("{seconds} second", "{seconds} seconds", seconds), fmt::arg("seconds", seconds));
|
||||
if (minutes != 0)
|
||||
if (auto const seconds_from_now = seconds % 60U; seconds_from_now > 0U)
|
||||
{
|
||||
return (minutes >= 4 || seconds == 0) ? m : fmt::format(FMT_STRING("{:s}, {:s}"), m, s);
|
||||
return fmt::format(
|
||||
ngettext("{seconds_from_now:L} second from now", "{seconds_from_now:L} seconds from now", seconds_from_now),
|
||||
fmt::arg("seconds_from_now", seconds_from_now));
|
||||
}
|
||||
|
||||
return s;
|
||||
return _("now");
|
||||
}
|
||||
|
||||
std::string tr_format_past_time(time_t seconds)
|
||||
{
|
||||
if (auto const days_ago = seconds / 86400U; days_ago > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{days_ago:L} day ago", "{days_ago:L} days ago", days_ago), fmt::arg("days_ago", days_ago));
|
||||
}
|
||||
|
||||
if (auto const hours_ago = (seconds % 86400U) / 3600U; hours_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{hours_ago:L} hour ago", "{hours_ago:L} hours ago", hours_ago),
|
||||
fmt::arg("hours_ago", hours_ago));
|
||||
}
|
||||
|
||||
if (auto const minutes_ago = (seconds % 3600U) / 60U; minutes_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{minutes_ago:L} minute ago", "{minutes_ago:L} minutes ago", minutes_ago),
|
||||
fmt::arg("minutes_ago", minutes_ago));
|
||||
}
|
||||
|
||||
if (auto const seconds_ago = seconds % 60U; seconds_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{seconds_ago:L} second ago", "{seconds_ago:L} seconds ago", seconds_ago),
|
||||
fmt::arg("seconds_ago", seconds_ago));
|
||||
}
|
||||
|
||||
return _("now");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string tr_format_time(time_t secs)
|
||||
{
|
||||
if (auto const days = secs / 86400U; days > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{days:L} day", "{days:L} days", days), fmt::arg("days", days));
|
||||
}
|
||||
|
||||
if (auto const hours = (secs % 86400U) / 3600U; hours > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{hours:L} hour", "{hours:L} hours", hours), fmt::arg("hours", hours));
|
||||
}
|
||||
|
||||
if (auto const minutes = (secs % 3600U) / 60U; minutes > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{minutes:L} minute", "{minutes:L} minutes", minutes), fmt::arg("minutes", minutes));
|
||||
}
|
||||
|
||||
if (auto const seconds = secs % 60U; seconds > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{seconds:L} second", "{seconds:L} seconds", seconds), fmt::arg("seconds", seconds));
|
||||
}
|
||||
|
||||
return _("now");
|
||||
}
|
||||
|
||||
std::string tr_format_time_left(time_t seconds)
|
||||
{
|
||||
if (auto const days_left = seconds / 86400U; days_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{days_left:L} day left", "{days_left:L} days left", days_left),
|
||||
fmt::arg("days_left", days_left));
|
||||
}
|
||||
|
||||
if (auto const hours_left = (seconds % 86400U) / 3600U; hours_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{hours_left:L} hour left", "{hours_left:L} hours left", hours_left),
|
||||
fmt::arg("hours_left", hours_left));
|
||||
}
|
||||
|
||||
if (auto const minutes_left = (seconds % 3600U) / 60U; minutes_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{minutes_left:L} minute left", "{minutes_left:L} minutes left", minutes_left),
|
||||
fmt::arg("minutes_left", minutes_left));
|
||||
}
|
||||
|
||||
if (auto const seconds_left = seconds % 60U; seconds_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{seconds_left:L} second left", "{seconds_left:L} seconds left", seconds_left),
|
||||
fmt::arg("seconds_left", seconds_left));
|
||||
}
|
||||
|
||||
return _("now");
|
||||
}
|
||||
|
||||
std::string tr_format_time_relative(time_t src, time_t dst)
|
||||
{
|
||||
return src < dst ? tr_format_future_time(dst - src) : tr_format_past_time(src - dst);
|
||||
}
|
||||
|
||||
namespace
|
||||
|
||||
@@ -56,8 +56,9 @@ Glib::ustring tr_strlsize(guint64 size);
|
||||
/* return a human-readable string for the given ratio. */
|
||||
Glib::ustring tr_strlratio(double ratio);
|
||||
|
||||
/* return a human-readable string for the time given in seconds. */
|
||||
Glib::ustring tr_strltime(time_t secs);
|
||||
std::string tr_format_time_relative(time_t src, time_t tgt);
|
||||
std::string tr_format_time_left(time_t seconds);
|
||||
std::string tr_format_time(time_t seconds);
|
||||
|
||||
/***
|
||||
****
|
||||
|
||||
Reference in New Issue
Block a user