Multilanguage support (#960)
This commit is contained in:
parent
380cd461b9
commit
27be0806cc
37 changed files with 277 additions and 59 deletions
|
|
@ -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 } });
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
|
|
|
|||
|
|
@ -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
73
assets/js/i18n/de.js
Normal 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
73
assets/js/i18n/en.js
Normal 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
22
assets/js/i18n/index.js
Normal 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 },
|
||||
});
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
1
dist/css/index.307488cf.css
vendored
1
dist/css/index.307488cf.css
vendored
|
|
@ -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
1
dist/css/index.85babc91.css
vendored
Normal 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
4
dist/index.html
vendored
|
|
@ -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
1
dist/js/chunk-vendors.442e4546.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/js/chunk-vendors.47c2aa5a.js.map
vendored
1
dist/js/chunk-vendors.47c2aa5a.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/js/index.ab413d5d.js
vendored
2
dist/js/index.ab413d5d.js
vendored
File diff suppressed because one or more lines are too long
1
dist/js/index.ab413d5d.js.map
vendored
1
dist/js/index.ab413d5d.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/js/index.acd544b8.js
vendored
Normal file
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
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
5
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue