diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 618db9a34..ce84707c4 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -422,7 +422,7 @@ group by day order by day""" i, "Average answer time", self.col.tr.statistics_average_answer_time( - average_seconds=average_secs + average_seconds=average_secs, cards_per_minute=perMin ), ) return self._lineTbl(i), int(tot) diff --git a/pylib/tools/rewrite_tr.py b/pylib/tools/rewrite_tr.py index a6cc2f549..6db97a2cf 100644 --- a/pylib/tools/rewrite_tr.py +++ b/pylib/tools/rewrite_tr.py @@ -9,13 +9,13 @@ from re import Match import stringcase -TR_REF = re.compile(r"i18n\.tr\(i18n\.TR\.([^,) ]+)\)") +TR_REF = re.compile(r"i18n.tr\(\s*i18n.TR.([^,) ]+),\s*([^)]+)\)") def repl(m: Match) -> str: name = stringcase.camelcase(m.group(1).lower()) - #args = m.group(2) - return f"i18n.{name}()" + args = m.group(2) + return f"i18n.{name}({args})" def update_py(path: str) -> None: @@ -24,7 +24,7 @@ def update_py(path: str) -> None: if buf != buf2: open(path, "w").writelines(buf2) print("updated", path) - #print(buf2) + # print(buf2) for dirpath, dirnames, fnames in os.walk(os.environ["BUILD_WORKSPACE_DIRECTORY"]): diff --git a/rslib/i18n/build/extract.rs b/rslib/i18n/build/extract.rs index 86da5a20b..9aa5da4db 100644 --- a/rslib/i18n/build/extract.rs +++ b/rslib/i18n/build/extract.rs @@ -69,6 +69,15 @@ fn extract_metadata(ftl_text: &str) -> Vec { visitor.visit_pattern(&pattern); let key = m.id.name.to_string(); + // special case translations that were ported from gettext, and use embedded + // terms that reference other variables that aren't visible to our visitor + if key == "statistics-studied-today" { + visitor.variables.insert("amount".to_string()); + visitor.variables.insert("cards".to_string()); + } else if key == "statistics-average-answer-time" { + visitor.variables.insert("cards-per-minute".to_string()); + } + let (text, variables) = visitor.into_output(); output.push(Translation { @@ -141,10 +150,17 @@ impl From for Variable { // try to either reuse existing ones, or consider some sort of Hungarian notation let kind = match name.as_str() { "cards" | "notes" | "count" | "amount" | "reviews" | "total" | "selected" - | "kilobytes" => VariableKind::Int, - "average-seconds" => VariableKind::Float, - "val" | "found" | "expected" | "part" => VariableKind::Any, - _ => VariableKind::String, + | "kilobytes" | "daysStart" | "daysEnd" | "days" | "secs-per-card" | "remaining" + | "hourStart" | "hourEnd" | "correct" => VariableKind::Int, + "average-seconds" | "cards-per-minute" => VariableKind::Float, + "val" | "found" | "expected" | "part" | "percent" | "day" => VariableKind::Any, + term => { + if term.ends_with("Count") || term.ends_with("Secs") { + VariableKind::Int + } else { + VariableKind::String + } + } }; Variable { name, kind } } diff --git a/ts/congrats/lib.ts b/ts/congrats/lib.ts index f4464c747..a50c74665 100644 --- a/ts/congrats/lib.ts +++ b/ts/congrats/lib.ts @@ -25,11 +25,11 @@ export function buildNextLearnMsg( const unit = naturalUnit(secsUntil); const amount = Math.round(unitAmount(unit, secsUntil)); const unitStr = unitName(unit); - const nextLearnDue = i18n.tr(i18n.TR.SCHEDULING_NEXT_LEARN_DUE, { + const nextLearnDue = i18n.schedulingNextLearnDue({ amount, unit: unitStr, }); - const remaining = i18n.tr(i18n.TR.SCHEDULING_LEARN_REMAINING, { + const remaining = i18n.schedulingLearnRemaining({ remaining: info.learnRemaining, }); return `${nextLearnDue} ${remaining}`; diff --git a/ts/graphs/added.ts b/ts/graphs/added.ts index 70fa24e22..5c7b90069 100644 --- a/ts/graphs/added.ts +++ b/ts/graphs/added.ts @@ -100,11 +100,11 @@ export function buildHistogram( const tableData = [ { label: i18n.statisticsTotal(), - value: i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: totalInPeriod }), + value: i18n.statisticsCards({ cards: totalInPeriod }), }, { label: i18n.statisticsAverage(), - value: i18n.tr(i18n.TR.STATISTICS_CARDS_PER_DAY, { count: cardsPerDay }), + value: i18n.statisticsCardsPerDay({ count: cardsPerDay }), }, ]; @@ -114,9 +114,9 @@ export function buildHistogram( _percent: number ): string { const day = dayLabel(i18n, bin.x0!, bin.x1!); - const cards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: bin.length }); + const cards = i18n.statisticsCards({ cards: bin.length }); const total = i18n.statisticsRunningTotal(); - const totalCards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: cumulative }); + const totalCards = i18n.statisticsCards({ cards: cumulative }); return `${day}:
${cards}
${total}: ${totalCards}`; } diff --git a/ts/graphs/buttons.ts b/ts/graphs/buttons.ts index 2275b2867..eed746e9d 100644 --- a/ts/graphs/buttons.ts +++ b/ts/graphs/buttons.ts @@ -241,10 +241,7 @@ export function renderButtons( function tooltipText(d: Datum): string { const button = i18n.statisticsAnswerButtonsButtonNumber(); const timesPressed = i18n.statisticsAnswerButtonsButtonPressed(); - const correctStr = i18n.tr( - i18n.TR.STATISTICS_HOURS_CORRECT, - totalCorrect(d.group) - ); + const correctStr = i18n.statisticsHoursCorrect(totalCorrect(d.group)); return `${button}: ${d.buttonNum}
${timesPressed}: ${d.count}
${correctStr}`; } diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index c4e9184db..e3ade02d9 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -169,7 +169,7 @@ export function renderCalendar( month: "long", day: "numeric", }); - const cards = i18n.tr(i18n.TR.STATISTICS_REVIEWS, { reviews: d.count }); + const cards = i18n.statisticsReviews({ reviews: d.count }); return `${date}
${cards}`; } diff --git a/ts/graphs/ease.ts b/ts/graphs/ease.ts index fa9507099..44e6a9122 100644 --- a/ts/graphs/ease.ts +++ b/ts/graphs/ease.ts @@ -96,7 +96,7 @@ export function prepareData( const minPct = Math.floor(bin.x0!); const maxPct = Math.floor(bin.x1!); const percent = maxPct - minPct <= 10 ? `${bin.x0}%` : `${bin.x0}%-${bin.x1}%`; - return i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TOOLTIP, { + return i18n.statisticsCardEaseTooltip({ cards: bin.length, percent, }); diff --git a/ts/graphs/future-due.ts b/ts/graphs/future-due.ts index b6699602d..7dae25312 100644 --- a/ts/graphs/future-due.ts +++ b/ts/graphs/future-due.ts @@ -154,7 +154,7 @@ export function buildHistogram( _percent: number ): string { const days = dayLabel(i18n, bin.x0!, bin.x1!); - const cards = i18n.tr(i18n.TR.STATISTICS_CARDS_DUE, { + const cards = i18n.statisticsCardsDue({ cards: binValue(bin as any), }); const totalLabel = i18n.statisticsRunningTotal(); @@ -173,17 +173,17 @@ export function buildHistogram( const tableData = [ { label: i18n.statisticsTotal(), - value: i18n.tr(i18n.TR.STATISTICS_REVIEWS, { reviews: total }), + value: i18n.statisticsReviews({ reviews: total }), }, { label: i18n.statisticsAverage(), - value: i18n.tr(i18n.TR.STATISTICS_REVIEWS_PER_DAY, { + value: i18n.statisticsReviewsPerDay({ count: Math.round(total / periodDays), }), }, { label: i18n.statisticsDueTomorrow(), - value: i18n.tr(i18n.TR.STATISTICS_REVIEWS, { + value: i18n.statisticsReviews({ reviews: sourceData.dueCounts.get(1) ?? 0, }), }, diff --git a/ts/graphs/hours.ts b/ts/graphs/hours.ts index 0ecfe6ad8..2c85e6f61 100644 --- a/ts/graphs/hours.ts +++ b/ts/graphs/hours.ts @@ -185,11 +185,11 @@ export function renderHours( ); function tooltipText(d: Hour): string { - const hour = i18n.tr(i18n.TR.STATISTICS_HOURS_RANGE, { + const hour = i18n.statisticsHoursRange({ hourStart: d.hour, hourEnd: d.hour + 1, }); - const correct = i18n.tr(i18n.TR.STATISTICS_HOURS_CORRECT, { + const correct = i18n.statisticsHoursCorrect({ correct: d.correctCount, total: d.totalCount, percent: d.totalCount ? (d.correctCount / d.totalCount) * 100 : 0, diff --git a/ts/graphs/intervals.ts b/ts/graphs/intervals.ts index 674834fe6..6452cf29b 100644 --- a/ts/graphs/intervals.ts +++ b/ts/graphs/intervals.ts @@ -50,13 +50,13 @@ export function intervalLabel( ): string { if (daysEnd - daysStart <= 1) { // singular - return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_SINGLE, { + return i18n.statisticsIntervalsDaySingle({ day: daysStart, cards, }); } else { // range - return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_RANGE, { + return i18n.statisticsIntervalsDayRange({ daysStart, daysEnd: daysEnd - 1, cards, diff --git a/ts/graphs/reviews.ts b/ts/graphs/reviews.ts index 3012df2d2..056a7ce27 100644 --- a/ts/graphs/reviews.ts +++ b/ts/graphs/reviews.ts @@ -228,7 +228,7 @@ export function renderReviews( if (showTime) { return timeSpan(i18n, n / 1000); } else { - return i18n.tr(i18n.TR.STATISTICS_REVIEWS, { reviews: n }); + return i18n.statisticsReviews({ reviews: n }); } } @@ -378,10 +378,10 @@ export function renderReviews( averageAnswerTimeLabel: string; if (showTime) { totalString = timeSpan(i18n, total / 1000, false); - averageForDaysStudied = i18n.tr(i18n.TR.STATISTICS_MINUTES_PER_DAY, { + averageForDaysStudied = i18n.statisticsMinutesPerDay({ count: Math.round(studiedAvg / 1000 / 60), }); - averageForPeriod = i18n.tr(i18n.TR.STATISTICS_MINUTES_PER_DAY, { + averageForPeriod = i18n.statisticsMinutesPerDay({ count: Math.round(periodAvg / 1000 / 60), }); averageAnswerTimeLabel = i18n.statisticsAverageAnswerTimeLabel(); @@ -396,16 +396,16 @@ export function renderReviews( const totalSecs = total / 1000; const avgSecs = totalSecs / totalReviews; const cardsPerMin = (totalReviews * 60) / totalSecs; - averageAnswerTime = i18n.tr(i18n.TR.STATISTICS_AVERAGE_ANSWER_TIME, { - "average-seconds": avgSecs, - "cards-per-minute": cardsPerMin, + averageAnswerTime = i18n.statisticsAverageAnswerTime({ + averageSeconds: avgSecs, + cardsPerMinute: cardsPerMin, }); } else { - totalString = i18n.tr(i18n.TR.STATISTICS_REVIEWS, { reviews: total }); - averageForDaysStudied = i18n.tr(i18n.TR.STATISTICS_REVIEWS_PER_DAY, { + totalString = i18n.statisticsReviews({ reviews: total }); + averageForDaysStudied = i18n.statisticsReviewsPerDay({ count: Math.round(studiedAvg), }); - averageForPeriod = i18n.tr(i18n.TR.STATISTICS_REVIEWS_PER_DAY, { + averageForPeriod = i18n.statisticsReviewsPerDay({ count: Math.round(periodAvg), }); averageAnswerTime = averageAnswerTimeLabel = ""; @@ -414,7 +414,7 @@ export function renderReviews( const tableData: TableDatum[] = [ { label: i18n.statisticsDaysStudied(), - value: i18n.tr(i18n.TR.STATISTICS_AMOUNT_OF_TOTAL_WITH_PERCENTAGE, { + value: i18n.statisticsAmountOfTotalWithPercentage({ amount: studiedDays, total: periodDays, percent: Math.round((studiedDays / periodDays) * 100), diff --git a/ts/graphs/today.ts b/ts/graphs/today.ts index 98470ddf4..953d8eac5 100644 --- a/ts/graphs/today.ts +++ b/ts/graphs/today.ts @@ -76,7 +76,7 @@ export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): TodayDa againCountText += ` ${againCount} (${((againCount / answerCount) * 100).toFixed( 2 )}%)`; - const typeCounts = i18n.tr(i18n.TR.STATISTICS_TODAY_TYPE_COUNTS, { + const typeCounts = i18n.statisticsTodayTypeCounts({ learnCount, reviewCount, relearnCount, @@ -84,7 +84,7 @@ export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): TodayDa }); let matureText: string; if (matureCount) { - matureText = i18n.tr(i18n.TR.STATISTICS_TODAY_CORRECT_MATURE, { + matureText = i18n.statisticsTodayCorrectMature({ correct: matureCorrect, total: matureCount, percent: (matureCorrect / matureCount) * 100, diff --git a/ts/lib/genfluent.py b/ts/lib/genfluent.py index 89fe05318..d9a855719 100644 --- a/ts/lib/genfluent.py +++ b/ts/lib/genfluent.py @@ -84,7 +84,7 @@ def get_args(args: List[Variable]) -> str: def typescript_arg_name(arg: Variable) -> str: - name = stringcase.camelcase(arg["name"]) + name = stringcase.camelcase(arg["name"].replace("-", "_")) if name == "new": return "new_" else: diff --git a/ts/lib/time.ts b/ts/lib/time.ts index c3eebefb8..eae156fa3 100644 --- a/ts/lib/time.ts +++ b/ts/lib/time.ts @@ -79,11 +79,14 @@ export function studiedToday(i18n: I18n, cards: number, secs: number): string { if (cards > 0) { secsPer = secs / cards; } - return i18n.tr(i18n.TR.STATISTICS_STUDIED_TODAY, { + return i18n.statisticsStudiedToday({ + unit: name, + secsPerCard: secsPer, + // these two are required, but don't appear in the generated code + // because they are included as part of a separate term - a byproduct + // of them having been ported from earlier Qt translations cards, amount, - unit: name, - "secs-per-card": secsPer, }); } @@ -138,19 +141,19 @@ export function dayLabel(i18n: I18n, daysStart: number, daysEnd: number): string if (larger - smaller <= 1) { // singular if (daysStart >= 0) { - return i18n.tr(i18n.TR.STATISTICS_IN_DAYS_SINGLE, { days: daysStart }); + return i18n.statisticsInDaysSingle({ days: daysStart }); } else { - return i18n.tr(i18n.TR.STATISTICS_DAYS_AGO_SINGLE, { days: -daysStart }); + return i18n.statisticsDaysAgoSingle({ days: -daysStart }); } } else { // range if (daysStart >= 0) { - return i18n.tr(i18n.TR.STATISTICS_IN_DAYS_RANGE, { + return i18n.statisticsInDaysRange({ daysStart, daysEnd: daysEnd - 1, }); } else { - return i18n.tr(i18n.TR.STATISTICS_DAYS_AGO_RANGE, { + return i18n.statisticsDaysAgoRange({ daysStart: Math.abs(daysEnd - 1), daysEnd: -daysStart, });