feat(tours): import a ski track from GPX and keep it as recorded

SKI-02. Ski joins the types a GPX file can be imported as, offered in the
import preview's type field only: TREK routes no ski track, so the planner
never offers it. The preview asks for the discipline on purpose (alpine,
snowboard, cross-country or ski touring, none chosen at first) and, for
cross-country, the technique (classic, skating or unknown); Import waits
until both are named, and the technique stays in sight but dimmed for the
other disciplines. The track is stored exactly as recorded, with its file
name as provenance, its distance and heights from the file and no time:
no ski time is worked out and no hiking estimate stands in for one.

Opened in the planner, a ski track gets a rail card with its discipline on
a tinted tile, a cross-country technique as a badge beside it, the ski time
said as not worked out, and an info dot on what TREK does not say (snow,
avalanches, open runs, and that the 3D gradient colours are no difficulty
or avalanche rating). Change opens a dialog to classify the track again;
only the classification changes, through the offline core, with the
revision it was made at. Planning over the import is not offered for ski.
The existing 3D terrain view, the elevation profile and its focus work on
the track as for any import, with the standard map, topo and satellite and
no via ferratas. Lists, the day plan and the detail show the discipline as
a frosted corner badge on the tile and as a pill with the technique inside;
day assignment, copies and take-overs work as for any tour. The corner
badge is now one component that the winter label shares.

