feat: translate all pages
Some checks failed
TrafficCue CI / check (push) Failing after 32s
TrafficCue CI / build (push) Failing after 35s

This commit is contained in:
Cfp
2025-08-14 16:05:35 +02:00
parent b3f3c4f0b2
commit 4e3f1b06b2
23 changed files with 359 additions and 106 deletions

View File

@ -5,5 +5,122 @@
"school": "Schule",
"work": "Arbeit",
"no-location": "Kein {name} Speicherort gespeichert."
},
"location": {
"unlock": "Standort entsperren",
"lock": "Standort sperren",
"code": "Standortcode",
"start": "Standort teilen",
"join": "Remote-Standort beitreten"
},
"vehicles": {
"selector": {
"title": "Fahrzeugauswahl",
"description": "Wählen Sie Ihr Fahrzeug aus, um die Route individuell auf Sie abzustimmen.",
"add": "Fahrzeug hinzufügen"
},
"types": {
"car": "Auto",
"moped": "Moped"
},
"add": {
"name": "Fahrzeugname",
"legal-speed": "Legale Geschwindigkeit",
"actual-speed": "Tatsächliche Geschwindigkeit",
"fuel": "Kraftstoffart",
"preferred-fuel": "Bevorzugter Kraftstoff",
"diesel": "Diesel",
"petrol": "Benzin",
"electric": "Elektrisch",
"errors": {
"enter-name": "Bitte geben Sie einen Fahrzeugnamen ein.",
"enter-speeds": "Bitte geben Sie gültige Geschwindigkeiten ein.",
"select-fuel": "Bitte wählen Sie eine gültige Kraftstoffart und den bevorzugten Kraftstoff aus."
}
},
"default": "Standardfahrzeug"
},
"save": "Speichern",
"cancel": "Abbrechen",
"loading": "Laden...",
"more": "Mehr",
"error": "Fehler",
"sidebar": {
"about": {
"header": "Über",
"powered-by": "Angetrieben von",
"contributors": "Mitwirkende"
},
"developer": {
"header": "Entwicklereinstellungen"
},
"offline-maps": {
"header": "Offline-Karten",
"not-available": "Keine Offline-Karten verfügbar.",
"only-mobile": "Offline-Karten sind nur auf Mobilgeräten verfügbar.",
"downloaded": "{name} wurde heruntergeladen"
},
"language": {
"header": "Sprache"
},
"map-style": {
"header": "Kartenstil"
},
"settings": {
"header": "Einstellungen",
"general": "Allgemein",
"map": "Karte"
},
"info": {
"dropped": "Pin",
"route": "Route",
"email": "E-Mail",
"website": "Webseite",
"call": "Anrufen",
"set-as": "Speichern als {name}",
"payment-methods": "Zahlungsarten",
"fuel-types": "Kraftstoffarten",
"prices": "Kraftstoffpreise",
"no-prices": "Keine Kraftstoffpreise verfügbar.",
"error-loading-prices": "Fehler beim Laden der Kraftstoffpreise",
"internet-access": "Internetzugang",
"fee": "Gebühr",
"opening-hours": "Öffnungszeiten",
"open": "Offen",
"closed": "Geschlossen",
"restaurant": "Restaurant",
"reviews": "Bewertungen",
"no-reviews": "Keine Bewertungen.",
"write-review": "Eigene Bewertung schreiben"
},
"mapai": {
"ask-question": "Stellen Sie eine Frage zu diesem Ort ..."
},
"in-route": {
"left": "links",
"end-trip": "Route beenden",
"share-code": "Code teilen",
"stop-sharing": "Standortfreigabe beenden",
"share-location": "Routenstatus und Standort teilen"
},
"route": {
"header": "Route",
"driving-with": "Fahren mit",
"help": "Sie können <strong>aktuell</strong> für Ihren aktuellen Standort und <strong>home</strong>, <strong>school</strong> oder <strong>work</strong> für gespeicherte Standorte verwenden.",
"calculate": "Berechnen"
},
"search": {
"header": "Suchergebnisse für"
},
"trip": {
"header": "Routendetails",
"start": "Start",
"save": "Speichern",
"send": "Senden"
},
"user": {
"header": "Benutzer",
"login": "Login"
}
}
}

