Improve i18n handling (#5342)
This commit is contained in:
parent
2fdb2eb37f
commit
5ddf3c841f
34 changed files with 1552 additions and 3309 deletions
|
|
@ -3,7 +3,7 @@ import "bootstrap/dist/css/bootstrap.min.css";
|
|||
import "bootstrap";
|
||||
import smoothscroll from "smoothscroll-polyfill";
|
||||
import VueNumber from "vue-number-animation";
|
||||
import i18n from "./js/i18n";
|
||||
import setupI18n from "./js/i18n";
|
||||
import "./css/app.css";
|
||||
import { watchThemeChanges } from "./js/theme";
|
||||
|
||||
|
|
@ -11,6 +11,6 @@ smoothscroll.polyfill();
|
|||
watchThemeChanges();
|
||||
|
||||
export const setupVue3 = defineSetupVue3(({ app }) => {
|
||||
app.use(i18n);
|
||||
app.use(setupI18n());
|
||||
app.use(VueNumber);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"embed"
|
||||
)
|
||||
|
||||
//go:embed *.toml
|
||||
var LocaleFS embed.FS
|
||||
|
|
@ -15,7 +15,7 @@ dark = "Design: donker"
|
|||
availableShort = "update"
|
||||
availableLong = "update beschikbaar"
|
||||
modalTitle = "Update beschikbaar"
|
||||
modalUpdateStarted = "Evcc zal herstarten na de update.."
|
||||
modalUpdateStarted = "Evcc zal herstarten na de update."
|
||||
modalInstalledVersion = "Huidige versie"
|
||||
modalNoReleaseNotes = "Geen release notes beschikbaar. Meer informatie over de nieuwe versie vind je hier:"
|
||||
modalCancel = "Annuleer"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ import axios from "axios";
|
|||
|
||||
const { protocol, hostname, port, pathname } = window.location;
|
||||
|
||||
const base = protocol + "//" + hostname + (port ? ":" + port : "") + pathname;
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: protocol + "//" + hostname + (port ? ":" + port : "") + pathname + "api/",
|
||||
baseURL: base + "api/",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
|
|
@ -19,4 +21,12 @@ api.interceptors.response.use(
|
|||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
|
||||
export const i18n = axios.create({
|
||||
baseURL: base + "i18n/",
|
||||
headers: {
|
||||
Accept: "application/toml",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { createApp, h } from "vue";
|
|||
import { createMetaManager, plugin as metaPlugin } from "vue-meta";
|
||||
import App from "./views/App.vue";
|
||||
import VueNumber from "vue-number-animation";
|
||||
import router from "./router";
|
||||
import i18n from "./i18n";
|
||||
import setupRouter from "./router";
|
||||
import setupI18n from "./i18n";
|
||||
import featureflags from "./featureflags";
|
||||
import { watchThemeChanges } from "./theme";
|
||||
|
||||
|
|
@ -64,8 +64,9 @@ const app = createApp({
|
|||
},
|
||||
});
|
||||
|
||||
const i18n = setupI18n();
|
||||
app.use(i18n);
|
||||
app.use(router);
|
||||
app.use(setupRouter(i18n));
|
||||
app.use(createMetaManager());
|
||||
app.use(metaPlugin);
|
||||
app.use(featureflags);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import toml from "toml";
|
||||
import { nextTick } from "vue";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import de from "../i18n/de.toml";
|
||||
import en from "../i18n/en.toml";
|
||||
import fr from "../i18n/fr.toml";
|
||||
import it from "../i18n/it.toml";
|
||||
import lt from "../i18n/lt.toml";
|
||||
import nl from "../i18n/nl.toml";
|
||||
import nb_no from "../i18n/nb_NO.toml";
|
||||
import pl from "../i18n/pl.toml";
|
||||
import pt from "../i18n/pt.toml";
|
||||
import en from "../../i18n/en.toml";
|
||||
import { i18n as i18nApi } from "./api";
|
||||
|
||||
const PREFERRED_LOCALE_KEY = "preferred_locale";
|
||||
|
||||
|
|
@ -20,8 +15,43 @@ function getBrowserLocale() {
|
|||
return navigatorLocale.trim().split(/-|_/)[0];
|
||||
}
|
||||
|
||||
export default createI18n({
|
||||
locale: window.localStorage[PREFERRED_LOCALE_KEY] || getBrowserLocale(),
|
||||
function getLocale() {
|
||||
return window.localStorage[PREFERRED_LOCALE_KEY] || getBrowserLocale();
|
||||
}
|
||||
|
||||
export default function setupI18n() {
|
||||
const i18n = createI18n({
|
||||
legacy: true,
|
||||
silentFallbackWarn: true,
|
||||
silentTranslationWarn: true,
|
||||
locale: "en",
|
||||
fallbackLocale: "en",
|
||||
messages: { de, en, fr, it, lt, nb_no, nl, pl, pt },
|
||||
});
|
||||
messages: { en },
|
||||
});
|
||||
setI18nLanguage(i18n, getLocale());
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export function setI18nLanguage(i18n, locale) {
|
||||
i18n.global.locale = locale;
|
||||
document.querySelector("html").setAttribute("lang", locale);
|
||||
}
|
||||
|
||||
async function loadLocaleMessages(i18n, locale) {
|
||||
try {
|
||||
const response = await i18nApi.get(`${locale}.toml`, { params: { v: window.evcc?.version } });
|
||||
const messages = toml.parse(response.data);
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
} catch (e) {
|
||||
console.error(`unable to load translation for [${locale}]`, e);
|
||||
}
|
||||
|
||||
return nextTick();
|
||||
}
|
||||
|
||||
export async function ensureCurrentLocaleMessages(i18n) {
|
||||
const { locale } = i18n.global;
|
||||
if (!i18n.global.availableLocales.includes(locale)) {
|
||||
await loadLocaleMessages(i18n, locale);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,19 @@ import { createRouter, createWebHashHistory } from "vue-router";
|
|||
|
||||
import Main from "./views/Main.vue";
|
||||
import ChargingSessions from "./views/ChargingSessions.vue";
|
||||
import { ensureCurrentLocaleMessages } from "./i18n";
|
||||
|
||||
export default createRouter({
|
||||
export default function setupRouter(i18n) {
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{ path: "/", component: Main, props: true },
|
||||
{ path: "/sessions", component: ChargingSessions, props: true },
|
||||
],
|
||||
});
|
||||
});
|
||||
router.beforeEach(async () => {
|
||||
await ensureCurrentLocaleMessages(i18n);
|
||||
return true;
|
||||
});
|
||||
return router;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
74
dist/assets/index.4e414929.js
vendored
Normal file
74
dist/assets/index.4e414929.js
vendored
Normal file
File diff suppressed because one or more lines are too long
70
dist/assets/index.841a487a.js
vendored
70
dist/assets/index.841a487a.js
vendored
File diff suppressed because one or more lines are too long
6
dist/index.html
vendored
6
dist/index.html
vendored
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
|
@ -20,8 +20,8 @@
|
|||
<meta name="theme-color" content="#020318" />
|
||||
|
||||
<title>evcc</title>
|
||||
<script type="module" crossorigin src="./assets/index.841a487a.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.932fb253.css">
|
||||
<script type="module" crossorigin src="./assets/index.4e414929.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.14ecea29.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -114,6 +114,9 @@ require (
|
|||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.19.2 // indirect
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -264,6 +264,12 @@ github.com/glebarez/sqlite v1.5.0/go.mod h1:0wzXzTvfVJIN2GqRhCdMbnYd+m+aH5/QV7B3
|
|||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 h1:zga7zaRE8HCbWjcXMDlfvmQtH0/kMVLo7cQ48dy6kWg=
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1/go.mod h1:PumS+5d59wmAGsZo6IfRpVNaJUq+6xjC4Utt/k8GO6Q=
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 h1:O6yi4xa9b2DMosGsXzlMe2E9qXgXCVkRLCoRX+5amxI=
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27/go.mod h1:AYvN8omj7nKLmbcXS2dyABYU6JB1Lz1bHmkkq1kf4I4=
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ dark = "Design: dark"
|
|||
availableShort = "update"
|
||||
availableLong = "update available"
|
||||
modalTitle = "Update available"
|
||||
modalUpdateStarted = "Evcc will restart after the update.."
|
||||
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"
|
||||
220
i18n/nl.toml
Normal file
220
i18n/nl.toml
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
[header]
|
||||
sessions = "Laadsessies"
|
||||
docs = "Documentatie"
|
||||
blog = "Blog"
|
||||
github = "GitHub"
|
||||
login = "Voertuig logins"
|
||||
about = "Over evcc"
|
||||
|
||||
[header.theme]
|
||||
auto = "Design: systeem"
|
||||
light = "Design: licht"
|
||||
dark = "Design: donker"
|
||||
|
||||
[footer.version]
|
||||
availableShort = "update"
|
||||
availableLong = "update beschikbaar"
|
||||
modalTitle = "Update beschikbaar"
|
||||
modalUpdateStarted = "Evcc zal herstarten na de update."
|
||||
modalInstalledVersion = "Huidige versie"
|
||||
modalNoReleaseNotes = "Geen release notes beschikbaar. Meer informatie over de nieuwe versie vind je hier:"
|
||||
modalCancel = "Annuleer"
|
||||
modalUpdate = "Update"
|
||||
modalUpdateNow = "Nu updaten"
|
||||
modalDownload = "Download"
|
||||
modalUpdateStatusStart = "Update gestart:"
|
||||
modalUpdateStatusFailed = "Update mislukt:"
|
||||
|
||||
[footer.savings]
|
||||
tabTitle = "Mijn data"
|
||||
footerShort = "{percent}% PV"
|
||||
footerLong = "{percent}% zonne-energie"
|
||||
modalTitle = "Energieoverzicht"
|
||||
since = "sinds {since}"
|
||||
percentTitle = "Zonne-energie"
|
||||
percentSelf = "{self} kWh PV"
|
||||
percentGrid = "{grid} kWh afgenomen"
|
||||
priceTitle = "Energieprijs"
|
||||
priceFeedIn = "{feedInPrice} injectie"
|
||||
priceGrid = "{gridPrice} afname"
|
||||
savingsTitle = "Besparing"
|
||||
savingsComparedToGrid = "i.v.m. netafname"
|
||||
savingsTotalEnergy = "{total} kWh geladen"
|
||||
|
||||
[footer.community]
|
||||
tabTitle = "Live community"
|
||||
power = "Laadvermogen"
|
||||
powerSub1 = "{activeClients} van {totalClients} deelnemers"
|
||||
powerSub2 = "zijn nu aan het laden"
|
||||
greenShare = "Zonne-energie"
|
||||
greenShareSub1 = "van de energie"
|
||||
greenShareSub2 = "komt van PV & batterijopslag"
|
||||
greenEnergy = "Zonne-energie"
|
||||
greenEnergySub1 = "geladen met evcc"
|
||||
greenEnergySub2 = "sinds oktober 2022"
|
||||
|
||||
[footer.telemetry]
|
||||
optIn = "Ik wil mijn data ook bijdragen."
|
||||
optInMoreDetails = "Meer info is {0} beschikbaar."
|
||||
optInMoreDetailsLink = "hier"
|
||||
optInSponsorship = "(Sponsoring nodig)"
|
||||
|
||||
[footer.sponsor]
|
||||
titleSponsor = "Je bent een sponsor!"
|
||||
titleNoSponsor = "Sponsor ons"
|
||||
thanks = "Bedankt voor je hulp, {sponsor}! Je helpt ons bij de verdere ontwikkeling van evcc."
|
||||
confetti = "Klaar voor confetti?"
|
||||
supportUs = "Onze missie: Van laden op zonne-energie de standaard maken. Help ons en draag financieel bij aan evcc."
|
||||
sticker = "… of evcc stickers?"
|
||||
confettiPromise = "Er zullen stickers en digitale confetti zijn ;)"
|
||||
becomeSponsor = "Word sponsor"
|
||||
|
||||
[notifications]
|
||||
modalTitle = "Notificaties"
|
||||
dismissAll = "Alles verwijderen"
|
||||
|
||||
[main]
|
||||
vehicles = "Parking"
|
||||
|
||||
[main.energyflow]
|
||||
noEnergy = "Geen meter data"
|
||||
homePower = "Consumptie"
|
||||
pvProduction = "Productie"
|
||||
loadpoints = "Laadpunt | Laadpunt | {count} Laadpunten"
|
||||
battery = "Batterij"
|
||||
batteryCharge = "Batterij laden"
|
||||
batteryDischarge = "Batterij ontladen"
|
||||
gridImport = "Netafname"
|
||||
selfConsumption = "Zelfverbruik"
|
||||
pvExport = "Netinjectie"
|
||||
|
||||
[main.mode]
|
||||
off = "Uit"
|
||||
minpv = "Min+PV"
|
||||
pv = "PV"
|
||||
now = "Snel"
|
||||
|
||||
[main.loadpoint]
|
||||
fallbackName = "Laadpunt"
|
||||
remoteDisabledSoft = "{source}: adaptief PV-laden uitgeschakeld"
|
||||
remoteDisabledHard = "{source}: uitgeschakeld"
|
||||
power = "Snelheid"
|
||||
charged = "Geladen"
|
||||
duration = "Duur"
|
||||
remaining = "Resterend"
|
||||
|
||||
[main.loadpointSettings]
|
||||
title = 'Instellingen "{0}"'
|
||||
vehicle = "Voertuig"
|
||||
currents = "Opladen"
|
||||
default = "standaard"
|
||||
disclaimerHint = "Letop:"
|
||||
disclaimerText = "Instellingen worden niet bijgehouden. Ze worden gereset bij het herstarten van de server."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
label = "Min batterijniveau"
|
||||
description = 'Noodbereik. Voertuig wordt "snel" geladen tot {0}% in PV mode. Daarna wordt enkel de PV-overschot gebruikt.'
|
||||
|
||||
[main.loadpointSettings.phasesConfigured]
|
||||
label = "Fasen"
|
||||
phases_0 = "automatisch wisselend"
|
||||
phases_1 = "1 fase"
|
||||
phases_1_hint = "({min} - {max})"
|
||||
phases_3 = "3 fasen"
|
||||
phases_3_hint = "({min} - {max})"
|
||||
|
||||
[main.loadpointSettings.maxCurrent]
|
||||
label = "Max. vermogen"
|
||||
|
||||
[main.loadpointSettings.minCurrent]
|
||||
label = "Min. vermogen"
|
||||
|
||||
[main.vehicle]
|
||||
fallbackName = "Voertuig"
|
||||
vehicleSoC = "Batterijniveau"
|
||||
targetSoC = "Limiet"
|
||||
none = "Geen voertuig"
|
||||
unknown = "Gast"
|
||||
changeVehicle = "Wijzig voertuig"
|
||||
detectionActive = "Voertuig detecteren ..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
disconnected = "niet verbonden"
|
||||
charging = "laden"
|
||||
ready = "klaar"
|
||||
connected = "verbonden"
|
||||
vehicleTarget = "Voertuiglimiet: {soc}%"
|
||||
|
||||
[main.vehicleStatus]
|
||||
minCharge = "laden naar {soc}% minimum."
|
||||
waitForVehicle = "Klaar. Wachten op voertuig."
|
||||
vehicleTargetReached = "Voertuiglimiet {soc}% bereikt."
|
||||
charging = "Laden."
|
||||
targetChargePlanned = "Doel gepland. Starten op {time}."
|
||||
targetChargeWaitForVehicle = "Doel gepland. Wachten op voertuig."
|
||||
targetChargeActive = "Doelladen actief."
|
||||
connected = "Verbonden."
|
||||
pvDisable = "Onvoldoende PV overschot beschikbaar. Pauzeren over {remaining}."
|
||||
pvEnable = "Voldoende PV overschot beschikbaar. Starten over {remaining}."
|
||||
scale1p = "Wisselen naar 1 fase over {remaining}."
|
||||
scale3p = "Wisselen naar 3 fasen over {remaining}."
|
||||
disconnected = "Niet verbonden."
|
||||
unknown = ""
|
||||
|
||||
[main.provider]
|
||||
login = "aanmelden"
|
||||
logout = "afmelden"
|
||||
|
||||
[main.targetCharge]
|
||||
title = "Doel"
|
||||
inactiveLabel = "Ingestelde tijd"
|
||||
activeLabel = "{time}"
|
||||
modalTitle = "Stel tijd in"
|
||||
setTargetTime = "geen"
|
||||
description = "Tegen wanneer moet het voertuig geladen zijn naar {targetSoC}%?"
|
||||
today = "Vandaag"
|
||||
tomorrow = "Morgen"
|
||||
targetIsInThePast = "De ingestelde tijd ligt in het verleden"
|
||||
remove = "Verwijder"
|
||||
activate = "Activeer"
|
||||
experimentalLabel = "Experimenteel"
|
||||
experimentalText = "Deze feature werkt nog niet perfect. Rapporteer ongewenst gedrag op"
|
||||
|
||||
[main.targetEnergy]
|
||||
label = "Limiet"
|
||||
noLimit = "geen"
|
||||
|
||||
[startupError]
|
||||
title = "Start Error"
|
||||
description = "Check je evcc configuratie. Helpt de foutboodschap je niet, bekijk dan de {0}."
|
||||
discussions = "GitHub Discussions"
|
||||
hint = "Je kan deze boodschap ook krijgen bij een kapot toestel (inverter, meter, ...). Check je network connectie."
|
||||
configuration = "Config"
|
||||
configFile = "Gebruikt configuratiebestand:"
|
||||
lineError = "Fout gedetecteerd op {0}."
|
||||
lineErrorLink = "lijn {0}"
|
||||
fixAndRestart = "Verhelp het probleem en herstart de server."
|
||||
restartButton = "Herstart"
|
||||
|
||||
[sessions]
|
||||
title = "Laadsessies"
|
||||
downloadCsv = "Download CSV"
|
||||
loadpoint = "Laadpunt"
|
||||
vehicle = "Voertuig"
|
||||
energy = "Geladen"
|
||||
date = "Periode"
|
||||
|
||||
[sessions.csv]
|
||||
loadpoint = "Laadpunt"
|
||||
vehicle = "Voertuig"
|
||||
odometer = "kilometerstand (km)"
|
||||
identifier = "ID"
|
||||
chargedenergy = "Energie (kWh)"
|
||||
meterstart = "Meter Start (kWh)"
|
||||
meterstop = "Meter Stop (kWh)"
|
||||
created = "Gestart"
|
||||
finished = "Beëindigd"
|
||||
|
||||
[offline]
|
||||
message = "Geen verbinding met de server."
|
||||
reload = "Opnieuw laden?"
|
||||
24
main.go
24
main.go
|
|
@ -9,23 +9,35 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/cmd"
|
||||
"github.com/evcc-io/evcc/server"
|
||||
"github.com/evcc-io/evcc/server/assets"
|
||||
_ "github.com/evcc-io/evcc/util/goversion" // require minimum go version
|
||||
)
|
||||
|
||||
//go:embed dist
|
||||
var assets embed.FS
|
||||
var (
|
||||
//go:embed dist
|
||||
web embed.FS
|
||||
|
||||
//go:embed i18n/*.toml
|
||||
i18n embed.FS
|
||||
)
|
||||
|
||||
// init loads embedded assets unless live assets are already loaded
|
||||
func init() {
|
||||
// TODO remove this once Go 1.20 is out with it.
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if server.Assets == nil {
|
||||
fsys, err := fs.Sub(assets, "dist")
|
||||
if !assets.Live() {
|
||||
var err error
|
||||
|
||||
assets.Web, err = fs.Sub(web, "dist")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
assets.I18n, err = fs.Sub(i18n, "i18n")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.Assets = fsys
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
4256
package-lock.json
generated
4256
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
|
@ -18,9 +18,11 @@
|
|||
"bootstrap": "^5.2.2",
|
||||
"canvas-confetti": "^1.4.0",
|
||||
"smoothscroll-polyfill": "^0.4.4",
|
||||
"toml": "^3.0.0",
|
||||
"vite-plugin-toml": "^0.5.0",
|
||||
"vue": "^3.2.29",
|
||||
"vue-hot-reload-api": "^2.3.4",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-meta": "^3.0.0-alpha.2",
|
||||
"vue-number-animation": "^1.1.2",
|
||||
"vue-router": "^4.0.12"
|
||||
|
|
@ -29,7 +31,6 @@
|
|||
"@babel/core": "^7.17.2",
|
||||
"@histoire/plugin-vue": "^0.11.6",
|
||||
"@vue/compiler-sfc": "^3.2.30",
|
||||
"babel-loader": "^8.2.3",
|
||||
"eslint": "^8.27.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
|
|
@ -38,14 +39,11 @@
|
|||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"histoire": "^0.11.6",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"postcss-custom-media": "^8.0.2",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-toml": "^0.3.1",
|
||||
"rollup-plugin-analyzer": "^4.0.0",
|
||||
"vite": "^3.0.4",
|
||||
"vite-plugin-toml": "^0.4.0",
|
||||
"vue-eslint-parser": "^9.1.0",
|
||||
"vue-loader": "^17.0.0"
|
||||
"vue-eslint-parser": "^9.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=8.0.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
const postcssCustomMedia = require("postcss-custom-media");
|
||||
|
||||
module.exports = {
|
||||
plugins: [postcssCustomMedia()],
|
||||
};
|
||||
16
server/assets/assets.go
Normal file
16
server/assets/assets.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package assets
|
||||
|
||||
import "io/fs"
|
||||
|
||||
var (
|
||||
// Web is the embedded dist file system
|
||||
Web fs.FS
|
||||
|
||||
// I18n is the embedded i18n file system
|
||||
I18n fs.FS
|
||||
)
|
||||
|
||||
// Live indicates assets are passed-through from filesystem
|
||||
func Live() bool {
|
||||
return Web != nil
|
||||
}
|
||||
12
server/assets/assets_live.go
Normal file
12
server/assets/assets_live.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//go:build !release
|
||||
|
||||
package assets
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Web = os.DirFS("dist")
|
||||
I18n = os.DirFS("i18n")
|
||||
}
|
||||
|
|
@ -2,20 +2,18 @@ package server
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/core/site"
|
||||
"github.com/evcc-io/evcc/server/assets"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/telemetry"
|
||||
"github.com/go-http-utils/etag"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Assets is the embedded assets file system
|
||||
var Assets fs.FS
|
||||
|
||||
type route struct {
|
||||
Methods []string
|
||||
Pattern string
|
||||
|
|
@ -53,11 +51,15 @@ func NewHTTPd(addr string, hub *SocketHub) *HTTPd {
|
|||
// static - individual handlers per root and folders
|
||||
static := router.PathPrefix("/").Subrouter()
|
||||
static.Use(handlers.CompressHandler)
|
||||
static.Use(handlers.CompressHandler, func(h http.Handler) http.Handler {
|
||||
return etag.Handler(h, false)
|
||||
})
|
||||
|
||||
static.HandleFunc("/", indexHandler())
|
||||
for _, dir := range []string{"assets", "meta"} {
|
||||
static.PathPrefix("/" + dir).Handler(http.FileServer(http.FS(Assets)))
|
||||
static.PathPrefix("/" + dir).Handler(http.FileServer(http.FS(assets.Web)))
|
||||
}
|
||||
static.PathPrefix("/i18n").Handler(http.StripPrefix("/i18n", http.FileServer(http.FS(assets.I18n))))
|
||||
|
||||
srv := &HTTPd{
|
||||
Server: &http.Server{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/evcc-io/evcc/core/db"
|
||||
"github.com/evcc-io/evcc/core/loadpoint"
|
||||
"github.com/evcc-io/evcc/core/site"
|
||||
"github.com/evcc-io/evcc/server/assets"
|
||||
dbserver "github.com/evcc-io/evcc/server/db"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/locale"
|
||||
|
|
@ -28,7 +29,7 @@ func indexHandler() http.HandlerFunc {
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
|
||||
|
||||
indexTemplate, err := fs.ReadFile(Assets, "index.html")
|
||||
indexTemplate, err := fs.ReadFile(assets.Web, "index.html")
|
||||
if err != nil {
|
||||
log.FATAL.Print("httpd: failed to load embedded template:", err.Error())
|
||||
log.FATAL.Print("Make sure templates are included using the `release` build tag or use `make build`")
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
//go:build !release
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Assets = os.DirFS("dist")
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@ package locale
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/cloudfoundry/jibber_jabber"
|
||||
assets "github.com/evcc-io/evcc/assets/i18n"
|
||||
"github.com/evcc-io/evcc/server/assets"
|
||||
"github.com/evcc-io/evcc/util/locale/internal"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/text/language"
|
||||
|
|
@ -25,13 +26,13 @@ func Init() error {
|
|||
Bundle = i18n.NewBundle(language.English)
|
||||
Bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
|
||||
|
||||
dir, err := assets.LocaleFS.ReadDir(".")
|
||||
dir, err := fs.ReadDir(assets.I18n, ".")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, d := range dir {
|
||||
if _, err := Bundle.LoadMessageFileFS(assets.LocaleFS, d.Name()); err != nil {
|
||||
if _, err := Bundle.LoadMessageFileFS(assets.I18n, d.Name()); err != nil {
|
||||
return fmt.Errorf("loading locales failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { defineConfig } from "vite";
|
||||
import vuePlugin from "@vitejs/plugin-vue";
|
||||
import { ViteToml } from "vite-plugin-toml";
|
||||
//import analyze from "rollup-plugin-analyzer";
|
||||
|
||||
export default defineConfig({
|
||||
root: "./assets",
|
||||
|
|
@ -20,6 +21,7 @@ export default defineConfig({
|
|||
port: 7071,
|
||||
proxy: {
|
||||
"/api": "http://localhost:7070",
|
||||
"/i18n": "http://localhost:7070",
|
||||
"/ws": { target: "ws://localhost:7070", ws: true },
|
||||
},
|
||||
},
|
||||
|
|
@ -32,5 +34,6 @@ export default defineConfig({
|
|||
},
|
||||
}),
|
||||
ViteToml(),
|
||||
//analyze(),
|
||||
],
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue