mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -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-added-title = Added
|
||||
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}`;
|
||||
}
|
||||
|
||||
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 {
|
||||
return this.TR[msg].toLowerCase().replace(/_/g, "-");
|
||||
}
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
|
||||
let addedData: GraphData | null = null;
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
addedData = gatherData(sourceData);
|
||||
}
|
||||
|
||||
$: if (addedData) {
|
||||
console.log("preparing data");
|
||||
histogramData = buildHistogram(addedData, range);
|
||||
}
|
||||
|
||||
|
@ -29,6 +27,7 @@
|
|||
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||
const year = timeSpan(i18n, 1 * YEAR);
|
||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||
</script>
|
||||
|
||||
{#if histogramData}
|
||||
|
@ -54,6 +53,6 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<HistogramGraph data={histogramData} xText="Days" yText="Number of cards" />
|
||||
<HistogramGraph data={histogramData} xText="Days" {yText} {i18n} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
<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
|
||||
|
@ -12,6 +20,7 @@
|
|||
</text>
|
||||
<text
|
||||
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}
|
||||
</text>
|
||||
|
|
|
@ -11,16 +11,15 @@
|
|||
|
||||
const bounds = defaultGraphBounds();
|
||||
const xText = "";
|
||||
const yText = "Times pressed";
|
||||
|
||||
let svg = null as HTMLElement | SVGElement | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
renderButtons(svg as SVGElement, bounds, gatherData(sourceData));
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_ANSWER_BUTTONS_TITLE);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_ANSWER_COUNT);
|
||||
</script>
|
||||
|
||||
<div class="graph">
|
||||
|
@ -30,6 +29,6 @@
|
|||
<g class="bars" />
|
||||
<g class="hoverzone" />
|
||||
<AxisTicks {bounds} />
|
||||
<AxisLabels {bounds} {xText} {yText} />
|
||||
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||
</svg>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
let cardCounts: CardCounts | null = null;
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
cardCounts = gatherData(sourceData, i18n);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -12,17 +12,17 @@
|
|||
let histogramData = null as HistogramData | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
histogramData = prepareData(gatherData(sourceData));
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||
</script>
|
||||
|
||||
{#if histogramData}
|
||||
<div class="graph">
|
||||
<h1>{title}</h1>
|
||||
|
||||
<HistogramGraph data={histogramData} xText="Ease (%)" yText="Number of cards" />
|
||||
<HistogramGraph data={histogramData} xText="Ease (%)" {yText} {i18n} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -23,12 +23,10 @@
|
|||
let range = FutureDueRange.Month;
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
graphData = gatherData(sourceData);
|
||||
}
|
||||
|
||||
$: if (graphData) {
|
||||
console.log("preparing data");
|
||||
histogramData = buildHistogram(graphData, range);
|
||||
}
|
||||
|
||||
|
@ -37,6 +35,7 @@
|
|||
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||
const year = timeSpan(i18n, 1 * YEAR);
|
||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||
</script>
|
||||
|
||||
{#if histogramData}
|
||||
|
@ -44,6 +43,10 @@
|
|||
<div class="graph">
|
||||
<h1>{title}</h1>
|
||||
|
||||
<div class="range-box-inner">
|
||||
The number of cards studied each day, relative to today.
|
||||
</div>
|
||||
|
||||
<div class="range-box-inner">
|
||||
<label>
|
||||
<input type="radio" bind:group={range} value={FutureDueRange.Month} />
|
||||
|
@ -63,10 +66,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<HistogramGraph
|
||||
data={histogramData}
|
||||
xText="Days from now"
|
||||
yText="Number of cards" />
|
||||
<HistogramGraph data={histogramData} xText="" {yText} {i18n} />
|
||||
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
import AxisLabels from "./AxisLabels.svelte";
|
||||
import AxisTicks from "./AxisTicks.svelte";
|
||||
import { defaultGraphBounds } from "./graphs";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
export let data: HistogramData | null = null;
|
||||
export let i18n: I18n;
|
||||
export let xText: string;
|
||||
export let yText: string;
|
||||
|
||||
|
@ -21,5 +23,5 @@
|
|||
<g class="hoverzone" />
|
||||
<path class="area" />
|
||||
<AxisTicks {bounds} />
|
||||
<AxisLabels {bounds} {xText} {yText} />
|
||||
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||
</svg>
|
||||
|
|
|
@ -11,16 +11,15 @@
|
|||
|
||||
const bounds = defaultGraphBounds();
|
||||
const xText = "";
|
||||
const yText = "Times pressed";
|
||||
|
||||
let svg = null as HTMLElement | SVGElement | null;
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
renderHours(svg as SVGElement, bounds, gatherData(sourceData));
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_HOURS_TITLE);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_ANSWER_COUNT);
|
||||
</script>
|
||||
|
||||
<div class="graph">
|
||||
|
@ -31,6 +30,6 @@
|
|||
<path class="area" />
|
||||
<g class="hoverzone" />
|
||||
<AxisTicks {bounds} />
|
||||
<AxisLabels {bounds} {xText} {yText} />
|
||||
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||
</svg>
|
||||
</div>
|
||||
|
|
|
@ -21,18 +21,17 @@
|
|||
let range = IntervalRange.Percentile95;
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
intervalData = gatherIntervalData(sourceData);
|
||||
}
|
||||
|
||||
$: if (intervalData) {
|
||||
console.log("preparing data");
|
||||
histogramData = prepareIntervalData(intervalData, range);
|
||||
}
|
||||
|
||||
const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE);
|
||||
const month = timeSpan(i18n, 1 * MONTH);
|
||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||
const yText = i18n.tr(i18n.TR.STATISTICS_AXIS_LABEL_CARD_COUNT);
|
||||
</script>
|
||||
|
||||
{#if histogramData}
|
||||
|
@ -71,9 +70,6 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<HistogramGraph
|
||||
data={histogramData}
|
||||
xText="Interval (days)"
|
||||
yText="Number of cards" />
|
||||
<HistogramGraph data={histogramData} xText="Interval (days)" {yText} {i18n} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -32,21 +32,27 @@
|
|||
}
|
||||
|
||||
const xText = "";
|
||||
const yText = "Times pressed";
|
||||
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
graphData = gatherData(sourceData);
|
||||
}
|
||||
|
||||
$: 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 month = timeSpan(i18n, 1 * MONTH);
|
||||
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||
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>
|
||||
|
||||
<div class="graph">
|
||||
|
@ -75,7 +81,7 @@
|
|||
{#if revlogRange === RevlogRange.All}
|
||||
<label>
|
||||
<input type="radio" bind:group={range} value={ReviewRange.AllTime} />
|
||||
All time
|
||||
{all}
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -87,7 +93,7 @@
|
|||
<path class="area" />
|
||||
<g class="hoverzone" />
|
||||
<AxisTicks {bounds} />
|
||||
<AxisLabels {bounds} {xText} {yText} />
|
||||
<AxisLabels {bounds} {xText} {yText} {i18n} />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -81,8 +81,7 @@
|
|||
}
|
||||
|
||||
.y-axis-label {
|
||||
writing-mode: vertical-rl;
|
||||
rotate: 180;
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
|
||||
.hoverzone rect {
|
||||
|
|
|
@ -21,6 +21,8 @@ import { showTooltip, hideTooltip } from "./tooltip";
|
|||
import { GraphBounds } from "./graphs";
|
||||
import { area, curveBasis } from "d3-shape";
|
||||
import { min, histogram, sum, max, Bin, cumsum } from "d3-array";
|
||||
import { timeSpan } from "../time";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
interface Reviews {
|
||||
mature: number;
|
||||
|
@ -111,7 +113,8 @@ export function renderReviews(
|
|||
bounds: GraphBounds,
|
||||
sourceData: GraphData,
|
||||
range: ReviewRange,
|
||||
showTime: boolean
|
||||
showTime: boolean,
|
||||
i18n: I18n
|
||||
): void {
|
||||
const xMax = 0;
|
||||
let xMin = 0;
|
||||
|
@ -161,6 +164,17 @@ export function renderReviews(
|
|||
axisLeft(y)
|
||||
.ticks(bounds.height / 80)
|
||||
.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
|
||||
|
|
Loading…
Reference in a new issue