View File

@ -1,9 +1,126 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"save": "Save",
"cancel": "Cancel",
"loading": "Loading...",
"more": "More",
"error": "Error",
"saved": {
"home": "Home",
"school": "School",
"work": "Work",
"no-location": "No {name} location saved."
},
"location": {
"unlock": "Unlock Location",
"lock": "Lock Location",
"code": "Advertise code",
"start": "Advertise Location",
"join": "Join Remote Location"
},
"vehicles": {
"default": "Default Vehicle",
"selector": {
"title": "Vehicle Selector",
"description": "Select your vehicle to customize routing just for you.",
"add": "Add Vehicle"
},
"types": {
"car": "Car",
"moped": "Moped"
},
"add": {
"name": "Vehicle Name",
"legal-speed": "Legal Speed",
"actual-speed": "Actual Speed",
"fuel": "Fuel Type",
"preferred-fuel": "Preferred Fuel",
"diesel": "Diesel",
"petrol": "Petrol",
"electric": "Electric",
"errors": {
"enter-name": "Please enter a vehicle name.",
"enter-speeds": "Please enter valid speeds.",
"select-fuel": "Please select a valid fuel type and preferred fuel."
}
}
},
"sidebar": {
"about": {
"header": "About",
"powered-by": "Powered by",
"contributors": "contributors"
},
"developer": {
"header": "Developer Settings"
},
"offline-maps": {
"header": "Offline Maps",
"not-available": "No offline maps available.",
"only-mobile": "Offline maps are only available on mobile.",
"downloaded": "Downloaded {name}"
},
"language": {
"header": "Language"
},
"map-style": {
"header": "Map Style"
},
"settings": {
"header": "Settings",
"general": "General",
"map": "Map"
},
"info": {
"dropped": "Dropped Pin",
"route": "Route",
"email": "Email",
"website": "Website",
"call": "Call",
"set-as": "Set as {name}",
"payment-methods": "Payment Methods",
"fuel-types": "Fuel Types",
"prices": "Fuel Prices",
"no-prices": "No fuel prices available.",
"error-loading-prices": "Error loading fuel prices",
"internet-access": "Internet Access",
"fee": "Fee",
"opening-hours": "Opening Hours",
"open": "Open",
"closed": "Closed",
"restaurant": "Restaurant",
"reviews": "Reviews",
"no-reviews": "No reviews.",
"write-review": "Write a review"
},
"mapai": {
"ask-question": "Ask a question about this place..."
},
"in-route": {
"left": "left",
"end-trip": "End Trip",
"share-code": "Share Code",
"stop-sharing": "Stop Sharing Location",
"share-location": "Share Trip Status & Location"
},
"route": {
"header": "Route",
"driving-with": "Driving with",
"help": "You can use <strong>current</strong> for your current location, <strong>home</strong>, <strong>school</strong> or <strong>work</strong> for saved locations.",
"calculate": "Calculate"
},
"search": {
"header": "Search Results for"
},
"trip": {
"header": "Trip Details",
"start": "Start Navigation",
"save": "Save",
"send": "Send"
},
"user": {
"header": "User",
"login": "Login"
}
}
}

View File

@ -21,6 +21,7 @@
import * as Select from "../ui/select";
import Input from "../ui/input/input.svelte";
import EvConnectorSelect from "./EVConnectorSelect.svelte";
import { m } from "$lang/messages";
let open = $state(false);
@ -62,7 +63,7 @@
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Add Vehicle</Drawer.Title>
<Drawer.Title>{m["vehicles.selector.add"]()}</Drawer.Title>
</Drawer.Header>
<div class="p-4 pt-0 flex flex-col gap-2">
<div class="flex gap-2">
@ -71,75 +72,75 @@
{@const Icon = getVehicleIcon(vehicle.type)}
<Icon />
{vehicle.type === "car"
? "Car"
? m["vehicles.types.car"]()
: vehicle.type === "motor_scooter"
? "Moped"
? m["vehicles.types.moped"]()
: "?"}
</Select.Trigger>
<Select.Content>
<Select.Item value="car">
<CarIcon />
Car
{m["vehicles.types.car"]()}
</Select.Item>
<Select.Item value="motor_scooter">
<TractorIcon />
Moped
{m["vehicles.types.moped"]()}
</Select.Item>
</Select.Content>
</Select.Root>
<Input
type="text"
placeholder="Vehicle Name"
placeholder={m["vehicles.add.name"]()}
bind:value={vehicle.name}
class="w-full"
aria-label="Vehicle Name"
aria-label={m["vehicles.add.name"]()}
aria-required="true"
/>
</div>
<div class="flex justify-around mt-4">
<span>Legal Speed</span>
<span>{m["vehicles.add.legal-speed"]()}</span>
<span>/</span>
<span>Actual Speed</span>
<span>{m["vehicles.add.actual-speed"]()}</span>
</div>
<div class="flex gap-2">
<Input
type="number"
placeholder="Legal Speed"
placeholder={m["vehicles.add.legal-speed"]()}
bind:value={vehicle.legalMaxSpeed}
class="w-full text-center"
aria-label="Legal Max Speed"
aria-label={m["vehicles.add.legal-speed"]()}
aria-required="true"
/>
<Input
type="number"
placeholder="Actual Speed"
placeholder={m["vehicles.add.actual-speed"]()}
bind:value={vehicle.actualMaxSpeed}
class="w-full text-center"
aria-label="Actual Max Speed"
aria-label={m["vehicles.add.actual-speed"]()}
aria-required="true"
/>
</div>
<div class="flex justify-around mt-4">
<span>Fuel Type</span>
<span>{m["vehicles.add.fuel"]()}</span>
<span>/</span>
<span>Preferred Fuel</span>
<span>{m["vehicles.add.preferred-fuel"]()}</span>
</div>
<div class="flex gap-2">
<Select.Root type="single" bind:value={vehicle.fuelType}>
<Select.Trigger class="w-full">
{vehicle.fuelType === "diesel"
? "Diesel"
? m["vehicles.add.diesel"]()
: vehicle.fuelType === "petrol"
? "Petrol"
: "Electric"}
? m["vehicles.add.petrol"]()
: m["vehicles.add.electric"]()}
</Select.Trigger>
<Select.Content>
<Select.Item value="diesel">Diesel</Select.Item>
<Select.Item value="petrol">Petrol</Select.Item>
<Select.Item value="electric">Electric</Select.Item>
<Select.Item value="diesel">{m["vehicles.add.diesel"]()}</Select.Item>
<Select.Item value="petrol">{m["vehicles.add.petrol"]()}</Select.Item>
<Select.Item value="electric">{m["vehicles.add.electric"]()}</Select.Item>
</Select.Content>
</Select.Root>
@ -165,15 +166,15 @@
onclick={() => {
open = false;
if (vehicle.name.trim() === "") {
alert("Please enter a vehicle name.");
alert(m["vehicles.add.errors.enter-name"]());
return;
}
if (vehicle.legalMaxSpeed <= 0 || vehicle.actualMaxSpeed <= 0) {
alert("Please enter valid speeds.");
alert(m["vehicles.add.errors.enter-speeds"]());
return;
}
if (!isValidFuel(vehicle)) {
alert("Please select a valid fuel type and preferred fuel.");
alert(m["vehicles.add.errors.select-fuel"]());
return;
}
setVehicles([...vehicles, vehicle]);
@ -182,7 +183,7 @@
}}
>
<SaveIcon />
Save
{m.save()}
</Button>
<Button
variant="secondary"
@ -191,7 +192,7 @@
}}
>
<XIcon />
Cancel
{m.cancel()}
</Button>
</Drawer.Footer>
</Drawer.Content>

View File

