1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-15 00:03:28 +01:00

vueui: i18n support (from @vanecat):

- add localized text to frame_insert.html and query.html (for now) to be passed to vueui
- ensure all vue strings localized
- use variable names in localizabls strings, eg. "view capture on {date}"
- simplified text (no cardinal suffixes, use 24-hourt clock)
This commit is contained in:
Ivan Velev 2022-02-08 19:17:12 -08:00 committed by Tessa Walsh
parent 14e464bd1c
commit 72cb588936
12 changed files with 723 additions and 922 deletions

File diff suppressed because one or more lines are too long

View File

@ -18,13 +18,72 @@ html, body
{{ banner_html }} {{ banner_html }}
<script>
var i18nStrings = {
jan_long: "{{ _Q('January') }}",
feb_long: "{{ _Q('February') }}",
mar_long: "{{ _Q('March') }}",
apr_long: "{{ _Q('April') }}",
may_long: "{{ _Q('May') }}",
jun_long: "{{ _Q('June') }}",
jul_long: "{{ _Q('July') }}",
aug_long: "{{ _Q('August') }}",
sep_long: "{{ _Q('September') }}",
oct_long: "{{ _Q('October') }}",
nov_long: "{{ _Q('November') }}",
dec_long: "{{ _Q('December') }}",
jan_short: "{{ _Q('Jan') }}",
feb_short: "{{ _Q('Feb') }}",
mar_short: "{{ _Q('Mar') }}",
apr_short: "{{ _Q('Apr') }}",
may_short: "{{ _Q('May') }}",
jun_short: "{{ _Q('Jun') }}",
jul_short: "{{ _Q('Jul') }}",
aug_short: "{{ _Q('Aug') }}",
sep_short: "{{ _Q('Sep') }}",
oct_short: "{{ _Q('Oct') }}",
nov_short: "{{ _Q('Nov') }}",
dec_short: "{{ _Q('Dec') }}",
mon_short: "{{ _Q('Mon') }}",
tue_short: "{{ _Q('Tue') }}",
wed_short: "{{ _Q('Wed') }}",
thu_short: "{{ _Q('Thu') }}",
fri_short: "{{ _Q('Fri') }}",
sat_short: "{{ _Q('Sat') }}",
sun_short: "{{ _Q('Sun') }}",
mon_long: "{{ _Q('Monday') }}",
tue_long: "{{ _Q('Tuesday') }}",
wed_long: "{{ _Q('Wednesday') }}",
thu_long: "{{ _Q('Thursday') }}",
fri_long: "{{ _Q('Friday') }}",
sat_long: "{{ _Q('Saturday') }}",
sun_long: "{{ _Q('Sunday') }}",
"All-time": "{{ _Q('All-time') }}",
"show timeline":"{{ _Q('show timeline') }}",
"hide timeline":"{{ _Q('hide timeline') }}",
"show calendar":"{{ _Q('show calendar') }}",
"hide calendar":"{{ _Q('hide calendar') }}",
"View capture on {date}":"{{ _Q('View capture on {date}') }}",
"{count} capture":"{{ _Q('{count} capture') }}",
"{count} captures":"{{ _Q('{count} captures') }}",
"{capture_text} on {date}":"{{ _Q('{capture_text} on {date}') }}",
"{capture_text} in {month}":"{{ _Q('{capture_text} in {month}') }}",
"current":"{{ _Q('current') }}",
"Loading...": "{{ _Q('Loading...') }}",
"Current Capture": "{{ _Q('Current Capture') }}",
"capture": "{{ _Q('capture') }}",
"captures": "{{ _Q('captures') }}",
"from {hour1} to {hour2}": "{{ _Q('from {hour1} to {hour2}') }}",
};
</script>
</head> </head>
<body style="margin: 0px; padding: 0px;"> <body style="margin: 0px; padding: 0px;">
{% if ui.vue_timeline_banner %} {% if ui.vue_timeline_banner %}
<div id="app" style="width: 100%; height: 200px"></div> <div id="app" style="width: 100%; height: 200px"></div>
<script> <script>
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ wb_prefix }}", "{{ timestamp }}", "{{ ui.logo }}"); VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ wb_prefix }}", "{{ timestamp }}", "{{ ui.logo }}", "{{ env.pywb_lang | default('en') }}", i18nStrings);
</script> </script>
{% endif %} {% endif %}

