diff --git a/website/package-lock.json b/website/package-lock.json index e31229d2..f6b3d48b 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -17,6 +17,7 @@ "lucide-svelte": "^0.365.0", "mapbox-gl": "^3.2.0", "sortablejs": "^1.15.2", + "svelte-i18n": "^4.0.0", "tailwind-merge": "^2.2.2", "tailwind-variants": "^0.2.1" }, @@ -332,6 +333,21 @@ "node": ">=12" } }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/linux-arm64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", @@ -656,6 +672,50 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz", + "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==", + "dependencies": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz", + "integrity": "sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-skeleton-parser": "1.8.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz", + "integrity": "sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -2095,6 +2155,21 @@ "node": ">= 6" } }, + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -2217,6 +2292,18 @@ "node": ">=4" } }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2300,7 +2387,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2415,12 +2501,60 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", "dev": true }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/esbuild": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", @@ -2459,6 +2593,22 @@ "@esbuild/win32-x64": "0.20.2" } }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -2650,6 +2800,20 @@ "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", "dev": true }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -2717,6 +2881,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", @@ -2730,6 +2903,14 @@ "node": ">=0.8.x" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3017,8 +3198,7 @@ "node_modules/globalyzer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" }, "node_modules/globby": { "version": "11.1.0", @@ -3043,8 +3223,7 @@ "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" }, "node_modules/got": { "version": "11.8.6", @@ -3234,6 +3413,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/intl-messageformat": { + "version": "10.5.11", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.11.tgz", + "integrity": "sha512-eYq5fkFBVxc7GIFDzpFQkDOZgNayNTQn4Oufe8jw6YY6OHVw70/4pA3FyCsQ0Gb2DnvEJEMmN2tOaXUGByM+kg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "tslib": "^2.4.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3313,6 +3503,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, "node_modules/is-reference": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", @@ -3507,6 +3702,14 @@ "node": ">=10" } }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, "node_modules/lucide-svelte": { "version": "0.365.0", "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.365.0.tgz", @@ -3575,6 +3778,21 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -3725,7 +3943,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, "engines": { "node": ">=4" } @@ -3783,6 +4000,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -4619,7 +4841,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, "dependencies": { "mri": "^1.1.0" }, @@ -5066,6 +5287,401 @@ "svelte": "^3.19.0 || ^4.0.0" } }, + "node_modules/svelte-i18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.0.tgz", + "integrity": "sha512-4vivjKZADUMRIhTs38JuBNy3unbnh9AFRxWFLxq62P4NHic+/BaIZZlAsvqsCdnp7IdJf5EoSiH6TNdItcjA6g==", + "dependencies": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^10.5.3", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "bin": { + "svelte-i18n": "dist/cli.js" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "svelte": "^3 || ^4" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/svelte-i18n/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/svelte-preprocess": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz", @@ -5278,11 +5894,19 @@ "node": ">=0.8" } }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" @@ -5351,6 +5975,11 @@ "url": "https://github.com/sponsors/cocopon" } }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/website/package.json b/website/package.json index 6993f8a0..982c295c 100644 --- a/website/package.json +++ b/website/package.json @@ -50,6 +50,7 @@ "lucide-svelte": "^0.365.0", "mapbox-gl": "^3.2.0", "sortablejs": "^1.15.2", + "svelte-i18n": "^4.0.0", "tailwind-merge": "^2.2.2", "tailwind-variants": "^0.2.1" } diff --git a/website/src/lib/components/App.svelte b/website/src/lib/components/App.svelte new file mode 100644 index 00000000..7d5e882a --- /dev/null +++ b/website/src/lib/components/App.svelte @@ -0,0 +1,25 @@ + + +
+
+ + + + + + +
+
+ + +
+
diff --git a/website/src/lib/components/ElevationProfile.svelte b/website/src/lib/components/ElevationProfile.svelte index cdf1727c..29f1cd76 100644 --- a/website/src/lib/components/ElevationProfile.svelte +++ b/website/src/lib/components/ElevationProfile.svelte @@ -6,7 +6,7 @@ import Chart from 'chart.js/auto'; import mapboxgl from 'mapbox-gl'; - import { map, fileCollection, fileOrder, selectedFiles } from '$lib/stores'; + import { map, fileCollection, fileOrder, selectedFiles, settings } from '$lib/stores'; import { onDestroy, onMount } from 'svelte'; import { @@ -21,6 +21,28 @@ import { GPXFiles } from 'gpx'; import { surfaceColors } from '$lib/assets/surfaces'; + import { _ } from 'svelte-i18n'; + import { + getCadenceUnits, + getCadenceWithUnits, + getConvertedDistance, + getConvertedElevation, + getConvertedTemperature, + getConvertedVelocity, + getDistanceUnits, + getDistanceWithUnits, + getElevationUnits, + getElevationWithUnits, + getHeartRateUnits, + getHeartRateWithUnits, + getPowerUnits, + getPowerWithUnits, + getTemperatureUnits, + getTemperatureWithUnits, + getVelocityUnits, + getVelocityWithUnits + } from '$lib/units'; + let canvas: HTMLCanvasElement; let chart: Chart; @@ -41,7 +63,7 @@ type: 'linear', title: { display: true, - text: 'Distance (km)', + text: `${$_('quantities.distance')} (${getDistanceUnits()})`, padding: 0, align: 'end' } @@ -50,7 +72,7 @@ type: 'linear', title: { display: true, - text: 'Elevation (m)', + text: `${$_('quantities.elevation')} (${getElevationUnits()})`, padding: 0 } } @@ -82,41 +104,34 @@ label: function (context: Chart.TooltipContext) { let point = context.raw; if (context.datasetIndex === 0) { - let elevation = point.y.toFixed(0); if ($map && marker) { marker.addTo($map); marker.setLngLat(point.coordinates); } - return `Elevation: ${elevation} m`; + return `${$_('quantities.elevation')}: ${getElevationWithUnits(point.y, false)}`; } else if (context.datasetIndex === 1) { - let speed = point.y.toFixed(2); - return `Speed: ${speed} km/h`; + return `${$settings.velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')}: ${getVelocityWithUnits(point.y, false)}`; } else if (context.datasetIndex === 2) { - let hr = point.y; - return `Heart Rate: ${hr} bpm`; + return `${$_('quantities.heartrate')}: ${getHeartRateWithUnits(point.y)}`; } else if (context.datasetIndex === 3) { - let cad = point.y; - return `Cadence: ${cad} rpm`; + return `${$_('quantities.cadence')}: ${getCadenceWithUnits(point.y)}`; } else if (context.datasetIndex === 4) { - let atemp = point.y.toFixed(1); - return `Temperature: ${atemp} °C`; + return `${$_('quantities.temperature')}: ${getTemperatureWithUnits(point.y, false)}`; } else if (context.datasetIndex === 5) { - let power = point.y; - return `Power: ${power} W`; + return `${$_('quantities.power')}: ${getPowerWithUnits(point.y)}`; } }, afterBody: function (contexts: Chart.TooltipContext[]) { let context = contexts.filter((context) => context.datasetIndex === 0); if (context.length === 0) return; let point = context[0].raw; - let distance = point.x.toFixed(2); let slope = point.slope.toFixed(1); let surface = point.surface ? point.surface : 'unknown'; return [ - ` Distance: ${distance} km`, - ` Slope: ${slope} %`, - ` Surface: ${surface}` + ` ${$_('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`, + ` ${$_('quantities.slope')}: ${slope} %`, + ` ${$_('quantities.surface')}: ${surface}` ]; } } @@ -134,28 +149,28 @@ } = { speed: { id: 'speed', - label: 'Speed', - units: 'km/h' + label: $_('quantities.speed'), + units: getVelocityUnits() }, hr: { id: 'hr', - label: 'Heart Rate', - units: 'bpm' + label: $_('quantities.heartrate'), + units: getHeartRateUnits() }, cad: { id: 'cad', - label: 'Cadence', - units: 'rpm' + label: $_('quantities.cadence'), + units: getCadenceUnits() }, atemp: { id: 'atemp', - label: 'Temperature', - units: '°C' + label: $_('quantities.temperature'), + units: getTemperatureUnits() }, power: { id: 'power', - label: 'Power', - units: 'W' + label: $_('quantities.power'), + units: getPowerUnits() } }; @@ -211,11 +226,11 @@ let trackPointsAndStatistics = gpxFiles.getTrackPointsAndStatistics(); chart.data.datasets[0] = { - label: 'Elevation', + label: $_('quantities.elevation'), data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], - y: point.ele ? point.ele : 0, + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), + y: point.ele ? getConvertedElevation(point.ele) : 0, slope: trackPointsAndStatistics.statistics.slope[index], surface: point.getSurface(), coordinates: point.getCoordinates() @@ -229,8 +244,8 @@ label: datasets.speed.label, data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], - y: trackPointsAndStatistics.statistics.speed[index] + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), + y: getConvertedVelocity(trackPointsAndStatistics.statistics.speed[index]) }; }), normalized: true, @@ -241,7 +256,7 @@ label: datasets.hr.label, data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), y: point.getHeartRate() }; }), @@ -253,7 +268,7 @@ label: datasets.cad.label, data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), y: point.getCadence() }; }), @@ -265,8 +280,8 @@ label: datasets.atemp.label, data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], - y: point.getTemperature() + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), + y: getConvertedTemperature(point.getTemperature()) }; }), normalized: true, @@ -277,7 +292,7 @@ label: datasets.power.label, data: trackPointsAndStatistics.points.map((point, index) => { return { - x: trackPointsAndStatistics.statistics.distance[index], + x: getConvertedDistance(trackPointsAndStatistics.statistics.distance[index]), y: point.getPower() }; }), @@ -286,7 +301,8 @@ hidden: true }; chart.options.scales.x['min'] = 0; - chart.options.scales.x['max'] = gpxFiles.statistics.distance.total; + chart.options.scales.x['max'] = getConvertedDistance(gpxFiles.statistics.distance.total); + chart.update(); } diff --git a/website/src/lib/components/GPXData.svelte b/website/src/lib/components/GPXData.svelte index ab91f3c6..a50c3cae 100644 --- a/website/src/lib/components/GPXData.svelte +++ b/website/src/lib/components/GPXData.svelte @@ -1,6 +1,7 @@ @@ -35,30 +25,32 @@ - {gpxData.distance.total.toFixed(2)} km + Distance - {gpxData.elevation.gain.toFixed(0)} m + - {gpxData.elevation.loss.toFixed(0)} m + Elevation - {gpxData.speed.moving.toFixed(2)} km/h + Speed - {toHHMMSS(gpxData.time.moving)} / {toHHMMSS(gpxData.time.total)} + + / + Moving time / Total time diff --git a/website/src/lib/components/Menu.svelte b/website/src/lib/components/Menu.svelte index 89962d17..148e3bd7 100644 --- a/website/src/lib/components/Menu.svelte +++ b/website/src/lib/components/Menu.svelte @@ -23,11 +23,12 @@ removeAllFiles, removeSelectedFiles, triggerFileInput, - selectFiles + selectFiles, + settings } from '$lib/stores'; - let distanceUnits = 'metric'; - let velocityUnits = 'speed'; + import { _ } from 'svelte-i18n'; + let showDistanceMarkers = false; let showDirectionMarkers = false; @@ -39,10 +40,12 @@ - File + {$_('menu.file')} - New ⌘N + + {$_('menu.new')} + ⌘N @@ -108,7 +111,7 @@ > Distance units - + Metric Imperial @@ -117,12 +120,21 @@ Velocity units - + Speed Pace + + Temperature units + + + Celsius + Fahrenheit + + + Show distance markers diff --git a/website/src/lib/components/WithUnits.svelte b/website/src/lib/components/WithUnits.svelte new file mode 100644 index 00000000..ea3a148e --- /dev/null +++ b/website/src/lib/components/WithUnits.svelte @@ -0,0 +1,51 @@ + + +{#if type === 'distance'} + {#if $settings.distanceUnits === 'metric'} + {value.toFixed(2)} {$_('units.kilometers')} + {:else} + {kilometersToMiles(value).toFixed(2)} {$_('units.miles')} + {/if} +{:else if type === 'elevation'} + {#if $settings.distanceUnits === 'metric'} + {value.toFixed(0)} {$_('units.meters')} + {:else} + {metersToFeet(value).toFixed(0)} {$_('units.feet')} + {/if} +{:else if type === 'speed'} + {#if $settings.distanceUnits === 'metric'} + {#if $settings.velocityUnits === 'speed'} + {value.toFixed(2)} {$_('units.kilometers_per_hour')} + {:else} + {secondsToHHMMSS(distancePerHourToSecondsPerDistance(value))} + {$_('units.minutes_per_kilometer')} + {/if} + {:else if $settings.velocityUnits === 'speed'} + {kilometersToMiles(value).toFixed(2)} {$_('units.miles_per_hour')} + {:else} + {secondsToHHMMSS(distancePerHourToSecondsPerDistance(kilometersToMiles(value)))} + {$_('units.minutes_per_mile')} + {/if} +{:else if type === 'temperature'} + {#if $settings.temperatureUnits === 'celsius'} + {value} {$_('units.celsius')} + {:else} + {celsiusToFahrenheit(value)} {$_('units.fahrenheit')} + {/if} +{:else if type === 'time'} + {secondsToHHMMSS(value)} +{/if} diff --git a/website/src/lib/stores.ts b/website/src/lib/stores.ts index 1110bfdf..203bb1c9 100644 --- a/website/src/lib/stores.ts +++ b/website/src/lib/stores.ts @@ -8,6 +8,11 @@ export const fileCollection = writable(new GPXFiles([])); export const fileOrder = writable([]); export const selectedFiles = writable>(new Set()); export const selectFiles = writable<{ [key: string]: (file?: GPXFile) => void }>({}); +export const settings = writable<{ [key: string]: any }>({ + distanceUnits: 'metric', + velocityUnits: 'speed', + temperatureUnits: 'celsius', +}); export function addFile(file: GPXFile) { fileCollection.update($files => { diff --git a/website/src/lib/units.ts b/website/src/lib/units.ts new file mode 100644 index 00000000..fca1a654 --- /dev/null +++ b/website/src/lib/units.ts @@ -0,0 +1,144 @@ +import { get } from 'svelte/store'; +import { settings } from './stores'; +import { _ } from 'svelte-i18n'; + +export function kilometersToMiles(value: number) { + return value * 0.621371; +} + +export function metersToFeet(value: number) { + return value * 3.28084; +} + +export function celsiusToFahrenheit(value: number) { + return value * 1.8 + 32; +} + +export function distancePerHourToSecondsPerDistance(value: number) { + return 3600 / value; +} + +export function secondsToHHMMSS(value: number) { + var hours = Math.floor(value / 3600); + var minutes = Math.floor(value / 60) % 60; + var seconds = Math.round(value % 60); + + return [hours, minutes, seconds] + .map((v) => (v < 10 ? '0' + v : v)) + .filter((v, i) => v !== '00' || i > 0) + .join(':'); +} + +// Get a string representation of the value with units +export function getDistanceWithUnits(value: number, convert: boolean = true) { + if (convert) { + return getConvertedDistance(value).toFixed(2) + ' ' + getDistanceUnits(); + } else { + return value.toFixed(2) + ' ' + getDistanceUnits(); + } +} + +export function getVelocityWithUnits(value: number, convert: boolean = true) { + const velocityUnits = get(settings).velocityUnits; + const distanceUnits = get(settings).distanceUnits; + if (velocityUnits === 'speed') { + if (convert) { + return getConvertedVelocity(value).toFixed(2) + ' ' + getVelocityUnits(); + } else { + return value.toFixed(2) + ' ' + getVelocityUnits(); + } + } else { + if (convert) { + return secondsToHHMMSS(getConvertedVelocity(value)); + } else { + return secondsToHHMMSS(value); + } + } +} + +export function getElevationWithUnits(value: number, convert: boolean = true) { + if (convert) { + return getConvertedElevation(value).toFixed(0) + ' ' + getElevationUnits(); + } else { + return value.toFixed(0) + ' ' + getElevationUnits(); + } +} + +export function getHeartRateWithUnits(value: number) { + return value.toFixed(0) + ' ' + getHeartRateUnits(); +} + +export function getCadenceWithUnits(value: number) { + return value.toFixed(0) + ' ' + getCadenceUnits(); +} + +export function getPowerWithUnits(value: number) { + return value.toFixed(0) + ' ' + getPowerUnits(); +} + +export function getTemperatureWithUnits(value: number, convert: boolean = true) { + if (convert) { + return getConvertedTemperature(value).toFixed(0) + ' ' + getTemperatureUnits(); + } else { + return value.toFixed(0) + ' ' + getTemperatureUnits(); + } +} + +// Get the units +export function getDistanceUnits() { + return get(settings).distanceUnits === 'metric' ? get(_)('units.kilometers') : get(_)('units.miles'); +} + +export function getVelocityUnits() { + const velocityUnits = get(settings).velocityUnits; + const distanceUnits = get(settings).distanceUnits; + if (velocityUnits === 'speed') { + return distanceUnits === 'metric' ? get(_)('units.kilometers_per_hour') : get(_)('units.miles_per_hour'); + } else { + return distanceUnits === 'metric' ? get(_)('units.minutes_per_kilometer') : get(_)('units.minutes_per_mile'); + + } +} + +export function getElevationUnits() { + return get(settings).distanceUnits === 'metric' ? get(_)('units.meters') : get(_)('units.feet'); +} + +export function getHeartRateUnits() { + return get(_)('units.heartrate'); +} + +export function getCadenceUnits() { + return get(_)('units.cadence'); +} + +export function getPowerUnits() { + return get(_)('units.power'); +} + +export function getTemperatureUnits() { + return get(settings).temperatureUnits === 'celsius' ? get(_)('units.celsius') : get(_)('units.fahrenheit'); +} + +// Convert only the value +export function getConvertedDistance(value: number) { + return get(settings).distanceUnits === 'metric' ? value : kilometersToMiles(value); +} + +export function getConvertedElevation(value: number) { + return get(settings).distanceUnits === 'metric' ? value : metersToFeet(value); +} + +export function getConvertedVelocity(value: number) { + const velocityUnits = get(settings).velocityUnits; + const distanceUnits = get(settings).distanceUnits; + if (velocityUnits === 'speed') { + return distanceUnits === 'metric' ? value : kilometersToMiles(value); + } else { + return distanceUnits === 'metric' ? distancePerHourToSecondsPerDistance(value) : distancePerHourToSecondsPerDistance(kilometersToMiles(value)); + } +} + +export function getConvertedTemperature(value: number) { + return get(settings).temperatureUnits === 'celsius' ? value : celsiusToFahrenheit(value); +} \ No newline at end of file diff --git a/website/src/locales/en.json b/website/src/locales/en.json new file mode 100644 index 00000000..3fdc666e --- /dev/null +++ b/website/src/locales/en.json @@ -0,0 +1,33 @@ +{ + "menu": { + "file": "File", + "new": "New" + }, + "quantities": { + "distance": "Distance", + "elevation": "Elevation", + "temperature": "Temperature", + "speed": "Speed", + "pace": "Pace", + "heartrate": "Heart rate", + "cadence": "Cadence", + "power": "Power", + "slope": "Slope", + "surface": "Surface" + }, + "units": { + "meters": "m", + "feet": "ft", + "kilometers": "km", + "miles": "mi", + "celsius": "°C", + "fahrenheit": "°F", + "kilometers_per_hour": "km/h", + "miles_per_hour": "mph", + "minutes_per_kilometer": "min/km", + "minutes_per_mile": "min/mi", + "heartrate": "bpm", + "cadence": "rpm", + "power": "W" + } +} \ No newline at end of file diff --git a/website/src/routes/+layout.js b/website/src/routes/+layout.js new file mode 100644 index 00000000..7cb4c8ec --- /dev/null +++ b/website/src/routes/+layout.js @@ -0,0 +1,10 @@ +export const prerender = true; + +import { register, init } from 'svelte-i18n'; + +register('en', () => import('../locales/en.json')); + +init({ + fallbackLocale: 'en', + initialLocale: 'en', +}); \ No newline at end of file diff --git a/website/src/routes/+layout.ts b/website/src/routes/+layout.ts deleted file mode 100644 index c8cacf08..00000000 --- a/website/src/routes/+layout.ts +++ /dev/null @@ -1 +0,0 @@ -export const prerender = true; \ No newline at end of file diff --git a/website/src/routes/+page.svelte b/website/src/routes/+page.svelte index 7d5e882a..424a6359 100644 --- a/website/src/routes/+page.svelte +++ b/website/src/routes/+page.svelte @@ -1,25 +1,5 @@ -
-
- - - - - - -
-
- - -
-
+ diff --git a/website/src/routes/[language]/+page.svelte b/website/src/routes/[language]/+page.svelte new file mode 100644 index 00000000..0ca4040b --- /dev/null +++ b/website/src/routes/[language]/+page.svelte @@ -0,0 +1,10 @@ + + +