@ -31,6 +31,7 @@
import AboutSidebar from "./sidebar/settings/AboutSidebar.svelte";
import OfflineMapsSidebar from "./sidebar/settings/OfflineMapsSidebar.svelte";
import DeveloperSidebar from "./sidebar/settings/DeveloperSidebar.svelte";
import { m } from "$lang/messages";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const views: Record<string, Component<any>> = {
@ -188,10 +189,10 @@
location.toggleLock();
}}
>
{location.locked ? "Unlock Location" : "Lock Location"}
{location.locked ? m["location.unlock"]() : m["location.unlock"]()}
</Button>
{#if location.code}
<span>Advertise code: {location.code}</span>
<span>{m["location.code"]()}: {location.code}</span>
{/if}
<Button
variant="outline"
@ -199,7 +200,7 @@
advertiseRemoteLocation();
}}
>
Advertise Location
{m["location.start"]()}
</Button>
<Button
variant="outline"
@ -207,7 +208,7 @@
remoteLocation(prompt("Code?") || "");
}}
>
Join Remote Location
{m["location.join"]()}
</Button>
</div>
</Popover.Content>

View File

@ -16,6 +16,7 @@
type VehicleType,
} from "$lib/vehicles/vehicles.svelte";
import AddVehicleDrawer from "./AddVehicleDrawer.svelte";
import { m } from "$lang/messages";
let open = $state(false);
@ -48,9 +49,9 @@
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Vehicle Selector</Drawer.Title>
<Drawer.Title>{m["vehicles.selector.title"]()}</Drawer.Title>
<Drawer.Description
>Select your vehicle to customize routing just for you.</Drawer.Description
>{m["vehicles.selector.description"]()}</Drawer.Description
>
</Drawer.Header>
<div class="p-4 pt-0 flex flex-col gap-2">
@ -72,7 +73,7 @@
<AddVehicleDrawer>
<Button variant="secondary" class="w-full p-5">
<PlusCircleIcon />
Add Vehicle
{m["vehicles.selector.add"]()}
</Button>
</AddVehicleDrawer>
</div>

View File

