add remaining tooltip i18n

This commit is contained in:
Damien Elmes 2020-06-28 20:52:38 +10:00
parent 41b296e96c
commit 68978e7c4e
16 changed files with 146 additions and 113 deletions

View file

@ -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 }%)

View file

@ -19,7 +19,7 @@
}
$: if (addedData) {
histogramData = buildHistogram(addedData, range);
histogramData = buildHistogram(addedData, range, i18n);
}
const title = i18n.tr(i18n.TR.STATISTICS_ADDED_TITLE);

View file

@ -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>

View file

@ -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);

View file

@ -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}

View file

@ -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} />

View file

@ -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";

View file

@ -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);

View file

@ -25,7 +25,7 @@
}
$: if (intervalData) {
histogramData = prepareIntervalData(intervalData, range);
histogramData = prepareIntervalData(intervalData, range, i18n);
}
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);

View file

@ -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";

View file

@ -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 };
}

View file

@ -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)

View file

@ -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 };
}

View file

@ -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;
}

View file

@ -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")

View file

@ -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 };
}