mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 17:26:36 -04:00
i18n some axis labels, and support vertical CJK text
This commit is contained in:
parent
595c509546
commit
7e0bdb990c
14 changed files with 68 additions and 34 deletions
|
@ -95,3 +95,6 @@ statistics-answer-buttons-title = Answer Buttons
|
||||||
statistics-hours-title = Hourly Breakdown
|
statistics-hours-title = Hourly Breakdown
|
||||||
statistics-added-title = Added
|
statistics-added-title = Added
|
||||||
statistics-card-ease-title = Card Ease
|
statistics-card-ease-title = Card Ease
|
||||||
|
statistics-axis-label-answer-count = Answers
|
||||||
|
statistics-axis-label-card-count = Cards
|
||||||
|
statistics-axis-label-review-time = Review Time
|
||||||
|
|
|
@ -36,6 +36,15 @@ export class I18n {
|
||||||
return `missing key: ${key}`;
|
return `missing key: ${key}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportsVerticalText(): boolean {
|
||||||
|
const firstLang = this.bundles[0].locales[0];
|
||||||
|
return (
|
||||||
|
firstLang.startsWith("ja") ||
|
||||||
|
firstLang.startsWith("zh") ||
|
||||||
|
firstLang.startsWith("ko")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private keyName(msg: pb.BackendProto.FluentString): string {
|
private keyName(msg: pb.BackendProto.FluentString): string {
|
||||||
return this.TR[msg].toLowerCase().replace(/_/g, "-");
|
return this.TR[msg].toLowerCase().replace(/_/g, "-");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,10 @@
|
||||||
|
|
||||||
let addedData: GraphData | null = null;
|
let addedData: GraphData | null = null;
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
addedData = gatherData(sourceData);
|
addedData = gatherData(sourceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (addedData) {
|
$: if (addedData) {
|
||||||
console.log("preparing data");
|
|
||||||
histogramData = buildHistogram(addedData, range);
|
histogramData = buildHistogram(addedData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +27,7 @@
|
||||||
const month3 = timeSpan(i18n, 3 * MONTH);
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
const year = timeSpan(i18n, 1 * YEAR);
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -54,6 +53,6 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HistogramGraph data={histogramData} xText="Days" yText="Number of cards" />
|
<HistogramGraph data={histogramData} xText="Days" {yText} {i18n} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { GraphBounds } from "./graphs";
|
import { GraphBounds } from "./graphs";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export let bounds: GraphBounds;
|
export let bounds: GraphBounds;
|
||||||
export let xText: string;
|
export let xText: string;
|
||||||
export let yText: string;
|
export let yText: string;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
|
let yRotate = 180;
|
||||||
|
if (i18n.supportsVerticalText()) {
|
||||||
|
yRotate = 0;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<text
|
<text
|
||||||
|
@ -12,6 +20,7 @@
|
||||||
</text>
|
</text>
|
||||||
<text
|
<text
|
||||||
class="axis-label y-axis-label"
|
class="axis-label y-axis-label"
|
||||||
transform={`translate(${bounds.marginLeft / 3}, ${(bounds.height - bounds.marginBottom) / 2 + bounds.marginTop}) rotate(-180)`}>
|
transform={`translate(${bounds.marginLeft / 3}, ${(bounds.height - bounds.marginBottom) / 2 + bounds.marginTop})
|
||||||
|
rotate(${yRotate} 0 0)`}>
|
||||||
{yText}
|
{yText}
|
||||||
</text>
|
</text>
|
||||||
|
|
|
@ -11,16 +11,15 @@
|
||||||
|
|
||||||
const bounds = defaultGraphBounds();
|
const bounds = defaultGraphBounds();
|
||||||
const xText = "";
|
const xText = "";
|
||||||
const yText = "Times pressed";
|
|
||||||
|
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
renderButtons(svg as SVGElement, bounds, gatherData(sourceData));
|
renderButtons(svg as SVGElement, bounds, gatherData(sourceData));
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_ANSWER_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
|
@ -30,6 +29,6 @@
|
||||||
<g class="bars" />
|
<g class="bars" />
|
||||||
<g class="hoverzone" />
|
<g class="hoverzone" />
|
||||||
<AxisTicks {bounds} />
|
<AxisTicks {bounds} />
|
||||||
<AxisLabels {bounds} {xText} {yText} />
|
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
let cardCounts: CardCounts | null = null;
|
let cardCounts: CardCounts | null = null;
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
cardCounts = gatherData(sourceData, i18n);
|
cardCounts = gatherData(sourceData, i18n);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -12,17 +12,17 @@
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
histogramData = prepareData(gatherData(sourceData));
|
histogramData = prepareData(gatherData(sourceData));
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
|
||||||
<HistogramGraph data={histogramData} xText="Ease (%)" yText="Number of cards" />
|
<HistogramGraph data={histogramData} xText="Ease (%)" {yText} {i18n} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -23,12 +23,10 @@
|
||||||
let range = FutureDueRange.Month;
|
let range = FutureDueRange.Month;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
graphData = gatherData(sourceData);
|
graphData = gatherData(sourceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (graphData) {
|
$: if (graphData) {
|
||||||
console.log("preparing data");
|
|
||||||
histogramData = buildHistogram(graphData, range);
|
histogramData = buildHistogram(graphData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +35,7 @@
|
||||||
const month3 = timeSpan(i18n, 3 * MONTH);
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
const year = timeSpan(i18n, 1 * YEAR);
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -44,6 +43,10 @@
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
|
||||||
|
<div class="range-box-inner">
|
||||||
|
The number of cards studied each day, relative to today.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="range-box-inner">
|
<div class="range-box-inner">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={FutureDueRange.Month} />
|
<input type="radio" bind:group={range} value={FutureDueRange.Month} />
|
||||||
|
@ -63,10 +66,7 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HistogramGraph
|
<HistogramGraph data={histogramData} xText="" {yText} {i18n} />
|
||||||
data={histogramData}
|
|
||||||
xText="Days from now"
|
|
||||||
yText="Number of cards" />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
import AxisLabels from "./AxisLabels.svelte";
|
import AxisLabels from "./AxisLabels.svelte";
|
||||||
import AxisTicks from "./AxisTicks.svelte";
|
import AxisTicks from "./AxisTicks.svelte";
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export let data: HistogramData | null = null;
|
export let data: HistogramData | null = null;
|
||||||
|
export let i18n: I18n;
|
||||||
export let xText: string;
|
export let xText: string;
|
||||||
export let yText: string;
|
export let yText: string;
|
||||||
|
|
||||||
|
@ -21,5 +23,5 @@
|
||||||
<g class="hoverzone" />
|
<g class="hoverzone" />
|
||||||
<path class="area" />
|
<path class="area" />
|
||||||
<AxisTicks {bounds} />
|
<AxisTicks {bounds} />
|
||||||
<AxisLabels {bounds} {xText} {yText} />
|
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -11,16 +11,15 @@
|
||||||
|
|
||||||
const bounds = defaultGraphBounds();
|
const bounds = defaultGraphBounds();
|
||||||
const xText = "";
|
const xText = "";
|
||||||
const yText = "Times pressed";
|
|
||||||
|
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
renderHours(svg as SVGElement, bounds, gatherData(sourceData));
|
renderHours(svg as SVGElement, bounds, gatherData(sourceData));
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_ANSWER_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
|
@ -31,6 +30,6 @@
|
||||||
<path class="area" />
|
<path class="area" />
|
||||||
<g class="hoverzone" />
|
<g class="hoverzone" />
|
||||||
<AxisTicks {bounds} />
|
<AxisTicks {bounds} />
|
||||||
<AxisLabels {bounds} {xText} {yText} />
|
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,18 +21,17 @@
|
||||||
let range = IntervalRange.Percentile95;
|
let range = IntervalRange.Percentile95;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
intervalData = gatherIntervalData(sourceData);
|
intervalData = gatherIntervalData(sourceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (intervalData) {
|
$: if (intervalData) {
|
||||||
console.log("preparing data");
|
|
||||||
histogramData = prepareIntervalData(intervalData, range);
|
histogramData = prepareIntervalData(intervalData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
||||||
const month = timeSpan(i18n, 1 * MONTH);
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||||
|
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -71,9 +70,6 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HistogramGraph
|
<HistogramGraph data={histogramData} xText="Interval (days)" {yText} {i18n} />
|
||||||
data={histogramData}
|
|
||||||
xText="Interval (days)"
|
|
||||||
yText="Number of cards" />
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -32,21 +32,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const xText = "";
|
const xText = "";
|
||||||
const yText = "Times pressed";
|
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
|
||||||
graphData = gatherData(sourceData);
|
graphData = gatherData(sourceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (graphData) {
|
$: if (graphData) {
|
||||||
renderReviews(svg as SVGElement, bounds, graphData, range, showTime);
|
renderReviews(svg as SVGElement, bounds, graphData, range, showTime, i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = i18n.tr(i18n.TR.STATISTICS_REVIEWS_TITLE);
|
const title = i18n.tr(i18n.TR.STATISTICS_REVIEWS_TITLE);
|
||||||
const month = timeSpan(i18n, 1 * MONTH);
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
const month3 = timeSpan(i18n, 3 * MONTH);
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
const year = timeSpan(i18n, 1 * YEAR);
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
|
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||||
|
|
||||||
|
let yText: string;
|
||||||
|
$: if (showTime) {
|
||||||
|
yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_REVIEW_TIME);
|
||||||
|
} else {
|
||||||
|
yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_ANSWER_COUNT);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
|
@ -75,7 +81,7 @@
|
||||||
{#if revlogRange === RevlogRange.All}
|
{#if revlogRange === RevlogRange.All}
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={ReviewRange.AllTime} />
|
<input type="radio" bind:group={range} value={ReviewRange.AllTime} />
|
||||||
All time
|
{all}
|
||||||
</label>
|
</label>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -87,7 +93,7 @@
|
||||||
<path class="area" />
|
<path class="area" />
|
||||||
<g class="hoverzone" />
|
<g class="hoverzone" />
|
||||||
<AxisTicks {bounds} />
|
<AxisTicks {bounds} />
|
||||||
<AxisLabels {bounds} {xText} {yText} />
|
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -81,8 +81,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.y-axis-label {
|
.y-axis-label {
|
||||||
writing-mode: vertical-rl;
|
writing-mode: vertical-lr;
|
||||||
rotate: 180;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoverzone rect {
|
.hoverzone rect {
|
||||||
|
|
|
@ -21,6 +21,8 @@ import { showTooltip, hideTooltip } from "./tooltip";
|
||||||
import { GraphBounds } from "./graphs";
|
import { GraphBounds } from "./graphs";
|
||||||
import { area, curveBasis } from "d3-shape";
|
import { area, curveBasis } from "d3-shape";
|
||||||
import { min, histogram, sum, max, Bin, cumsum } from "d3-array";
|
import { min, histogram, sum, max, Bin, cumsum } from "d3-array";
|
||||||
|
import { timeSpan } from "../time";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
interface Reviews {
|
interface Reviews {
|
||||||
mature: number;
|
mature: number;
|
||||||
|
@ -111,7 +113,8 @@ export function renderReviews(
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData,
|
sourceData: GraphData,
|
||||||
range: ReviewRange,
|
range: ReviewRange,
|
||||||
showTime: boolean
|
showTime: boolean,
|
||||||
|
i18n: I18n
|
||||||
): void {
|
): void {
|
||||||
const xMax = 0;
|
const xMax = 0;
|
||||||
let xMin = 0;
|
let xMin = 0;
|
||||||
|
@ -161,6 +164,17 @@ export function renderReviews(
|
||||||
axisLeft(y)
|
axisLeft(y)
|
||||||
.ticks(bounds.height / 80)
|
.ticks(bounds.height / 80)
|
||||||
.tickSizeOuter(0)
|
.tickSizeOuter(0)
|
||||||
|
.tickFormat(((n: number): string => {
|
||||||
|
if (showTime) {
|
||||||
|
return timeSpan(i18n, n / 1000);
|
||||||
|
} else {
|
||||||
|
if (Math.round(n) != n) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return n.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as any)
|
||||||
);
|
);
|
||||||
|
|
||||||
// x bars
|
// x bars
|
||||||
|
|
Loading…
Reference in a new issue