@ -1,4 +1,5 @@
<script>
import { m } from "$lang/messages";
import Badge from "$lib/components/ui/badge/badge.svelte";
import { getStations } from "$lib/services/MTSK";
import RequiresCapability from "../RequiresCapability.svelte";
@ -6,7 +7,7 @@
let { tags, lat, lng } = $props();
</script>
<h3 class="text-lg font-bold mt-2">Fuel Types</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.fuel-types"]()}</h3>
<ul class="flex gap-2 flex-wrap">
{#each Object.entries(tags).filter( ([key]) => key.startsWith("fuel:"), ) as [key, tag] (key)}
<!-- <li>{key.replace("fuel:", "")}: {tag}</li> -->
@ -19,9 +20,9 @@
</ul>
<RequiresCapability capability="fuel">
<h3 class="text-lg font-bold mt-2">Prices</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.prices"]()}</h3>
{#await getStations(lat, lng)}
<p>Loading fuel prices...</p>
<p>{m.loading()}</p>
{:then stations}
{#if stations.stations.length > 0}
{@const station = stations.stations[0]}
@ -35,9 +36,9 @@
<p>E5: {station.e5}</p>
{/if}
{:else}
<p>No fuel prices available.</p>
<p>{m["sidebar.info.no-prices"]()}</p>
{/if}
{:catch err}
<p>Error loading fuel prices: {err.message}</p>
<p>{m["sidebar.info.error-loading-prices"]()}: {err.message}</p>
{/await}
</RequiresCapability>

View File

@ -1,4 +1,5 @@
<script>
import { m } from "$lang/messages";
import Badge from "$lib/components/ui/badge/badge.svelte";
let { tags } = $props();
@ -8,12 +9,12 @@
</script>
{#if tag != "no"}
<h3 class="text-lg font-bold mt-2">Internet Access</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.internet-access"]()}</h3>
{#each splitter as value, _index (value)}
<Badge>{value}</Badge>
{/each}
{#if tags["internet_access:fee"] && tags["internet_access:fee"] != "no"}
<Badge>Fee: {tags["internet_access:fee"]}</Badge>
<Badge>{m["sidebar.info.fee"]()}: {tags["internet_access:fee"]}</Badge>
{/if}
{#if tags["internet_access:ssid"]}
<Badge>{tags["internet_access:ssid"]}</Badge>

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { m } from "$lang/messages";
import Input from "$lib/components/ui/input/input.svelte";
import { ai } from "$lib/services/lnv";
import { SparklesIcon } from "@lucide/svelte";
@ -23,17 +24,17 @@
<SparklesIcon />
<div class="flex gap-2 flex-col w-full">
{#await ai(question, { lat, lon })}
<p>Loading...</p>
<p>{m.loading()}</p>
{:then data}
{@const text = getText(data)}
<p>{text}</p>
{:catch error}
<p>Error: {error.message}</p>
<p>{m.error()}: {error.message}</p>
{/await}
<Input
type="text"
value=""
placeholder="Ask a question about this place..."
placeholder={m["sidebar.mapai.ask-question"]()}
onchange={(e) => {
question = (e.target! as HTMLInputElement).value;
}}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { m } from "$lang/messages";
import Badge from "$lib/components/ui/badge/badge.svelte";
import opening_hours from "opening_hours";
@ -18,11 +19,11 @@
</script>
<h3 class="text-lg font-bold mt-2">
Opening Hours
{m["sidebar.info.opening-hours"]()}
{#if oh.getState()}
<Badge>Open</Badge>
<Badge>{m["sidebar.info.open"]()}</Badge>
{:else}
<Badge variant="destructive">Closed</Badge>
<Badge variant="destructive">{m["sidebar.info.closed"]()}</Badge>
{/if}
</h3>

View File

@ -1,4 +1,5 @@
<script>
import { m } from "$lang/messages";
import Badge from "$lib/components/ui/badge/badge.svelte";
let { tags } = $props();
@ -15,7 +16,7 @@
</script>
{#if matchingTags.length > 0}
<h3 class="text-lg font-bold mt-2">Restaurant</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.restaurant"]()}</h3>
<ul class="flex gap-2 flex-wrap">
{#each matchingTags as tag, _index (tag)}
{#if tag != "no"}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { m } from "$lang/messages";
import * as Avatar from "$lib/components/ui/avatar";
import { Button } from "$lib/components/ui/button";
import { getReviews, postReview } from "$lib/services/lnv";
@ -7,7 +8,7 @@
let { lat, lng }: { lat: number; lng: number } = $props();
</script>
<h3 class="text-lg font-bold mt-2">Reviews</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.reviews"]()}</h3>
{#await getReviews({ lat, lon: lng }) then reviews}
{#if reviews.length > 0}
<ul class="list-disc pl-5">
@ -25,7 +26,7 @@
{/each}
</ul>
{:else}
<p>No reviews available.</p>
<p>{m["sidebar.info.no-reviews"]()}</p>
{/if}
<Button
variant="secondary"
@ -46,8 +47,8 @@
alert("Review submission cancelled.");
}
}}
disabled>Write a review</Button
disabled>{m["sidebar.info.write-review"]()}</Button
><br />
{:catch error}
<p>Error loading reviews: {error.message}</p>
<p>{m.error()}: {error.message}</p>
{/await}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { m } from "$lang/messages";
import { Button } from "$lib/components/ui/button";
import {
decodePolyline,
@ -122,7 +123,7 @@
});
</script>
{fullDistanceText} left
{fullDistanceText} {m["sidebar.in-route.left"]()}
<Button
onclick={() => {
@ -133,12 +134,12 @@
<Button
onclick={() => {
stopNavigation();
}}>End Trip</Button
}}>{m["sidebar.in-route.end-trip"]()}</Button
>
<div class="flex flex-col gap-2 mt-5">
{#if location.code}
<span>Share Code: {location.code}</span>
<span>{m["sidebar.in-route.share-code"]()}: {location.code}</span>
<Button
variant="secondary"
onclick={() => {
@ -147,7 +148,7 @@
location.code = null;
}}
>
Stop Sharing Location
{m["sidebar.in-route.stop-sharing"]()}
</Button>
{:else}
<Button
@ -156,7 +157,7 @@
advertiseRemoteLocation();
}}
>
Share Trip Status & Location
{m["sidebar.in-route.share-location"]()}
</Button>
{/if}
</div>

View File

@ -26,6 +26,7 @@
import { getDeveloperToggle } from "./settings/developer.svelte";
import InternetAccess from "../info/InternetAccess.svelte";
import RestaurantInfo from "../info/RestaurantInfo.svelte";
import { m } from "$lang/messages";
// let { feature }: { feature: Feature } = $props();
@ -88,9 +89,9 @@
pin.liftPin();
}}
>
Dropped Pin
{m["sidebar.info.dropped"]()}
</SidebarHeader>
<p>Loading...</p>
<p>{m.loading()}</p>
{:then res}
{#if res.elements.length === 0}
<SidebarHeader
@ -98,7 +99,7 @@
pin.liftPin();
}}
>
Dropped Pin
{m["sidebar.info.dropped"]()}
</SidebarHeader>
<span style="color: #acacac;">&copy; OpenStreetMap</span>
<pre>{JSON.stringify(res, null, 2)}</pre>
@ -132,7 +133,7 @@
}}
>
<RouteIcon />
Route
{m["sidebar.info.route"]()}
</Button>
{#if tags.email || tags["contact:email"]}
<Button
@ -141,7 +142,7 @@
target="_blank"
>
<MailIcon />
Email
{m["sidebar.info.email"]()}
</Button>
{/if}
{#if tags.website || tags["contact:website"]}
@ -151,7 +152,7 @@
target="_blank"
>
<GlobeIcon />
Website
{m["sidebar.info.website"]()}
</Button>
{/if}
{#if tags.phone || tags["contact:phone"]}
@ -161,14 +162,14 @@
target="_blank"
>
<PhoneIcon />
Call
{m["sidebar.info.call"]()}
</Button>
{/if}
<Popover.Root>
<Popover.Trigger>
<Button variant="secondary">
<EllipsisIcon />
More
{m.more()}
</Button>
</Popover.Trigger>
<Popover.Content>
@ -185,7 +186,7 @@
}}
>
<HomeIcon />
Set as Home
{m["sidebar.info.set-as"]({ name: m["saved.home"]() })}
</Button>
<Button
variant="outline"
@ -195,7 +196,7 @@
}}
>
<SchoolIcon />
Set as School
{m["sidebar.info.set-as"]({ name: m["saved.school"]() })}
</Button>
<Button
variant="outline"
@ -209,7 +210,7 @@
}}
>
<BriefcaseIcon />
Set as Work
{m["sidebar.info.set-as"]({ name: m["saved.work"]() })}
</Button>
{#if dev.current}
<Button
@ -255,7 +256,7 @@
<!-- any payment:* tag -->
{#if Object.keys(tags).some((key) => key.startsWith("payment:"))}
<h3 class="text-lg font-bold mt-2">Payment Methods</h3>
<h3 class="text-lg font-bold mt-2">{m["sidebar.info.payment-methods"]()}</h3>
<ul style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
{#each Object.entries(tags).filter( ([key]) => key.startsWith("payment:"), ) as [key, value] (key)}
<Badge

View File

@ -22,12 +22,12 @@
onclick={() => {
const loc = saved.home;
if (!loc) {
alert(m["saved.no-location"](m["saved.home"]));
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"](m["saved.home"]));
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
return;
}
pin.dropPin(lat, lon);
@ -48,12 +48,12 @@
console.log(saved);
const loc = saved.school;
if (!loc) {
alert(m["saved.no-location"](m["saved.school"]));
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"](m["saved.school"]));
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
return;
}
pin.dropPin(lat, lon);
@ -73,12 +73,12 @@
onclick={() => {
const loc = saved.work;
if (!loc) {
alert(m["saved.no-location"](m["saved.work"]));
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"](m["saved.work"]));
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
return;
}
pin.dropPin(lat, lon);

View File

@ -19,6 +19,7 @@
} from "$lib/vehicles/vehicles.svelte";
import { location } from "../location.svelte";
import { saved } from "$lib/saved.svelte";
import { m } from "$lang/messages";
let {
from,
@ -46,11 +47,11 @@
removeAllRoutes();
}}
>
Route
{m["sidebar.route.header"]()}
</SidebarHeader>
<span
>Driving with <strong>{(selectedVehicle() ?? DefaultVehicle).name}</strong
>{m["sidebar.route.driving-with"]()} <strong>{(selectedVehicle() ?? DefaultVehicle).name}</strong
></span
>
<div class="flex flex-col gap-2 w-full mb-2">
@ -66,9 +67,7 @@
<Input bind:value={toLocation} />
</div>
<span>
You can use <strong>current</strong> for your current location,
<strong>home</strong>
or <strong>work</strong> for saved locations.
{@html m["sidebar.route.help"]()}
</span>
</div>
<Button
@ -110,7 +109,7 @@
}
drawAllRoutes(routes);
zoomToPoints(FROM, TO, map.value!);
}}>Calculate</Button
}}>{m["sidebar.route.calculate"]()}</Button
>
{#if routes}

View File

@ -6,6 +6,7 @@
import type { Feature } from "$lib/services/Search";
import SidebarHeader from "./SidebarHeader.svelte";
import { searchbar } from "../view.svelte";
import { m } from "$lang/messages";
let {
results,
@ -21,7 +22,7 @@
searchbar.text = "";
}}
>
Search Results for "{query}"
{m["sidebar.search.header"]()} "{query}"
</SidebarHeader>
<div
id="results"

View File

@ -9,6 +9,7 @@
import { Button } from "$lib/components/ui/button";
import { RouteIcon, SaveIcon, SendIcon } from "@lucide/svelte";
import { map } from "../map.svelte";
import { m } from "$lang/messages";
let {
route,
@ -27,7 +28,7 @@
removeAllRoutes();
}}
>
Trip Details
{m["sidebar.trip.header"]()}
</SidebarHeader>
<div id="actions" class="flex gap-2">
@ -40,15 +41,15 @@
}}
>
<RouteIcon />
Start Navigation
{m["sidebar.trip.start"]()}
</Button>
<Button variant="secondary" disabled>
<SaveIcon />
Save
{m["sidebar.trip.save"]()}
</Button>
<Button variant="secondary" disabled>
<SendIcon />
Send
{m["sidebar.trip.send"]()}
</Button>
</div>

