Multilanguage support (#960)

This commit is contained in:
Michael Geers 2021-05-04 10:58:38 +02:00 • committed by GitHub
parent 380cd461b9
commit 27be0806cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 277 additions and 59 deletions

View file

@ -5,6 +5,7 @@ import Vue from "vue";
import axios from "axios";
import App from "./views/App";
import router from "./router";
import i18n from "./i18n";
import store from "./store";
const loc = window.location;
@ -15,6 +16,7 @@ axios.defaults.headers.post["Content-Type"] = "application/json";
window.app = new Vue({
el: "#app",
router,
i18n,
data: { store, notifications: [] },
render: function (h) {
return h(App, { props: { notifications: this.notifications } });

View file

@ -1,4 +1,5 @@
import Footer from "./Footer.vue";
import i18n from "../i18n";
export default {
title: "Main/Footer",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Footer },
template: '<Footer v-bind="$props"></Footer>',

View file

@ -1,4 +1,5 @@
import Loadpoint from "./Loadpoint.vue";
import i18n from "../i18n";
export default {
title: "Main/Loadpoint",
@ -11,6 +12,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Loadpoint },
template: '<Loadpoint v-bind="$props"></Loadpoint>',

View file

@ -1,11 +1,13 @@
<template>
<div>
<p class="h3 mb-4 d-sm-block" :class="{ 'd-none': single }">{{ title || "Ladepunkt" }}</p>
<p class="h3 mb-4 d-sm-block" :class="{ 'd-none': single }">
{{ title || $t("main.loadpoint.fallbackName") }}
</p>
<div class="alert alert-warning mt-4 mb-2" role="alert" v-if="remoteDisabled == 'soft'">
{{ remoteDisabledSource }}: Adaptives PV-Laden deaktiviert
{{ $t("main.loadpoint.remoteDisabledSoft", { source: remoteDisabledSource }) }}
</div>
<div class="alert alert-danger mt-4 mb-2" role="alert" v-if="remoteDisabled == 'hard'">
{{ remoteDisabledSource }}: Deaktiviert
{{ $t("main.loadpoint.remoteDisabledHard", { source: remoteDisabledSource }) }}
</div>
<div class="row">

View file

@ -1,4 +1,5 @@
import LoadpointDetails from "./LoadpointDetails.vue";
import i18n from "../i18n";
export default {
title: "Main/LoadpointDetails",
@ -9,6 +10,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { LoadpointDetails },
template: '<LoadpointDetails v-bind="$props"></LoadpointDetails>',

View file

@ -3,7 +3,7 @@
<div class="row">
<div class="col-6 col-sm-3 col-lg-2 mt-3 offset-lg-4">
<div class="mb-2 value">
Leistung
{{ $t("main.loadpointDetails.power") }}
<fa-icon
class="text-primary ms-1"
icon="temperature-low"
@ -27,15 +27,15 @@
</div>
<div class="col-6 col-sm-3 col-lg-2 mt-3">
<div class="mb-2 value">Geladen</div>
<div class="mb-2 value">{{ $t("main.loadpointDetails.charged") }}</div>
<h3 class="value">
{{ fmt(chargedEnergy) }}
<small class="text-muted">{{ fmtUnit(chargedEnergy) }}Wh</small>
</h3>
</div>
<div class="col-6 col-sm-3 col-lg-2 mt-3" v-if="range >= 0">
<div class="mb-2 value">Reichweite</div>
<div class="col-6 col-sm-3 col-lg-2 mt-3" v-if="range && range >= 0">
<div class="mb-2 value">{{ $t("main.loadpointDetails.range") }}</div>
<h3 class="value">
{{ Math.round(range) }}
<small class="text-muted">km</small>
@ -43,7 +43,7 @@
</div>
<div class="col-6 col-sm-3 col-lg-2 mt-3" v-else>
<div class="mb-2 value">Dauer</div>
<div class="mb-2 value">{{ $t("main.loadpointDetails.duration") }}</div>
<h3 class="value">
{{ fmtShortDuration(chargeDuration) }}
<small class="text-muted">{{ fmtShortDurationUnit(chargeDuration) }}</small>
@ -51,7 +51,7 @@
</div>
<div class="col-6 col-sm-3 col-lg-2 mt-3" v-if="hasVehicle">
<div class="mb-2 value">Restzeit</div>
<div class="mb-2 value">{{ $t("main.loadpointDetails.remaining") }}</div>
<h3 class="value">
{{ fmtShortDuration(chargeEstimate) }}
<small class="text-muted">{{ fmtShortDurationUnit(chargeEstimate) }}</small>

View file

@ -1,4 +1,5 @@
import Mode from "./Mode.vue";
import i18n from "../i18n";
export default {
title: "Main/Mode",
@ -9,6 +10,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Mode },
template: '<Mode v-bind="$props"></Mode>',

View file

@ -1,6 +1,6 @@
<template>
<div>
<div class="mb-3">Modus</div>
<div class="mb-3">{{ $t("main.mode.title") }}</div>
<div class="btn-group w-100" role="group">
<input
id="mode_off"
@ -11,7 +11,9 @@
@click="setTargetMode('off')"
:checked="mode == 'off'"
/>
<label for="mode_off" class="btn btn-outline-primary"> Stop </label>
<label for="mode_off" class="btn btn-outline-primary">
{{ $t("main.mode.stop") }}
</label>
<input
id="mode_now"
type="radio"
@ -21,7 +23,9 @@
@click="setTargetMode('now')"
:checked="mode == 'now'"
/>
<label for="mode_now" class="btn btn-outline-primary"> Sofort </label>
<label for="mode_now" class="btn btn-outline-primary">
{{ $t("main.mode.now") }}
</label>
<input
id="mode_minpv"
type="radio"
@ -32,8 +36,8 @@
:checked="mode == 'minpv'"
/>
<label for="mode_minpv" class="btn btn-outline-primary">
<span class="d-inline d-sm-none">Min</span>
<span class="d-none d-sm-inline">Min + PV</span>
<span class="d-inline d-sm-none"> {{ $t("main.mode.minpvShort") }} </span>
<span class="d-none d-sm-inline"> {{ $t("main.mode.minpvLong") }} </span>
</label>
<input
id="mode_pv"
@ -45,8 +49,8 @@
:checked="mode == 'pv'"
/>
<label for="mode_pv" class="btn btn-outline-primary">
<span class="d-inline d-sm-none">PV</span>
<span class="d-none d-sm-inline">Nur PV</span>
<span class="d-inline d-sm-none"> {{ $t("main.mode.pvShort") }} </span>
<span class="d-none d-sm-inline"> {{ $t("main.mode.pvLong") }} </span>
</label>
</div>
</div>

View file

@ -1,4 +1,5 @@
import Notifications from "./Notifications.vue";
import i18n from "../i18n";
export default {
title: "Main/Notifications",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Notifications },
template: '<Notifications v-bind="$props"></Notifications>',

View file

@ -20,7 +20,7 @@
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Meldungen</h5>
<h5 class="modal-title">{{ $t("notifications.modalTitle") }}</h5>
<button
type="button"
class="btn-close"
@ -53,7 +53,7 @@
@click="clear"
class="btn btn-outline-secondary"
>
Meldungen entfernen
{{ $t("notifications.dismissAll") }}
</button>
</div>
</div>

View file

@ -1,4 +1,5 @@
import Site from "./Site.vue";
import i18n from "../i18n";
export default {
title: "Main/Site",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Site },
template: '<Site v-bind="$props"></Site>',

View file

@ -1,4 +1,5 @@
import SiteDetails from "./SiteDetails.vue";
import i18n from "../i18n";
export default {
title: "Main/SiteDetails",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { SiteDetails },
template: '<SiteDetails v-bind="$props"></SiteDetails>',

View file

@ -2,10 +2,12 @@
<div class="row justify-content-md-start overflow-hidden" :class="`row-cols-${numberOfPanels}`">
<div class="px-3" :class="panelClass" v-if="gridConfigured">
<div class="mb-2 value" v-if="gridPower > 0">
Bezug <fa-icon icon="arrow-down" class="text-primary" />
{{ $t("main.siteDetails.import") }}
<fa-icon icon="arrow-down" class="text-primary" />
</div>
<div class="mb-2 value" v-else>
Einspeisung <fa-icon icon="arrow-up" class="text-primary"></fa-icon>
{{ $t("main.siteDetails.export") }}
<fa-icon icon="arrow-up" class="text-primary"></fa-icon>
</div>
<h3 class="value">
{{ fmt(gridPower) }}
@ -15,7 +17,7 @@
<div class="px-3" :class="panelClass" v-if="pvConfigured">
<div class="mb-2 value">
Erzeugung
{{ $t("main.siteDetails.production") }}
<fa-icon
icon="sun"
:class="{
@ -32,10 +34,12 @@
<div class="px-3" :class="panelClass" v-if="batteryConfigured">
<div class="mb-2 value">
<div class="d-block d-sm-none">
Akku <span class="text-muted"> / {{ batterySoC }} %</span>
{{ $t("main.siteDetails.batteryShort") }}
<span class="text-muted"> / {{ batterySoC }} %</span>
</div>
<div class="d-none d-sm-block">
Batterie <span class="text-muted"> / {{ batterySoC }}% </span>
{{ $t("main.siteDetails.batteryLong") }}
<span class="text-muted"> / {{ batterySoC }}% </span>
<fa-icon class="text-primary" :icon="batteryIcon"></fa-icon>
</div>
</div>

View file

@ -1,4 +1,5 @@
import Sponsor from "./Sponsor.vue";
import i18n from "../i18n";
export default {
title: "Main/Footer/Sponsor",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Sponsor },
template: '<Sponsor v-bind="$props"></Sponsor>',

View file

@ -5,7 +5,10 @@
class="btn btn-link pe-0 text-decoration-none link-dark text-nowrap sponsor-button"
@click.stop.prevent="surprise"
>
Danke<span class="d-none d-sm-inline"> {{ sponsor }}</span>
<span class="d-inline d-sm-none">{{ $t("footer.sponsor.sponsoredShort") }}</span>
<span class="d-none d-sm-inline">{{
$t("footer.sponsor.sponsoredLong", { sponsor })
}}</span>
<fa-icon :icon="['fas', 'heart']" class="icon ms-1"></fa-icon>
</div>
<a
@ -14,7 +17,8 @@
target="_blank"
class="btn btn-link pe-0 text-decoration-none link-dark text-nowrap support-button"
>
<span class="d-none d-sm-inline">Projekt </span>unterstützen
<span class="d-inline d-sm-none">{{ $t("footer.sponsor.supportProjectShort") }}</span>
<span class="d-none d-sm-inline">{{ $t("footer.sponsor.supportProjectLong") }}</span>
<fa-icon :icon="['far', 'heart']" class="icon ms-1 outline"></fa-icon>
<fa-icon :icon="['fas', 'heart']" class="icon ms-1 solid"></fa-icon>
</a>

View file

@ -1,4 +1,5 @@
import Vehicle from "./Vehicle.vue";
import i18n from "../i18n";
export default {
title: "Main/Vehicle",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Vehicle },
template: '<Vehicle v-bind="$props"></Vehicle>',

View file

@ -1,7 +1,7 @@
<template>
<div>
<div class="mb-3">
{{ socTitle || "Fahrzeug" }}
{{ socTitle || $t("main.vehicle.fallbackName") }}
</div>
<VehicleSoc v-bind="vehicleSoc" @target-soc-updated="targetSocUpdated" />
<VehicleSubline v-bind="vehicleSubline" class="my-1" />

View file

@ -76,13 +76,13 @@ export default {
socChargeDisplayValue: function () {
// no soc or no soc value
if (!this.hasVehicle || !this.socCharge || this.socCharge < 0) {
let chargeStatus = "getrennt";
let chargeStatus = this.$t("main.vehicleSoc.disconnected");
if (this.charging) {
chargeStatus = "lädt";
chargeStatus = this.$t("main.vehicleSoc.charging");
} else if (this.enabled) {
chargeStatus = "bereit";
chargeStatus = this.$t("main.vehicleSoc.ready");
} else if (this.connected) {
chargeStatus = "verbunden";
chargeStatus = this.$t("main.vehicleSoc.connected");
}
return chargeStatus;
}

View file

@ -3,7 +3,7 @@
<small class="text-secondary">
<span v-if="minSoCActive">
<fa-icon class="text-muted me-1" icon="exclamation-circle"></fa-icon>
Mindestladung bis {{ minSoC }}%
{{ $t("main.vehicleSubline.mincharge", { soc: minSoC }) }}
</span>
</small>
<small

View file

@ -1,4 +1,5 @@
import Version from "./Version.vue";
import i18n from "../i18n";
export default {
title: "Main/Footer/Version",
@ -7,6 +8,7 @@ export default {
};
const Template = (args, { argTypes }) => ({
i18n,
props: Object.keys(argTypes),
components: { Version },
template: '<Version v-bind="$props"></Version>',

View file

@ -8,7 +8,9 @@
class="btn btn-link ps-0 text-decoration-none link-dark text-nowrap"
>
<fa-icon icon="gift" class="icon me-1"></fa-icon>
Update<span class="d-none d-sm-inline"> verfügbar</span>: {{ available }}
<span class="d-none d-sm-inline"> {{ $t("footer.version.availableLong") }}: </span>
<span class="d-inline d-sm-none"> {{ $t("footer.version.availableShort") }}: </span>
{{ available }}
</button>
<a
:href="releaseNotesUrl(installed)"
@ -16,14 +18,14 @@
class="btn btn-link ps-0 text-decoration-none link-dark text-nowrap"
v-else
>
Version {{ installed }}
{{ $t("footer.version.version") }} {{ installed }}
</a>
<div id="updateModal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update verfügbar</h5>
<h5 class="modal-title">{{ $t("footer.version.modalTitle") }}</h5>
<button
type="button"
class="btn-close"
@ -33,7 +35,7 @@
</div>
<div class="modal-body">
<div v-if="updateStarted">
<p>Nach der Aktualisierung wird evcc neu gestartet.</p>
<p>{{ $t("footer.version.modalUpdateStarted") }}</p>
<div class="progress my-3">
<div
class="progress-bar progress-bar-striped progress-bar-animated"
@ -45,13 +47,15 @@
</div>
<div v-else>
<p>
<small>Aktuell installierte Version: {{ installed }}</small>
<small>
{{ $t("footer.version.modalInstalledVersion") }}:
{{ installed }}
</small>
</p>
<div v-if="releaseNotes" v-html="releaseNotes"></div>
<p v-else>
Keine Releasenotes verfügbar. Mehr Informationen zur neuen Version
findest du
<a :href="releaseNotesUrl(available)">hier</a>.
{{ $t("footer.version.modalNoReleaseNotes") }}
<a :href="releaseNotesUrl(available)">GitHub</a>.
</p>
</div>
</div>
@ -62,7 +66,7 @@
:disabled="updateStarted"
data-bs-dismiss="modal"
>
Abbrechen
{{ $t("footer.version.modalCancel") }}
</button>
<div>
<button
@ -79,12 +83,12 @@
aria-hidden="true"
>
</span>
Akualisieren
{{ $t("footer.version.modalUpdate") }}
</span>
<span v-else>Jetzt aktualisieren</span>
<span v-else>{{ $t("footer.version.modalUpdateNow") }}</span>
</button>
<a :href="releaseNotesUrl(available)" class="btn btn-primary" v-else>
Download
{{ $t("footer.version.modalDownload") }}
</a>
</div>
</div>
@ -118,10 +122,10 @@ export default {
update: async function () {
try {
await axios.post("update");
this.updateStatus = "Aktualisierung gestartet: ";
this.updateStatus = this.$t("footer.version.modalUpdateStatusStart");
this.updateStarted = true;
} catch (e) {
this.updateStatus = "Aktualisierung nicht möglich: " + e;
this.updateStatus = this.$t("footer.version.modalUpdateStatusStart") + e;
}
},
releaseNotesUrl: function (version) {

73
assets/js/i18n/de.js Normal file
View file

@ -0,0 +1,73 @@
export default {
header: { support: "Support" },
footer: {
version: {
version: "Version",
availableShort: "verfügbar",
availableLong: "Update verfügbar",
modalTitle: "Update verfügbar",
modalUpdateStarted: "Nach der Aktualisierung wird evcc neu gestartet.",
modalInstalledVersion: "Aktuell installierte Version",
modalNoReleaseNotes:
"Keine Releasenotes verfügbar. Mehr Informationen zur neuen Version findest du hier:",
modalCancel: "Abbrechen",
modalUpdate: "Aktualisieren",
modalUpdateNow: "Jetzt aktualisieren",
modalDownload: "Download",
modalUpdateStatusStart: "Aktualisierung gestartet: ",
modalUpdateStatusFailed: "Aktualisierung nicht möglich: ",
},
sponsor: {
sponsoredShort: "Danke",
sponsoredLong: "Danke {sponsor}",
supportProjectShort: "unterstützen",
supportProjectLong: "Projekt unterstützen",
},
},
notifications: {
modalTitle: "Meldungen",
dismissAll: "Meldungen entfernen",
},
main: {
siteDetails: {
import: "Bezug",
export: "Einspeisung",
production: "Erzeugung",
batteryShort: "Akku",
batteryLong: "Batterie",
},
mode: {
title: "Modus",
stop: "Stop",
now: "Sofort",
minpvShort: "Min",
minpvLong: "Min + PV",
pvShort: "PV",
pvLong: "Nur PV",
},
loadpoint: {
fallbackName: "Ladepunkt",
remoteDisabledSoft: "{source}: Adaptives PV-Laden deaktiviert",
remoteDisabledHard: "{source}: Deaktiviert",
},
vehicle: {
fallbackName: "Fahrzeug",
},
vehicleSoc: {
disconnected: "getrennt",
charging: "lädt",
ready: "bereit",
connected: "verbunden",
},
vehicleSubline: {
mincharge: "Mindestladung bis {soc}%",
},
loadpointDetails: {
power: "Leistung",
range: "Reichweite",
charged: "Geladen",
duration: "Dauer",
remaining: "Restzeit",
},
},
};

73
assets/js/i18n/en.js Normal file
View file

@ -0,0 +1,73 @@
export default {
header: { support: "Support" },
footer: {
version: {
version: "version",
availableShort: "available",
availableLong: "update available",
modalTitle: "Update available",
modalUpdateStarted: "Evcc will restart after the update..",
modalInstalledVersion: "Currently installed version",
modalNoReleaseNotes:
"No release notes available. More information about the new version can be found here:",
modalCancel: "Cancel",
modalUpdate: "Update",
modalUpdateNow: "Update now",
modalDownload: "Download",
modalUpdateStatusStart: "Update started: ",
modalUpdateStatusFailed: "Update failed: ",
},
sponsor: {
sponsoredShort: "thanks",
sponsoredLong: "thanks {sponsor}",
supportProjectShort: "support",
supportProjectLong: "support the project",
},
},
notifications: {
modalTitle: "Notifications",
dismissAll: "Dismiss all",
},
main: {
siteDetails: {
import: "Import",
export: "Export",
production: "Production",
batteryShort: "Batt.",
batteryLong: "Battery",
},
mode: {
title: "Mode",
stop: "Stop",
now: "Now",
minpvShort: "Min",
minpvLong: "Min + PV",
pvShort: "PV",
pvLong: "PV only",
},
loadpoint: {
fallbackName: "Loadpoint",
remoteDisabledSoft: "{source}: adaptive PV charging disabled",
remoteDisabledHard: "{source}: disabled",
},
vehicle: {
fallbackName: "Vehicle",
},
vehicleSoc: {
disconnected: "disconnected",
charging: "charging",
ready: "ready",
connected: "connected",
},
vehicleSubline: {
mincharge: "minimum charging to {soc}%",
},
loadpointDetails: {
power: "Power",
range: "Range",
charged: "Charged",
duration: "Duration",
remaining: "Remaining",
},
},
};

22
assets/js/i18n/index.js Normal file
View file

@ -0,0 +1,22 @@
import Vue from "vue";
import VueI18n from "vue-i18n";
import de from "./de";
import en from "./en";
Vue.use(VueI18n);
function getBrowserLocale() {
const navigatorLocale =
navigator.languages !== undefined ? navigator.languages[0] : navigator.language;
if (!navigatorLocale) {
return undefined;
}
const trimmedLocale = navigatorLocale.trim().split(/-|_/)[0];
return trimmedLocale;
}
export default new VueI18n({
locale: getBrowserLocale(),
fallbackLocale: "en",
messages: { de, en },
});

View file

@ -32,7 +32,7 @@
href="https://github.com/andig/evcc/discussions"
target="_blank"
>
Support
{{ $t("header.support") }}
</a>
</li>
</ul>
@ -40,7 +40,6 @@
</div>
</div>
</nav>
<router-view
class="flex-grow-1 d-flex flex-column justify-content-stretch"
></router-view>

View file

@ -1 +0,0 @@
.progress-bar-animated{animation-direction:reverse}.btn.caption{opacity:1}.btn.first{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}.value,input[type=radio],label.btn{white-space:nowrap!important}.text-muted a,.text-muted a:hover{color:#6c757d!important;text-decoration:none}.bg-muted{opacity:.25}.icon[data-v-0f71c17c]{color:#0fdd42}.icon[data-v-60f66a8e]{color:#0fdd42;display:inline-block}.sponsor-button[data-v-60f66a8e]{touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.support-button .solid[data-v-60f66a8e]{display:none}.support-button:hover .solid[data-v-60f66a8e]{display:inline-block}.support-button:hover .outline[data-v-60f66a8e]{display:none}.logo[data-v-59c735a7]{width:85px}.app[data-v-59c735a7]{min-height:100vh}.btn[data-v-a92589b2]{flex-basis:0}.vehicle-soc[data-v-61c611fd]{--height:38px;--thumb-overlap:3px;--thumb-width:3px;--thumb-horizontal-padding:15px;--label-height:26px;position:relative;height:var(--height)}.progress[data-v-61c611fd]{height:100%;font-size:1rem}.progress-bar.bg-muted[data-v-61c611fd]{color:var(--white)}.bg-light[data-v-61c611fd]{color:var(--bs-gray-dark)}.target-label[data-v-61c611fd]{width:3em;margin-left:-1.5em;height:var(--label-height);position:absolute;top:calc(var(--label-height)*-1 + var(--thumb-overlap)*-1);text-align:center;color:var(--bs-gray-dark);font-size:1rem;opacity:1;transition:opacity .2s ease 1s}.target-slider[data-v-61c611fd]{-webkit-appearance:none;position:absolute;top:calc(var(--thumb-overlap)*-1);left:calc(var(--thumb-horizontal-padding)*-1);height:calc(100% + var(--thumb-overlap)*2);width:calc(100% + var(--thumb-horizontal-padding)*2);background:transparent}.target-slider[data-v-61c611fd]:focus{outline:none}.target-slider[data-v-61c611fd]::-webkit-slider-runnable-track{position:relative;background:transparent;border:none;height:100%;cursor:pointer}.target-slider[data-v-61c611fd]::-moz-range-track{background:transparent;border:none;height:100%;cursor:pointer}.target-slider[data-v-61c611fd]::-webkit-slider-thumb{-webkit-appearance:none;position:relative;top:calc(var(--label-height)*-1);height:100%;width:var(--thumb-width);padding:var(--label-height) var(--thumb-horizontal-padding) 0;box-sizing:content-box;background-clip:content-box;background-color:var(--bs-gray-dark);cursor:-webkit-grab;cursor:grab;border:none;opacity:1;-webkit-transition:opacity .2s ease 1s;transition:opacity .2s ease 1s}.target-slider[data-v-61c611fd]::-moz-range-thumb{position:relative;top:calc(var(--label-height)*-1);height:100%;width:var(--thumb-width);padding:var(--label-height) var(--thumb-horizontal-padding) 0;box-sizing:content-box;background-clip:content-box;background-color:var(--bs-gray-dark);cursor:grab;border:none;opacity:1;-moz-transition:opacity .2s ease 1s;transition:opacity .2s ease 1s}.target--max .target-label[data-v-61c611fd],.target--max .target-slider[data-v-61c611fd]::-webkit-slider-thumb{opacity:0}.target--max .target-label[data-v-61c611fd],.target--max .target-slider[data-v-61c611fd]::-moz-range-thumb{opacity:0}.target:hover .target-label[data-v-61c611fd],.target:hover .target-slider[data-v-61c611fd]::-webkit-slider-thumb{opacity:1;transition-delay:0s}.target:hover .target-label[data-v-61c611fd],.target:hover .target-slider[data-v-61c611fd]::-moz-range-thumb{opacity:1;transition-delay:0s}

1
dist/css/index.85babc91.css vendored Normal file
View file

@ -0,0 +1 @@
.progress-bar-animated{animation-direction:reverse}.btn.caption{opacity:1}.btn.first{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}.value,input[type=radio],label.btn{white-space:nowrap!important}.text-muted a,.text-muted a:hover{color:#6c757d!important;text-decoration:none}.bg-muted{opacity:.25}.icon[data-v-99c2e68a]{color:#0fdd42}.icon[data-v-1eecc7b7]{color:#0fdd42;display:inline-block}.sponsor-button[data-v-1eecc7b7]{touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.support-button .solid[data-v-1eecc7b7]{display:none}.support-button:hover .solid[data-v-1eecc7b7]{display:inline-block}.support-button:hover .outline[data-v-1eecc7b7]{display:none}.logo[data-v-1d8baa41]{width:85px}.app[data-v-1d8baa41]{min-height:100vh}.btn[data-v-9bc25d82]{flex-basis:0}.vehicle-soc[data-v-7a0fd750]{--height:38px;--thumb-overlap:3px;--thumb-width:3px;--thumb-horizontal-padding:15px;--label-height:26px;position:relative;height:var(--height)}.progress[data-v-7a0fd750]{height:100%;font-size:1rem}.progress-bar.bg-muted[data-v-7a0fd750]{color:var(--white)}.bg-light[data-v-7a0fd750]{color:var(--bs-gray-dark)}.target-label[data-v-7a0fd750]{width:3em;margin-left:-1.5em;height:var(--label-height);position:absolute;top:calc(var(--label-height)*-1 + var(--thumb-overlap)*-1);text-align:center;color:var(--bs-gray-dark);font-size:1rem;opacity:1;transition:opacity .2s ease 1s}.target-slider[data-v-7a0fd750]{-webkit-appearance:none;position:absolute;top:calc(var(--thumb-overlap)*-1);left:calc(var(--thumb-horizontal-padding)*-1);height:calc(100% + var(--thumb-overlap)*2);width:calc(100% + var(--thumb-horizontal-padding)*2);background:transparent}.target-slider[data-v-7a0fd750]:focus{outline:none}.target-slider[data-v-7a0fd750]::-webkit-slider-runnable-track{position:relative;background:transparent;border:none;height:100%;cursor:pointer}.target-slider[data-v-7a0fd750]::-moz-range-track{background:transparent;border:none;height:100%;cursor:pointer}.target-slider[data-v-7a0fd750]::-webkit-slider-thumb{-webkit-appearance:none;position:relative;top:calc(var(--label-height)*-1);height:100%;width:var(--thumb-width);padding:var(--label-height) var(--thumb-horizontal-padding) 0;box-sizing:content-box;background-clip:content-box;background-color:var(--bs-gray-dark);cursor:-webkit-grab;cursor:grab;border:none;opacity:1;-webkit-transition:opacity .2s ease 1s;transition:opacity .2s ease 1s}.target-slider[data-v-7a0fd750]::-moz-range-thumb{position:relative;top:calc(var(--label-height)*-1);height:100%;width:var(--thumb-width);padding:var(--label-height) var(--thumb-horizontal-padding) 0;box-sizing:content-box;background-clip:content-box;background-color:var(--bs-gray-dark);cursor:grab;border:none;opacity:1;-moz-transition:opacity .2s ease 1s;transition:opacity .2s ease 1s}.target--max .target-label[data-v-7a0fd750],.target--max .target-slider[data-v-7a0fd750]::-webkit-slider-thumb{opacity:0}.target--max .target-label[data-v-7a0fd750],.target--max .target-slider[data-v-7a0fd750]::-moz-range-thumb{opacity:0}.target:hover .target-label[data-v-7a0fd750],.target:hover .target-slider[data-v-7a0fd750]::-webkit-slider-thumb{opacity:1;transition-delay:0s}.target:hover .target-label[data-v-7a0fd750],.target:hover .target-slider[data-v-7a0fd750]::-moz-range-thumb{opacity:1;transition-delay:0s}

4
dist/index.html vendored
View file

@ -1,4 +1,4 @@
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.0f1ada3b.css" rel="preload" as="style"><link href="css/index.307488cf.css" rel="preload" as="style"><link href="js/chunk-vendors.47c2aa5a.js" rel="preload" as="script"><link href="js/index.ab413d5d.js" rel="preload" as="script"><link href="css/chunk-vendors.0f1ada3b.css" rel="stylesheet"><link href="css/index.307488cf.css" rel="stylesheet"></head><body><script>window.evcc = {
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.0f1ada3b.css" rel="preload" as="style"><link href="css/index.85babc91.css" rel="preload" as="style"><link href="js/chunk-vendors.442e4546.js" rel="preload" as="script"><link href="js/index.acd544b8.js" rel="preload" as="script"><link href="css/chunk-vendors.0f1ada3b.css" rel="stylesheet"><link href="css/index.85babc91.css" rel="stylesheet"></head><body><script>window.evcc = {
version: "[[.Version]]",
configured: "[[.Configured]]",
};</script><div id="app"></div><script src="js/chunk-vendors.47c2aa5a.js"></script><script src="js/index.ab413d5d.js"></script></body></html>
};</script><div id="app"></div><script src="js/chunk-vendors.442e4546.js"></script><script src="js/index.acd544b8.js"></script></body></html>

File diff suppressed because one or more lines are too long

1
dist/js/chunk-vendors.442e4546.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/js/index.acd544b8.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/index.acd544b8.js.map vendored Normal file

File diff suppressed because one or more lines are too long

5
package-lock.json generated
View file

@ -19544,6 +19544,11 @@
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
"integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog=="
},
"vue-i18n": {
"version": "8.24.4",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.24.4.tgz",
"integrity": "sha512-RZE94WUAGxEiBAANxQ0pptbRwDkNKNSXl3fnJslpFOxVMF6UkUtMDSuYGuW2blDrVgweIXVpethOVkYoNNT9xw=="
},
"vue-inbrowser-compiler-utils": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.37.0.tgz",

View file

@ -50,6 +50,7 @@
"canvas-confetti": "^1.4.0",
"vue": "^2.6.12",
"vue-hot-reload-api": "^2.3.4",
"vue-i18n": "^8.24.4",
"vue-router": "^3.5.1"
},
"resolutions": {