View File

@ -18,6 +18,64 @@
{% endif %} {% endif %}
<script>
var i18nStrings = {
jan_long: "{{ _Q('January') }}",
feb_long: "{{ _Q('February') }}",
mar_long: "{{ _Q('March') }}",
apr_long: "{{ _Q('April') }}",
may_long: "{{ _Q('May') }}",
jun_long: "{{ _Q('June') }}",
jul_long: "{{ _Q('July') }}",
aug_long: "{{ _Q('August') }}",
sep_long: "{{ _Q('September') }}",
oct_long: "{{ _Q('October') }}",
nov_long: "{{ _Q('November') }}",
dec_long: "{{ _Q('December') }}",
jan_short: "{{ _Q('Jan') }}",
feb_short: "{{ _Q('Feb') }}",
mar_short: "{{ _Q('Mar') }}",
apr_short: "{{ _Q('Apr') }}",
may_short: "{{ _Q('May') }}",
jun_short: "{{ _Q('Jun') }}",
jul_short: "{{ _Q('Jul') }}",
aug_short: "{{ _Q('Aug') }}",
sep_short: "{{ _Q('Sep') }}",
oct_short: "{{ _Q('Oct') }}",
nov_short: "{{ _Q('Nov') }}",
dec_short: "{{ _Q('Dec') }}",
mon_short: "{{ _Q('Mon') }}",
tue_short: "{{ _Q('Tue') }}",
wed_short: "{{ _Q('Wed') }}",
thu_short: "{{ _Q('Thu') }}",
fri_short: "{{ _Q('Fri') }}",
sat_short: "{{ _Q('Sat') }}",
sun_short: "{{ _Q('Sun') }}",
mon_long: "{{ _Q('Monday') }}",
tue_long: "{{ _Q('Tuesday') }}",
wed_long: "{{ _Q('Wednesday') }}",
thu_long: "{{ _Q('Thursday') }}",
fri_long: "{{ _Q('Friday') }}",
sat_long: "{{ _Q('Saturday') }}",
sun_long: "{{ _Q('Sunday') }}",
"All-time": "{{ _Q('All-time') }}",
"show timeline":"{{ _Q('show timeline') }}",
"hide timeline":"{{ _Q('hide timeline') }}",
"show calendar":"{{ _Q('show calendar') }}",
"hide calendar":"{{ _Q('hide calendar') }}",
"View capture on {date}":"{{ _Q('View capture on {date}') }}",
"{count} capture":"{{ _Q('{count} capture') }}",
"{count} captures":"{{ _Q('{count} captures') }}",
"{capture_text} on {date}":"{{ _Q('{capture_text} on {date}') }}",
"{capture_text} in {month}":"{{ _Q('{capture_text} in {month}') }}",
"current":"{{ _Q('current') }}", // translators: current capture in list of captures
"Loading...": "{{ _Q('Loading...') }}",
"Current Capture": "{{ _Q('Current Capture') }}",
"capture": "{{ _Q('capture') }}",
"captures": "{{ _Q('captures') }}",
"from {hour1} to {hour2}": "{{ _Q('from {hour1} to {hour2}') }}",
};
</script>
{% endblock %} {% endblock %}
@ -80,7 +138,7 @@
renderCal.init(); renderCal.init();
{% else %} {% else %}
VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ prefix }}", undefined, "{{ ui.logo }}"); VueUI.main("{{ static_prefix }}", "{{ url }}", "{{ prefix }}", undefined, "{{ ui.logo }}", "{{ env.pywb_lang | default('en') }}", i18nStrings);
{% endif %} {% endif %}

View File