View File

@ -4,6 +4,7 @@
import Button from "$lib/components/ui/button/button.svelte";
import { getAuthURL, getOIDCUser } from "$lib/services/oidc";
import * as Avatar from "$lib/components/ui/avatar";
import { m } from "$lang/messages";
interface OIDCUser {
sub: string;
@ -26,7 +27,7 @@
</script>
{#if !user}
<SidebarHeader>User</SidebarHeader>
<SidebarHeader>{m["sidebar.user.header"]()}</SidebarHeader>
<Button
onclick={async () => {
@ -57,7 +58,7 @@
atob((localStorage.getItem("lnv-id") || "").split(".")[1]),
);
});
}}>Login</Button
}}>{m["sidebar.user.login"]()}</Button
>
{:else}
<SidebarHeader>

View File

@ -1,4 +1,5 @@
<script>
import { m } from "$lang/messages";
import SidebarHeader from "../SidebarHeader.svelte";
import { getDeveloperToggle } from "./developer.svelte";
@ -6,7 +7,7 @@
const dev = getDeveloperToggle();
</script>
<SidebarHeader>About</SidebarHeader>
<SidebarHeader>{m["sidebar.about.header"]()}</SidebarHeader>
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
@ -21,9 +22,9 @@
>
TrafficCue
</h1>
<span>Powered by:</span>
<span>{m["sidebar.about.powered-by"]()}:</span>
<ul>
<li>© OpenStreetMap contributors</li>
<li>© OpenStreetMap {m["sidebar.about.contributors"]()}</li>
<li>Natural Earth</li>
<li>MapLibre</li>
<li>OpenMapTiles</li>

View File

@ -6,11 +6,12 @@
import { downloadPMTiles } from "$lib/services/OfflineTiles";
import { getDeveloperToggle } from "./developer.svelte";
import { view } from "../../view.svelte";
import { m } from "$lang/messages";
const dev = getDeveloperToggle();
</script>
<SidebarHeader>Developer Settings</SidebarHeader>
<SidebarHeader>{m["sidebar.developer.header"]()}</SidebarHeader>
<div id="sections">
<section>

View File

@ -3,22 +3,23 @@
import { DownloadCloudIcon } from "@lucide/svelte";
import SettingsButton from "./SettingsButton.svelte";
import SidebarHeader from "../SidebarHeader.svelte";
import { m } from "$lang/messages";
let progresses: Record<string, number> = $state({});
</script>
<SidebarHeader>Offline Maps</SidebarHeader>
<SidebarHeader>{m["sidebar.offline-maps.header"]()}</SidebarHeader>
{#await getRemoteList()}
<p>Loading...</p>
<p>{m.loading()}</p>
{:then list}
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
{#if list.length === 0}
<p>No offline maps available.</p>
<p>{m["sidebar.offline-maps.not-available"]()}</p>
{/if}
{#if !window.__TAURI__}
<p>Offline maps are only available on mobile.</p>
<p>{m["sidebar.offline-maps.only-mobile"]()}</p>
{/if}
{#each list as item, _index (item.file)}
@ -35,7 +36,7 @@
progresses[item.file] = (progress / total) * 100;
},
);
alert(`Downloaded ${item.name}`);
alert(m["sidebar.offline-maps.downloaded"]({ name: item.name }));
location.reload();
}}
/>

