1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-24 06:59:52 +01:00

vue ui: added i18n to loader and vue app; added a i18n model that initializes with a config from the server/template and can be used directly or via the vue app root _() method

This commit is contained in:
Ivan Velev 2022-02-07 16:49:49 -08:00
parent af04deabe6
commit ea2e3d7120
8 changed files with 257 additions and 216 deletions

File diff suppressed because one or more lines are too long

View File

@ -18,13 +18,46 @@ 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') }}",
"Loading...": "{{ _Q('Loading...') }}",
"Current Capture": "{{ _Q('Current Capture') }}",
"capture": "{{ _Q('capture') }}",
"captures": "{{ _Q('captures') }}",
};
</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 }}", i18nStrings);
</script> </script>
{% endif %} {% endif %}

View File

@ -18,6 +18,38 @@
{% 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') }}",
"Loading...": "{{ _Q('Loading...') }}",
"Current Capture": "{{ _Q('Current Capture') }}",
"capture": "{{ _Q('capture') }}",
"captures": "{{ _Q('captures') }}",
};
</script>
{% endblock %} {% endblock %}
@ -71,7 +103,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 }}", i18nStrings);
{% endif %} {% endif %}

View File

@ -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) {
return PywbI18N.instance.getText(id);
},
gotoPeriod: function(newPeriod, onlyZoomToPeriod) { gotoPeriod: function(newPeriod, onlyZoomToPeriod) {
if (this.timelineHighlight) { if (this.timelineHighlight) {
setTimeout((() => { setTimeout((() => {

View File

@ -91,7 +91,7 @@
<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="(day) in ['S', 'M', 'T', 'W', 'H', 'F', 'S']" class="day" :style="dayStyle">{{day}}</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">{{day.snapshotCount}} capture<span v-if="day.snapshotCount!==1">s</span></span></template><template v-else v-html="'&nbsp;'"></template></span></span>
@ -101,7 +101,7 @@
</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"],
@ -143,6 +143,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;

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

@ -0,0 +1,30 @@
export class PywbI18N {
static init = config => {
if (PywbI18N.instance) {
throw new Error('cannot instantiate PywbI18N twice');
}
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]);
}
getText(id) {
return decodeURIComponent(this.config[id] || id);
}
}

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, i18nStrings) {
const loadingSpinner = new LoadingSpinner(); // bootstrap loading-spinner EARLY ON PywbI18N.init(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,11 +117,11 @@ export class PywbSnapshot {
} }
getTimeDateFormatted() { getTimeDateFormatted() {
return `${PywbMonthLabels[this.month]} ${this.day}, ${this.year} at ${this.getTimeFormatted()}`; return `${PywbI18N.instance.getMonth(this.month, 'short')} ${this.day}, ${this.year} at ${this.getTimeFormatted()}`;
} }
getDateFormatted() { getDateFormatted() {
return `${this.year}-${PywbMonthLabels[this.month]}-${this.day}`; return `${this.year}-${PywbI18N.instance.getMonth(this.month, 'short')}-${this.day}`;
} }
getTimeFormatted() { getTimeFormatted() {
@ -486,7 +487,7 @@ PywbPeriod.prototype.getReadableId = 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 PywbMonthLabels[this.id]; return PywbI18N.instance.getMonth(this.id, 'short');
case PywbPeriod.Type.day: { case PywbPeriod.Type.day: {
let suffix = ""; let suffix = "";
if (hasDayCardinalSuffix) { if (hasDayCardinalSuffix) {