mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add remaining tooltip i18n
This commit is contained in:
parent
41b296e96c
commit
68978e7c4e
16 changed files with 146 additions and 113 deletions
|
@ -95,9 +95,19 @@ statistics-future-due-title = Future Due
|
|||
statistics-reviews-title = Reviews
|
||||
statistics-intervals-title = Review Intervals
|
||||
statistics-answer-buttons-title = Answer Buttons
|
||||
# eg Button: 4
|
||||
statistics-answer-buttons-button-number = Button
|
||||
# eg Times pressed: 123
|
||||
statistics-answer-buttons-button-pressed = Times pressed
|
||||
statistics-hours-title = Hourly Breakdown
|
||||
statistics-added-title = Added
|
||||
statistics-card-ease-title = Card Ease
|
||||
statistics-card-ease-subtitle = The lower the ease, the more frequently a card will appear.
|
||||
# eg "3 cards with 150-170% ease"
|
||||
statistics-card-ease-tooltip = { $cards ->
|
||||
[one] 1 card with { $percent } ease
|
||||
*[other] { $cards } cards with { $percent } ease
|
||||
}
|
||||
statistics-future-due-subtitle = The number of reviews due in the future.
|
||||
statistics-added-subtitle = The number of new cards you have added.
|
||||
statistics-reviews-count-subtitle = The number of questions you have answered.
|
||||
|
@ -111,15 +121,26 @@ statistics-in-days-single = { $days ->
|
|||
[1] Tomorrow
|
||||
*[other] In { $days } days
|
||||
}
|
||||
statistics-in-days-range = In { $daysStart }~{ $daysEnd } days
|
||||
statistics-in-days-range = In { $daysStart }-{ $daysEnd } days
|
||||
statistics-days-ago-single = { $days ->
|
||||
[1] Yesterday
|
||||
*[other] { $days } days ago
|
||||
}
|
||||
statistics-days-ago-range = { $daysStart }~{ $daysEnd } days ago
|
||||
statistics-days-ago-range = { $daysStart }-{ $daysEnd } days ago
|
||||
statistics-running-total = Running total
|
||||
statistics-cards-due = { $cards ->
|
||||
[one] 1 card due
|
||||
*[other] { $cards } cards due
|
||||
}
|
||||
statistics-backlog-checkbox = Backlog
|
||||
statistics-intervals-day-range = { $cards ->
|
||||
[one] 1 card with a { $daysStart }~{ $daysEnd } day interval
|
||||
*[other] { $cards } cards with a { $daysStart }~{ $daysEnd } day interval
|
||||
}
|
||||
statistics-intervals-day-single = { $cards ->
|
||||
[one] 1 card with a { $day } day interval
|
||||
*[other] { $cards } card with a { $day } day interval
|
||||
}
|
||||
# hour range, eg "From 14:00-15:00"
|
||||
statistics-hours-range = From { $hourStart }:00~{ $hourEnd }:00
|
||||
statistics-hours-correct = { $correct }/{ $total } correct ({ $percent }%)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
|
||||
$: if (addedData) {
|
||||
histogramData = buildHistogram(addedData, range);
|
||||
histogramData = buildHistogram(addedData, range, i18n);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_ADDED_TITLE);
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<script lang="typescript">
|
||||
import { GraphBounds } from "./graphs";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
export let bounds: GraphBounds;
|
||||
export let xText: string;
|
||||
export let yText: string;
|
||||
export let i18n: I18n;
|
||||
|
||||
let yRotate = 180;
|
||||
if (i18n.supportsVerticalText()) {
|
||||
yRotate = 0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<text
|
||||
class="axis-label"
|
||||
transform={`translate(${bounds.width / 2}, ${bounds.height - 5})`}>
|
||||
{xText}
|
||||
</text>
|
||||
<text
|
||||
class="axis-label y-axis-label"
|
||||
transform={`translate(${bounds.marginLeft / 3}, ${(bounds.height - bounds.marginBottom) / 2 + bounds.marginTop})
|
||||
rotate(${yRotate} 0 0)`}>
|
||||
{yText}
|
||||
</text>
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="typescript">
|
||||
import { defaultGraphBounds } from "./graphs";
|
||||
import AxisTicks from "./AxisTicks.svelte";
|
||||
import AxisLabels from "./AxisLabels.svelte";
|
||||
import { gatherData, GraphData, renderButtons } from "./buttons";
|
||||
import pb from "../backend/proto";
|
||||
import { I18n } from "../i18n";
|
||||
|
@ -14,7 +13,7 @@
|
|||
let svg = null as HTMLElement | SVGElement | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
renderButtons(svg as SVGElement, bounds, gatherData(sourceData));
|
||||
renderButtons(svg as SVGElement, bounds, gatherData(sourceData), i18n);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
let histogramData = null as HistogramData | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
histogramData = prepareData(gatherData(sourceData));
|
||||
histogramData = prepareData(gatherData(sourceData), i18n);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
||||
const subtitle = "temp"; //i18n.tr(i18n.TR.STATISTICS_EASE_SUBTITLE);
|
||||
const subtitle = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_SUBTITLE);
|
||||
</script>
|
||||
|
||||
{#if histogramData}
|
||||
|
|
|
@ -140,6 +140,6 @@
|
|||
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
||||
<IntervalsGraph {sourceData} {i18n} />
|
||||
<EaseGraph {sourceData} {i18n} />
|
||||
<ButtonsGraph {sourceData} {i18n} />
|
||||
<HourGraph {sourceData} {i18n} />
|
||||
<ButtonsGraph {sourceData} {i18n} />
|
||||
<AddedGraph {sourceData} {i18n} />
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="typescript">
|
||||
import { HistogramData, histogramGraph } from "./histogram-graph";
|
||||
import AxisLabels from "./AxisLabels.svelte";
|
||||
import AxisTicks from "./AxisTicks.svelte";
|
||||
import { defaultGraphBounds } from "./graphs";
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="typescript">
|
||||
import { defaultGraphBounds } from "./graphs";
|
||||
import AxisTicks from "./AxisTicks.svelte";
|
||||
import AxisLabels from "./AxisLabels.svelte";
|
||||
import { gatherData, GraphData, renderHours } from "./hours";
|
||||
import pb from "../backend/proto";
|
||||
import { I18n } from "../i18n";
|
||||
|
@ -14,7 +13,7 @@
|
|||
let svg = null as HTMLElement | SVGElement | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
renderHours(svg as SVGElement, bounds, gatherData(sourceData));
|
||||
renderHours(svg as SVGElement, bounds, gatherData(sourceData), i18n);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
}
|
||||
|
||||
$: if (intervalData) {
|
||||
histogramData = prepareIntervalData(intervalData, range);
|
||||
histogramData = prepareIntervalData(intervalData, range, i18n);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="typescript">
|
||||
import { HistogramData, histogramGraph } from "./histogram-graph";
|
||||
import AxisLabels from "./AxisLabels.svelte";
|
||||
import AxisTicks from "./AxisTicks.svelte";
|
||||
import { defaultGraphBounds, RevlogRange } from "./graphs";
|
||||
import { GraphData, gatherData, renderReviews, ReviewRange } from "./reviews";
|
||||
|
|
|
@ -11,6 +11,8 @@ import { extent, histogram } from "d3-array";
|
|||
import { scaleLinear, scaleSequential } from "d3-scale";
|
||||
import { HistogramData } from "./histogram-graph";
|
||||
import { interpolateBlues } from "d3-scale-chromatic";
|
||||
import { I18n } from "../i18n";
|
||||
import { dayLabel } from "../time";
|
||||
|
||||
export enum AddedRange {
|
||||
Month = 0,
|
||||
|
@ -31,22 +33,10 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
|||
return { daysAdded };
|
||||
}
|
||||
|
||||
function hoverText(
|
||||
data: HistogramData,
|
||||
binIdx: number,
|
||||
cumulative: number,
|
||||
_percent: number
|
||||
): string {
|
||||
const bin = data.bins[binIdx];
|
||||
return (
|
||||
`${bin.length} at ${bin.x1! - 1} days.<br>` +
|
||||
` ${cumulative} cards at or below this point.`
|
||||
);
|
||||
}
|
||||
|
||||
export function buildHistogram(
|
||||
data: GraphData,
|
||||
range: AddedRange
|
||||
range: AddedRange,
|
||||
i18n: I18n
|
||||
): HistogramData | null {
|
||||
// get min/max
|
||||
const total = data.daysAdded.length;
|
||||
|
@ -81,5 +71,19 @@ export function buildHistogram(
|
|||
|
||||
const colourScale = scaleSequential(interpolateBlues).domain([xMin!, xMax]);
|
||||
|
||||
function hoverText(
|
||||
data: HistogramData,
|
||||
binIdx: number,
|
||||
cumulative: number,
|
||||
_percent: number
|
||||
): string {
|
||||
const bin = data.bins[binIdx];
|
||||
const day = dayLabel(i18n, bin.x0!, bin.x1!);
|
||||
const cards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: bin.length });
|
||||
const total = i18n.tr(i18n.TR.STATISTICS_RUNNING_TOTAL);
|
||||
const totalCards = i18n.tr(i18n.TR.STATISTICS_CARDS, { cards: cumulative });
|
||||
return `${day}:<br>${cards}<br>${total}: ${totalCards}`;
|
||||
}
|
||||
|
||||
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { scaleLinear, scaleBand, scaleSequential } from "d3-scale";
|
|||
import { axisBottom, axisLeft } from "d3-axis";
|
||||
import { showTooltip, hideTooltip } from "./tooltip";
|
||||
import { GraphBounds } from "./graphs";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
type ButtonCounts = [number, number, number, number];
|
||||
|
||||
|
@ -67,14 +68,11 @@ interface Datum {
|
|||
count: number;
|
||||
}
|
||||
|
||||
function tooltipText(d: Datum): string {
|
||||
return JSON.stringify(d);
|
||||
}
|
||||
|
||||
export function renderButtons(
|
||||
svgElem: SVGElement,
|
||||
bounds: GraphBounds,
|
||||
sourceData: GraphData
|
||||
sourceData: GraphData,
|
||||
i18n: I18n
|
||||
): void {
|
||||
const data = [
|
||||
...sourceData.learning.map((count: number, idx: number) => {
|
||||
|
@ -108,11 +106,24 @@ export function renderButtons(
|
|||
const xGroup = scaleBand()
|
||||
.domain(["learning", "young", "mature"])
|
||||
.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
|
||||
svg.select<SVGGElement>(".x-ticks").transition(trans).call(
|
||||
axisBottom(xGroup)
|
||||
// .ticks()
|
||||
.tickSizeOuter(0)
|
||||
);
|
||||
svg.select<SVGGElement>(".x-ticks")
|
||||
.transition(trans)
|
||||
.call(
|
||||
axisBottom(xGroup)
|
||||
.tickFormat(((d: string) => {
|
||||
switch (d) {
|
||||
case "learning":
|
||||
return i18n.tr(i18n.TR.STATISTICS_COUNTS_LEARNING_CARDS);
|
||||
case "young":
|
||||
return i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS);
|
||||
case "mature":
|
||||
return i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS);
|
||||
default:
|
||||
console.log(d);
|
||||
}
|
||||
}) as any)
|
||||
.tickSizeOuter(0)
|
||||
);
|
||||
|
||||
const xButton = scaleBand()
|
||||
.domain(["1", "2", "3", "4"])
|
||||
|
@ -177,6 +188,13 @@ export function renderButtons(
|
|||
);
|
||||
|
||||
// hover/tooltip
|
||||
|
||||
function tooltipText(d: Datum): string {
|
||||
const button = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_BUTTON_NUMBER);
|
||||
const timesPressed = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_BUTTON_PRESSED);
|
||||
return `${button}: ${d.buttonNum}<br>${timesPressed}: ${d.count}`;
|
||||
}
|
||||
|
||||
svg.select("g.hoverzone")
|
||||
.selectAll("rect")
|
||||
.data(data)
|
||||
|
|
|
@ -12,6 +12,7 @@ import { scaleLinear, scaleSequential } from "d3-scale";
|
|||
import { CardQueue } from "../cards";
|
||||
import { HistogramData } from "./histogram-graph";
|
||||
import { interpolateRdYlGn } from "d3-scale-chromatic";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
export interface GraphData {
|
||||
eases: number[];
|
||||
|
@ -24,16 +25,7 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
|||
return { eases };
|
||||
}
|
||||
|
||||
function hoverText(data: HistogramData, binIdx: number, _percent: number): string {
|
||||
const bin = data.bins[binIdx];
|
||||
const minPct = Math.floor(bin.x0!);
|
||||
const maxPct = Math.floor(bin.x1!);
|
||||
const ease = maxPct - minPct <= 10 ? `${bin.x0}%` : `${bin.x0}%~${bin.x1}%`;
|
||||
|
||||
return `${bin.length} cards with ${ease} ease.`;
|
||||
}
|
||||
|
||||
export function prepareData(data: GraphData): HistogramData | null {
|
||||
export function prepareData(data: GraphData, i18n: I18n): HistogramData | null {
|
||||
// get min/max
|
||||
const allEases = data.eases;
|
||||
if (!allEases.length) {
|
||||
|
@ -54,5 +46,16 @@ export function prepareData(data: GraphData): HistogramData | null {
|
|||
|
||||
const colourScale = scaleSequential(interpolateRdYlGn).domain([xMin, 300]);
|
||||
|
||||
function hoverText(data: HistogramData, binIdx: number, _percent: number): string {
|
||||
const bin = data.bins[binIdx];
|
||||
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, {
|
||||
cards: bin.length,
|
||||
percent,
|
||||
});
|
||||
}
|
||||
|
||||
return { scale, bins, total, hoverText, colourScale, showArea: false };
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.graph-tooltip {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
|
@ -23,25 +27,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* .cards-graph-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 0;
|
||||
padding-right: 1em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cards-graph-grid svg {
|
||||
flex: 3 0 0;
|
||||
flex-direction: column;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.cards-graph-grid div.graph-description {
|
||||
flex: 0 0 10em;
|
||||
font-family: "Arial";
|
||||
} */
|
||||
|
||||
.no-domain-line .domain {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { axisBottom, axisLeft } from "d3-axis";
|
|||
import { showTooltip, hideTooltip } from "./tooltip";
|
||||
import { GraphBounds } from "./graphs";
|
||||
import { area, curveBasis } from "d3-shape";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
type ButtonCounts = [number, number, number, number];
|
||||
|
||||
|
@ -52,14 +53,11 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData {
|
|||
return { hours };
|
||||
}
|
||||
|
||||
function tooltipText(d: Hour): string {
|
||||
return JSON.stringify(d);
|
||||
}
|
||||
|
||||
export function renderHours(
|
||||
svgElem: SVGElement,
|
||||
bounds: GraphBounds,
|
||||
sourceData: GraphData
|
||||
sourceData: GraphData,
|
||||
i18n: I18n
|
||||
): void {
|
||||
const data = sourceData.hours;
|
||||
|
||||
|
@ -141,6 +139,19 @@ export function renderHours(
|
|||
})
|
||||
);
|
||||
|
||||
function tooltipText(d: Hour): string {
|
||||
const hour = i18n.tr(i18n.TR.STATISTICS_HOURS_RANGE, {
|
||||
hourStart: d.hour,
|
||||
hourEnd: d.hour + 1,
|
||||
});
|
||||
const correct = i18n.tr(i18n.TR.STATISTICS_HOURS_CORRECT, {
|
||||
correct: d.correctCount,
|
||||
total: d.totalCount,
|
||||
percent: d.totalCount ? (d.correctCount / d.totalCount) * 100 : 0,
|
||||
});
|
||||
return `${hour}<br>${correct}`;
|
||||
}
|
||||
|
||||
// hover/tooltip
|
||||
svg.select("g.hoverzone")
|
||||
.selectAll("rect")
|
||||
|
|
|
@ -12,6 +12,7 @@ import { scaleLinear, scaleSequential } from "d3-scale";
|
|||
import { CardQueue } from "../cards";
|
||||
import { HistogramData } from "./histogram-graph";
|
||||
import { interpolateBlues } from "d3-scale-chromatic";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
export interface IntervalGraphData {
|
||||
intervals: number[];
|
||||
|
@ -32,26 +33,32 @@ export function gatherIntervalData(data: pb.BackendProto.GraphsOut): IntervalGra
|
|||
return { intervals };
|
||||
}
|
||||
|
||||
function hoverText(
|
||||
data: HistogramData,
|
||||
binIdx: number,
|
||||
_cumulative: number,
|
||||
percent: number
|
||||
export function intervalLabel(
|
||||
i18n: I18n,
|
||||
daysStart: number,
|
||||
daysEnd: number,
|
||||
cards: number
|
||||
): string {
|
||||
const bin = data.bins[binIdx];
|
||||
const interval =
|
||||
bin.x1! - bin.x0! === 1
|
||||
? `${bin.x0} day interval`
|
||||
: `${bin.x0}~${bin.x1} day interval`;
|
||||
return (
|
||||
`${bin.length} cards with ${interval}. ` +
|
||||
`<br>${percent.toFixed(1)}% cards at or before this point.`
|
||||
);
|
||||
if (daysEnd - daysStart <= 1) {
|
||||
// singular
|
||||
return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_SINGLE, {
|
||||
day: daysStart,
|
||||
cards,
|
||||
});
|
||||
} else {
|
||||
// range
|
||||
return i18n.tr(i18n.TR.STATISTICS_INTERVALS_DAY_RANGE, {
|
||||
daysStart,
|
||||
daysEnd,
|
||||
cards,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function prepareIntervalData(
|
||||
data: IntervalGraphData,
|
||||
range: IntervalRange
|
||||
range: IntervalRange,
|
||||
i18n: I18n
|
||||
): HistogramData | null {
|
||||
// get min/max
|
||||
const allIntervals = data.intervals;
|
||||
|
@ -60,7 +67,7 @@ export function prepareIntervalData(
|
|||
}
|
||||
|
||||
const total = allIntervals.length;
|
||||
const [xMin, origXMax] = extent(allIntervals);
|
||||
const [_xMinOrig, origXMax] = extent(allIntervals);
|
||||
let xMax = origXMax;
|
||||
|
||||
// cap max to selected range
|
||||
|
@ -80,6 +87,7 @@ export function prepareIntervalData(
|
|||
case IntervalRange.All:
|
||||
break;
|
||||
}
|
||||
const xMin = 0;
|
||||
xMax = xMax! + 1;
|
||||
|
||||
// cap bars to available range
|
||||
|
@ -94,5 +102,18 @@ export function prepareIntervalData(
|
|||
const shiftedMin = xMin! - Math.round((xMax - xMin!) / 10);
|
||||
const colourScale = scaleSequential(interpolateBlues).domain([shiftedMin, xMax]);
|
||||
|
||||
function hoverText(
|
||||
data: HistogramData,
|
||||
binIdx: number,
|
||||
_cumulative: number,
|
||||
percent: number
|
||||
): string {
|
||||
const bin = data.bins[binIdx];
|
||||
// const day = dayLabel(i18n, bin.x0!, bin.x1!);
|
||||
const interval = intervalLabel(i18n, bin.x0!, bin.x1!, bin.length);
|
||||
const total = i18n.tr(i18n.TR.STATISTICS_RUNNING_TOTAL);
|
||||
return `${interval}<br>${total}: ${percent.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
return { scale, bins, total, hoverText, colourScale, showArea: true };
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue