fitness: add German translations for all 77 exercises
Add per-exercise de property with translated name and instructions. Add shared term translation map for bodyPart, equipment, target, and muscle names. Add localizeExercise() and translateTerm() helpers. Update all display components to use localized fields (localName, localBodyPart, localEquipment, etc.) and pass lang to search/lookup.
This commit is contained in:
@@ -5,12 +5,13 @@
|
||||
|
||||
let { exerciseId } = $props();
|
||||
|
||||
const exercise = $derived(getExerciseById(exerciseId));
|
||||
const sl = $derived(fitnessSlugs(detectFitnessLang($page.url.pathname)));
|
||||
const lang = $derived(detectFitnessLang($page.url.pathname));
|
||||
const exercise = $derived(getExerciseById(exerciseId, lang));
|
||||
const sl = $derived(fitnessSlugs(lang));
|
||||
</script>
|
||||
|
||||
{#if exercise}
|
||||
<a href="/fitness/{sl.exercises}/{exerciseId}" class="exercise-link">{exercise.name}</a>
|
||||
<a href="/fitness/{sl.exercises}/{exerciseId}" class="exercise-link">{exercise.localName}</a>
|
||||
{:else}
|
||||
<span class="exercise-unknown">Unknown Exercise</span>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { exercises, getFilterOptions, searchExercises } from '$lib/data/exercises';
|
||||
import { getFilterOptions, searchExercises, translateTerm } from '$lib/data/exercises';
|
||||
import { Search, X } from 'lucide-svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { detectFitnessLang, t } from '$lib/js/fitnessI18n';
|
||||
@@ -23,7 +23,8 @@
|
||||
const filtered = $derived(searchExercises({
|
||||
search: query || undefined,
|
||||
bodyPart: bodyPartFilter || undefined,
|
||||
equipment: equipmentFilter || undefined
|
||||
equipment: equipmentFilter || undefined,
|
||||
lang
|
||||
}));
|
||||
|
||||
/** @param {string} id */
|
||||
@@ -58,13 +59,13 @@
|
||||
<select bind:value={bodyPartFilter}>
|
||||
<option value="">{t('all_body_parts', lang)}</option>
|
||||
{#each filterOptions.bodyParts as bp (bp)}
|
||||
<option value={bp}>{bp.charAt(0).toUpperCase() + bp.slice(1)}</option>
|
||||
{@const label = translateTerm(bp, lang)}<option value={bp}>{label.charAt(0).toUpperCase() + label.slice(1)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<select bind:value={equipmentFilter}>
|
||||
<option value="">{t('all_equipment', lang)}</option>
|
||||
{#each filterOptions.equipment as eq (eq)}
|
||||
<option value={eq}>{eq.charAt(0).toUpperCase() + eq.slice(1)}</option>
|
||||
{@const label = translateTerm(eq, lang)}<option value={eq}>{label.charAt(0).toUpperCase() + label.slice(1)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
@@ -73,8 +74,8 @@
|
||||
{#each filtered as exercise (exercise.id)}
|
||||
<li>
|
||||
<button class="exercise-item" onclick={() => select(exercise.id)}>
|
||||
<span class="ex-name">{exercise.name}</span>
|
||||
<span class="ex-meta">{exercise.bodyPart} · {exercise.equipment}</span>
|
||||
<span class="ex-name">{exercise.localName}</span>
|
||||
<span class="ex-meta">{exercise.localBodyPart} · {exercise.localEquipment}</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
import { Clock, Weight, Trophy, Route, Gauge } from 'lucide-svelte';
|
||||
import { detectFitnessLang, fitnessSlugs } from '$lib/js/fitnessI18n';
|
||||
|
||||
const sl = $derived(fitnessSlugs(detectFitnessLang($page.url.pathname)));
|
||||
const lang = $derived(detectFitnessLang($page.url.pathname));
|
||||
const sl = $derived(fitnessSlugs(lang));
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
@@ -59,7 +60,7 @@
|
||||
* @param {string} exerciseId
|
||||
*/
|
||||
function bestSetLabel(sets, exerciseId) {
|
||||
const exercise = getExerciseById(exerciseId);
|
||||
const exercise = getExerciseById(exerciseId, lang);
|
||||
const metrics = getExerciseMetrics(exercise);
|
||||
const isCardio = metrics.includes('distance');
|
||||
|
||||
@@ -116,7 +117,7 @@
|
||||
|
||||
/** Check if this session has any cardio exercise with GPS data */
|
||||
const hasGpsCardio = $derived(session.exercises.some(ex => {
|
||||
const exercise = getExerciseById(ex.exerciseId);
|
||||
const exercise = getExerciseById(ex.exerciseId, lang);
|
||||
return exercise?.bodyPart === 'cardio' && ex.totalDistance;
|
||||
}));
|
||||
|
||||
@@ -125,7 +126,7 @@
|
||||
let dist = 0;
|
||||
let dur = 0;
|
||||
for (const ex of session.exercises) {
|
||||
const exercise = getExerciseById(ex.exerciseId);
|
||||
const exercise = getExerciseById(ex.exerciseId, lang);
|
||||
if (exercise?.bodyPart !== 'cardio') continue;
|
||||
if (ex.totalDistance) {
|
||||
dist += ex.totalDistance;
|
||||
@@ -166,10 +167,10 @@
|
||||
|
||||
<div class="exercise-list">
|
||||
{#each session.exercises as ex (ex.exerciseId)}
|
||||
{@const exercise = getExerciseById(ex.exerciseId)}
|
||||
{@const exercise = getExerciseById(ex.exerciseId, lang)}
|
||||
{@const label = bestSetLabel(ex.sets, ex.exerciseId)}
|
||||
<div class="exercise-row">
|
||||
<span class="ex-sets">{ex.sets.length} × {exercise?.name ?? ex.exerciseId}</span>
|
||||
<span class="ex-sets">{ex.sets.length} × {exercise?.localName ?? ex.exerciseId}</span>
|
||||
{#if label}
|
||||
<span class="ex-best">{label}</span>
|
||||
{/if}
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
</div>
|
||||
<ul class="exercise-preview">
|
||||
{#each template.exercises.slice(0, 4) as ex}
|
||||
{@const exercise = getExerciseById(ex.exerciseId)}
|
||||
<li>{ex.sets.length} × {exercise?.name ?? ex.exerciseId}</li>
|
||||
{@const exercise = getExerciseById(ex.exerciseId, lang)}
|
||||
<li>{ex.sets.length} × {exercise?.localName ?? ex.exerciseId}</li>
|
||||
{/each}
|
||||
{#if template.exercises.length > 4}
|
||||
<li class="more">+{template.exercises.length - 4} {t('more', lang)}</li>
|
||||
|
||||
+775
-83
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user