add non-abbreviated timespan translation; update existing short=True calls

- drop the '5m3s' special casing done in the card stats screen, and
just use decimals
- change alignment of the review log so that the non-abbreviated
spans are easier to read
This commit is contained in:
Damien Elmes 2020-02-20 16:07:28 +10:00
parent 99c07cfdcb
commit 2fc15d0d3a
8 changed files with 69 additions and 51 deletions

View file

@ -308,7 +308,8 @@ message TranslateArgValue {
message FormatTimeSpanIn {
enum Context {
ANSWER_BUTTONS = 0;
NORMAL = 0;
ANSWER_BUTTONS = 1;
}
float seconds = 1;

View file

@ -345,7 +345,11 @@ class RustBackend:
)
).translate_string
def format_time_span(self, seconds: float, context: FormatTimeSpanContext) -> str:
def format_time_span(
self,
seconds: float,
context: FormatTimeSpanContext = FormatTimeSpanContext.NORMAL,
) -> str:
return self._run_command(
pb.BackendInput(
format_time_span=pb.FormatTimeSpanIn(seconds=seconds, context=context)

View file

@ -28,8 +28,6 @@ class CardStats:
def report(self) -> str:
c = self.card
# pylint: disable=unnecessary-lambda
fmt = lambda x, **kwargs: fmtTimeSpan(x, short=True, **kwargs)
self.txt = "<table width=100%>"
self.addLine(_("Added"), self.date(c.id / 1000))
first = self.col.db.scalar("select min(id) from revlog where cid = ?", c.id)
@ -52,7 +50,9 @@ class CardStats:
next,
)
if c.queue == QUEUE_TYPE_REV:
self.addLine(_("Interval"), fmt(c.ivl * 86400))
self.addLine(
_("Interval"), self.col.backend.format_time_span(c.ivl * 86400)
)
self.addLine(_("Ease"), "%d%%" % (c.factor / 10.0))
self.addLine(_("Reviews"), "%d" % c.reps)
self.addLine(_("Lapses"), "%d" % c.lapses)
@ -83,13 +83,8 @@ class CardStats:
def date(self, tm) -> str:
return time.strftime("%Y-%m-%d", time.localtime(tm))
def time(self, tm) -> str:
s = ""
if tm >= 60:
s = fmtTimeSpan((tm / 60) * 60, short=True, point=-1, unit=1)
if tm % 60 != 0 or not s:
s += fmtTimeSpan(tm % 60, point=2 if not s else -1, short=True)
return s
def time(self, tm: float) -> str:
return self.col.backend.format_time_span(tm)
# Collection stats

View file

@ -24,7 +24,7 @@ from html.entities import name2codepoint
from typing import Iterable, Iterator, List, Optional, Tuple, Union
from anki.db import DB
from anki.lang import _, ngettext
from anki.lang import ngettext
_tmpdir: Optional[str]
@ -56,28 +56,10 @@ inTimeTable = {
}
def shortTimeFmt(type: str) -> str:
return {
# T: year is an abbreviation for year. %s is a number of years
"years": _("%sy"),
# T: m is an abbreviation for month. %s is a number of months
"months": _("%smo"),
# T: d is an abbreviation for day. %s is a number of days
"days": _("%sd"),
# T: h is an abbreviation for hour. %s is a number of hours
"hours": _("%sh"),
# T: m is an abbreviation for minute. %s is a number of minutes
"minutes": _("%sm"),
# T: s is an abbreviation for second. %s is a number of seconds
"seconds": _("%ss"),
}[type]
def fmtTimeSpan(
time: Union[int, float],
pad: int = 0,
point: int = 0,
short: bool = False,
inTime: bool = False,
unit: int = 99,
) -> str:
@ -86,13 +68,10 @@ def fmtTimeSpan(
time = convertSecondsTo(time, type)
if not point:
time = int(round(time))
if short:
fmt = shortTimeFmt(type)
if inTime:
fmt = inTimeTable[type](_pluralCount(time, point))
else:
if inTime:
fmt = inTimeTable[type](_pluralCount(time, point))
else:
fmt = timeTable[type](_pluralCount(time, point))
fmt = timeTable[type](_pluralCount(time, point))
timestr = "%%%(a)d.%(b)df" % {"a": pad, "b": point}
return locale.format_string(fmt % timestr, time)

View file

@ -1451,13 +1451,10 @@ border: 1px solid #000; padding: 3px; '>%s</div>"""
if not entries:
return ""
s = "<table width=100%%><tr><th align=left>%s</th>" % _("Date")
s += ("<th align=right>%s</th>" * 5) % (
_("Type"),
_("Rating"),
_("Interval"),
_("Ease"),
_("Time"),
)
s += "<th align=right>%s</th>" % _("Type")
s += "<th align=center>%s</th>" % _("Rating")
s += "<th align=left>%s</th>" % _("Interval")
s += ("<th align=right>%s</th>" * 2) % (_("Ease"), _("Time"),)
cnt = 0
for (date, ease, ivl, factor, taken, type) in reversed(entries):
cnt += 1
@ -1485,13 +1482,14 @@ border: 1px solid #000; padding: 3px; '>%s</div>"""
if ivl == 0:
ivl = _("0d")
elif ivl > 0:
ivl = fmtTimeSpan(ivl * 86400, short=True)
ivl = fmtTimeSpan(ivl * 86400)
else:
ivl = cs.time(-ivl)
s += ("<td align=right>%s</td>" * 5) % (
tstr,
ease,
ivl,
s += "<td align=right>%s</td>" % tstr
s += "<td align=center>%s</td>" % ease
s += "<td align=left>%s</td>" % ivl
s += ("<td align=right>%s</td>" * 2) % (
"%d%%" % (factor / 10) if factor else "",
cs.time(taken),
) + "</tr>"

View file

@ -11,7 +11,7 @@ use crate::media::check::MediaChecker;
use crate::media::sync::MediaSyncProgress;
use crate::media::MediaManager;
use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today};
use crate::sched::timespan::answer_button_time;
use crate::sched::timespan::{answer_button_time, time_span};
use crate::template::{
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
RenderedNode,
@ -406,6 +406,7 @@ impl Backend {
None => return "".to_string(),
};
match context {
pb::format_time_span_in::Context::Normal => time_span(input.seconds, &self.i18n),
pb::format_time_span_in::Context::AnswerButtons => {
answer_button_time(input.seconds, &self.i18n)
}

View file

@ -9,3 +9,31 @@ answer-button-time-hours = {$amount}h
answer-button-time-days = {$amount}d
answer-button-time-months = {$amount}mo
answer-button-time-years = {$amount}y
## A span of time, such as the delay until a card is shown again, the
## amount of time taken to answer a card, and so on.
time-span-seconds = { $amount ->
[one] {$amount} second
*[other] {$amount} seconds
}
time-span-minutes = { $amount ->
[one] {$amount} minute
*[other] {$amount} minutes
}
time-span-hours = { $amount ->
[one] {$amount} hour
*[other] {$amount} hours
}
time-span-days = { $amount ->
[one] {$amount} day
*[other] {$amount} days
}
time-span-months = { $amount ->
[one] {$amount} month
*[other] {$amount} months
}
time-span-years = { $amount ->
[one] {$amount} year
*[other] {$amount} years
}

View file

@ -3,11 +3,12 @@
use crate::i18n::{tr_args, I18n, StringsGroup};
/// Short string like '4d' to place above answer buttons.
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
let span = Timespan::from_secs(seconds).natural_span();
let amount = match span.unit() {
TimespanUnit::Months | TimespanUnit::Years => span.as_unit(),
// we don't show fractional value except for months/years
// we don't show fractional values except for months/years
_ => span.as_unit().round(),
};
let unit = span.unit().as_str();
@ -16,6 +17,17 @@ pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
.trn(&format!("answer-button-time-{}", unit), args)
}
/// Describe the given seconds using the largest appropriate unit
/// eg 70 seconds -> "1.17 minutes"
pub fn time_span(seconds: f32, i18n: &I18n) -> String {
let span = Timespan::from_secs(seconds).natural_span();
let amount = span.as_unit();
let unit = span.unit().as_str();
let args = tr_args!["amount" => amount];
i18n.get(StringsGroup::Scheduling)
.trn(&format!("time-span-{}", unit), args)
}
const SECOND: f32 = 1.0;
const MINUTE: f32 = 60.0 * SECOND;
const HOUR: f32 = 60.0 * MINUTE;