refactor(i18n): split fitness translations into per-locale files
The fitness UI translation table previously lived as one combined
object in fitnessI18n.ts where every entry held both languages. That
hides drift (an English string can silently disappear without TypeScript
noticing) and makes adding strings a multi-edit dance.
Split into src/lib/i18n/fitness/{de,en}.ts. de.ts is the source of
truth for the key set; en.ts uses `as const satisfies
Record<keyof typeof de, string>` so any missing English translation is
a build-time error. fitnessI18n.ts now re-exports both as a typed
table m and adds FitnessLang/FitnessKey types — the existing
t/fitnessSlugs/fitnessLabels API stays so call sites don't churn.
The strict typing immediately surfaced one real bug: t('initializing_gps')
was being called from the active workout page but the key never existed
in the dictionary, so it had been rendering the literal string
'initializing_gps' through the fallback. Added the missing key in both
locales.
Tightened BodyPartCard.labelKey and the body-parts Step JSDoc to
FitnessKey instead of plain string so card data drift catches drift at
the data site, not the call site. Two dynamic-key sites (partKeyMap
fallbacks for unmapped measurement keys) are cast pragmatically.
The 360-entry split was done by a one-shot extraction script
(scripts/split-fitness-i18n.ts) — kept for re-use against
cospendI18n.ts and calendarI18n.ts in follow-up commits.
This commit is contained in:
@@ -0,0 +1,366 @@
|
||||
/** Generated by scripts/split-fitness-i18n.ts. */
|
||||
/** German fitness UI strings — source of truth for the key set. */
|
||||
|
||||
export const de = {
|
||||
save: "Speichern",
|
||||
saving: "Speichern…",
|
||||
cancel: "ABBRECHEN",
|
||||
delete_: "Löschen",
|
||||
edit: "Bearbeiten",
|
||||
loading: "Laden…",
|
||||
set: "Satz",
|
||||
sets: "Sätze",
|
||||
exercise: "Übung",
|
||||
exercises_word: "Übungen",
|
||||
kg: "kg",
|
||||
km: "km",
|
||||
min: "Min",
|
||||
stats_title: "Statistik",
|
||||
workout_singular: "Training",
|
||||
workouts_plural: "Trainings",
|
||||
lifted: "Gehoben",
|
||||
est_kcal: "Gesch. kcal",
|
||||
burned: "Verbrannt",
|
||||
kcal_set_profile: "Geschlecht & Grösse unter",
|
||||
covered: "Zurückgelegt",
|
||||
workouts_per_week: "Trainings pro Woche",
|
||||
sex: "Geschlecht",
|
||||
male: "Männlich",
|
||||
female: "Weiblich",
|
||||
height: "Grösse (cm)",
|
||||
birth_year: "Geburtsjahr",
|
||||
no_workout_data: "Noch keine Trainingsdaten vorhanden.",
|
||||
weight: "Gewicht",
|
||||
history_title: "Verlauf",
|
||||
no_workouts_yet: "Noch keine Trainings. Starte dein erstes Training!",
|
||||
load_more: "Mehr laden",
|
||||
date: "Datum",
|
||||
time: "Uhrzeit",
|
||||
duration_min: "Dauer (Min)",
|
||||
notes: "Notizen",
|
||||
notes_placeholder: "Trainingsnotizen...",
|
||||
gps_track_stored: "GPS-Track gespeichert",
|
||||
add_set: "+ SATZ HINZUFÜGEN",
|
||||
add_exercise: "+ ÜBUNG HINZUFÜGEN",
|
||||
splits: "Splits",
|
||||
pace: "TEMPO",
|
||||
upload_gpx: "GPX hochladen",
|
||||
uploading: "Hochladen...",
|
||||
download_gpx: "GPX herunterladen",
|
||||
elevation: "Höhenprofil",
|
||||
elevation_unit: "m",
|
||||
elevation_gain: "Anstieg",
|
||||
elevation_loss: "Abstieg",
|
||||
cadence: "Kadenz",
|
||||
cadence_unit: "spm",
|
||||
cadence_permission_missing: "Kadenz deaktiviert — Aktivitätserkennung in den Einstellungen erlauben",
|
||||
personal_records: "Persönliche Rekorde",
|
||||
delete_session_confirm: "Dieses Training löschen?",
|
||||
remove_gps_confirm: "GPS-Track von dieser Übung entfernen?",
|
||||
recalc_title: "Volumen, PRs und GPS-Vorschauen neu berechnen",
|
||||
next_in_schedule: "Nächstes im Plan",
|
||||
start_empty_workout: "leeres Training",
|
||||
templates: "Vorlagen",
|
||||
schedule: "Zeitplan",
|
||||
my_templates: "Meine Vorlagen",
|
||||
no_templates_yet: "Noch keine Vorlagen. Stöbere in der Bibliothek oder erstelle deine eigene.",
|
||||
template_library: "Vorlagen-Bibliothek",
|
||||
browse_library: "Bibliothek durchsuchen",
|
||||
template_added: "Vorlage hinzugefügt",
|
||||
edit_template: "Vorlage bearbeiten",
|
||||
new_template: "Neue Vorlage",
|
||||
template_name_placeholder: "Vorlagenname",
|
||||
add_set_lower: "+ Satz hinzufügen",
|
||||
add_exercise_btn: "Übung hinzufügen",
|
||||
save_template: "Vorlage speichern",
|
||||
workout_schedule: "Trainingsplan",
|
||||
schedule_hint: "Wähle Vorlagen und ordne sie an. Nach Abschluss eines Trainings wird das nächste in der Rotation vorgeschlagen.",
|
||||
available_templates: "Verfügbare Vorlagen",
|
||||
all_templates_scheduled: "Alle Vorlagen sind im Zeitplan",
|
||||
save_schedule: "Zeitplan speichern",
|
||||
start_workout: "Training starten",
|
||||
delete_template: "Löschen",
|
||||
workout_complete: "Training abgeschlossen",
|
||||
workout_saved_offline: "Offline gespeichert — wird bei Verbindung synchronisiert.",
|
||||
duration: "Dauer",
|
||||
tonnage: "Tonnage",
|
||||
distance: "Distanz",
|
||||
exercises_heading: "Übungen",
|
||||
volume: "Volumen",
|
||||
avg: "Ø",
|
||||
update_template: "Vorlage aktualisieren",
|
||||
template_updated: "Vorlage aktualisiert",
|
||||
template_diff_desc: "Gewichte oder Wiederholungen weichen von der Vorlage ab:",
|
||||
updating: "Aktualisieren...",
|
||||
view_workout: "TRAINING ANSEHEN",
|
||||
done: "FERTIG",
|
||||
workout_name_placeholder: "Trainingsname",
|
||||
cancel_workout: "TRAINING ABBRECHEN",
|
||||
finish: "BEENDEN",
|
||||
new_set_added: "neuer Satz",
|
||||
new_sets_added: "neue Sätze",
|
||||
exercises_title: "Übungen",
|
||||
search_exercises: "Übungen suchen…",
|
||||
no_exercises_match: "Keine Übungen gefunden.",
|
||||
type_any: "Alle Arten",
|
||||
type_weights: "Kraft",
|
||||
type_stretches: "Dehnen",
|
||||
stretch_pill: "Dehnung",
|
||||
strength_pill: "Kraft",
|
||||
cardio_pill: "Cardio",
|
||||
plyo_pill: "Plyo",
|
||||
about: "INFO",
|
||||
history_tab: "VERLAUF",
|
||||
charts: "DIAGRAMME",
|
||||
records: "REKORDE",
|
||||
instructions: "Anleitung",
|
||||
no_history_yet: "Noch kein Verlauf für diese Übung.",
|
||||
est_1rm: "GESCH. 1RM",
|
||||
best_set_1rm: "Bester Satz (Gesch. 1RM)",
|
||||
best_set_max: "Bester Satz (Max. Gewicht)",
|
||||
total_volume: "Gesamtvolumen",
|
||||
not_enough_data: "Noch nicht genug Daten für Diagramme.",
|
||||
estimated_1rm: "Geschätztes 1RM",
|
||||
max_volume: "Max. Volumen",
|
||||
max_weight: "Max. Gewicht",
|
||||
rep_records: "Wiederholungsrekorde",
|
||||
reps: "WDH",
|
||||
best_performance: "BESTLEISTUNG",
|
||||
measure_title: "Messen",
|
||||
profile: "Profil",
|
||||
profile_setup_cta: "Größe & Geburtsjahr eintragen, um BMI, TDEE und Kalorienbilanz freizuschalten.",
|
||||
set_up_profile: "Einrichten",
|
||||
edit_profile: "Profil bearbeiten",
|
||||
dismiss: "Verwerfen",
|
||||
new_measurement: "Neue Messung",
|
||||
edit_measurement: "Messung bearbeiten",
|
||||
weight_kg: "Gewicht (kg)",
|
||||
body_fat: "Körperfett %",
|
||||
calories_kcal: "Kalorien (kcal)",
|
||||
body_parts_cm: "Körpermasse (cm)",
|
||||
neck: "Hals",
|
||||
shoulders: "Schultern",
|
||||
chest: "Brust",
|
||||
l_bicep: "L Bizeps",
|
||||
r_bicep: "R Bizeps",
|
||||
l_forearm: "L Unterarm",
|
||||
r_forearm: "R Unterarm",
|
||||
waist: "Taille",
|
||||
hips: "Hüfte",
|
||||
l_thigh: "L Oberschenkel",
|
||||
r_thigh: "R Oberschenkel",
|
||||
l_calf: "L Wade",
|
||||
r_calf: "R Wade",
|
||||
biceps: "Bizeps",
|
||||
forearms: "Unterarme",
|
||||
thighs: "Oberschenkel",
|
||||
calves: "Waden",
|
||||
measure_tip_neck: "Direkt unter dem Adamsapfel, Band parallel zum Boden.",
|
||||
measure_tip_shoulders: "Breiteste Stelle über die Schultern, Arme entspannt hängend.",
|
||||
measure_tip_chest: "In Brustwarzenhöhe nach normalem Ausatmen, Band waagerecht.",
|
||||
measure_tip_biceps: "Arm angespannt im Peak; um die dickste Stelle messen.",
|
||||
measure_tip_forearms: "Breiteste Stelle unterhalb des Ellenbogens, Arm entspannt.",
|
||||
measure_tip_waist: "In Nabelhöhe, locker — nicht einziehen.",
|
||||
measure_tip_hips: "Um die breiteste Stelle des Gesäßes.",
|
||||
measure_tip_thighs: "Mittig zwischen Leistenfalte und Knie.",
|
||||
measure_tip_calves: "Breiteste Stelle, beidseitig belastet stehend.",
|
||||
save_measurement: "Messung speichern",
|
||||
update_measurement: "Messung aktualisieren",
|
||||
measure_body_parts: "Körpermasse erfassen",
|
||||
measure_body_parts_sub: "Geführter Ablauf — ein Körperteil nach dem anderen.",
|
||||
last_measured: "Zuletzt gemessen",
|
||||
no_measurements_yet: "Noch keine Messungen",
|
||||
step_n_of_m: "Schritt {n} von {m}",
|
||||
over_time: "{label} im Verlauf",
|
||||
first_measurement_hint: "Erste Messung — dein Wert erscheint hier.",
|
||||
running_totals: "Laufende Übersicht",
|
||||
review_save: "Prüfen & speichern",
|
||||
ready_to_save: "Bereit zum Speichern",
|
||||
review_numbers: "Prüfe deine Werte unten.",
|
||||
skip: "Auslassen",
|
||||
next: "Weiter",
|
||||
back: "Zurück",
|
||||
review: "Prüfen",
|
||||
edit_again: "Erneut bearbeiten",
|
||||
exit: "Schließen",
|
||||
same_both_sides: "Auf beiden Seiten gleich",
|
||||
copy_l_to_r: "L → R übernehmen",
|
||||
copy_l_to_r_before: "L",
|
||||
copy_l_to_r_after: "R übernehmen",
|
||||
kbd_nav: "Navigation",
|
||||
kbd_next: "weiter",
|
||||
kbd_skip: "auslassen",
|
||||
kbd_wheel: "±0,1",
|
||||
kbd_hint: "? drücken für Tastenkürzel",
|
||||
no_body_parts_selected: "Bitte mindestens einen Wert eingeben.",
|
||||
today_short: "heute",
|
||||
latest: "Aktuell",
|
||||
body_fat_short: "Körperfett",
|
||||
calories: "Kalorien",
|
||||
body_parts: "Körpermasse",
|
||||
body_measurements_only: "Nur Körpermasse",
|
||||
delete_measurement_confirm: "Diese Messung löschen?",
|
||||
general: "Allgemein",
|
||||
body_fat_pct: "Körperfett (%)",
|
||||
history: "Verlauf",
|
||||
past_measurements: "Frühere Messungen",
|
||||
show_more: "Mehr anzeigen",
|
||||
overwrite_title: "Bestehende Werte überschreiben?",
|
||||
overwrite_message: "Für dieses Datum sind bereits Werte erfasst: {fields}. Überschreiben?",
|
||||
overwrite_confirm: "Überschreiben",
|
||||
same_as_last: "Wie zuletzt",
|
||||
set_header: "SATZ",
|
||||
prev_header: "VORH",
|
||||
rpe: "RPE",
|
||||
picker_title: "Übung hinzufügen",
|
||||
no_exercises_found: "Keine Übungen gefunden",
|
||||
last_performed: "Zuletzt durchgeführt:",
|
||||
today: "Heute",
|
||||
yesterday: "Gestern",
|
||||
days_ago: "Tagen",
|
||||
more: "weitere",
|
||||
active_workout: "Aktives Training",
|
||||
streak: "Serie",
|
||||
streak_weeks: "Wochen",
|
||||
streak_week: "Woche",
|
||||
weekly_goal: "Wochenziel",
|
||||
workouts_per_week_goal: "Trainings / Woche",
|
||||
set_goal: "Ziel setzen",
|
||||
goal_set: "Ziel gesetzt",
|
||||
intervals: "Intervalle",
|
||||
no_intervals: "Keine",
|
||||
new_interval: "Neues Intervall",
|
||||
edit_interval: "Intervall bearbeiten",
|
||||
delete_interval: "Löschen",
|
||||
delete_interval_confirm: "Diese Intervallvorlage löschen?",
|
||||
add_step: "+ Schritt hinzufügen",
|
||||
add_group: "+ Wiederholungsgruppe",
|
||||
repeat_times: "mal",
|
||||
ungroup: "Auflösen",
|
||||
group_label: "Wiederholen",
|
||||
step_label: "Bezeichnung",
|
||||
meters: "Meter",
|
||||
seconds: "Sekunden",
|
||||
intervals_complete: "Intervalle abgeschlossen",
|
||||
select_interval: "Intervall wählen",
|
||||
custom: "Eigene",
|
||||
steps_count: "Schritte",
|
||||
save_interval: "Intervall speichern",
|
||||
interval_name_placeholder: "Intervallname",
|
||||
label_easy: "Leicht",
|
||||
label_moderate: "Moderat",
|
||||
label_hard: "Hart",
|
||||
label_sprint: "Sprint",
|
||||
label_recovery: "Erholung",
|
||||
label_hill_sprints: "Bergsprints",
|
||||
label_tempo: "Tempo",
|
||||
label_warm_up: "Aufwärmen",
|
||||
label_cool_down: "Abkühlen",
|
||||
nutrition_title: "Ernährung",
|
||||
breakfast: "Frühstück",
|
||||
lunch: "Mittagessen",
|
||||
dinner: "Abendessen",
|
||||
snack: "Snack",
|
||||
add_food: "Essen hinzufügen",
|
||||
search_food: "Essen suchen…",
|
||||
amount_grams: "Menge (g)",
|
||||
meal_type: "Mahlzeit",
|
||||
daily_goal: "Tagesziel",
|
||||
calorie_target: "Kalorienziel (kcal)",
|
||||
protein_goal: "Proteinziel",
|
||||
protein_fixed: "Fest (g/Tag)",
|
||||
protein_per_kg: "Pro kg Körpergewicht",
|
||||
fat_percent: "Fett-Anteil",
|
||||
carb_percent: "KH-Anteil",
|
||||
kcal: "kcal",
|
||||
protein: "Protein",
|
||||
fat: "Fett",
|
||||
carbs: "Kohlenhydrate",
|
||||
remaining: "übrig",
|
||||
over: "über",
|
||||
no_entries_yet: "Noch keine Einträge. Füge Essen hinzu, um zu tracken.",
|
||||
set_goal_prompt: "Setze ein Kalorienziel, um mit dem Tracking zu beginnen.",
|
||||
micro_details: "Mikronährstoffe",
|
||||
of_daily: "vom Tagesziel",
|
||||
per_serving: "pro Portion",
|
||||
log_food: "Eintragen",
|
||||
delete_entry_confirm: "Diesen Eintrag löschen?",
|
||||
period_tracker: "Periodentracker",
|
||||
current_period: "Aktuelle Periode",
|
||||
no_period_data: "Noch keine Periodendaten. Erfasse deine erste Periode.",
|
||||
no_active_period: "Keine aktive Periode.",
|
||||
start_period: "Periode starten",
|
||||
end_period: "Periode vorbei",
|
||||
period_day: "Tag",
|
||||
predicted_end: "Voraussichtliches Ende",
|
||||
next_period: "Nächste Periode",
|
||||
cycle_length: "Zykluslänge",
|
||||
period_length: "Periodenlänge",
|
||||
avg_cycle: "Ø Zyklus",
|
||||
avg_period: "Ø Periode",
|
||||
days: "Tage",
|
||||
delete_period_confirm: "Diesen Periodeneintrag löschen?",
|
||||
add_past_period: "Vergangene Periode hinzufügen",
|
||||
period_start: "Beginn",
|
||||
period_end: "Ende",
|
||||
ongoing: "laufend",
|
||||
share: "Teilen",
|
||||
shared_with: "Geteilt mit",
|
||||
add_user: "Nutzer hinzufügen…",
|
||||
no_shared: "Mit niemandem geteilt.",
|
||||
shared_by: "Geteilt von",
|
||||
fertile_window: "Fruchtbares Fenster",
|
||||
peak_fertility: "Höchste Fruchtbarkeit",
|
||||
ovulation: "Eisprung",
|
||||
fertile: "Fruchtbar",
|
||||
luteal_phase: "Luteal",
|
||||
predicted_ovulation: "Voraussichtlicher Eisprung",
|
||||
to: "bis",
|
||||
overview: "Überblick",
|
||||
tips: "Tipps",
|
||||
similar_exercises: "Ähnliche Übungen",
|
||||
primary_muscles: "Primär",
|
||||
secondary_muscles: "Sekundär",
|
||||
play_video: "Video abspielen",
|
||||
nutrition_stats: "Ernährung",
|
||||
protein_per_kg_unit: "g/kg",
|
||||
calorie_balance: "Kalorienbilanz",
|
||||
calorie_balance_unit: "kcal/Tag",
|
||||
diet_adherence: "Einhaltung",
|
||||
seven_day_avg: "7-Tage-Ø",
|
||||
thirty_day: "30 Tage",
|
||||
macro_split: "Makroverteilung",
|
||||
no_nutrition_data: "Noch keine Ernährungsdaten. Beginne mit dem Tracking.",
|
||||
target: "Ziel",
|
||||
days_tracked: "Tage erfasst",
|
||||
since_start: "Seit Beginn",
|
||||
no_weight_data: "Gewicht eintragen",
|
||||
no_calorie_goal: "Kalorienziel setzen",
|
||||
muscle_balance: "Muskelbalance",
|
||||
weekly_sets: "Sätze pro Woche",
|
||||
custom_meals: "Eigene Mahlzeiten",
|
||||
custom_meal: "Eigene Mahlzeit",
|
||||
new_meal: "Neue Mahlzeit",
|
||||
meal_name: "Name der Mahlzeit",
|
||||
add_ingredient: "Zutat hinzufügen",
|
||||
no_custom_meals: "Noch keine eigenen Mahlzeiten.",
|
||||
create_meal_hint: "Erstelle wiederverwendbare Mahlzeiten zum schnellen Eintragen.",
|
||||
ingredients: "Zutaten",
|
||||
total: "Gesamt",
|
||||
log_meal: "Mahlzeit eintragen",
|
||||
delete_meal_confirm: "Diese Mahlzeit löschen?",
|
||||
save_meal: "Mahlzeit speichern",
|
||||
favorites: "Favoriten",
|
||||
per_100g: "pro 100 g",
|
||||
macros: "Makronährstoffe",
|
||||
minerals: "Mineralstoffe",
|
||||
vitamins: "Vitamine",
|
||||
amino_acids: "Aminosäuren",
|
||||
essential: "Essenziell",
|
||||
non_essential: "Nicht-essenziell",
|
||||
saturated_fat: "Gesättigte Fettsäuren",
|
||||
fiber: "Ballaststoffe",
|
||||
sugars: "Zucker",
|
||||
source_db: "Quelle",
|
||||
initializing_gps: "GPS wird initialisiert…",
|
||||
} as const;
|
||||
Reference in New Issue
Block a user