@ -15,10 +15,10 @@
</div> </div>
<div class="toggles"> <div class="toggles">
<span class="toggle" :class="{expanded: showFullView}" @click="showFullView = !showFullView" :title="(showTimelineView ? 'show':'hide') + ' calendar'"> <span class="toggle" :class="{expanded: showFullView}" @click="showFullView = !showFullView" :title="(showTimelineView ? _('show calendar'):_('hide calendar'))">
<img src="/static/calendar-icon.png" /> <img src="/static/calendar-icon.png" />
</span> </span>
<span class="toggle" :class="{expanded: showTimelineView}" @click="showTimelineView = !showTimelineView" :title="(showTimelineView ? 'show':'hide') + ' timeline'"> <span class="toggle" :class="{expanded: showTimelineView}" @click="showTimelineView = !showTimelineView" :title="(showTimelineView ? _('show timeline'):_('hide timeline'))">
<img src="/static/timeline-icon.png" /> <img src="/static/timeline-icon.png" />
</span> </span>
</div> </div>
@ -40,7 +40,7 @@
</form> </form>
<div v-if="currentSnapshot && !showFullView"> <div v-if="currentSnapshot && !showFullView">
<span v-if="config.title">{{ config.title }}</span> <span v-if="config.title">{{ config.title }}</span>
Current capture: {{currentSnapshot.getTimeDateFormatted()}} {{_('Current Capture')}}: {{currentSnapshot.getTimeDateFormatted()}}
</div> </div>
</div> </div>
<CalendarYear v-if="showFullView && currentPeriod && currentPeriod.children.length" <CalendarYear v-if="showFullView && currentPeriod && currentPeriod.children.length"
@ -57,6 +57,7 @@ import TimelineBreadcrumbs from "./components/TimelineBreadcrumbs.vue";
import CalendarYear from "./components/CalendarYear.vue"; import CalendarYear from "./components/CalendarYear.vue";
import { PywbSnapshot, PywbPeriod } from "./model.js"; import { PywbSnapshot, PywbPeriod } from "./model.js";
import {PywbI18N} from "./i18n";
export default { export default {
name: "PywbReplayApp", name: "PywbReplayApp",
@ -74,7 +75,7 @@ export default {
title: "", title: "",
initialView: {} initialView: {}
}, },
timelineHighlight: false timelineHighlight: false,
}; };
}, },
components: {Timeline, TimelineBreadcrumbs, CalendarYear}, components: {Timeline, TimelineBreadcrumbs, CalendarYear},
@ -87,6 +88,9 @@ export default {
} }
}, },
methods: { methods: {
_(id, embeddedVariableStrings=null) {
return PywbI18N.instance.getText(id, embeddedVariableStrings);
},
gotoPeriod: function(newPeriod, onlyZoomToPeriod) { gotoPeriod: function(newPeriod, onlyZoomToPeriod) {
if (this.timelineHighlight) { if (this.timelineHighlight) {
setTimeout((() => { setTimeout((() => {

View File

@ -91,17 +91,17 @@
<template> <template>
<div class="calendar-month" :class="{current: isCurrent, 'contains-current-snapshot': containsCurrentSnapshot}"> <div class="calendar-month" :class="{current: isCurrent, 'contains-current-snapshot': containsCurrentSnapshot}">
<h3>{{month.getReadableId()}} <span v-if="month.snapshotCount">({{ month.snapshotCount }})</span></h3> <h3>{{getLongMonthName(month.id)}} <span v-if="month.snapshotCount">({{ month.snapshotCount }})</span></h3>
<div v-if="month.snapshotCount"> <div v-if="month.snapshotCount">
<span v-for="(day) in ['S', 'M', 'T', 'W', 'H', 'F', 'S']" class="day" :style="dayStyle">{{day}}</span><br/> <span v-for="(dayInitial) in dayInitials" class="day" :style="dayStyle">{{dayInitial}}</span><br/>
<span v-for="(day,i) in days"><br v-if="i && i % 7===0"/><span class="day" :class="{empty: !day || !day.snapshotCount, 'contains-current-snapshot':dayContainsCurrentSnapshot(day)}" :style="dayStyle" @click="gotoDay(day, $event)"><template v-if="day"><span class="size" v-if="day.snapshotCount" :style="getDayCountCircleStyle(day.snapshotCount)"> </span><span class="day-id">{{day.id}}</span><span v-if="day.snapshotCount" class="count">{{day.snapshotCount}} capture<span v-if="day.snapshotCount!==1">s</span></span></template><template v-else v-html="'&nbsp;'"></template></span></span> <span v-for="(day,i) in days"><br v-if="i && i % 7===0"/><span class="day" :class="{empty: !day || !day.snapshotCount, 'contains-current-snapshot':dayContainsCurrentSnapshot(day)}" :style="dayStyle" @click="gotoDay(day, $event)"><template v-if="day"><span class="size" v-if="day.snapshotCount" :style="getDayCountCircleStyle(day.snapshotCount)"> </span><span class="day-id">{{day.id}}</span><span v-if="day.snapshotCount" class="count">{{ $root._(day.snapshotCount !== 1 ? '{count} captures':'{count} capture', {count: day.snapshotCount}) }}</span></template><template v-else v-html="'&nbsp;'"></template></span></span>
</div> </div>
<div v-else class="empty">no captures</div> <div v-else class="empty">no captures</div>
</div> </div>
</template> </template>
<script> <script>
import {PywbPeriod} from "../model"; import {PywbI18N} from "../i18n";
export default { export default {
props: ["month", "year", "isCurrent", "yearContainsCurrentSnapshot", "currentSnapshot"], props: ["month", "year", "isCurrent", "yearContainsCurrentSnapshot", "currentSnapshot"],
@ -112,6 +112,9 @@ export default {
}; };
}, },
computed: { computed: {
dayInitials() {
return PywbI18N.instance.getWeekDays().map(d => d.substr(0,1));
},
dayStyle() { dayStyle() {
const s = this.daySize; const s = this.daySize;
return `height: ${s}px; width: ${s}px; line-height: ${s}px`; return `height: ${s}px; width: ${s}px; line-height: ${s}px`;
@ -143,6 +146,9 @@ export default {
} }
}, },
methods: { methods: {
getLongMonthName(id) {
return PywbI18N.instance.getMonth(id);
},
gotoDay(day, event) { gotoDay(day, event) {
if (!day || !day.snapshotCount) { if (!day || !day.snapshotCount) {
return; return;

View File

@ -23,7 +23,7 @@
<template> <template>
<div class="full-view"> <div class="full-view">
<h2>{{year.id}} ({{year.snapshotCount}} captures)</h2> <h2>{{year.id}} ({{ $root._(year.snapshotCount !== 1 ? '{count} captures':'{count} capture', {count: year.snapshotCount}) }})</h2>
<div class="months"> <div class="months">
<CalendarMonth <CalendarMonth
v-for="month in year.children" v-for="month in year.children"

View File

@ -2,16 +2,17 @@
<div class="timeline"> <div class="timeline">
<div class="period-tooltip" v-show="tooltipPeriod" :style="{left: tooltipPeriodPos.x+'px', top: tooltipPeriodPos.y+'px'}"> <div class="period-tooltip" v-show="tooltipPeriod" :style="{left: tooltipPeriodPos.x+'px', top: tooltipPeriodPos.y+'px'}">
<template v-if="tooltipPeriod"> <template v-if="tooltipPeriod">
<div v-if="tooltipPeriod.snapshot">View capture on <div v-if="tooltipPeriod.snapshot">
{{ tooltipPeriod.snapshot.getTimeDateFormatted() }} {{ $root._('View capture on {date}', {date: tooltipPeriod.snapshot.getTimeDateFormatted()}) }}
</div> </div>
<div v-else-if="tooltipPeriod.snapshotPeriod">View capture on <div v-else-if="tooltipPeriod.snapshotPeriod">
{{ tooltipPeriod.snapshotPeriod.snapshot.getTimeDateFormatted() }} {{ $root._('View capture on {date}', {date: tooltipPeriod.snapshotPeriod.snapshot.getTimeDateFormatted()}) }}
</div> </div>
<div v-else-if="tooltipPeriod.snapshotCount"> <div v-else-if="tooltipPeriod.snapshotCount">
{{ tooltipPeriod.snapshotCount }} captures {{ $root._(
<span v-if="isTooltipPeriodDayOrHour">on</span><span v-else>in</span> isTooltipPeriodDayOrHour ? '{capture_text} on {date}':'{capture_text} in {month}', // TODO: split translation into "in {year}" and "in {month}"
{{ tooltipPeriod.getFullReadableId() }} { capture_text: $root._(tooltipPeriod.snapshotCount !== 1 ? '{count} captures' : '{count} capture', {count: tooltipPeriod.snapshotCount}), [isTooltipPeriodDayOrHour ? 'date':'month']: tooltipPeriod.getFullReadableId() } )
}}
</div> </div>
</template> </template>
</div> </div>

View File

@ -11,12 +11,11 @@
<span class="goto" @click="changePeriod(parent)" :title="getPeriodZoomOutText(parent)"> <span class="goto" @click="changePeriod(parent)" :title="getPeriodZoomOutText(parent)">
{{parent.getReadableId(true)}} {{parent.getReadableId(true)}}
</span> </span>
&gt;
</span> </span>
</template> </template>
<span class="item"> <span class="item">
<span class="current">{{period.getReadableId(true)}}</span> <span class="current">{{period.getReadableId(true)}}</span>
<span class="count">({{period.snapshotCount}} capture<span v-if="period.snapshotCount !== 1">s</span>)</span> <span class="count">({{ $root._(period.snapshotCount !== 1 ? '{count} captures':'{count} capture', {count: period.snapshotCount}) }})</span>
</span> </span>
</div> </div>
</template> </template>

View File

@ -2,13 +2,13 @@
<div class="timeline-linear"> <div class="timeline-linear">
<div class="title"> <div class="title">
<div>{{ period.getFullReadableId() }}</div> <div>{{ period.getFullReadableId() }}</div>
<div>{{ period.snapshotCount }} capture<span v-if="period.snapshotCount > 1">s</span></div> <div>{{ $root._(period.snapshotCount !== 1 ? '{count} captures':'{count} capture', {count: period.snapshotCount}) }}</div>
</div> </div>
<div class="list"> <div class="list">
<div v-for="period in snapshotPeriods"> <div v-for="period in snapshotPeriods">
<span class="link" @click="gotoPeriod(period)" >{{period.snapshot.getTimeFormatted()}}</span> <span class="link" @click="gotoPeriod(period)" >{{period.snapshot.getTimeFormatted()}}</span>
<span v-if="isCurrentSnapshot(period)" class="current">current</span> <span v-if="isCurrentSnapshot(period)" class="current">{{$root._('current')}}</span>
</div> </div>
</div> </div>
</div> </div>

51
pywb/vueui/src/i18n.js Normal file
View File

@ -0,0 +1,51 @@
export class PywbI18N {
static #locale = ''; // private (can only be set here)
static getLocale() { // get via public static method
return PywbI18N.#locale;
}
static init = (locale, config) => {
if (PywbI18N.instance) {
throw new Error('cannot instantiate PywbI18N twice');
}
PywbI18N.#locale = locale;
PywbI18N.instance = new PywbI18N(config);
}
// PywbI18N expects from the i18n string source to receive months SHORT and LONG names in the config like this:
// config.jan_short, config.jan_long, ...., config.<mmm>_short, config.<mmm>_long
static monthIdPrefix = {1:"jan", 2:"feb",3:"mar",4:"apr",5:"may",6:"jun",7:"jul",8:"aug",9:"sep",10:"oct",11:"nov",12:"dec"};
/**
*
* @type {PywbI18N|null}
*/
static instance = null;
constructor(config) {
this.config = {...config}; // make a copy of config
}
// can get long (default) or short month string
getMonth(id, type='long') {
return decodeURIComponent(this.config[PywbI18N.monthIdPrefix[id]+'_'+type]);
}
// can get long (default) or short day string or intial
// PywbI18N expects to receive day's initials like:
// config.mon_short, config.tue_long, ...., config.<mmm>_short, config.<mmm>_long
getWeekDay(id, type='long') {
return decodeURIComponent(this.config[id+'_'+type])
}
getWeekDays(type='long') {
return ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].map(d => this.getWeekDay(d, type));
}
getText(id, embeddedVariableStrings=null) {
const translated = decodeURIComponent(this.config[id] || id);
if (embeddedVariableStrings && id.indexOf('{') >= 0 && id.indexOf('}') >= 0 ) {
return translated.replace(/{(\w+)}/g, (match, stringId) => embeddedVariableStrings[stringId]);
}
return translated
}
_(id, embeddedVariableStrings=null) {
return this.getText(id, embeddedVariableStrings);
}
}

View File

@ -1,13 +1,15 @@
import appData from "./App.vue"; import appData from "./App.vue";
import { PywbData } from "./model.js"; import { PywbData } from "./model.js";
import { PywbI18N } from "./i18n.js";
import Vue from "vue/dist/vue.esm.browser"; import Vue from "vue/dist/vue.esm.browser";
// =========================================================================== // ===========================================================================
export function main(staticPrefix, url, prefix, timestamp, logoUrl) { export function main(staticPrefix, url, prefix, timestamp, logoUrl, locale, i18nStrings) {
const loadingSpinner = new LoadingSpinner(); // bootstrap loading-spinner EARLY ON PywbI18N.init(locale, i18nStrings);
const loadingSpinner = new LoadingSpinner({text: PywbI18N.instance?.getText('Loading...')}); // bootstrap loading-spinner EARLY ON
new CDXLoader(staticPrefix, url, prefix, timestamp, logoUrl, loadingSpinner); new CDXLoader(staticPrefix, url, prefix, timestamp, logoUrl, loadingSpinner);
} }

View File

@ -1,4 +1,5 @@
const PywbMonthLabels = {1:"Jan", 2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"}; import { PywbI18N } from './i18n.js';
const PywbPeriodIdDelimiter = '-'; const PywbPeriodIdDelimiter = '-';
export function PywbData(rawSnaps) { export function PywbData(rawSnaps) {
const allTimePeriod = new PywbPeriod({type: PywbPeriod.Type.all, id: "all"}); const allTimePeriod = new PywbPeriod({type: PywbPeriod.Type.all, id: "all"});
@ -116,15 +117,15 @@ export class PywbSnapshot {
} }
getTimeDateFormatted() { getTimeDateFormatted() {
return `${PywbMonthLabels[this.month]} ${this.day}, ${this.year} at ${this.getTimeFormatted()}`; return new Date(this.year, this.month-1, this.day, this.hour, this.minute, this.second).toLocaleString(PywbI18N.getLocale()).toLowerCase();
} }
getDateFormatted() { getDateFormatted() {
return `${this.year}-${PywbMonthLabels[this.month]}-${this.day}`; return new Date(this.year, this.month-1, this.day).toLocaleDateString(PywbI18N.getLocale()).toLowerCase();
} }
getTimeFormatted() { getTimeFormatted() {
return (this.hour < 13 ? this.hour : (this.hour % 12)) + ":" + ((this.minute < 10 ? "0":"")+this.minute) + ":" + ((this.second < 10 ? "0":"")+this.second) + " " + (this.hour < 12 ? "am":"pm"); return new Date(2000, 0, 1, this.hour, this.minute, this.second).toLocaleTimeString(PywbI18N.getLocale()).toLowerCase();
} }
getParentIds() { getParentIds() {
@ -461,7 +462,7 @@ PywbPeriod.prototype.findByFullId = function(fullId) {
} }
return found; return found;
}; };
PywbPeriod.prototype.getFullReadableId = function(hasDayCardinalSuffix) { PywbPeriod.prototype.getFullReadableId = function() {
// remove "all-time" from parents (getParents(true) when printing readable id (of all parents and currrent // remove "all-time" from parents (getParents(true) when printing readable id (of all parents and currrent
switch (this.type) { switch (this.type) {
case PywbPeriod.Type.all: case PywbPeriod.Type.all:
@ -469,36 +470,47 @@ PywbPeriod.prototype.getFullReadableId = function(hasDayCardinalSuffix) {
case PywbPeriod.Type.year: case PywbPeriod.Type.year:
return this.id; return this.id;
case PywbPeriod.Type.month: case PywbPeriod.Type.month:
return this.getReadableId(hasDayCardinalSuffix) + ' ' + this.parent.id; return this.getReadableId() + ' ' + this.parent.id;
case PywbPeriod.Type.day: { case PywbPeriod.Type.day: {
return this.parent.getReadableId(hasDayCardinalSuffix) + ' ' + this.getReadableId(hasDayCardinalSuffix) + ', ' + this.parent.parent.id; return new Date(this.parent.parent.id, this.parent.id, this.getReadableId()).toLocaleDateString(PywbI18N.getLocale());
} }
case PywbPeriod.Type.hour: case PywbPeriod.Type.hour:
return this.parent.parent.getReadableId(hasDayCardinalSuffix) + ' ' + this.parent.getReadableId(hasDayCardinalSuffix) + ', ' + this.parent.parent.parent.id + ' at ' + this.getReadableId(hasDayCardinalSuffix); const hourRange = this.getReadableId({hourRange: true});
return this.parent.getFullReadableId() + ' ' + PywbI18N.instance._('from {hour1} to {hour2}', {
hour1: hourRange[0],
hour2: hourRange[1]
});
case PywbPeriod.Type.snapshot: case PywbPeriod.Type.snapshot:
return this.parent.parent.getReadableId(hasDayCardinalSuffix) + ' ' + this.parent.getReadableId(hasDayCardinalSuffix) + ', ' + this.parent.parent.parent.id + ' at ' + this.snapshot.getTimeFormatted(); return this.snapshot.getTimeDateFormatted();
} }
}; };
PywbPeriod.prototype.getReadableId = function(hasDayCardinalSuffix) { PywbPeriod.prototype.getReadableId = function(opts={hourRange:null}) {
switch (this.type) { switch (this.type) {
case PywbPeriod.Type.all: case PywbPeriod.Type.all:
return "All-time"; return PywbI18N.instance._("All-time");
case PywbPeriod.Type.year: case PywbPeriod.Type.year:
return this.id; return this.id;
case PywbPeriod.Type.month: case PywbPeriod.Type.month:
return PywbMonthLabels[this.id]; return PywbI18N.instance.getMonth(this.id, 'short');
case PywbPeriod.Type.day: { case PywbPeriod.Type.day: {
let suffix = ""; let suffix = "";
if (hasDayCardinalSuffix) { // DISABLING cardinal suffix for now, as it is complicated to replicate in multiple locales with 1 simple function
const singleDigit = this.id % 10; // TODO: add cardinal suffix handling later IF REQUESTED!
const isTens = Math.floor(this.id / 10) === 1; // if (cardinalSuffix) {
const suffixes = {1:"st", 2:"nd",3:"rd"}; // const singleDigit = this.id % 10;
suffix = (isTens || !suffixes[singleDigit]) ? "th" : suffixes[singleDigit]; // const isTens = Math.floor(this.id / 10) === 1;
} // const suffixes = {1:"st", 2:"nd",3:"rd"};
// suffix = (isTens || !suffixes[singleDigit]) ? "th" : suffixes[singleDigit];
// }
return this.id + suffix; return this.id + suffix;
} }
case PywbPeriod.Type.hour: case PywbPeriod.Type.hour:
return ({1:"12 am", 2: "3 am", 3: "6 am", 4: "9 am", 5: "noon", 6: "3 pm", 7: "6 pm", 8: "9 pm"})[this.id]; // use browser's locale setting to get time string and remove seconds, and lower-case it (in case AM-PM)
const hours = [0, 3, 6, 9, 12, 15, 18, 21].map(hour => new Date(2000, 0, 1, hour, 0, 0).toLocaleTimeString(PywbI18N.getLocale()).replace(/^(\d{1,2}:\d\d):\d\d/, (m, m1)=> m1).toLowerCase());
if (opts.hourRange) {
return [hours[this.id-1], hours[this.id % hours.length]];
}
return hours[this.id-1];
//return ({1:'midnight', 2: '6 am', 3: 'noon', 4: '6 pm'})[this.id]; //return ({1:'midnight', 2: '6 am', 3: 'noon', 4: '6 pm'})[this.id];
//return (this.id < 13 ? this.id : this.id % 12) + ' ' + (this.id < 12 ? 'am':'pm'); //return (this.id < 13 ? this.id : this.id % 12) + ' ' + (this.id < 12 ? 'am':'pm');
case PywbPeriod.Type.snapshot: case PywbPeriod.Type.snapshot: