Implement shared packages (tasks 2.1-2.6)

- @trails-cool/types: Route, Activity, Waypoint, RouteVersion, RouteMetadata
- @trails-cool/gpx: GPX parser (XML→waypoints/tracks/elevation) and generator
  with round-trip test, haversine distance, elevation gain/loss computation
- @trails-cool/map: MapView (Leaflet + OSM/OpenTopoMap/CyclOSM layer switcher),
  RouteLayer (GeoJSON polyline rendering)
- @trails-cool/ui: Button (primary/secondary/ghost), Input (with label/error),
  Card components with Tailwind styling
- @trails-cool/i18n: react-i18next config with English + German translations,
  browser language detection, fallback to English

All 13 unit tests pass. Both apps build successfully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-03-22 13:09:35 +01:00
parent 325a4466d5
commit 2cfa5e54e7
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
23 changed files with 809 additions and 30 deletions

View file

@ -2,6 +2,15 @@
"name": "@trails-cool/i18n",
"version": "0.0.1",
"type": "module",
"exports": {
".": "./src/index.ts",
"./locales/*": "./src/locales/*"
},
"main": "./src/index.ts",
"types": "./src/index.ts"
"types": "./src/index.ts",
"peerDependencies": {
"react": ">=18",
"i18next": ">=23",
"react-i18next": ">=14"
}
}

View file

@ -1,5 +1,29 @@
/**
* Shared i18n configuration and translation strings for trails.cool
* Uses react-i18next. Start with English + German.
*/
export {};
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import en from "./locales/en";
import de from "./locales/de";
export const defaultNS = "common";
export const resources = {
en: { common: en.common, planner: en.planner, journal: en.journal },
de: { common: de.common, planner: de.planner, journal: de.journal },
} as const;
export function initI18n() {
return i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
defaultNS,
fallbackLng: "en",
supportedLngs: ["en", "de"],
interpolation: {
escapeValue: false,
},
});
}
export { i18n };

View file

@ -0,0 +1,59 @@
export default {
common: {
save: "Speichern",
cancel: "Abbrechen",
delete: "Löschen",
edit: "Bearbeiten",
close: "Schließen",
loading: "Wird geladen...",
error: "Etwas ist schiefgelaufen",
},
planner: {
title: "trails.cool Planer",
subtitle: "Gemeinsame Routenplanung",
newSession: "Neue Sitzung",
saveRoute: "Route speichern",
exportGpx: "GPX exportieren",
profile: "Routing-Profil",
profiles: {
trekking: "Wandern",
bike: "Radfahren",
shortest: "Kürzeste",
},
elevation: {
gain: "Höhenmeter aufwärts",
loss: "Höhenmeter abwärts",
profile: "Höhenprofil",
},
},
journal: {
title: "trails.cool",
subtitle: "Dein Outdoor-Aktivitäten-Tagebuch",
routes: {
title: "Routen",
new: "Neue Route",
edit: "Route bearbeiten",
editInPlanner: "Im Planer bearbeiten",
importGpx: "GPX importieren",
exportGpx: "GPX exportieren",
delete: "Route löschen",
distance: "Strecke",
elevationGain: "Höhenmeter",
},
activities: {
title: "Aktivitäten",
new: "Neue Aktivität",
duration: "Dauer",
linkToRoute: "Mit Route verknüpfen",
createRouteFromActivity: "Route aus Aktivität erstellen",
},
auth: {
login: "Anmelden",
register: "Registrieren",
logout: "Abmelden",
email: "E-Mail",
password: "Passwort",
username: "Benutzername",
},
},
} as const;

View file

@ -0,0 +1,59 @@
export default {
common: {
save: "Save",
cancel: "Cancel",
delete: "Delete",
edit: "Edit",
close: "Close",
loading: "Loading...",
error: "Something went wrong",
},
planner: {
title: "trails.cool Planner",
subtitle: "Collaborative route planning",
newSession: "New Session",
saveRoute: "Save Route",
exportGpx: "Export GPX",
profile: "Routing Profile",
profiles: {
trekking: "Hiking",
bike: "Cycling",
shortest: "Shortest",
},
elevation: {
gain: "Elevation Gain",
loss: "Elevation Loss",
profile: "Elevation Profile",
},
},
journal: {
title: "trails.cool",
subtitle: "Your outdoor activity journal",
routes: {
title: "Routes",
new: "New Route",
edit: "Edit Route",
editInPlanner: "Edit in Planner",
importGpx: "Import GPX",
exportGpx: "Export GPX",
delete: "Delete Route",
distance: "Distance",
elevationGain: "Elevation Gain",
},
activities: {
title: "Activities",
new: "New Activity",
duration: "Duration",
linkToRoute: "Link to Route",
createRouteFromActivity: "Create Route from Activity",
},
auth: {
login: "Log In",
register: "Sign Up",
logout: "Log Out",
email: "Email",
password: "Password",
username: "Username",
},
},
} as const;