View File

@ -9,34 +9,35 @@
import SidebarHeader from "../SidebarHeader.svelte";
import SettingsButton from "./SettingsButton.svelte";
import { getDeveloperToggle } from "./developer.svelte";
import { m } from "$lang/messages";
const dev = getDeveloperToggle();
</script>
<SidebarHeader>Settings</SidebarHeader>
<SidebarHeader>{m["sidebar.settings.header"]()}</SidebarHeader>
<div id="sections">
<section>
<h2>General</h2>
<SettingsButton icon={LanguagesIcon} text="Language" disabled />
<h2>{m["sidebar.settings.general"]()}</h2>
<SettingsButton icon={LanguagesIcon} text={m["sidebar.language.header"]()} disabled />
</section>
<section>
<h2>Map</h2>
<SettingsButton icon={MapIcon} text="Offline Maps" view="offline-maps" />
<SettingsButton icon={PaintbrushIcon} text="Map Style" disabled />
<h2>{m["sidebar.settings.map"]()}</h2>
<SettingsButton icon={MapIcon} text={m["sidebar.offline-maps.header"]()} view="offline-maps" />
<SettingsButton icon={PaintbrushIcon} text={m["sidebar.map-style.header"]()} disabled />
</section>
<section>
<h2>About</h2>
<h2>{m["sidebar.about.header"]()}</h2>
{#if dev.current == "true"}
<SettingsButton
icon={CodeIcon}
text="Developer Settings"
text={m["sidebar.developer.header"]()}
view="dev-options"
/>
{/if}
<SettingsButton icon={InfoIcon} text="About" view="about" />
<SettingsButton icon={InfoIcon} text={m["sidebar.about.header"]()} view="about" />
</section>
</div>

View File

@ -1,3 +1,5 @@
import { m } from "$lang/messages";
/*
Valhalla costing:
auto, prioritizes motorways = car, (truck), motorcycle
@ -57,7 +59,7 @@ export interface Vehicle {
}
export const DefaultVehicle: Vehicle = {
name: "Default Vehicle",
name: m["vehicles.default"](),
legalMaxSpeed: 45,
actualMaxSpeed: 45,
type: "motor_scooter",