The server keeps tours.ski_discipline and tours.ski_technique (CHECK lists,
NULL on every other type), switches the ski type on without a routing
profile, refuses a ski import without its discipline, a technique outside
cross-country and ski fields on any other type, and takes a planned save's
classification off. PUT /api/trips/:tripId/tours/:placeId/ski classifies a
ski track again (place_edit, 409 on a stale revision, 400 for a tour that
is no ski track) and broadcasts tours:changed. GPX export names the
discipline (alpine_skiing, snowboarding, cross_country_skiing,
backcountry_skiing), and list_tours describes both fields with what a ski
track does not say. Ski is labelled plainly as Ski in every language, so
it no longer reads as ski touring.
This commit is contained in:
Maurice
2026-10-20 13:45:56 +02:00
parent 68907ec811
commit 0169be3d79
93 changed files with 2853 additions and 183 deletions
+22 -1
View File
@@ -216,7 +216,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'مشي طويل',
'tourTypes.bike': 'جولة بالدراجة',
'tourTypes.run': 'جري',
'tourTypes.ski': 'جولة تزلج',
'tourTypes.ski': 'تزلج',
'tourTypes.kayak': 'جولة تجديف',
'tourTypes.walk': 'مشي في المدينة / مشاهدة المعالم',
'tourTypes.food': 'جولة طعام / نبيذ',
@@ -414,6 +414,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.winter_hike': 'مشي شتوي',
'tours.winter.activity.snowshoe': 'مشي بأحذية الثلج',
'tours.winter.labelOnly': 'وضعها المسافرون. يُخطَّط المسار والوقت كما في أي نزهة، دون فحص لظروف الشتاء.',
'tours.planner.tourTypeHint.ski': 'من ملف GPX كما سُجّل',
'tours.ski.title': 'مسار تزلج',
'tours.ski.discipline': 'النوع',
'tours.ski.discipline.alpine': 'تزلج ألبي',
'tours.ski.discipline.snowboard': 'تزلج على اللوح',
'tours.ski.discipline.cross_country': 'تزلج ريفي',
'tours.ski.discipline.touring': 'تزلج جبلي',
'tours.ski.technique': 'الأسلوب',
'tours.ski.technique.classic': 'كلاسيكي',
'tours.ski.technique.skating': 'تزحلق',
'tours.ski.technique.unknown': 'غير معروف',
'tours.ski.chooseDiscipline': 'اختر نوعًا لاستيراد المسار كتزلج.',
'tours.ski.chooseTechnique': 'اختر الأسلوب، أو غير معروف.',
'tours.ski.infoLabel': 'ما يفعله TREK بمسار التزلج',
'tours.ski.info':
'يبقى كما سُجّل ولا يُعاد حسابه أبدًا. المسافة والارتفاعات من الملف. لا يحسب TREK وقتًا للتزلج، ولا يقول المسار ولا ألوان الانحدار في العرض ثلاثي الأبعاد شيئًا عن الصعوبة أو الثلج أو الانهيارات أو ما إذا كانت المسارات مفتوحة.',
'tours.ski.time': 'وقت التزلج',
'tours.ski.timeUnknown': 'غير محسوب',
'tours.ski.classifyTitle': 'تصنيف مسار التزلج',
'tours.ski.saved': 'تم حفظ التصنيف',
'tours.ski.saveFailed': 'تعذّر حفظ التصنيف.',
'tours.planner.tourTypeHint.run': 'سيرًا على الأقدام بإيقاعك الخاص، على طرق مناسبة للجري',
'tours.planner.namePlaceholder.run': 'جري بلا عنوان',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -200,7 +200,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Yürüş',
'tourTypes.bike': 'Velosiped turu',
'tourTypes.run': 'Qaçış',
'tourTypes.ski': 'Xizək turu',
'tourTypes.ski': 'Xizək',
'tourTypes.kayak': 'Avarlı tur',
'tourTypes.walk': 'Şəhər / görməli yerlər gəzintisi',
'tourTypes.food': 'Yemək/şərab turu',
@@ -404,6 +404,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Qar ayaqqabısı ilə yürüş',
'tours.winter.labelOnly':
'Səyahətçilər tərəfindən qeyd edilib. Marşrut və vaxt istənilən yürüşdəki kimi planlaşdırılır, qış şəraiti üçün yoxlanılmır.',
'tours.planner.tourTypeHint.ski': 'GPX faylından, qeydə alındığı kimi',
'tours.ski.title': 'Xizək izi',
'tours.ski.discipline': 'Növ',
'tours.ski.discipline.alpine': 'Alp xizəyi',
'tours.ski.discipline.snowboard': 'Snoubord',
'tours.ski.discipline.cross_country': 'Xizək qaçışı',
'tours.ski.discipline.touring': 'Dağ xizəyi turu',
'tours.ski.technique': 'Texnika',
'tours.ski.technique.classic': 'Klassik',
'tours.ski.technique.skating': 'Konki üslubu',
'tours.ski.technique.unknown': 'Naməlum',
'tours.ski.chooseDiscipline': 'İzi xizək kimi idxal etmək üçün növ seç.',
'tours.ski.chooseTechnique': 'Texnikanı və ya Naməlum seç.',
'tours.ski.infoLabel': 'TREK xizək izi ilə nə edir',
'tours.ski.info':
'Qeydə alındığı kimi qalır və heç vaxt marşrutlanmır. Məsafə və hündürlüklər fayldan gəlir. TREK xizək vaxtı hesablamır, nə iz, nə də 3D-dəki meyil rəngləri çətinlik, qar, uçqun və ya trassaların açıq olub-olmaması barədə heç nə demir.',
'tours.ski.time': 'Xizək vaxtı',
'tours.ski.timeUnknown': 'Hesablanmayıb',
'tours.ski.classifyTitle': 'Xizək izini təsnif et',
'tours.ski.saved': 'Təsnifat saxlanıldı',
'tours.ski.saveFailed': 'Təsnifatı saxlamaq alınmadı.',
'tours.planner.tourTypeHint.run': 'Piyada öz tempinizlə, qaçış üçün rahat yollarla',
'tours.planner.namePlaceholder.run': 'Adsız qaçış',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -205,7 +205,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Trilha',
'tourTypes.bike': 'Passeio de bicicleta',
'tourTypes.run': 'Corrida',
'tourTypes.ski': 'Passeio de esqui',
'tourTypes.ski': 'Esqui',
'tourTypes.kayak': 'Passeio de remo',
'tourTypes.walk': 'Caminhada urbana / turística',
'tourTypes.food': 'Passeio gastronômico / enológico',
@@ -410,6 +410,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Caminhada com raquetes de neve',
'tours.winter.labelOnly':
'Marcado pelos viajantes. Rota e tempo são planejados como em qualquer caminhada, sem verificação para o inverno.',
'tours.planner.tourTypeHint.ski': 'De um arquivo GPX, como foi gravado',
'tours.ski.title': 'Trilha de esqui',
'tours.ski.discipline': 'Modalidade',
'tours.ski.discipline.alpine': 'Alpino',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Esqui cross-country',
'tours.ski.discipline.touring': 'Esqui de travessia',
'tours.ski.technique': 'Técnica',
'tours.ski.technique.classic': 'Clássica',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Desconhecida',
'tours.ski.chooseDiscipline': 'Escolha uma modalidade para importar a trilha como esqui.',
'tours.ski.chooseTechnique': 'Escolha a técnica, ou Desconhecida.',
'tours.ski.infoLabel': 'O que o TREK faz com uma trilha de esqui',
'tours.ski.info':
'Fica como foi gravada e nunca é roteada. Distância e altitudes vêm do arquivo. O TREK não calcula tempo de esqui, e nem a trilha nem as cores de inclinação em 3D dizem algo sobre dificuldade, neve, avalanches ou se pistas e trilhas estão abertas.',
'tours.ski.time': 'Tempo de esqui',
'tours.ski.timeUnknown': 'Não calculado',
'tours.ski.classifyTitle': 'Classificar trilha de esqui',
'tours.ski.saved': 'Classificação salva',
'tours.ski.saveFailed': 'Não foi possível salvar a classificação.',
'tours.planner.tourTypeHint.run': 'A pé no seu ritmo, por caminhos bons para correr',
'tours.planner.namePlaceholder.run': 'Corrida sem título',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -205,7 +205,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Senderisme',
'tourTypes.bike': 'Ruta en bicicleta',
'tourTypes.run': 'Córrer',
'tourTypes.ski': 'Esquí de muntanya',
'tourTypes.ski': 'Esquí',
'tourTypes.kayak': 'Ruta en piragua',
'tourTypes.walk': 'Passeig urbà / turístic',
'tourTypes.food': 'Ruta gastronòmica / enològica',
@@ -411,6 +411,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Excursió amb raquetes de neu',
'tours.winter.labelOnly':
'Marcat pels viatgers. La ruta i el temps es planifiquen com en qualsevol excursió, sense comprovar l’hivern.',
'tours.planner.tourTypeHint.ski': 'D’un fitxer GPX, tal com es va gravar',
'tours.ski.title': 'Traça d’esquí',
'tours.ski.discipline': 'Disciplina',
'tours.ski.discipline.alpine': 'Alpí',
'tours.ski.discipline.snowboard': 'Surf de neu',
'tours.ski.discipline.cross_country': 'Esquí de fons',
'tours.ski.discipline.touring': 'Esquí de muntanya',
'tours.ski.technique': 'Tècnica',
'tours.ski.technique.classic': 'Clàssica',
'tours.ski.technique.skating': 'Patinador',
'tours.ski.technique.unknown': 'Desconeguda',
'tours.ski.chooseDiscipline': 'Tria una disciplina per importar la traça com a esquí.',
'tours.ski.chooseTechnique': 'Tria la tècnica, o Desconeguda.',
'tours.ski.infoLabel': 'Què fa TREK amb una traça d’esquí',
'tours.ski.info':
'Es manté tal com es va gravar i mai no s’enruta. La distància i les altituds vénen del fitxer. TREK no calcula cap temps d’esquí, i ni la traça ni els colors de pendent en 3D diuen res de dificultat, neu, allaus o si les pistes estan obertes.',
'tours.ski.time': 'Temps d’esquí',
'tours.ski.timeUnknown': 'Sense calcular',
'tours.ski.classifyTitle': 'Classificar la traça d’esquí',
'tours.ski.saved': 'Classificació desada',
'tours.ski.saveFailed': 'No s’ha pogut desar la classificació.',
'tours.planner.tourTypeHint.run': 'A peu al teu ritme, per camins bons per córrer',
'tours.planner.namePlaceholder.run': 'Cursa sense títol',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -203,7 +203,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Pěší túra',
'tourTypes.bike': 'Cyklovýlet',
'tourTypes.run': 'Běh',
'tourTypes.ski': 'Skialpinistická túra',
'tourTypes.ski': 'Lyže',
'tourTypes.kayak': 'Vodácká túra',
'tourTypes.walk': 'Procházka městem / po památkách',
'tourTypes.food': 'Gastronomický / vinařský výlet',
@@ -403,6 +403,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Túra na sněžnicích',
'tours.winter.labelOnly':
'Označeno cestovateli. Trasa a čas se plánují jako u každé túry, bez kontroly zimních podmínek.',
'tours.planner.tourTypeHint.ski': 'Ze souboru GPX, tak jak byla zaznamenána',
'tours.ski.title': 'Lyžařská trasa',
'tours.ski.discipline': 'Disciplína',
'tours.ski.discipline.alpine': 'Sjezd',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Běh na lyžích',
'tours.ski.discipline.touring': 'Skialpinismus',
'tours.ski.technique': 'Technika',
'tours.ski.technique.classic': 'Klasika',
'tours.ski.technique.skating': 'Bruslení',
'tours.ski.technique.unknown': 'Neznámá',
'tours.ski.chooseDiscipline': 'Vyberte disciplínu pro import trasy jako lyže.',
'tours.ski.chooseTechnique': 'Vyberte techniku, nebo Neznámá.',
'tours.ski.infoLabel': 'Co TREK dělá s lyžařskou trasou',
'tours.ski.info':
'Zůstává tak, jak byla zaznamenána, a nikdy se nepřepočítává. Vzdálenost a výšky pocházejí ze souboru. TREK nepočítá žádný čas na lyžích a ani trasa, ani barvy sklonu ve 3D neříkají nic o obtížnosti, sněhu, lavinách ani o tom, zda jsou sjezdovky a stopy otevřené.',
'tours.ski.time': 'Čas na lyžích',
'tours.ski.timeUnknown': 'Nepočítá se',
'tours.ski.classifyTitle': 'Zařadit lyžařskou trasu',
'tours.ski.saved': 'Zařazení uloženo',
'tours.ski.saveFailed': 'Zařazení se nepodařilo uložit.',
'tours.planner.tourTypeHint.run': 'Pěšky vlastním tempem, po dobře běhatelných cestách',
'tours.planner.namePlaceholder.run': 'Běh bez názvu',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -206,7 +206,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Wanderung',
'tourTypes.bike': 'Radtour',
'tourTypes.run': 'Lauf',
'tourTypes.ski': 'Skitour',
'tourTypes.ski': 'Ski', // same-as-en
'tourTypes.kayak': 'Paddeltour',
'tourTypes.walk': 'Stadt- und Spaziergang',
'tourTypes.food': 'Genusstour',
@@ -413,6 +413,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Schneeschuhwandern',
'tours.winter.labelOnly':
'Von den Reisenden gekennzeichnet. Route und Zeit sind wie bei jeder Wanderung geplant, nicht für den Winter geprüft.',
'tours.planner.tourTypeHint.ski': 'Aus einer GPX-Datei, wie aufgezeichnet',
'tours.ski.title': 'Skistrecke',
'tours.ski.discipline': 'Disziplin',
'tours.ski.discipline.alpine': 'Alpin',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Langlauf',
'tours.ski.discipline.touring': 'Skitour',
'tours.ski.technique': 'Technik',
'tours.ski.technique.classic': 'Klassisch',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Unbekannt',
'tours.ski.chooseDiscipline': 'Wähle eine Disziplin, um die Strecke als Ski zu importieren.',
'tours.ski.chooseTechnique': 'Wähle die Technik oder Unbekannt.',
'tours.ski.infoLabel': 'Was TREK mit einer Skistrecke macht',
'tours.ski.info':
'Bleibt wie aufgezeichnet und wird nie geroutet. Distanz und Höhen stammen aus der Datei. TREK berechnet keine Skizeit, und weder die Strecke noch die Steigungsfarben in 3D sagen etwas über Schwierigkeit, Schnee, Lawinen oder geöffnete Pisten und Loipen.',
'tours.ski.time': 'Skizeit',
'tours.ski.timeUnknown': 'Nicht berechnet',
'tours.ski.classifyTitle': 'Skistrecke einordnen',
'tours.ski.saved': 'Einordnung gespeichert',
'tours.ski.saveFailed': 'Die Einordnung konnte nicht gespeichert werden.',
'tours.planner.tourTypeHint.run': 'Zu Fuß in deiner Pace, auf gut laufbaren Wegen',
'tours.planner.namePlaceholder.run': 'Unbenannter Lauf',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -199,7 +199,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Hike',
'tourTypes.bike': 'Bike tour',
'tourTypes.run': 'Run',
'tourTypes.ski': 'Ski tour',
'tourTypes.ski': 'Ski',
'tourTypes.kayak': 'Paddle tour',
'tourTypes.walk': 'City / sightseeing walk',
'tourTypes.food': 'Food/wine tour',
@@ -403,6 +403,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Snowshoe hike',
'tours.winter.labelOnly':
'Marked by the travellers. Route and time are planned as for any hike, not checked for winter.',
'tours.planner.tourTypeHint.ski': 'From a GPX file, kept as recorded',
'tours.ski.title': 'Ski track',
'tours.ski.discipline': 'Discipline',
'tours.ski.discipline.alpine': 'Alpine',
'tours.ski.discipline.snowboard': 'Snowboard',
'tours.ski.discipline.cross_country': 'Cross-country',
'tours.ski.discipline.touring': 'Ski touring',
'tours.ski.technique': 'Technique',
'tours.ski.technique.classic': 'Classic',
'tours.ski.technique.skating': 'Skating',
'tours.ski.technique.unknown': 'Unknown',
'tours.ski.chooseDiscipline': 'Choose a discipline to import the track as ski.',
'tours.ski.chooseTechnique': 'Choose the technique, or Unknown.',
'tours.ski.infoLabel': 'What TREK does with a ski track',
'tours.ski.info':
'Kept as recorded and never routed. Distance and heights come from the file. TREK works out no ski time, and neither the track nor the gradient colours in 3D say anything about difficulty, snow, avalanches or whether runs and trails are open.',
'tours.ski.time': 'Ski time',
'tours.ski.timeUnknown': 'Not worked out',
'tours.ski.classifyTitle': 'Classify ski track',
'tours.ski.saved': 'Classification saved',
'tours.ski.saveFailed': 'The classification could not be saved.',
'tours.planner.tourTypeHint.run': 'On foot at your own pace, on runnable ways',
'tours.planner.namePlaceholder.run': 'Untitled run',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -205,7 +205,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Senderismo',
'tourTypes.bike': 'Ruta en bicicleta',
'tourTypes.run': 'Carrera a pie',
'tourTypes.ski': 'Esquí de travesía',
'tourTypes.ski': 'Esquí',
'tourTypes.kayak': 'Ruta en piragua',
'tourTypes.walk': 'Paseo urbano / turístico',
'tourTypes.food': 'Ruta gastronómica / enológica',
@@ -410,6 +410,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Ruta con raquetas de nieve',
'tours.winter.labelOnly':
'Marcado por los viajeros. Ruta y tiempo se planifican como en cualquier ruta, sin comprobación invernal.',
'tours.planner.tourTypeHint.ski': 'Desde un archivo GPX, tal como se grabó',
'tours.ski.title': 'Ruta de esquí',
'tours.ski.discipline': 'Disciplina',
'tours.ski.discipline.alpine': 'Alpino',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Esquí de fondo',
'tours.ski.discipline.touring': 'Esquí de travesía',
'tours.ski.technique': 'Técnica',
'tours.ski.technique.classic': 'Clásica',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Desconocida',
'tours.ski.chooseDiscipline': 'Elige una disciplina para importar la ruta como esquí.',
'tours.ski.chooseTechnique': 'Elige la técnica, o Desconocida.',
'tours.ski.infoLabel': 'Qué hace TREK con una ruta de esquí',
'tours.ski.info':
'Se conserva tal como se grabó y nunca se enruta. La distancia y las alturas vienen del archivo. TREK no calcula ningún tiempo de esquí, y ni la ruta ni los colores de pendiente en 3D dicen nada sobre dificultad, nieve, aludes o si las pistas están abiertas.',
'tours.ski.time': 'Tiempo de esquí',
'tours.ski.timeUnknown': 'Sin calcular',
'tours.ski.classifyTitle': 'Clasificar ruta de esquí',
'tours.ski.saved': 'Clasificación guardada',
'tours.ski.saveFailed': 'No se pudo guardar la clasificación.',
'tours.planner.tourTypeHint.run': 'A pie a tu ritmo, por caminos buenos para correr',
'tours.planner.namePlaceholder.run': 'Carrera sin título',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -202,7 +202,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Matk',
'tourTypes.bike': 'Rattaretk',
'tourTypes.run': 'Jooks',
'tourTypes.ski': 'Suusaretk',
'tourTypes.ski': 'Suusad',
'tourTypes.kayak': 'Aerutamismatk',
'tourTypes.walk': 'Linna- / vaatamisväärsuste jalutuskäik',
'tourTypes.food': 'Toidu- / veiniretk',
@@ -405,6 +405,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Räätsamatk',
'tours.winter.labelOnly':
'Reisijate märgitud. Marsruut ja aeg on kavandatud nagu igal matkal, talve jaoks kontrollimata.',
'tours.planner.tourTypeHint.ski': 'GPX-failist, nagu salvestati',
'tours.ski.title': 'Suusarada',
'tours.ski.discipline': 'Ala',
'tours.ski.discipline.alpine': 'Mäesuusatamine',
'tours.ski.discipline.snowboard': 'Lumelaud',
'tours.ski.discipline.cross_country': 'Murdmaasuusatamine',
'tours.ski.discipline.touring': 'Matkasuusatamine',
'tours.ski.technique': 'Tehnika',
'tours.ski.technique.classic': 'Klassika',
'tours.ski.technique.skating': 'Uisutehnika',
'tours.ski.technique.unknown': 'Teadmata',
'tours.ski.chooseDiscipline': 'Vali ala, et importida rada suusatamisena.',
'tours.ski.chooseTechnique': 'Vali tehnika või Teadmata.',
'tours.ski.infoLabel': 'Mida TREK suusarajaga teeb',
'tours.ski.info':
'Jääb nii, nagu salvestati, ja seda ei marsruudita kunagi. Pikkus ja kõrgused tulevad failist. TREK ei arvuta suusaaega ning ei rada ega 3D kallakuvärvid ütle midagi raskuse, lume, laviinide ega nõlvade ja radade avatuse kohta.',
'tours.ski.time': 'Suusaaeg',
'tours.ski.timeUnknown': 'Arvutamata',
'tours.ski.classifyTitle': 'Liigita suusarada',
'tours.ski.saved': 'Liigitus salvestatud',
'tours.ski.saveFailed': 'Liigitust ei õnnestunud salvestada.',
'tours.planner.tourTypeHint.run': 'Jalgsi omas tempos, hästi joostavatel teedel',
'tours.planner.namePlaceholder.run': 'Nimetu jooks',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -208,7 +208,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Randonnée',
'tourTypes.bike': 'Sortie à vélo',
'tourTypes.run': 'Course à pied',
'tourTypes.ski': 'Ski de randonnée',
'tourTypes.ski': 'Ski', // same-as-en
'tourTypes.kayak': 'Sortie en pagaie',
'tourTypes.walk': 'Balade urbaine / touristique',
'tourTypes.food': 'Circuit gastronomique / œnologique',
@@ -416,6 +416,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Randonnée en raquettes',
'tours.winter.labelOnly':
'Marqué par les voyageurs. Itinéraire et durée sont planifiés comme pour toute randonnée, sans vérification hivernale.',
'tours.planner.tourTypeHint.ski': 'Depuis un fichier GPX, tel qu’enregistré',
'tours.ski.title': 'Trace de ski',
'tours.ski.discipline': 'Discipline', // same-as-en
'tours.ski.discipline.alpine': 'Alpin',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Ski de fond',
'tours.ski.discipline.touring': 'Ski de randonnée',
'tours.ski.technique': 'Technique', // same-as-en
'tours.ski.technique.classic': 'Classique',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Inconnue',
'tours.ski.chooseDiscipline': 'Choisissez une discipline pour importer la trace en ski.',
'tours.ski.chooseTechnique': 'Choisissez la technique, ou Inconnue.',
'tours.ski.infoLabel': 'Ce que TREK fait d’une trace de ski',
'tours.ski.info':
'Conservée telle qu’enregistrée et jamais calculée par un routeur. Distance et altitudes viennent du fichier. TREK ne calcule aucun temps de ski, et ni la trace ni les couleurs de pente en 3D ne disent quoi que ce soit sur la difficulté, la neige, les avalanches ou l’ouverture des pistes.',
'tours.ski.time': 'Temps de ski',
'tours.ski.timeUnknown': 'Non calculé',
'tours.ski.classifyTitle': 'Classer la trace de ski',
'tours.ski.saved': 'Classement enregistré',
'tours.ski.saveFailed': 'Le classement n’a pas pu être enregistré.',
'tours.planner.tourTypeHint.run': 'À pied, à ton allure, sur des chemins où l’on court bien',
'tours.planner.namePlaceholder.run': 'Course sans titre',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -209,7 +209,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Πεζοπορία',
'tourTypes.bike': 'Ποδηλατική εκδρομή',
'tourTypes.run': 'Τρέξιμο',
'tourTypes.ski': 'Ορειβατικό σκι',
'tourTypes.ski': 'Σκι',
'tourTypes.kayak': 'Διαδρομή με κουπί',
'tourTypes.walk': 'Περίπατος στην πόλη / στα αξιοθέατα',
'tourTypes.food': 'Γαστρονομική / οινική εκδρομή',
@@ -418,6 +418,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Πεζοπορία με χιονοπέδιλα',
'tours.winter.labelOnly':
'Σημειώθηκε από τους ταξιδιώτες. Η διαδρομή και ο χρόνος σχεδιάζονται όπως σε κάθε πεζοπορία, χωρίς έλεγχο για τον χειμώνα.',
'tours.planner.tourTypeHint.ski': 'Από αρχείο GPX, όπως καταγράφηκε',
'tours.ski.title': 'Διαδρομή σκι',
'tours.ski.discipline': 'Αγώνισμα',
'tours.ski.discipline.alpine': 'Αλπικό',
'tours.ski.discipline.snowboard': 'Σνόουμπορντ',
'tours.ski.discipline.cross_country': 'Σκι αντοχής',
'tours.ski.discipline.touring': 'Ορειβατικό σκι',
'tours.ski.technique': 'Τεχνική',
'tours.ski.technique.classic': 'Κλασική',
'tours.ski.technique.skating': 'Ελεύθερη',
'tours.ski.technique.unknown': 'Άγνωστη',
'tours.ski.chooseDiscipline': 'Επιλέξτε αγώνισμα για εισαγωγή της διαδρομής ως σκι.',
'tours.ski.chooseTechnique': 'Επιλέξτε τεχνική ή Άγνωστη.',
'tours.ski.infoLabel': 'Τι κάνει το TREK με μια διαδρομή σκι',
'tours.ski.info':
'Μένει όπως καταγράφηκε και δεν δρομολογείται ποτέ. Απόσταση και υψόμετρα προέρχονται από το αρχείο. Το TREK δεν υπολογίζει χρόνο σκι, και ούτε η διαδρομή ούτε τα χρώματα κλίσης στο 3D λένε κάτι για δυσκολία, χιόνι, χιονοστιβάδες ή αν οι πίστες είναι ανοιχτές.',
'tours.ski.time': 'Χρόνος σκι',
'tours.ski.timeUnknown': 'Δεν υπολογίστηκε',
'tours.ski.classifyTitle': 'Κατάταξη διαδρομής σκι',
'tours.ski.saved': 'Η κατάταξη αποθηκεύτηκε',
'tours.ski.saveFailed': 'Η κατάταξη δεν αποθηκεύτηκε.',
'tours.planner.tourTypeHint.run': 'Με τα πόδια, με τον δικό σου ρυθμό, σε δρόμους κατάλληλους για τρέξιμο',
'tours.planner.namePlaceholder.run': 'Τρέξιμο χωρίς τίτλο',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -204,7 +204,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Gyalogtúra',
'tourTypes.bike': 'Kerékpártúra',
'tourTypes.run': 'Futás',
'tourTypes.ski': 'Sítúra',
'tourTypes.ski': 'Sí',
'tourTypes.kayak': 'Evezős túra',
'tourTypes.walk': 'Városi / városnéző séta',
'tourTypes.food': 'Gasztro- / bortúra',
@@ -410,6 +410,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Hótalpas túra',
'tours.winter.labelOnly':
'Az utazók jelölték meg. Az útvonal és az idő úgy van tervezve, mint bármely túránál, téli szempontból nincs ellenőrizve.',
'tours.planner.tourTypeHint.ski': 'GPX-fájlból, ahogy rögzítették',
'tours.ski.title': 'Sínyomvonal',
'tours.ski.discipline': 'Szakág',
'tours.ski.discipline.alpine': 'Alpesi',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Sífutás',
'tours.ski.discipline.touring': 'Sítúra',
'tours.ski.technique': 'Technika',
'tours.ski.technique.classic': 'Klasszikus',
'tours.ski.technique.skating': 'Korcsolyázó',
'tours.ski.technique.unknown': 'Ismeretlen',
'tours.ski.chooseDiscipline': 'Válassz szakágat, hogy sínyomvonalként importáld.',
'tours.ski.chooseTechnique': 'Válaszd ki a technikát, vagy az Ismeretlent.',
'tours.ski.infoLabel': 'Mit kezd a TREK egy sínyomvonallal',
'tours.ski.info':
'Úgy marad, ahogy rögzítették, útvonaltervező soha nem számolja újra. A táv és a magasságok a fájlból származnak. A TREK nem számol síidőt, és sem a nyomvonal, sem a 3D lejtőszínei nem mondanak semmit a nehézségről, a hóról, a lavinákról vagy arról, hogy nyitva vannak-e a pályák.',
'tours.ski.time': 'Síidő',
'tours.ski.timeUnknown': 'Nincs kiszámolva',
'tours.ski.classifyTitle': 'Sínyomvonal besorolása',
'tours.ski.saved': 'Besorolás mentve',
'tours.ski.saveFailed': 'A besorolást nem sikerült menteni.',
'tours.planner.tourTypeHint.run': 'Gyalog a saját tempódban, jól futható utakon',
'tours.planner.namePlaceholder.run': 'Névtelen futás',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -198,7 +198,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Pendakian',
'tourTypes.bike': 'Tur sepeda',
'tourTypes.run': 'Lari',
'tourTypes.ski': 'Tur ski',
'tourTypes.ski': 'Ski', // same-as-en
'tourTypes.kayak': 'Tur dayung',
'tourTypes.walk': 'Jalan-jalan kota / wisata',
'tourTypes.food': 'Tur kuliner / anggur',
@@ -405,6 +405,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Pendakian dengan sepatu salju',
'tours.winter.labelOnly':
'Ditandai oleh para pelancong. Rute dan waktu direncanakan seperti pendakian lain, tidak diperiksa untuk musim dingin.',
'tours.planner.tourTypeHint.ski': 'Dari berkas GPX, seperti yang direkam',
'tours.ski.title': 'Jalur ski',
'tours.ski.discipline': 'Disiplin',
'tours.ski.discipline.alpine': 'Alpen',
'tours.ski.discipline.snowboard': 'Papan seluncur salju',
'tours.ski.discipline.cross_country': 'Ski lintas alam',
'tours.ski.discipline.touring': 'Tur ski',
'tours.ski.technique': 'Teknik',
'tours.ski.technique.classic': 'Klasik',
'tours.ski.technique.skating': 'Gaya bebas',
'tours.ski.technique.unknown': 'Tidak diketahui',
'tours.ski.chooseDiscipline': 'Pilih disiplin untuk mengimpor jalur sebagai ski.',
'tours.ski.chooseTechnique': 'Pilih teknik, atau Tidak diketahui.',
'tours.ski.infoLabel': 'Apa yang TREK lakukan dengan jalur ski',
'tours.ski.info':
'Disimpan seperti yang direkam dan tidak pernah dirutekan. Jarak dan ketinggian berasal dari berkas. TREK tidak menghitung waktu ski, dan baik jalur maupun warna kemiringan dalam 3D tidak mengatakan apa pun tentang kesulitan, salju, longsoran, atau apakah lintasan sedang dibuka.',
'tours.ski.time': 'Waktu ski',
'tours.ski.timeUnknown': 'Tidak dihitung',
'tours.ski.classifyTitle': 'Klasifikasikan jalur ski',
'tours.ski.saved': 'Klasifikasi disimpan',
'tours.ski.saveFailed': 'Klasifikasi tidak dapat disimpan.',
'tours.planner.tourTypeHint.run': 'Berjalan kaki dengan tempomu sendiri, di jalan yang enak untuk lari',
'tours.planner.namePlaceholder.run': 'Lari tanpa judul',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -204,7 +204,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Escursione a piedi',
'tourTypes.bike': 'Escursione in bici',
'tourTypes.run': 'Corsa',
'tourTypes.ski': 'Escursione con gli sci',
'tourTypes.ski': 'Sci',
'tourTypes.kayak': 'Uscita in pagaia',
'tourTypes.walk': 'Passeggiata urbana / turistica',
'tourTypes.food': 'Tour enogastronomico',
@@ -413,6 +413,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Ciaspolata',
'tours.winter.labelOnly':
'Contrassegnato dai viaggiatori. Percorso e tempi sono pianificati come per ogni escursione, non verificati per l’inverno.',
'tours.planner.tourTypeHint.ski': 'Da un file GPX, così come registrato',
'tours.ski.title': 'Traccia di sci',
'tours.ski.discipline': 'Disciplina',
'tours.ski.discipline.alpine': 'Alpino',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Sci di fondo',
'tours.ski.discipline.touring': 'Scialpinismo',
'tours.ski.technique': 'Tecnica',
'tours.ski.technique.classic': 'Classica',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Sconosciuta',
'tours.ski.chooseDiscipline': 'Scegli una disciplina per importare la traccia come sci.',
'tours.ski.chooseTechnique': 'Scegli la tecnica, oppure Sconosciuta.',
'tours.ski.infoLabel': 'Cosa fa TREK con una traccia di sci',
'tours.ski.info':
'Resta come registrata e non viene mai ricalcolata. Distanza e quote vengono dal file. TREK non calcola alcun tempo di sci, e né la traccia né i colori della pendenza in 3D dicono nulla su difficoltà, neve, valanghe o apertura di piste e anelli.',
'tours.ski.time': 'Tempo di sci',
'tours.ski.timeUnknown': 'Non calcolato',
'tours.ski.classifyTitle': 'Classifica traccia di sci',
'tours.ski.saved': 'Classificazione salvata',
'tours.ski.saveFailed': 'Impossibile salvare la classificazione.',
'tours.planner.tourTypeHint.run': 'A piedi al tuo passo, su percorsi adatti alla corsa',
'tours.planner.namePlaceholder.run': 'Corsa senza titolo',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -195,7 +195,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'ハイキング',
'tourTypes.bike': 'サイクリング',
'tourTypes.run': 'ランニング',
'tourTypes.ski': 'スキーツアー',
'tourTypes.ski': 'スキー',
'tourTypes.kayak': 'パドルツアー',
'tourTypes.walk': '街歩き・観光',
'tourTypes.food': 'グルメ・ワインツアー',
@@ -395,6 +395,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'スノーシューハイキング',
'tours.winter.labelOnly':
'旅行者が付けたラベルです。ルートと時間は通常のハイキングと同じく計画され、冬向けには確認されていません。',
'tours.planner.tourTypeHint.ski': 'GPX ファイルから、記録されたまま',
'tours.ski.title': 'スキーのトラック',
'tours.ski.discipline': '種目',
'tours.ski.discipline.alpine': 'アルペン',
'tours.ski.discipline.snowboard': 'スノーボード',
'tours.ski.discipline.cross_country': 'クロスカントリー',
'tours.ski.discipline.touring': '山スキー',
'tours.ski.technique': '走法',
'tours.ski.technique.classic': 'クラシカル',
'tours.ski.technique.skating': 'スケーティング',
'tours.ski.technique.unknown': '不明',
'tours.ski.chooseDiscipline': 'スキーとして取り込むには種目を選んでください。',
'tours.ski.chooseTechnique': '走法を選ぶか、不明を選んでください。',
'tours.ski.infoLabel': 'TREK がスキーのトラックで行うこと',
'tours.ski.info':
'記録されたまま保持され、ルート検索されることはありません。距離と標高はファイルの値です。TREK はスキーの所要時間を計算せず、トラックも 3D の勾配の色も、難易度、積雪、雪崩、コースの営業状況について何も示しません。',
'tours.ski.time': 'スキー時間',
'tours.ski.timeUnknown': '計算なし',
'tours.ski.classifyTitle': 'スキーのトラックを分類',
'tours.ski.saved': '分類を保存しました',
'tours.ski.saveFailed': '分類を保存できませんでした。',
'tours.planner.tourTypeHint.run': '自分のペースで、走りやすい道を',
'tours.planner.namePlaceholder.run': '無題のラン',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -193,7 +193,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': '하이킹',
'tourTypes.bike': '자전거 투어',
'tourTypes.run': '달리기',
'tourTypes.ski': '스키 투어',
'tourTypes.ski': '스키',
'tourTypes.kayak': '패들 투어',
'tourTypes.walk': '도심 산책 / 관광',
'tourTypes.food': '미식 / 와인 투어',
@@ -393,6 +393,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': '스노슈 하이킹',
'tours.winter.labelOnly':
'여행자가 표시했습니다. 경로와 시간은 다른 하이킹과 같이 계획되며 겨울 조건은 확인하지 않았습니다.',
'tours.planner.tourTypeHint.ski': 'GPX 파일에서, 기록된 그대로',
'tours.ski.title': '스키 트랙',
'tours.ski.discipline': '종목',
'tours.ski.discipline.alpine': '알파인',
'tours.ski.discipline.snowboard': '스노보드',
'tours.ski.discipline.cross_country': '크로스컨트리',
'tours.ski.discipline.touring': '산악 스키',
'tours.ski.technique': '주법',
'tours.ski.technique.classic': '클래식',
'tours.ski.technique.skating': '스케이팅',
'tours.ski.technique.unknown': '알 수 없음',
'tours.ski.chooseDiscipline': '트랙을 스키로 가져오려면 종목을 선택하세요.',
'tours.ski.chooseTechnique': '주법을 선택하거나 알 수 없음을 선택하세요.',
'tours.ski.infoLabel': 'TREK이 스키 트랙으로 하는 일',
'tours.ski.info':
'기록된 그대로 유지되며 경로를 다시 계산하지 않습니다. 거리와 고도는 파일에서 가져옵니다. TREK은 스키 시간을 계산하지 않으며, 트랙도 3D 경사 색상도 난이도, 눈, 눈사태, 슬로프 운영 여부에 대해 아무것도 알려 주지 않습니다.',
'tours.ski.time': '스키 시간',
'tours.ski.timeUnknown': '계산 안 함',
'tours.ski.classifyTitle': '스키 트랙 분류',
'tours.ski.saved': '분류를 저장했습니다',
'tours.ski.saveFailed': '분류를 저장하지 못했습니다.',
'tours.planner.tourTypeHint.run': '내 페이스로 걸어서, 달리기 좋은 길로',
'tours.planner.namePlaceholder.run': '이름 없는 달리기',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -204,7 +204,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Wandeling',
'tourTypes.bike': 'Fietstocht',
'tourTypes.run': 'Hardlooptocht',
'tourTypes.ski': 'Skitocht',
'tourTypes.ski': 'Ski', // same-as-en
'tourTypes.kayak': 'Peddeltocht',
'tourTypes.walk': 'Stadswandeling / bezienswaardigheden',
'tourTypes.food': 'Culinaire tocht / wijntocht',
@@ -412,6 +412,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Sneeuwschoenwandeling',
'tours.winter.labelOnly':
'Gemarkeerd door de reizigers. Route en tijd worden gepland als bij elke wandeling, niet gecontroleerd op de winter.',
'tours.planner.tourTypeHint.ski': 'Uit een GPX-bestand, zoals opgenomen',
'tours.ski.title': 'Skispoor',
'tours.ski.discipline': 'Discipline', // same-as-en
'tours.ski.discipline.alpine': 'Alpine', // same-as-en
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Langlaufen',
'tours.ski.discipline.touring': 'Toerskiën',
'tours.ski.technique': 'Techniek',
'tours.ski.technique.classic': 'Klassiek',
'tours.ski.technique.skating': 'Skating', // same-as-en
'tours.ski.technique.unknown': 'Onbekend',
'tours.ski.chooseDiscipline': 'Kies een discipline om het spoor als ski te importeren.',
'tours.ski.chooseTechnique': 'Kies de techniek, of Onbekend.',
'tours.ski.infoLabel': 'Wat TREK met een skispoor doet',
'tours.ski.info':
'Blijft zoals opgenomen en wordt nooit gerouteerd. Afstand en hoogtes komen uit het bestand. TREK berekent geen skitijd, en noch het spoor noch de hellingkleuren in 3D zeggen iets over moeilijkheid, sneeuw, lawines of of pistes en loipes open zijn.',
'tours.ski.time': 'Skitijd',
'tours.ski.timeUnknown': 'Niet berekend',
'tours.ski.classifyTitle': 'Skispoor indelen',
'tours.ski.saved': 'Indeling opgeslagen',
'tours.ski.saveFailed': 'De indeling kon niet worden opgeslagen.',
'tours.planner.tourTypeHint.run': 'Te voet in je eigen tempo, over goed beloopbare wegen',
'tours.planner.namePlaceholder.run': 'Naamloze looptocht',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -212,7 +212,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Wędrówka piesza',
'tourTypes.bike': 'Wycieczka rowerowa',
'tourTypes.run': 'Bieg',
'tourTypes.ski': 'Wycieczka skiturowa',
'tourTypes.ski': 'Narty',
'tourTypes.kayak': 'Spływ',
'tourTypes.walk': 'Spacer po mieście / zwiedzanie',
'tourTypes.food': 'Wycieczka kulinarna / winiarska',
@@ -419,6 +419,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Wędrówka na rakietach śnieżnych',
'tours.winter.labelOnly':
'Oznaczone przez podróżnych. Trasa i czas są planowane jak przy każdej wędrówce, bez sprawdzania pod kątem zimy.',
'tours.planner.tourTypeHint.ski': 'Z pliku GPX, tak jak został nagrany',
'tours.ski.title': 'Ślad narciarski',
'tours.ski.discipline': 'Dyscyplina',
'tours.ski.discipline.alpine': 'Zjazdowe',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Biegowe',
'tours.ski.discipline.touring': 'Skitury',
'tours.ski.technique': 'Technika',
'tours.ski.technique.classic': 'Klasyczna',
'tours.ski.technique.skating': 'Łyżwowa',
'tours.ski.technique.unknown': 'Nieznana',
'tours.ski.chooseDiscipline': 'Wybierz dyscyplinę, aby zaimportować ślad jako narty.',
'tours.ski.chooseTechnique': 'Wybierz technikę lub Nieznana.',
'tours.ski.infoLabel': 'Co TREK robi ze śladem narciarskim',
'tours.ski.info':
'Pozostaje taki, jak został nagrany, i nigdy nie jest wyznaczany na nowo. Dystans i wysokości pochodzą z pliku. TREK nie wylicza czasu na nartach, a ani ślad, ani kolory nachylenia w 3D nie mówią nic o trudności, śniegu, lawinach ani o tym, czy trasy są otwarte.',
'tours.ski.time': 'Czas na nartach',
'tours.ski.timeUnknown': 'Nie wyliczono',
'tours.ski.classifyTitle': 'Sklasyfikuj ślad narciarski',
'tours.ski.saved': 'Klasyfikację zapisano',
'tours.ski.saveFailed': 'Nie udało się zapisać klasyfikacji.',
'tours.planner.tourTypeHint.run': 'Pieszo we własnym tempie, po drogach dobrych do biegania',
'tours.planner.namePlaceholder.run': 'Bieg bez nazwy',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -214,7 +214,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Пеший поход',
'tourTypes.bike': 'Велотур',
'tourTypes.run': 'Пробежка',
'tourTypes.ski': 'Лыжный тур',
'tourTypes.ski': 'Лыжи',
'tourTypes.kayak': 'Сплав',
'tourTypes.walk': 'Прогулка по городу / достопримечательностям',
'tourTypes.food': 'Гастрономический / винный тур',
@@ -423,6 +423,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Поход на снегоступах',
'tours.winter.labelOnly':
'Отмечено путешественниками. Маршрут и время рассчитаны как для любого похода, без проверки на зимние условия.',
'tours.planner.tourTypeHint.ski': 'Из файла GPX, как записано',
'tours.ski.title': 'Лыжный трек',
'tours.ski.discipline': 'Дисциплина',
'tours.ski.discipline.alpine': 'Горные лыжи',
'tours.ski.discipline.snowboard': 'Сноуборд',
'tours.ski.discipline.cross_country': 'Беговые лыжи',
'tours.ski.discipline.touring': 'Ски-тур',
'tours.ski.technique': 'Техника',
'tours.ski.technique.classic': 'Классика',
'tours.ski.technique.skating': 'Конёк',
'tours.ski.technique.unknown': 'Неизвестно',
'tours.ski.chooseDiscipline': 'Выберите дисциплину, чтобы импортировать трек как лыжи.',
'tours.ski.chooseTechnique': 'Выберите технику или «Неизвестно».',
'tours.ski.infoLabel': 'Что TREK делает с лыжным треком',
'tours.ski.info':
'Остаётся таким, как записан, и никогда не прокладывается заново. Расстояние и высоты взяты из файла. TREK не рассчитывает время на лыжах, а ни трек, ни цвета уклона в 3D ничего не говорят о сложности, снеге, лавинах или о том, открыты ли трассы и лыжни.',
'tours.ski.time': 'Время на лыжах',
'tours.ski.timeUnknown': 'Не рассчитано',
'tours.ski.classifyTitle': 'Классифицировать лыжный трек',
'tours.ski.saved': 'Классификация сохранена',
'tours.ski.saveFailed': 'Не удалось сохранить классификацию.',
'tours.planner.tourTypeHint.run': 'Пешком в своём темпе, по удобным для бега дорогам',
'tours.planner.namePlaceholder.run': 'Пробежка без названия',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -204,7 +204,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Pešia túra',
'tourTypes.bike': 'Cyklotúra',
'tourTypes.run': 'Beh',
'tourTypes.ski': 'Lyžiarska túra',
'tourTypes.ski': 'Lyže',
'tourTypes.kayak': 'Vodácka túra',
'tourTypes.walk': 'Prechádzka mestom / po pamiatkach',
'tourTypes.food': 'Gastronomická / vínna túra',
@@ -404,6 +404,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Túra na snežniciach',
'tours.winter.labelOnly':
'Označené cestovateľmi. Trasa a čas sa plánujú ako pri každej túre, bez kontroly zimných podmienok.',
'tours.planner.tourTypeHint.ski': 'Zo súboru GPX, tak ako bola zaznamenaná',
'tours.ski.title': 'Lyžiarska trasa',
'tours.ski.discipline': 'Disciplína',
'tours.ski.discipline.alpine': 'Zjazd',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Beh na lyžiach',
'tours.ski.discipline.touring': 'Skialpinizmus',
'tours.ski.technique': 'Technika',
'tours.ski.technique.classic': 'Klasika',
'tours.ski.technique.skating': 'Korčuľovanie',
'tours.ski.technique.unknown': 'Neznáma',
'tours.ski.chooseDiscipline': 'Vyberte disciplínu na import trasy ako lyže.',
'tours.ski.chooseTechnique': 'Vyberte techniku, alebo Neznáma.',
'tours.ski.infoLabel': 'Čo TREK robí s lyžiarskou trasou',
'tours.ski.info':
'Zostáva tak, ako bola zaznamenaná, a nikdy sa neprepočítava. Vzdialenosť a výšky pochádzajú zo súboru. TREK nepočíta žiadny čas na lyžiach a ani trasa, ani farby sklonu v 3D nehovoria nič o náročnosti, snehu, lavínach ani o tom, či sú zjazdovky a stopy otvorené.',
'tours.ski.time': 'Čas na lyžiach',
'tours.ski.timeUnknown': 'Nepočíta sa',
'tours.ski.classifyTitle': 'Zaradiť lyžiarsku trasu',
'tours.ski.saved': 'Zaradenie uložené',
'tours.ski.saveFailed': 'Zaradenie sa nepodarilo uložiť.',
'tours.planner.tourTypeHint.run': 'Pešo vlastným tempom, po dobre bežateľných cestách',
'tours.planner.namePlaceholder.run': 'Beh bez názvu',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -199,7 +199,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Vandring',
'tourTypes.bike': 'Cykeltur',
'tourTypes.run': 'Löptur',
'tourTypes.ski': 'Skidtur',
'tourTypes.ski': 'Skidor',
'tourTypes.kayak': 'Paddeltur',
'tourTypes.walk': 'Stadspromenad / sightseeing',
'tourTypes.food': 'Mat- och vintur',
@@ -402,6 +402,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Snöskovandring',
'tours.winter.labelOnly':
'Märkt av resenärerna. Rutt och tid planeras som för alla vandringar, inte kontrollerade för vinter.',
'tours.planner.tourTypeHint.ski': 'Från en GPX-fil, som den spelades in',
'tours.ski.title': 'Skidspår',
'tours.ski.discipline': 'Gren',
'tours.ski.discipline.alpine': 'Utförsåkning',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Längdskidor',
'tours.ski.discipline.touring': 'Topptur',
'tours.ski.technique': 'Teknik',
'tours.ski.technique.classic': 'Klassisk',
'tours.ski.technique.skating': 'Skate',
'tours.ski.technique.unknown': 'Okänd',
'tours.ski.chooseDiscipline': 'Välj en gren för att importera spåret som skidor.',
'tours.ski.chooseTechnique': 'Välj teknik, eller Okänd.',
'tours.ski.infoLabel': 'Vad TREK gör med ett skidspår',
'tours.ski.info':
'Behålls som det spelades in och ruttplaneras aldrig. Sträcka och höjder kommer från filen. TREK räknar inte ut någon skidtid, och varken spåret eller lutningsfärgerna i 3D säger något om svårighet, snö, laviner eller om backar och spår är öppna.',
'tours.ski.time': 'Skidtid',
'tours.ski.timeUnknown': 'Inte uträknad',
'tours.ski.classifyTitle': 'Klassa skidspår',
'tours.ski.saved': 'Klassning sparad',
'tours.ski.saveFailed': 'Klassningen kunde inte sparas.',
'tours.planner.tourTypeHint.run': 'Till fots i din egen takt, på bra löpvägar',
'tours.planner.namePlaceholder.run': 'Namnlös löptur',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -191,7 +191,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'เดินป่า',
'tourTypes.bike': 'ทัวร์จักรยาน',
'tourTypes.run': 'วิ่ง',
'tourTypes.ski': 'ทัวร์สกี',
'tourTypes.ski': 'สกี',
'tourTypes.kayak': 'ทริปพายเรือ',
'tourTypes.walk': 'เดินชมเมือง / สถานที่ท่องเที่ยว',
'tourTypes.food': 'ทัวร์อาหาร/ไวน์',
@@ -384,6 +384,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'เดินป่าด้วยรองเท้าลุยหิมะ',
'tours.winter.labelOnly':
'ผู้เดินทางเป็นผู้ทำเครื่องหมาย เส้นทางและเวลาวางแผนเหมือนการเดินป่าทั่วไป ไม่ได้ตรวจสอบสำหรับฤดูหนาว',
'tours.planner.tourTypeHint.ski': 'จากไฟล์ GPX ตามที่บันทึกไว้',
'tours.ski.title': 'เส้นทางสกี',
'tours.ski.discipline': 'ประเภท',
'tours.ski.discipline.alpine': 'อัลไพน์',
'tours.ski.discipline.snowboard': 'สโนว์บอร์ด',
'tours.ski.discipline.cross_country': 'สกีทางเรียบ',
'tours.ski.discipline.touring': 'สกีปีนเขา',
'tours.ski.technique': 'เทคนิค',
'tours.ski.technique.classic': 'คลาสสิก',
'tours.ski.technique.skating': 'สเก็ตติ้ง',
'tours.ski.technique.unknown': 'ไม่ทราบ',
'tours.ski.chooseDiscipline': 'เลือกประเภทเพื่อนำเข้าเส้นทางเป็นสกี',
'tours.ski.chooseTechnique': 'เลือกเทคนิค หรือไม่ทราบ',
'tours.ski.infoLabel': 'TREK ทำอะไรกับเส้นทางสกี',
'tours.ski.info':
'เก็บไว้ตามที่บันทึกและไม่คำนวณเส้นทางใหม่ ระยะทางและความสูงมาจากไฟล์ TREK ไม่คำนวณเวลาเล่นสกี และทั้งเส้นทางและสีความชันในมุมมอง 3D ไม่ได้บอกอะไรเกี่ยวกับความยาก หิมะ หิมะถล่ม หรือว่าลานสกีเปิดอยู่หรือไม่',
'tours.ski.time': 'เวลาเล่นสกี',
'tours.ski.timeUnknown': 'ไม่ได้คำนวณ',
'tours.ski.classifyTitle': 'จัดประเภทเส้นทางสกี',
'tours.ski.saved': 'บันทึกการจัดประเภทแล้ว',
'tours.ski.saveFailed': 'บันทึกการจัดประเภทไม่สำเร็จ',
'tours.planner.tourTypeHint.run': 'เดินเท้าตามจังหวะของคุณ บนทางที่วิ่งได้สะดวก',
'tours.planner.namePlaceholder.run': 'การวิ่งที่ไม่มีชื่อ',
'tours.planner.unreachable.run':
+1 -1
View File
@@ -20,7 +20,7 @@ const tokens = (value: string) => [...value.matchAll(/\{[^{}]+\}/g)].map((match)
// placeholder spec; here only the general keys are.
const groups = pluralGroups(Object.keys(english));
const generalKeys = (keys: string[]) => keys.filter((key) => !pluralFormOf(key, groups));
const GENERAL_KEY_COUNT = 861;
const GENERAL_KEY_COUNT = 881;
function literalKeys(source: string): string[] {
const tree = ts.createSourceFile('locale.ts', source, ts.ScriptTarget.Latest, true);
+22 -1
View File
@@ -200,7 +200,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Doğa yürüyüşü',
'tourTypes.bike': 'Bisiklet turu',
'tourTypes.run': 'Koşu',
'tourTypes.ski': 'Kayak turu',
'tourTypes.ski': 'Kayak',
'tourTypes.kayak': 'Kürek turu',
'tourTypes.walk': 'Şehir / gezi yürüyüşü',
'tourTypes.food': 'Yemek / şarap turu',
@@ -404,6 +404,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Kar ayakkabısıyla yürüyüş',
'tours.winter.labelOnly':
'Gezginler tarafından işaretlendi. Rota ve süre her yürüyüşteki gibi planlanır, kış koşullarına göre denetlenmez.',
'tours.planner.tourTypeHint.ski': 'Bir GPX dosyasından, kaydedildiği gibi',
'tours.ski.title': 'Kayak izi',
'tours.ski.discipline': 'Branş',
'tours.ski.discipline.alpine': 'Alp disiplini',
'tours.ski.discipline.snowboard': 'Snowboard', // same-as-en
'tours.ski.discipline.cross_country': 'Kayaklı koşu',
'tours.ski.discipline.touring': 'Dağ kayağı',
'tours.ski.technique': 'Teknik',
'tours.ski.technique.classic': 'Klasik',
'tours.ski.technique.skating': 'Serbest stil',
'tours.ski.technique.unknown': 'Bilinmiyor',
'tours.ski.chooseDiscipline': 'İzi kayak olarak içe aktarmak için bir branş seç.',
'tours.ski.chooseTechnique': 'Tekniği ya da Bilinmiyor seçeneğini seç.',
'tours.ski.infoLabel': 'TREK bir kayak iziyle ne yapar',
'tours.ski.info':
'Kaydedildiği gibi kalır ve hiçbir zaman yeniden rotalanmaz. Mesafe ve yükseklikler dosyadan gelir. TREK kayak süresi hesaplamaz; ne iz ne de 3D eğim renkleri zorluk, kar, çığ ya da pist ve parkurların açık olup olmadığı hakkında bir şey söyler.',
'tours.ski.time': 'Kayak süresi',
'tours.ski.timeUnknown': 'Hesaplanmadı',
'tours.ski.classifyTitle': 'Kayak izini sınıflandır',
'tours.ski.saved': 'Sınıflandırma kaydedildi',
'tours.ski.saveFailed': 'Sınıflandırma kaydedilemedi.',
'tours.planner.tourTypeHint.run': 'Yürüyerek kendi temponda, koşmaya uygun yollarda',
'tours.planner.namePlaceholder.run': 'Adsız koşu',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -211,7 +211,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Піший похід',
'tourTypes.bike': 'Велотур',
'tourTypes.run': 'Пробіжка',
'tourTypes.ski': 'Лижний тур',
'tourTypes.ski': 'Лижі',
'tourTypes.kayak': 'Сплав',
'tourTypes.walk': 'Прогулянка містом / визначними місцями',
'tourTypes.food': 'Гастрономічний / винний тур',
@@ -418,6 +418,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Похід на снігоступах',
'tours.winter.labelOnly':
'Позначено мандрівниками. Маршрут і час сплановано як для будь-якого походу, без перевірки на зимові умови.',
'tours.planner.tourTypeHint.ski': 'З файлу GPX, як записано',
'tours.ski.title': 'Лижний трек',
'tours.ski.discipline': 'Дисципліна',
'tours.ski.discipline.alpine': 'Гірські лижі',
'tours.ski.discipline.snowboard': 'Сноуборд',
'tours.ski.discipline.cross_country': 'Бігові лижі',
'tours.ski.discipline.touring': 'Скітур',
'tours.ski.technique': 'Техніка',
'tours.ski.technique.classic': 'Класика',
'tours.ski.technique.skating': 'Ковзанярський хід',
'tours.ski.technique.unknown': 'Невідомо',
'tours.ski.chooseDiscipline': 'Виберіть дисципліну, щоб імпортувати трек як лижі.',
'tours.ski.chooseTechnique': 'Виберіть техніку або «Невідомо».',
'tours.ski.infoLabel': 'Що TREK робить з лижним треком',
'tours.ski.info':
'Залишається таким, як записано, і ніколи не прокладається заново. Відстань і висоти взято з файлу. TREK не розраховує час на лижах, а ні трек, ні кольори нахилу в 3D нічого не кажуть про складність, сніг, лавини чи про те, чи відкриті траси й лижні.',
'tours.ski.time': 'Час на лижах',
'tours.ski.timeUnknown': 'Не розраховано',
'tours.ski.classifyTitle': 'Класифікувати лижний трек',
'tours.ski.saved': 'Класифікацію збережено',
'tours.ski.saveFailed': 'Не вдалося зберегти класифікацію.',
'tours.planner.tourTypeHint.run': 'Пішки у власному темпі, дорогами, зручними для бігу',
'tours.planner.namePlaceholder.run': 'Пробіжка без назви',
'tours.planner.unreachable.run':
+22 -1
View File
@@ -200,7 +200,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': 'Đi bộ đường dài',
'tourTypes.bike': 'Chuyến đạp xe',
'tourTypes.run': 'Chạy bộ',
'tourTypes.ski': 'Chuyến trượt tuyết',
'tourTypes.ski': 'Trượt tuyết',
'tourTypes.kayak': 'Chuyến chèo thuyền',
'tourTypes.walk': 'Đi bộ trong thành phố / ngắm cảnh',
'tourTypes.food': 'Chuyến khám phá ẩm thực / rượu vang',
@@ -402,6 +402,27 @@ const tours: TranslationStrings = {
'tours.winter.activity.snowshoe': 'Đi bộ bằng giày tuyết',
'tours.winter.labelOnly':
'Do du khách đánh dấu. Tuyến đường và thời gian được lập như mọi chuyến đi bộ khác, không kiểm tra cho mùa đông.',
'tours.planner.tourTypeHint.ski': 'Từ tệp GPX, giữ nguyên như đã ghi',
'tours.ski.title': 'Đường trượt tuyết',
'tours.ski.discipline': 'Bộ môn',
'tours.ski.discipline.alpine': 'Trượt đổ dốc',
'tours.ski.discipline.snowboard': 'Trượt ván tuyết',
'tours.ski.discipline.cross_country': 'Trượt tuyết băng đồng',
'tours.ski.discipline.touring': 'Trượt tuyết leo núi',
'tours.ski.technique': 'Kỹ thuật',
'tours.ski.technique.classic': 'Cổ điển',
'tours.ski.technique.skating': 'Trượt kiểu lướt',
'tours.ski.technique.unknown': 'Không rõ',
'tours.ski.chooseDiscipline': 'Chọn bộ môn để nhập đường đi thành trượt tuyết.',
'tours.ski.chooseTechnique': 'Chọn kỹ thuật, hoặc Không rõ.',
'tours.ski.infoLabel': 'TREK làm gì với một đường trượt tuyết',
'tours.ski.info':
'Được giữ nguyên như đã ghi và không bao giờ được định tuyến. Quãng đường và độ cao lấy từ tệp. TREK không tính thời gian trượt, và cả đường đi lẫn màu độ dốc trong 3D đều không nói gì về độ khó, tuyết, tuyết lở hay việc đường trượt có mở hay không.',
'tours.ski.time': 'Thời gian trượt',
'tours.ski.timeUnknown': 'Không tính',
'tours.ski.classifyTitle': 'Phân loại đường trượt tuyết',
'tours.ski.saved': 'Đã lưu phân loại',
'tours.ski.saveFailed': 'Không thể lưu phân loại.',
'tours.planner.tourTypeHint.run': 'Đi bộ theo nhịp của bạn, trên những con đường dễ chạy',
'tours.planner.namePlaceholder.run': 'Buổi chạy chưa đặt tên',
'tours.planner.unreachable.run':
+21 -1
View File
@@ -184,7 +184,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': '健行',
'tourTypes.bike': '單車遊程',
'tourTypes.run': '跑步',
'tourTypes.ski': '滑雪登山',
'tourTypes.ski': '滑雪',
'tourTypes.kayak': '划槳行程',
'tourTypes.walk': '城市散步 / 觀光',
'tourTypes.food': '美食 / 葡萄酒之旅',
@@ -367,6 +367,26 @@ const tours: TranslationStrings = {
'tours.winter.activity.winter_hike': '冬季健行',
'tours.winter.activity.snowshoe': '雪鞋健行',
'tours.winter.labelOnly': '由旅行者標記。路線與時間按一般健行規劃,未針對冬季查核。',
'tours.planner.tourTypeHint.ski': '來自 GPX 檔案,保持記錄原樣',
'tours.ski.title': '滑雪軌跡',
'tours.ski.discipline': '項目',
'tours.ski.discipline.alpine': '高山滑雪',
'tours.ski.discipline.snowboard': '單板滑雪',
'tours.ski.discipline.cross_country': '越野滑雪',
'tours.ski.discipline.touring': '登山滑雪',
'tours.ski.technique': '技術',
'tours.ski.technique.classic': '傳統式',
'tours.ski.technique.skating': '自由式',
'tours.ski.technique.unknown': '未知',
'tours.ski.chooseDiscipline': '請選擇項目,以便將軌跡匯入為滑雪。',
'tours.ski.chooseTechnique': '請選擇技術,或選擇未知。',
'tours.ski.infoLabel': 'TREK 如何處理滑雪軌跡',
'tours.ski.info': '保持記錄原樣,絕不重新規劃路線。距離和海拔來自檔案。TREK 不計算滑雪時間,軌跡和 3D 中的坡度顏色都不代表難度、積雪、雪崩或雪道是否開放。',
'tours.ski.time': '滑雪時間',
'tours.ski.timeUnknown': '未計算',
'tours.ski.classifyTitle': '為滑雪軌跡分類',
'tours.ski.saved': '分類已儲存',
'tours.ski.saveFailed': '無法儲存分類。',
'tours.planner.tourTypeHint.run': '依你自己的配速步行,走適合跑步的道路',
'tours.planner.namePlaceholder.run': '未命名的跑步',
'tours.planner.unreachable.run': '沒有適合跑步的道路能到達第 {point} 個點:路由器所知最近的道路在 {distance} 之外。請把該點移到道路上,或手動繪製到那裡的路段。',
+21 -1
View File
@@ -185,7 +185,7 @@ const tours: TranslationStrings = {
'tourTypes.hike': '徒步',
'tourTypes.bike': '骑行',
'tourTypes.run': '跑步',
'tourTypes.ski': '滑雪穿越',
'tourTypes.ski': '滑雪',
'tourTypes.kayak': '划桨游',
'tourTypes.walk': '城市漫步 / 观光',
'tourTypes.food': '美食 / 葡萄酒之旅',
@@ -368,6 +368,26 @@ const tours: TranslationStrings = {
'tours.winter.activity.winter_hike': '冬季徒步',
'tours.winter.activity.snowshoe': '雪鞋徒步',
'tours.winter.labelOnly': '由旅行者标记。路线和时间按普通徒步规划,未针对冬季核查。',
'tours.planner.tourTypeHint.ski': '来自 GPX 文件,保持记录原样',
'tours.ski.title': '滑雪轨迹',
'tours.ski.discipline': '项目',
'tours.ski.discipline.alpine': '高山滑雪',
'tours.ski.discipline.snowboard': '单板滑雪',
'tours.ski.discipline.cross_country': '越野滑雪',
'tours.ski.discipline.touring': '登山滑雪',
'tours.ski.technique': '技术',
'tours.ski.technique.classic': '传统式',
'tours.ski.technique.skating': '自由式',
'tours.ski.technique.unknown': '未知',
'tours.ski.chooseDiscipline': '请选择项目,以便将轨迹导入为滑雪。',
'tours.ski.chooseTechnique': '请选择技术,或选择未知。',
'tours.ski.infoLabel': 'TREK 如何处理滑雪轨迹',
'tours.ski.info': '保持记录原样,绝不重新规划路线。距离和海拔来自文件。TREK 不计算滑雪用时,轨迹和 3D 中的坡度颜色都不代表难度、积雪、雪崩或雪道是否开放。',
'tours.ski.time': '滑雪用时',
'tours.ski.timeUnknown': '未计算',
'tours.ski.classifyTitle': '为滑雪轨迹分类',
'tours.ski.saved': '分类已保存',
'tours.ski.saveFailed': '无法保存分类。',
'tours.planner.tourTypeHint.run': '按你自己的配速步行,走适合跑步的道路',
'tours.planner.namePlaceholder.run': '未命名的跑步',
'tours.planner.unreachable.run': '没有适合跑步的道路能到达第 {point} 个点:路由器知道的最近道路在 {distance} 之外。请把该点移到道路上,或手动绘制到那里的路段。',
+1
View File
@@ -56,6 +56,7 @@ export * from './roadtrip/roadtrip.schema';
export * from './place/track-colors';
export * from './tours/tours.schema';
export * from './tours/run-pace';
export * from './tours/ski.schema';
export * from './tours/tour-provenance.schema';
export * from './tours/tour-library.schema';
export * from './tours/tour-poi-search.schema';
+114
View File
@@ -0,0 +1,114 @@
import { SKI_DISCIPLINES, SKI_TECHNIQUES, skiIssues, tourSkiClassificationRequestSchema } from './ski.schema';
import { tourGpxImportFieldsSchema } from './tour-gpx-import.schema';
import {
importableTourTypeSchema,
plannableTourTypeSchema,
tourCreateRequestSchema,
tourListItemSchema,
} from './tours.schema';
import { describe, expect, it } from 'vitest';
describe('ski tracks (SKI-02)', () => {
it('SKI-SCHEMA-001: knows four disciplines and three cross-country techniques, unknown among them on purpose', () => {
expect(SKI_DISCIPLINES).toEqual(['alpine', 'snowboard', 'cross_country', 'touring']);
expect(SKI_TECHNIQUES).toEqual(['classic', 'skating', 'unknown']);
});
it('SKI-SCHEMA-002: ski is a type a track can be imported as, never one the planner plans', () => {
expect(importableTourTypeSchema.options).toEqual([...plannableTourTypeSchema.options, 'ski']);
expect(plannableTourTypeSchema.safeParse('ski').success).toBe(false);
const planned = {
name: 'Valley run',
tour_type: 'ski',
route_geometry: [
[47, 11, 1200],
[47.01, 11.01, 1100],
],
waypoints: [
{ lat: 47, lng: 11, role: 'start', sequence: 0 },
{ lat: 47.01, lng: 11.01, role: 'end', sequence: 1 },
],
};
expect(tourCreateRequestSchema.safeParse(planned).success).toBe(false);
});
it('SKI-SCHEMA-003: a ski track names its discipline, a cross-country one its technique, and nothing else may', () => {
expect(skiIssues(true, { ski_discipline: 'alpine' })).toEqual([]);
expect(skiIssues(true, { ski_discipline: 'cross_country', ski_technique: 'unknown' })).toEqual([]);
expect(skiIssues(true, {})).toEqual([{ path: ['ski_discipline'], message: 'a ski track needs a discipline' }]);
expect(skiIssues(true, { ski_discipline: 'cross_country' })[0]?.path).toEqual(['ski_technique']);
expect(skiIssues(true, { ski_discipline: 'touring', ski_technique: 'skating' })[0]?.message).toMatch(
/only a cross-country track/,
);
expect(skiIssues(false, { ski_discipline: 'alpine', ski_technique: 'classic' }).map((i) => i.path)).toEqual([
['ski_discipline'],
['ski_technique'],
]);
expect(skiIssues(false, { ski_discipline: null, ski_technique: null })).toEqual([]);
});
it('SKI-SCHEMA-004: imports a GPX file as ski only with its discipline, and ski fields only for ski', () => {
expect(tourGpxImportFieldsSchema.parse({ tour_type: 'ski', ski_discipline: 'touring' })).toEqual({
tour_type: 'ski',
ski_discipline: 'touring',
});
expect(
tourGpxImportFieldsSchema.parse({ tour_type: 'ski', ski_discipline: 'cross_country', ski_technique: 'skating' }),
).toMatchObject({ ski_technique: 'skating' });
for (const fields of [
{ tour_type: 'ski' },
{ tour_type: 'ski', ski_discipline: 'cross_country' },
{ tour_type: 'ski', ski_discipline: 'alpine', ski_technique: 'classic' },
{ tour_type: 'ski', ski_discipline: 'slalom' },
{ tour_type: 'ski', ski_discipline: 'alpine', run_profile: 'running' },
{ tour_type: 'hike', ski_discipline: 'alpine' },
{ tour_type: 'run', run_profile: 'trail', ski_technique: 'classic' },
]) {
expect(tourGpxImportFieldsSchema.safeParse(fields).success).toBe(false);
}
});
it('SKI-SCHEMA-005: a classification names a discipline, a technique only for cross-country, and an optional revision', () => {
expect(tourSkiClassificationRequestSchema.parse({ ski_discipline: 'snowboard', expected_revision: 3 })).toEqual({
ski_discipline: 'snowboard',
expected_revision: 3,
});
expect(
tourSkiClassificationRequestSchema.parse({ ski_discipline: 'cross_country', ski_technique: 'classic' }),
).toMatchObject({ ski_technique: 'classic' });
for (const body of [
{},
{ ski_discipline: 'cross_country' },
{ ski_discipline: 'cross_country', ski_technique: null },
{ ski_discipline: 'alpine', ski_technique: 'skating' },
{ ski_discipline: 'alpine', expected_revision: 0 },
]) {
expect(tourSkiClassificationRequestSchema.safeParse(body).success).toBe(false);
}
});
it('SKI-SCHEMA-006: lists the discipline and technique, and reads a list cached before they existed', () => {
const listed = {
place_id: 4,
tour_type: 'ski',
distance: 12.4,
elevation_gain: 980,
elevation_loss: 990,
duration: null,
difficulty: null,
wanderer_ref: null,
match_confidence: 1,
max_hiking_difficulty: null,
name: 'Up to the hut',
planned: false,
caution: false,
};
expect(tourListItemSchema.parse({ ...listed, ski_discipline: 'touring', ski_technique: null })).toMatchObject({
ski_discipline: 'touring',
ski_technique: null,
});
expect(tourListItemSchema.parse(listed).ski_discipline).toBeUndefined();
expect(tourListItemSchema.safeParse({ ...listed, ski_discipline: 'freeride' }).success).toBe(false);
});
});
+63
View File
@@ -0,0 +1,63 @@
import { z } from 'zod';
/**
* SKI-02: a ski track comes in as a GPX file and is kept as it was recorded.
* TREK routes no ski track and works out no ski time: its distance and heights
* are the file's, and nothing is said about snow, avalanches or whether a run
* or a trail is open. What the skier names on purpose is the discipline, and
* for cross-country the technique.
*/
export const SKI_DISCIPLINES = ['alpine', 'snowboard', 'cross_country', 'touring'] as const;
export const skiDisciplineSchema = z.enum(SKI_DISCIPLINES);
export type SkiDiscipline = z.infer<typeof skiDisciplineSchema>;
/**
* How a cross-country track is skied. `unknown` is an answer the skier gives
* on purpose (a borrowed track, a mixed day), not a gap TREK fills in; every
* other discipline has no technique at all.
*/
export const SKI_TECHNIQUES = ['classic', 'skating', 'unknown'] as const;
export const skiTechniqueSchema = z.enum(SKI_TECHNIQUES);
export type SkiTechnique = z.infer<typeof skiTechniqueSchema>;
type SkiIssue = { path: string[]; message: string };
/**
* What a ski track names, and what nothing else may: a ski track its
* discipline, a cross-country track its technique as well; a technique on any
* other discipline, and either field on another type, is refused.
*/
export function skiIssues(
isSki: boolean,
value: { ski_discipline?: SkiDiscipline | null; ski_technique?: SkiTechnique | null },
): SkiIssue[] {
if (!isSki) {
return (['ski_discipline', 'ski_technique'] as const)
.filter((field) => value[field] != null)
.map((field) => ({ path: [field], message: `${field} belongs to a ski track` }));
}
if (value.ski_discipline == null) return [{ path: ['ski_discipline'], message: 'a ski track needs a discipline' }];
const crossCountry = value.ski_discipline === 'cross_country';
if (crossCountry && value.ski_technique == null)
return [{ path: ['ski_technique'], message: 'a cross-country track needs a technique' }];
if (!crossCountry && value.ski_technique != null)
return [{ path: ['ski_technique'], message: 'only a cross-country track has a technique' }];
return [];
}
/**
* A ski track classified again after its import: the discipline and the
* technique, nothing of the track. Refused with a 409 once the tour moved on
* past the revision the choice was made at.
*/
export const tourSkiClassificationRequestSchema = z
.object({
ski_discipline: skiDisciplineSchema,
ski_technique: skiTechniqueSchema.nullable().optional(),
/** P2C-03: the revision the classification was made at. */
expected_revision: z.number().int().min(1).nullable().optional(),
})
.superRefine((value, ctx) =>
skiIssues(true, value).forEach((issue) => ctx.addIssue({ code: z.ZodIssueCode.custom, ...issue })),
);
export type TourSkiClassificationRequest = z.infer<typeof tourSkiClassificationRequestSchema>;
@@ -18,7 +18,7 @@ describe('Tours GPX import fields', () => {
it('refuses a paddle import without a boat, and a hike that names one', () => {
expect(() => tourGpxImportFieldsSchema.parse({ tour_type: 'kayak', water_context: 'lake' })).toThrow(/boat_type/);
expect(() => tourGpxImportFieldsSchema.parse({ tour_type: 'hike', boat_type: 'sup' })).toThrow(/paddle import/);
expect(() => tourGpxImportFieldsSchema.parse({ tour_type: 'ski' })).toThrow();
expect(() => tourGpxImportFieldsSchema.parse({ tour_type: 'food' })).toThrow();
});
});
+11 -3
View File
@@ -1,8 +1,9 @@
import { skiDisciplineSchema, skiIssues, skiTechniqueSchema } from './ski.schema';
import {
bikeProfileSchema,
importableTourTypeSchema,
paddleBoatTypeSchema,
paddleWaterContextSchema,
plannableTourTypeSchema,
runProfileSchema,
} from './tours.schema';
@@ -41,17 +42,24 @@ export type TourGpxPreviewResponse = z.infer<typeof tourGpxPreviewResponseSchema
* form sends them. Left out, the import is a hike, as it always was; a paddle
* import names its boat and water, and a bike import and a run import (RUN-01)
* their profile, like a planned tour of that type does. A run import has no
* pace: its time stays unknown until a run is planned from it.
* pace: its time stays unknown until a run is planned from it. A ski import
* (SKI-02) names its discipline, a cross-country one its technique too; the
* track is kept as recorded, with no time worked out for it.
*/
export const tourGpxImportFieldsSchema = z
.object({
tour_type: plannableTourTypeSchema.default('hike'),
tour_type: importableTourTypeSchema.default('hike'),
bike_profile: bikeProfileSchema.optional(),
run_profile: runProfileSchema.optional(),
boat_type: paddleBoatTypeSchema.optional(),
water_context: paddleWaterContextSchema.optional(),
ski_discipline: skiDisciplineSchema.optional(),
ski_technique: skiTechniqueSchema.optional(),
})
.superRefine((value, ctx) => {
skiIssues(value.tour_type === 'ski', value).forEach((issue) =>
ctx.addIssue({ code: z.ZodIssueCode.custom, ...issue }),
);
const owners = { bike_profile: 'bike', run_profile: 'run', boat_type: 'kayak', water_context: 'kayak' } as const;
const names = { bike: 'a bike import', run: 'a run import', kayak: 'a paddle import' } as const;
for (const [field, owner] of Object.entries(owners) as Array<[keyof typeof owners, keyof typeof names]>) {
+13
View File
@@ -6,6 +6,7 @@ import {
trackMarksSchema,
} from './paddle-plan.schema';
import { runPaceSecondsPerKmSchema } from './run-pace';
import { skiDisciplineSchema, skiTechniqueSchema } from './ski.schema';
import { tourPoiCategorySchema } from './tour-poi-category';
import { tourOriginSchema, tourProvenanceSchema } from './tour-provenance.schema';
@@ -37,6 +38,14 @@ export type TourTypeKey = z.infer<typeof tourTypeKeySchema>;
export const plannableTourTypeSchema = z.enum(['hike', 'walk', 'run', 'bike', 'kayak']);
export type PlannableTourType = z.infer<typeof plannableTourTypeSchema>;
/**
* The types a tour can be imported and kept as: every type the planner plans,
* and ski (SKI-02), which TREK does not route. A ski track comes in as it was
* recorded and stays so; the planner never opens it as a draft.
*/
export const importableTourTypeSchema = z.enum(['hike', 'walk', 'run', 'bike', 'kayak', 'ski']);
export type ImportableTourType = z.infer<typeof importableTourTypeSchema>;
/**
* BIK-01 to BIK-04: what a bike tour is ridden on, the one profile it is routed
* for. Touring/city keeps to paved ways, cycle paths and quiet roads; road
@@ -194,6 +203,10 @@ export const tourSchema = z.object({
run_profile: runProfileSchema.nullable().optional(),
/** RUN-01: the pace a run is planned at, seconds per km; null while none is entered and for every other type. */
run_pace_seconds_per_km: z.number().nullable().optional(),
/** SKI-02: a ski track's discipline; null for every other type. Absent in a list cached before it existed. */
ski_discipline: skiDisciplineSchema.nullable().optional(),
/** SKI-02: how a cross-country track is skied; null for every other discipline and type. */
ski_technique: skiTechniqueSchema.nullable().optional(),
/** A paddle tour's boat and water; null for every other type. Absent in a list cached before they existed. */
boat_type: paddleBoatTypeSchema.nullable().optional(),
water_context: paddleWaterContextSchema.nullable().optional(),