diff --git a/.gitattributes b/.gitattributes
index 6a2348052..98163b558 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9,3 +9,6 @@
# Dart file in the repo, which is a working-tree-wide change with nothing to do
# with reviewable diffs.
*.dart diff
+
+# generated by tool/update_wger_exercises.dart
+lib/ui2/activity/wger_exercises.g.dart linguist-generated=true
diff --git a/NOTICE.md b/NOTICE.md
index 97c3ad5d9..cd2880c66 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -9,3 +9,14 @@ in this repository. The Bluetooth protocol support in this project was
independently developed by observing the band's own Bluetooth communications;
see [the protocol repo's README](https://github.com/OpenStrap/protocol) for
methodology notes.
+
+## wger exercise data
+
+`lib/ui2/activity/wger_exercises.g.dart` contains selected fields (names,
+aliases, muscles, equipment, categories) from the [wger](https://wger.de)
+exercise database, limited to the movements listed in
+`tool/wger_weightlifting_selection.dart`. Each entry is available under
+CC BY-SA 3.0, CC BY-SA 4.0 or CC0, and each row keeps its source UUID and the
+author/license credits of its base data and translations. This data stays under
+those licenses, separate from the MIT-licensed code. Refresh with
+`dart run tool/update_wger_exercises.dart`.
diff --git a/docs/notice.html b/docs/notice.html
index 3c953676f..2d71ae457 100644
--- a/docs/notice.html
+++ b/docs/notice.html
@@ -55,6 +55,18 @@
Open Food Facts
purposes. Edge treats a scanned figure as something you entered, never as
something it measured, and drops any value that fails a basic
plausibility check rather than showing it to you.
+
+ wger exercise data
+
+ The exercise picker includes selected fields (names, aliases, muscles,
+ equipment, categories) from the wger
+ exercise database. Each entry is available under
+ CC BY-SA 3.0 ,
+ CC BY-SA 4.0
+ or CC0 ,
+ and stays under that license, separate from Edge's MIT-licensed code. The
+ info button next to each exercise opens its wger record with authors and
+ license.
diff --git a/lib/data/csv_export.dart b/lib/data/csv_export.dart
index e061cafe6..fa51ebd42 100644
--- a/lib/data/csv_export.dart
+++ b/lib/data/csv_export.dart
@@ -20,6 +20,7 @@ import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
+import '../ui2/activity/catalogue.dart' show exerciseByKey;
import 'db.dart';
/// One exportable table: a filename stem, a header, and the query behind it.
@@ -29,6 +30,7 @@ class CsvExportSet {
required this.title,
required this.columns,
required this.sql,
+ this.addColumns,
});
/// Filename stem, e.g. `daily` → `openstrap_daily_.csv`.
@@ -36,6 +38,9 @@ class CsvExportSet {
final String title;
final List columns;
final String sql;
+
+ /// Values for columns the SQL can't produce, e.g. a label held in Dart.
+ final Map Function(Map row)? addColumns;
}
/// What can be exported. Ordered as the picker shows them.
@@ -228,6 +233,7 @@ const kCsvExportSets = [
'at_ts',
'session_id',
'exercise',
+ 'exercise_name',
'set_index',
'reps',
'load_kg',
@@ -238,8 +244,9 @@ const kCsvExportSets = [
],
// Exercises live as a Dart constant (lib/ui2/activity/catalogue.dart), not
// in `exercise_def` — nothing inserts into that table — so `exercise` is
- // the storage key, the same deliberate fallback the habits set documents
- // above. The LEFT JOIN that used to be here could only ever miss.
+ // the storage key and the name is filled in from the catalogue. wger lifts
+ // are keyed `wger:`, which nobody can read back without it.
+ addColumns: _exerciseName,
sql: '''
SELECT s.at_ts, s.session_id, s.exercise_key AS exercise,
s.set_index, s.reps, s.load_kg, s.rpe, s.hold_sec, s.rest_sec, s.note
@@ -285,6 +292,10 @@ const kCsvExportSets = [
),
];
+Map _exerciseName(Map row) => {
+ 'exercise_name': exerciseByKey(row['exercise'] as String)?.label,
+};
+
/// What CSV deliberately does NOT carry, and why. Shown to the user on the
/// export screen, because "your data" with a silent gap in it is the same
/// dishonesty as a fabricated number.
@@ -405,7 +416,11 @@ Future exportCsvFiles(
for (final set in sets) {
try {
- final rows = await db.rawQuery(set.sql);
+ final add = set.addColumns;
+ final rows = [
+ for (final r in await db.rawQuery(set.sql))
+ add == null ? r : {...r, ...add(r)},
+ ];
// No rows means no file. A header-only CSV is not "your data", and
// emitting one made an empty database indistinguishable from a working
// export to the caller.
diff --git a/lib/data/db.dart b/lib/data/db.dart
index f4ee1ecfe..c9235a7ad 100644
--- a/lib/data/db.dart
+++ b/lib/data/db.dart
@@ -4071,7 +4071,7 @@ class LocalDb {
/// which the UI states in as many words.
///
/// Derived on read, never stored: total volume, set/rep counts, 1RM
- /// estimates, per-muscle volume, "vs last session", PR detection.
+ /// estimates, "vs last session", PR detection.
static Future _createStrengthTables(Database db) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS strength_set (
@@ -4154,6 +4154,44 @@ class LocalDb {
);
}
+ /// Newest-set and heaviest-set candidates per logged exercise, in two
+ /// queries. The caller keeps the first row per key (most reps wins a tie).
+ /// No window functions: minSdk 26 can ship SQLite older than 3.25.
+ static Future<({
+ List> previous,
+ List> best,
+ })> strengthHistoryCandidates() async {
+ final db = await instance;
+ final previous = await db.rawQuery('''
+ SELECT s.*
+ FROM strength_set s
+ JOIN (
+ SELECT exercise_key, MAX(COALESCE(at_ts, 0)) AS newest_at
+ FROM strength_set
+ GROUP BY exercise_key
+ ) newest
+ ON newest.exercise_key = s.exercise_key
+ AND newest.newest_at = COALESCE(s.at_ts, 0)
+ ORDER BY s.exercise_key ASC, COALESCE(s.at_ts, 0) DESC,
+ s.session_id DESC, s.seq DESC
+ ''');
+ final best = await db.rawQuery('''
+ SELECT s.*
+ FROM strength_set s
+ JOIN (
+ SELECT exercise_key, MAX(load_kg) AS best_load
+ FROM strength_set
+ WHERE load_kg IS NOT NULL
+ GROUP BY exercise_key
+ ) heaviest
+ ON heaviest.exercise_key = s.exercise_key
+ AND heaviest.best_load = s.load_kg
+ ORDER BY s.exercise_key ASC, COALESCE(s.reps, 0) DESC,
+ COALESCE(s.at_ts, 0) DESC, s.session_id DESC, s.seq DESC
+ ''');
+ return (previous: previous, best: best);
+ }
+
// ── USER-DATA STORE (journal / cycle / workouts / notifications) ────────────
// On-device user-entered + locally-generated data. All keyed for idempotent
// upserts; none of it round-trips to a server (cloud excised).
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index e1c2e6125..a8bf056a1 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -2000,8 +2000,21 @@
"activitySummaryTabOverview": "Übersicht",
"activitySummaryTabSplits": "Zwischenzeiten",
"activitySummaryTabGraphs": "Grafiken",
+ "activityExercisePickerChooseTitle": "Übung auswählen",
+ "activityExercisePickerChangeTitle": "Übung wechseln",
+ "activityExercisePickerSearchLabel": "Übungen suchen",
+ "activityExercisePickerSearchHint": "{count} Übungen durchsuchen",
+ "activityExercisePickerCommonFilter": "Häufig",
+ "activityExercisePickerAllFilter": "Alle",
+ "activityExercisePickerNoMatch": "Keine passenden Übungen",
+ "activityExercisePickerWgerCredit": "Übungskatalog von wger",
+ "activityExercisePickerSourceLabel": "Quelle und Lizenzen für {exercise} ansehen",
"activityLiveAddALap": "Bahn hinzufügen",
"activityLiveAddExerciseTitle": "Übung hinzufügen",
+ "activityLiveChooseExerciseBody": "Wähle die erste Übung aus, bevor du einen Satz erfasst.",
+ "activityLiveChangeExercise": "Übung wechseln",
+ "activityExerciseCategory": "{category, select, Abs{Bauch} Arms{Arme} Back{Rücken} Calves{Waden} Chest{Brust} Legs{Beine} Shoulders{Schultern} other{{category}}}",
+ "activityExerciseEquipment": "{key, select, Barbell{Langhantel} Bench{Bank} Cablemachine{Kabelzug} Dumbbell{Kurzhantel} Gymmat{Matte} Inclinebench{Schrägbank} Kettlebell{Kettlebell} nonebodyweightexercise{Körpergewicht} Pullupbar{Klimmzugstange} SZBar{SZ-Stange} other{{name}}}",
"activityLiveAllowLocation": "Standort erlauben",
"activityLiveBestLabel": "Bestwert",
"activityLiveBodyweightExcludedNote": "Körpergewicht — nicht im Volumen enthalten",
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index ae97e419e..e2fc6c05a 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -10463,6 +10463,52 @@
"@activitySummaryTabGraphs": {
"description": "Sub-tab label for the Graphs tab on the activity summary screen"
},
+ "activityExercisePickerChooseTitle": "Choose exercise",
+ "@activityExercisePickerChooseTitle": {
+ "description": "Title of the strength exercise picker when selecting a new exercise."
+ },
+ "activityExercisePickerChangeTitle": "Change exercise",
+ "@activityExercisePickerChangeTitle": {
+ "description": "Title of the strength exercise picker when replacing or switching from the current exercise."
+ },
+ "activityExercisePickerSearchLabel": "Search exercises",
+ "@activityExercisePickerSearchLabel": {
+ "description": "Accessibility label for the strength exercise search field."
+ },
+ "activityExercisePickerSearchHint": "Search {count} exercises",
+ "@activityExercisePickerSearchHint": {
+ "description": "Placeholder in the strength exercise search field.",
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "activityExercisePickerCommonFilter": "Common",
+ "@activityExercisePickerCommonFilter": {
+ "description": "Filter showing Edge's short common-exercise list."
+ },
+ "activityExercisePickerAllFilter": "All",
+ "@activityExercisePickerAllFilter": {
+ "description": "Filter showing the whole strength exercise catalogue."
+ },
+ "activityExercisePickerNoMatch": "No exercises match that",
+ "@activityExercisePickerNoMatch": {
+ "description": "Empty result shown for an exercise search or category filter."
+ },
+ "activityExercisePickerWgerCredit": "Exercise catalogue from wger",
+ "@activityExercisePickerWgerCredit": {
+ "description": "Attribution under the strength exercise picker."
+ },
+ "activityExercisePickerSourceLabel": "View source and credits for {exercise}",
+ "@activityExercisePickerSourceLabel": {
+ "description": "Accessibility label and tooltip for a wger exercise's exact source record.",
+ "placeholders": {
+ "exercise": {
+ "type": "String"
+ }
+ }
+ },
"activityLiveAddALap": "Add a lap",
"@activityLiveAddALap": {
"description": "Live swim screen: accessibility label for the tap-a-lap button."
@@ -10471,6 +10517,35 @@
"@activityLiveAddExerciseTitle": {
"description": "Live strength screen: title of the sheet for adding another exercise."
},
+ "activityLiveChooseExerciseBody": "Pick the first exercise before logging a set.",
+ "@activityLiveChooseExerciseBody": {
+ "description": "Empty live-strength state before an exercise has been selected."
+ },
+ "activityLiveChangeExercise": "Change exercise",
+ "@activityLiveChangeExercise": {
+ "description": "Accessibility label for changing the current strength exercise."
+ },
+ "activityExerciseCategory": "{category, select, Abs{Abs} Arms{Arms} Back{Back} Calves{Calves} Chest{Chest} Legs{Legs} Shoulders{Shoulders} other{{category}}}",
+ "@activityExerciseCategory": {
+ "description": "Strength exercise category name, used as a picker filter and row detail.",
+ "placeholders": {
+ "category": {
+ "type": "String"
+ }
+ }
+ },
+ "activityExerciseEquipment": "{key, select, Barbell{Barbell} Bench{Bench} Cablemachine{Cable machine} Dumbbell{Dumbbell} Gymmat{Gym mat} Inclinebench{Incline bench} Kettlebell{Kettlebell} nonebodyweightexercise{Bodyweight} Pullupbar{Pull-up bar} SZBar{SZ-Bar} other{{name}}}",
+ "@activityExerciseEquipment": {
+ "description": "Strength exercise equipment name in the picker row detail. key is the English name with non-letters removed; name is the English name, shown when there is no translation.",
+ "placeholders": {
+ "key": {
+ "type": "String"
+ },
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"activityLiveAllowLocation": "Allow location",
"@activityLiveAllowLocation": {
"description": "Live measured session: fix action when location permission was denied."
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 8612c4529..55980bbf7 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -2012,8 +2012,21 @@
"activitySummaryTabOverview": "Resumen",
"activitySummaryTabSplits": "Parciales",
"activitySummaryTabGraphs": "Gráficos",
+ "activityExercisePickerChooseTitle": "Elegir ejercicio",
+ "activityExercisePickerChangeTitle": "Cambiar ejercicio",
+ "activityExercisePickerSearchLabel": "Buscar ejercicios",
+ "activityExercisePickerSearchHint": "Buscar entre {count} ejercicios",
+ "activityExercisePickerCommonFilter": "Comunes",
+ "activityExercisePickerAllFilter": "Todos",
+ "activityExercisePickerNoMatch": "No hay ejercicios coincidentes",
+ "activityExercisePickerWgerCredit": "Catálogo de ejercicios de wger",
+ "activityExercisePickerSourceLabel": "Ver la fuente y las licencias de {exercise}",
"activityLiveAddALap": "Añadir un largo",
"activityLiveAddExerciseTitle": "Añadir ejercicio",
+ "activityLiveChooseExerciseBody": "Elige el primer ejercicio antes de registrar una serie.",
+ "activityLiveChangeExercise": "Cambiar ejercicio",
+ "activityExerciseCategory": "{category, select, Abs{Abdominales} Arms{Brazos} Back{Espalda} Calves{Pantorrillas} Chest{Pecho} Legs{Piernas} Shoulders{Hombros} other{{category}}}",
+ "activityExerciseEquipment": "{key, select, Barbell{Barra} Bench{Banco} Cablemachine{Polea} Dumbbell{Mancuerna} Gymmat{Esterilla} Inclinebench{Banco inclinado} Kettlebell{Pesa rusa} nonebodyweightexercise{Peso corporal} Pullupbar{Barra de dominadas} SZBar{Barra Z} other{{name}}}",
"activityLiveAllowLocation": "Permitir ubicación",
"activityLiveBestLabel": "Mejor",
"activityLiveBodyweightExcludedNote": "Peso corporal — excluido del volumen",
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index c760566d0..de17a39cb 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -2000,8 +2000,21 @@
"activitySummaryTabOverview": "Aperçu",
"activitySummaryTabSplits": "Temps intermédiaires",
"activitySummaryTabGraphs": "Graphiques",
+ "activityExercisePickerChooseTitle": "Choisir un exercice",
+ "activityExercisePickerChangeTitle": "Changer d’exercice",
+ "activityExercisePickerSearchLabel": "Rechercher des exercices",
+ "activityExercisePickerSearchHint": "Rechercher parmi {count} exercices",
+ "activityExercisePickerCommonFilter": "Courants",
+ "activityExercisePickerAllFilter": "Tous",
+ "activityExercisePickerNoMatch": "Aucun exercice correspondant",
+ "activityExercisePickerWgerCredit": "Catalogue d’exercices de wger",
+ "activityExercisePickerSourceLabel": "Voir la source et les licences de {exercise}",
"activityLiveAddALap": "Ajouter une longueur",
"activityLiveAddExerciseTitle": "Ajouter un exercice",
+ "activityLiveChooseExerciseBody": "Choisissez le premier exercice avant d’enregistrer une série.",
+ "activityLiveChangeExercise": "Changer d’exercice",
+ "activityExerciseCategory": "{category, select, Abs{Abdos} Arms{Bras} Back{Dos} Calves{Mollets} Chest{Pectoraux} Legs{Jambes} Shoulders{Épaules} other{{category}}}",
+ "activityExerciseEquipment": "{key, select, Barbell{Barre} Bench{Banc} Cablemachine{Poulie} Dumbbell{Haltère} Gymmat{Tapis} Inclinebench{Banc incliné} Kettlebell{Kettlebell} nonebodyweightexercise{Poids du corps} Pullupbar{Barre de traction} SZBar{Barre EZ} other{{name}}}",
"activityLiveAllowLocation": "Autoriser la localisation",
"activityLiveBestLabel": "Meilleur",
"activityLiveBodyweightExcludedNote": "Poids du corps — exclu du volume",
diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb
index a41b9a60b..919812883 100644
--- a/lib/l10n/app_hi.arb
+++ b/lib/l10n/app_hi.arb
@@ -2000,8 +2000,21 @@
"activitySummaryTabOverview": "अवलोकन",
"activitySummaryTabSplits": "स्प्लिट",
"activitySummaryTabGraphs": "ग्राफ़",
+ "activityExercisePickerChooseTitle": "व्यायाम चुनें",
+ "activityExercisePickerChangeTitle": "व्यायाम बदलें",
+ "activityExercisePickerSearchLabel": "व्यायाम खोजें",
+ "activityExercisePickerSearchHint": "{count} व्यायाम खोजें",
+ "activityExercisePickerCommonFilter": "सामान्य",
+ "activityExercisePickerAllFilter": "सभी",
+ "activityExercisePickerNoMatch": "कोई मेल खाता व्यायाम नहीं",
+ "activityExercisePickerWgerCredit": "wger का व्यायाम कैटलॉग",
+ "activityExercisePickerSourceLabel": "{exercise} का स्रोत और लाइसेंस देखें",
"activityLiveAddALap": "एक चक्कर जोड़ें",
"activityLiveAddExerciseTitle": "व्यायाम जोड़ें",
+ "activityLiveChooseExerciseBody": "सेट दर्ज करने से पहले पहला व्यायाम चुनें।",
+ "activityLiveChangeExercise": "व्यायाम बदलें",
+ "activityExerciseCategory": "{category, select, Abs{पेट} Arms{बाहें} Back{पीठ} Calves{पिंडलियाँ} Chest{छाती} Legs{पैर} Shoulders{कंधे} other{{category}}}",
+ "activityExerciseEquipment": "{key, select, Barbell{बारबेल} Bench{बेंच} Cablemachine{केबल मशीन} Dumbbell{डम्बल} Gymmat{मैट} Inclinebench{इनक्लाइन बेंच} Kettlebell{केटलबेल} nonebodyweightexercise{बॉडीवेट} Pullupbar{पुल-अप बार} SZBar{EZ बार} other{{name}}}",
"activityLiveAllowLocation": "लोकेशन की अनुमति दें",
"activityLiveBestLabel": "सर्वश्रेष्ठ",
"activityLiveBodyweightExcludedNote": "बॉडीवेट — वॉल्यूम में शामिल नहीं",
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index f31d7ab86..530963d8b 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -2000,8 +2000,21 @@
"activitySummaryTabOverview": "概览",
"activitySummaryTabSplits": "分段",
"activitySummaryTabGraphs": "图表",
+ "activityExercisePickerChooseTitle": "选择动作",
+ "activityExercisePickerChangeTitle": "更换动作",
+ "activityExercisePickerSearchLabel": "搜索动作",
+ "activityExercisePickerSearchHint": "搜索 {count} 个动作",
+ "activityExercisePickerCommonFilter": "常用",
+ "activityExercisePickerAllFilter": "全部",
+ "activityExercisePickerNoMatch": "没有匹配的动作",
+ "activityExercisePickerWgerCredit": "动作目录来自 wger",
+ "activityExercisePickerSourceLabel": "查看 {exercise} 的来源和许可",
"activityLiveAddALap": "增加一趟",
"activityLiveAddExerciseTitle": "添加动作",
+ "activityLiveChooseExerciseBody": "记录一组前,请先选择第一个动作。",
+ "activityLiveChangeExercise": "更换动作",
+ "activityExerciseCategory": "{category, select, Abs{腹部} Arms{手臂} Back{背部} Calves{小腿} Chest{胸部} Legs{腿部} Shoulders{肩部} other{{category}}}",
+ "activityExerciseEquipment": "{key, select, Barbell{杠铃} Bench{训练凳} Cablemachine{绳索器械} Dumbbell{哑铃} Gymmat{训练垫} Inclinebench{上斜凳} Kettlebell{壶铃} nonebodyweightexercise{自重} Pullupbar{单杠} SZBar{曲杠} other{{name}}}",
"activityLiveAllowLocation": "允许定位",
"activityLiveBestLabel": "最佳",
"activityLiveBodyweightExcludedNote": "自重 — 不计入训练量",
diff --git a/lib/ui2/activity/catalogue.dart b/lib/ui2/activity/catalogue.dart
index 59743b9df..508a8d12c 100644
--- a/lib/ui2/activity/catalogue.dart
+++ b/lib/ui2/activity/catalogue.dart
@@ -20,6 +20,8 @@ import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../l10n/app_localizations.dart';
import '../theme.dart';
+part 'wger_exercises.g.dart';
+
/// How a session is tracked — which decides which live screen it opens, and
/// therefore which parameters the user is asked for.
enum Track {
@@ -332,60 +334,308 @@ Activity? activityByName(String? type) =>
type == null ? null : _byKey[type.toLowerCase().replaceAll(' ', '_')];
// ── EXERCISES ──────────────────────────────────────────────────────────────
-// The strength catalogue. `muscles` is the fraction of the set's work each
-// group takes, and it is what the muscle map is drawn from — so it is a claim
-// about anatomy, not decoration. Values are the conventional prime-mover /
-// synergist split; they are approximate and the map is labelled relative.
+// The strength catalogue. The hand-written list keeps the strength_set keys
+// already in use; the generated tail is a wger snapshot (see
+// tool/update_wger_exercises.dart).
+
+class ExerciseCredit {
+ final String licenseName;
+ final String licenseUrl;
+ final String author;
+
+ const ExerciseCredit(this.licenseName, this.licenseUrl, this.author);
+}
class ExerciseDef {
final String key;
final String label;
+ final List primaryMuscles;
+ final List secondaryMuscles;
+ final String category;
+ final List equipment;
+ final List aliases;
- /// group → 0…1 share of the work. Only the primary mover is shown, as the
- /// category label in the exercise picker — nothing paints these any more.
- final Map muscles;
+ /// Upstream translations for wger rows, hand-written for the core lifts;
+ /// missing falls back to [label].
+ final Map localizedLabels;
/// The plate/dumbbell increment this lift is normally loaded in, kg. A
/// barbell moves in 2.5, a dumbbell in 2 — a single global step is what
/// makes every strength app feel like a spreadsheet.
final double step;
- const ExerciseDef(this.key, this.label, this.muscles, {this.step = 2.5});
+ /// wger rows only. Base data and each translation carry their own credit.
+ final String? sourceId;
+ final String? sourceUpdatedAt;
+ final List sourceCredits;
+
+ const ExerciseDef(
+ this.key,
+ this.label,
+ this.primaryMuscles, {
+ this.secondaryMuscles = const [],
+ this.category = '',
+ this.equipment = const [],
+ this.aliases = const [],
+ this.localizedLabels = const {},
+ this.step = 2.5,
+ this.sourceId,
+ this.sourceUpdatedAt,
+ this.sourceCredits = const [],
+ });
+
+ bool get fromWger => sourceId != null;
+
+ /// The upstream record with its authors and licenses.
+ String? get sourceUrl => sourceId == null
+ ? null
+ : 'https://wger.de/api/v2/exerciseinfo/?uuid=$sourceId';
+
+ String labelFor(String languageCode) =>
+ localizedLabels[languageCode] ?? label;
+
+ bool matches(String query, String languageCode) {
+ final q = query.trim().toLowerCase();
+ if (q.isEmpty) return true;
+ return {
+ labelFor(languageCode),
+ label,
+ ...localizedLabels.values,
+ ...aliases,
+ ...primaryMuscles,
+ ...secondaryMuscles,
+ ...equipment,
+ category,
+ }.any((v) => v.toLowerCase().contains(q));
+ }
}
+const _edgeExerciseLibrary = [
+ ExerciseDef(
+ 'bench_press',
+ 'Bench press',
+ ['chest', 'triceps', 'shoulders'],
+ category: 'Chest',
+ equipment: ['Barbell', 'Bench'],
+ localizedLabels: {
+ 'de': 'Bankdrücken',
+ 'es': 'Press de banca',
+ 'fr': 'Développé couché',
+ },
+ ),
+ ExerciseDef(
+ 'incline_db_press',
+ 'Incline DB press',
+ ['chest', 'shoulders', 'triceps'],
+ category: 'Chest',
+ equipment: ['Dumbbell', 'Incline bench'],
+ aliases: ['incline dumbbell press'],
+ step: 2,
+ localizedLabels: {
+ 'de': 'Schrägbankdrücken mit Kurzhanteln',
+ 'es': 'Press inclinado con mancuernas',
+ 'fr': 'Développé incliné haltères',
+ },
+ ),
+ ExerciseDef(
+ 'cable_fly',
+ 'Cable fly',
+ ['chest', 'shoulders'],
+ category: 'Chest',
+ equipment: ['Cable machine'],
+ localizedLabels: {
+ 'de': 'Fliegende am Kabelzug',
+ 'es': 'Aperturas en polea',
+ 'fr': 'Écarté à la poulie',
+ },
+ ),
+ ExerciseDef(
+ 'overhead_press',
+ 'Overhead press',
+ ['shoulders', 'triceps', 'core'],
+ category: 'Shoulders',
+ equipment: ['Barbell'],
+ aliases: ['OHP'],
+ localizedLabels: {
+ 'de': 'Schulterdrücken',
+ 'es': 'Press militar',
+ 'fr': 'Développé militaire',
+ },
+ ),
+ ExerciseDef(
+ 'triceps_pushdown',
+ 'Triceps pushdown',
+ ['triceps'],
+ category: 'Arms',
+ equipment: ['Cable machine'],
+ localizedLabels: {
+ 'de': 'Trizepsdrücken am Kabel',
+ 'es': 'Extensión de tríceps en polea',
+ 'fr': 'Extension des triceps à la poulie',
+ },
+ ),
+ ExerciseDef(
+ 'overhead_extension',
+ 'Overhead extension',
+ ['triceps'],
+ category: 'Arms',
+ aliases: ['overhead triceps extension'],
+ localizedLabels: {
+ 'de': 'Trizepsstrecken über Kopf',
+ 'es': 'Extensión de tríceps sobre la cabeza',
+ 'fr': 'Extension des triceps au-dessus de la tête',
+ },
+ ),
+ ExerciseDef(
+ 'barbell_row',
+ 'Barbell row',
+ ['back', 'biceps', 'core'],
+ category: 'Back',
+ equipment: ['Barbell'],
+ localizedLabels: {
+ 'de': 'Langhantelrudern',
+ 'es': 'Remo con barra',
+ 'fr': 'Rowing barre',
+ },
+ ),
+ ExerciseDef(
+ 'lat_pulldown',
+ 'Lat pulldown',
+ ['back', 'biceps'],
+ category: 'Back',
+ equipment: ['Cable machine'],
+ localizedLabels: {
+ 'de': 'Latziehen',
+ 'es': 'Jalón al pecho',
+ 'fr': 'Tirage vertical',
+ },
+ ),
+ ExerciseDef(
+ 'pull_up',
+ 'Pull-up',
+ ['back', 'biceps', 'core'],
+ category: 'Back',
+ equipment: ['Pull-up bar'],
+ aliases: ['pull up', 'pull-ups'],
+ localizedLabels: {'de': 'Klimmzug', 'es': 'Dominada', 'fr': 'Traction'},
+ ),
+ ExerciseDef(
+ 'barbell_curl',
+ 'Barbell curl',
+ ['biceps'],
+ category: 'Arms',
+ equipment: ['Barbell'],
+ localizedLabels: {
+ 'de': 'Langhantelcurl',
+ 'es': 'Curl con barra',
+ 'fr': 'Curl barre',
+ },
+ ),
+ ExerciseDef(
+ 'back_squat',
+ 'Back squat',
+ ['legs', 'glutes', 'core'],
+ category: 'Legs',
+ equipment: ['Barbell'],
+ localizedLabels: {
+ 'de': 'Kniebeuge',
+ 'es': 'Sentadilla trasera',
+ 'fr': 'Squat arrière',
+ },
+ ),
+ ExerciseDef(
+ 'front_squat',
+ 'Front squat',
+ ['legs', 'glutes', 'core'],
+ category: 'Legs',
+ equipment: ['Barbell'],
+ localizedLabels: {
+ 'de': 'Frontkniebeuge',
+ 'es': 'Sentadilla frontal',
+ 'fr': 'Squat avant',
+ },
+ ),
+ ExerciseDef(
+ 'deadlift',
+ 'Deadlift',
+ ['back', 'legs', 'glutes', 'core'],
+ category: 'Back',
+ equipment: ['Barbell'],
+ localizedLabels: {
+ 'de': 'Kreuzheben',
+ 'es': 'Peso muerto',
+ 'fr': 'Soulevé de terre',
+ },
+ ),
+ ExerciseDef(
+ 'romanian_deadlift',
+ 'Romanian deadlift',
+ ['glutes', 'legs', 'back'],
+ category: 'Legs',
+ equipment: ['Barbell'],
+ aliases: ['RDL'],
+ localizedLabels: {
+ 'de': 'Rumänisches Kreuzheben',
+ 'es': 'Peso muerto rumano',
+ 'fr': 'Soulevé de terre roumain',
+ },
+ ),
+ ExerciseDef(
+ 'hip_thrust',
+ 'Hip thrust',
+ ['glutes', 'legs'],
+ category: 'Legs',
+ localizedLabels: {
+ 'de': 'Hip Thrust',
+ 'es': 'Empuje de cadera',
+ 'fr': 'Hip thrust',
+ },
+ ),
+ ExerciseDef(
+ 'leg_press',
+ 'Leg press',
+ ['legs', 'glutes'],
+ category: 'Legs',
+ localizedLabels: {
+ 'de': 'Beinpresse',
+ 'es': 'Prensa de piernas',
+ 'fr': 'Presse à cuisses',
+ },
+ ),
+ ExerciseDef(
+ 'plank',
+ 'Plank',
+ ['core'],
+ category: 'Abs',
+ equipment: ['none (bodyweight exercise)'],
+ step: 0,
+ localizedLabels: {'de': 'Unterarmstütz', 'es': 'Plancha', 'fr': 'Gainage'},
+ ),
+ ExerciseDef(
+ 'hanging_leg_raise',
+ 'Hanging leg raise',
+ ['core'],
+ category: 'Abs',
+ equipment: ['Pull-up bar'],
+ step: 0,
+ localizedLabels: {
+ 'de': 'Hängendes Beinheben',
+ 'es': 'Elevación de piernas colgado',
+ 'fr': 'Relevé de jambes suspendu',
+ },
+ ),
+];
+
const exerciseLibrary = [
- ExerciseDef('bench_press', 'Bench press',
- {'chest': .6, 'triceps': .25, 'shoulders': .15}),
- ExerciseDef('incline_db_press', 'Incline DB press',
- {'chest': .5, 'shoulders': .3, 'triceps': .2},
- step: 2),
- ExerciseDef('cable_fly', 'Cable fly', {'chest': .8, 'shoulders': .2}),
- ExerciseDef('overhead_press', 'Overhead press',
- {'shoulders': .6, 'triceps': .3, 'core': .1}),
- ExerciseDef('triceps_pushdown', 'Triceps pushdown', {'triceps': 1.0}),
- ExerciseDef('overhead_extension', 'Overhead extension', {'triceps': 1.0}),
- ExerciseDef('barbell_row', 'Barbell row',
- {'back': .65, 'biceps': .25, 'core': .1}),
- ExerciseDef('lat_pulldown', 'Lat pulldown', {'back': .7, 'biceps': .3}),
- ExerciseDef('pull_up', 'Pull-up', {'back': .65, 'biceps': .25, 'core': .1}),
- ExerciseDef('barbell_curl', 'Barbell curl', {'biceps': 1.0}),
- ExerciseDef('back_squat', 'Back squat',
- {'legs': .65, 'glutes': .25, 'core': .1}),
- ExerciseDef('front_squat', 'Front squat',
- {'legs': .6, 'glutes': .2, 'core': .2}),
- ExerciseDef('deadlift', 'Deadlift',
- {'back': .35, 'legs': .3, 'glutes': .3, 'core': .05}),
- ExerciseDef('romanian_deadlift', 'Romanian deadlift',
- {'glutes': .45, 'legs': .35, 'back': .2}),
- ExerciseDef('hip_thrust', 'Hip thrust', {'glutes': .8, 'legs': .2}),
- ExerciseDef('leg_press', 'Leg press', {'legs': .75, 'glutes': .25}),
- ExerciseDef('plank', 'Plank', {'core': 1.0}, step: 0),
- ExerciseDef('hanging_leg_raise', 'Hanging leg raise', {'core': 1.0}, step: 0),
+ ..._edgeExerciseLibrary,
+ ..._wgerExerciseLibrary,
];
+final coreExerciseCount = _edgeExerciseLibrary.length;
+final wgerExerciseCount = _wgerExerciseLibrary.length;
+
final Map _exercisesByKey = {
for (final e in exerciseLibrary) e.key: e,
};
ExerciseDef? exerciseByKey(String key) => _exercisesByKey[key];
-
diff --git a/lib/ui2/activity/exercise_picker.dart b/lib/ui2/activity/exercise_picker.dart
new file mode 100644
index 000000000..f9a688aad
--- /dev/null
+++ b/lib/ui2/activity/exercise_picker.dart
@@ -0,0 +1,358 @@
+// Exercise picker for choosing, adding and changing a lift.
+
+import 'package:flutter/material.dart';
+import 'package:lucide_icons_flutter/lucide_icons.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+import '../../l10n/app_localizations.dart';
+import '../grammar.dart';
+import '../theme.dart';
+import 'catalogue.dart';
+
+enum ExercisePickMode { choose, add, change }
+
+Future showExercisePicker(
+ BuildContext context, {
+ required ExercisePickMode mode,
+ Set selectedKeys = const {},
+ List recentKeys = const [],
+}) => showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ sheetAnimationStyle: sheetMotion(context),
+ backgroundColor: P.of(context).card,
+ shape: const RoundedRectangleBorder(borderRadius: R.rXl),
+ builder: (_) => FractionallySizedBox(
+ heightFactor: .92,
+ child: _ExercisePicker(
+ mode: mode,
+ selectedKeys: selectedKeys,
+ recentKeys: recentKeys,
+ ),
+ ),
+);
+
+class _ExercisePicker extends StatefulWidget {
+ const _ExercisePicker({
+ required this.mode,
+ required this.selectedKeys,
+ required this.recentKeys,
+ });
+
+ final ExercisePickMode mode;
+ final Set selectedKeys;
+ final List recentKeys;
+
+ @override
+ State<_ExercisePicker> createState() => _ExercisePickerState();
+}
+
+class _ExercisePickerState extends State<_ExercisePicker> {
+ static const _common = '__common__';
+ static const _all = '__all__';
+
+ String query = '';
+ String filter = _common;
+ final search = TextEditingController();
+
+ @override
+ void dispose() {
+ search.dispose();
+ super.dispose();
+ }
+
+ List get categories {
+ final out = {
+ for (final exercise in exerciseLibrary)
+ if (exercise.category.isNotEmpty) exercise.category,
+ }.toList()..sort();
+ return out;
+ }
+
+ List _visible(String languageCode, AppLocalizations? l) {
+ final needle = query.trim();
+ final q = needle.toLowerCase();
+ // the category and equipment on screen are translated; search those too
+ bool hit(ExerciseDef e) =>
+ e.matches(needle, languageCode) ||
+ (l != null &&
+ [
+ l.activityExerciseCategory(e.category),
+ for (final name in e.equipment) _equipment(l, name),
+ ].any((v) => v.toLowerCase().contains(q)));
+ final category = filter == _common || filter == _all ? null : filter;
+ // a highlighted category chip keeps narrowing the search
+ if (needle.isNotEmpty || category != null) {
+ return [
+ for (final exercise in exerciseLibrary)
+ if ((category == null || exercise.category == category) &&
+ hit(exercise))
+ exercise,
+ ];
+ }
+ if (filter == _all) return exerciseLibrary;
+
+ // recent lifts first, then the built-in short list
+ final out = [];
+ final seen = {};
+ for (final key in widget.recentKeys) {
+ final exercise = exerciseByKey(key);
+ if (exercise != null && seen.add(key)) out.add(exercise);
+ }
+ for (final exercise in exerciseLibrary.take(coreExerciseCount)) {
+ if (seen.add(exercise.key)) out.add(exercise);
+ }
+ return out;
+ }
+
+ String _title(AppLocalizations? l) => switch (widget.mode) {
+ ExercisePickMode.choose =>
+ l?.activityExercisePickerChooseTitle ?? 'Choose exercise',
+ ExercisePickMode.add => l?.activityLiveAddExerciseTitle ?? 'Add exercise',
+ ExercisePickMode.change =>
+ l?.activityExercisePickerChangeTitle ?? 'Change exercise',
+ };
+
+ @override
+ Widget build(BuildContext context) {
+ final p = P.of(context);
+ final l = AppLocalizations.of(context);
+ final languageCode = Localizations.localeOf(context).languageCode;
+ final visible = _visible(languageCode, l);
+ return SafeArea(
+ top: false,
+ child: Padding(
+ padding: EdgeInsets.only(
+ left: S.x4,
+ right: S.x4,
+ top: S.x4,
+ bottom: MediaQuery.viewInsetsOf(context).bottom,
+ ),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ Expanded(
+ child: Text(_title(l), style: F.t2.copyWith(color: p.ink)),
+ ),
+ Pressable(
+ semanticLabel: l?.actionCancel ?? 'Cancel',
+ onTap: () => Navigator.pop(context),
+ child: Icon(LucideIcons.x, size: 20, color: p.ink3),
+ ),
+ ],
+ ),
+ const SizedBox(height: S.x3),
+ Container(
+ constraints: const BoxConstraints(minHeight: S.tap),
+ padding: const EdgeInsets.symmetric(horizontal: S.x4),
+ decoration: BoxDecoration(color: p.card2, borderRadius: R.rMd),
+ child: Row(
+ children: [
+ Icon(LucideIcons.search, size: 17, color: p.ink3),
+ const SizedBox(width: S.x2),
+ Expanded(
+ child: Semantics(
+ label:
+ l?.activityExercisePickerSearchLabel ??
+ 'Search exercises',
+ textField: true,
+ child: TextField(
+ controller: search,
+ autofocus: false,
+ onChanged: (value) => setState(() => query = value),
+ style: F.body.copyWith(color: p.ink),
+ cursorColor: p.on(C.purple),
+ decoration: InputDecoration.collapsed(
+ hintText:
+ l?.activityExercisePickerSearchHint(
+ exerciseLibrary.length,
+ ) ??
+ 'Search ${exerciseLibrary.length} exercises',
+ hintStyle: F.body.copyWith(color: p.ink3),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: S.x3),
+ SizedBox(
+ height: S.tap,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ _filter(
+ p,
+ _common,
+ l?.activityExercisePickerCommonFilter ?? 'Common',
+ ),
+ _filter(p, _all, l?.activityExercisePickerAllFilter ?? 'All'),
+ for (final category in categories)
+ _filter(
+ p,
+ category,
+ l?.activityExerciseCategory(category) ?? category,
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: S.x2),
+ Expanded(
+ child: visible.isEmpty
+ ? Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(LucideIcons.searchX, size: 28, color: p.ink3),
+ const SizedBox(height: S.x3),
+ Text(
+ l?.activityExercisePickerNoMatch ??
+ 'No exercises match that',
+ style: F.body.copyWith(color: p.ink3),
+ ),
+ ],
+ ),
+ )
+ : ListView.separated(
+ keyboardDismissBehavior:
+ ScrollViewKeyboardDismissBehavior.onDrag,
+ itemCount: visible.length,
+ separatorBuilder: (_, _) =>
+ Divider(color: p.line, height: 1),
+ itemBuilder: (_, index) =>
+ _row(p, visible[index], languageCode),
+ ),
+ ),
+ Divider(color: p.line, height: 1),
+ Pressable(
+ onTap: () => launchUrl(
+ Uri.parse(
+ 'https://openstrap.github.io/edge/notice.html'
+ '#wger-exercise-data',
+ ),
+ mode: LaunchMode.externalApplication,
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: S.x2),
+ child: Text(
+ l?.activityExercisePickerWgerCredit ??
+ 'Exercise catalogue from wger',
+ textAlign: TextAlign.center,
+ style: F.over.copyWith(color: p.ink3),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _filter(P p, String value, String label) {
+ final selected = filter == value;
+ return Padding(
+ padding: const EdgeInsets.only(right: S.x2),
+ child: Pressable(
+ onTap: () => setState(() {
+ filter = value;
+ query = '';
+ search.clear();
+ }),
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: S.x3, vertical: S.x2),
+ decoration: BoxDecoration(
+ color: selected ? p.fill(C.purple) : p.card2,
+ borderRadius: R.rPill,
+ ),
+ child: Text(
+ label,
+ style: F.cap.copyWith(
+ color: selected ? p.inkOnFill : p.ink2,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _row(P p, ExerciseDef exercise, String languageCode) {
+ final l = AppLocalizations.of(context);
+ final label = exercise.labelFor(languageCode);
+ final selected = widget.selectedKeys.contains(exercise.key);
+ final sourceUrl = exercise.sourceUrl;
+ final details = [
+ if (exercise.category.isNotEmpty)
+ l?.activityExerciseCategory(exercise.category) ?? exercise.category,
+ if (exercise.equipment.isNotEmpty)
+ exercise.equipment.take(2).map((e) => _equipment(l, e)).join(', '),
+ ].join(' · ');
+ final sourceLabel =
+ l?.activityExercisePickerSourceLabel(label) ??
+ 'View source and credits for $label';
+ return Row(
+ children: [
+ Expanded(
+ child: Pressable(
+ onTap: () => Navigator.pop(context, exercise.key),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: S.x3),
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(label, style: F.body.copyWith(color: p.ink)),
+ if (details.isNotEmpty) ...[
+ const SizedBox(height: 2),
+ Text(
+ details,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: F.over.copyWith(color: p.ink3),
+ ),
+ ],
+ ],
+ ),
+ ),
+ if (selected)
+ Icon(
+ LucideIcons.circleCheck,
+ size: 18,
+ color: p.on(C.purple),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ if (sourceUrl != null)
+ Tooltip(
+ message: sourceLabel,
+ child: Pressable(
+ semanticLabel: sourceLabel,
+ onTap: () => launchUrl(
+ Uri.parse(sourceUrl),
+ mode: LaunchMode.externalApplication,
+ ),
+ child: SizedBox(
+ width: S.tap,
+ height: S.tap,
+ child: Icon(LucideIcons.info, size: 17, color: p.ink3),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+String _equipment(AppLocalizations? l, String name) =>
+ l?.activityExerciseEquipment(
+ name.replaceAll(RegExp('[^A-Za-z]'), ''),
+ name,
+ ) ??
+ name;
diff --git a/lib/ui2/activity/live.dart b/lib/ui2/activity/live.dart
index e37bd486c..b78c69b23 100644
--- a/lib/ui2/activity/live.dart
+++ b/lib/ui2/activity/live.dart
@@ -39,6 +39,7 @@ import '../grammar.dart';
import '../paint_activity.dart';
import '../theme.dart';
import 'catalogue.dart';
+import 'exercise_picker.dart';
import 'summary.dart';
/// What the band and the phone know right now. Read once per tick.
@@ -1177,7 +1178,7 @@ class LiveStrength extends StatefulWidget {
class _LiveStrengthState extends State {
/// The exercises this session has touched, in the order they were added.
- final plan = ['bench_press'];
+ final plan = [];
int index = 0;
double kg = 40;
@@ -1215,27 +1216,44 @@ class _LiveStrengthState extends State {
super.dispose();
}
- /// Sets logged before this screen was rebuilt — the session was minimised,
- /// or the process was killed and relaunched. Nothing measures a bench press,
- /// so these bytes cannot be recovered from anywhere else.
+ /// Plan and sets written before this screen was rebuilt — the session was
+ /// minimised, or the process was killed and relaunched.
void _restore() {
- final saved = LiveDraft.current?.data['sets'];
- if (saved is! List) return;
- for (final e in saved) {
- if (e is! Map) continue;
- final k = e['k'];
- if (k is! String) continue;
- logged.add(LoggedSet(
- k,
- (e['reps'] as num?)?.toInt() ?? 0,
- loadKg: (e['kg'] as num?)?.toDouble(),
- rpe: (e['rpe'] as num?)?.toInt(),
- restSec: (e['rest'] as num?)?.toInt(),
- at: DateTime.fromMillisecondsSinceEpoch((e['at'] as num?)?.toInt() ?? 0),
- ));
- if (!plan.contains(k)) plan.add(k);
+ final draft = LiveDraft.current;
+ if (draft == null) return;
+ final savedPlan = draft.data['exercise_plan'];
+ if (savedPlan is List) {
+ for (final value in savedPlan) {
+ if (value is String && value.isNotEmpty && !plan.contains(value)) {
+ plan.add(value);
+ }
+ }
+ }
+ final saved = draft.data['sets'];
+ if (saved is List) {
+ for (final e in saved) {
+ if (e is! Map) continue;
+ final k = e['k'];
+ if (k is! String) continue;
+ logged.add(LoggedSet(
+ k,
+ (e['reps'] as num?)?.toInt() ?? 0,
+ loadKg: (e['kg'] as num?)?.toDouble(),
+ rpe: (e['rpe'] as num?)?.toInt(),
+ restSec: (e['rest'] as num?)?.toInt(),
+ at: DateTime.fromMillisecondsSinceEpoch(
+ (e['at'] as num?)?.toInt() ?? 0),
+ ));
+ if (!plan.contains(k)) plan.add(k);
+ }
+ }
+ if (plan.isEmpty) return;
+ final savedIndex = (draft.data['exercise_index'] as num?)?.toInt();
+ if (savedIndex != null && savedIndex >= 0 && savedIndex < plan.length) {
+ index = savedIndex;
+ } else if (logged.isNotEmpty) {
+ index = plan.indexOf(logged.last.exerciseKey);
}
- if (logged.isNotEmpty) index = plan.indexOf(logged.last.exerciseKey);
}
/// Write the log through — to the draft, so minimising cannot lose it, and
@@ -1254,9 +1272,15 @@ class _LiveStrengthState extends State {
'at': s.at.millisecondsSinceEpoch,
},
]);
+ _persistPlan();
widget.onSets?.call(List.of(logged));
}
+ void _persistPlan() {
+ LiveDraft.current?.put('exercise_plan', List.of(plan));
+ LiveDraft.current?.put('exercise_index', index);
+ }
+
String get key => plan[index];
ExerciseDef? get def => exerciseByKey(key);
List get setsHere =>
@@ -1266,6 +1290,7 @@ class _LiveStrengthState extends State {
/// Open each exercise at what the user did last time. Nothing to go on →
/// leave the stepper where it is rather than guessing a load.
void _seedFromHistory() {
+ if (plan.isEmpty) return;
final prev = widget.history[key]?.previous;
if (prev == null) return;
setState(() {
@@ -1276,6 +1301,7 @@ class _LiveStrengthState extends State {
}
void logSet() {
+ if (plan.isEmpty) return;
HapticFeedback.mediumImpact();
final now = DateTime.now();
// The rest ACTUALLY taken before this set, not the 90 s target — the
@@ -1313,54 +1339,72 @@ class _LiveStrengthState extends State {
_rest?.cancel();
restLeft.value = 0;
setState(() => index = i);
+ _persistPlan();
_seedFromHistory();
}
Future addExercise() async {
- final picked = await showModalBottomSheet(
- context: context,
- // A sheet does not consult `pageTransitionsTheme`, so reduced motion has
- // to be handed to it here or the panel slides up at full duration.
- sheetAnimationStyle: sheetMotion(context),
- backgroundColor: P.of(context).card,
- shape: const RoundedRectangleBorder(borderRadius: R.rXl),
- builder: (c) {
- final p = P.of(c);
- final l = AppLocalizations.of(c);
- return SafeArea(
- child: ListView(shrinkWrap: true, children: [
- Padding(
- padding: const EdgeInsets.all(S.x4),
- child: Text(l?.activityLiveAddExerciseTitle ?? 'Add exercise',
- style: F.head.copyWith(color: p.ink)),
- ),
- for (final e in exerciseLibrary)
- Pressable(
- onTap: () => Navigator.pop(c, e.key),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: S.x4, vertical: S.x3),
- child: Row(children: [
- Expanded(
- child: Text(e.label,
- style: F.body.copyWith(color: p.ink))),
- Text(e.muscles.keys.first,
- style: F.over.copyWith(color: p.ink3)),
- ]),
- ),
- ),
- ]),
- );
- },
+ final picked = await showExercisePicker(
+ context,
+ mode: plan.isEmpty ? ExercisePickMode.choose : ExercisePickMode.add,
+ selectedKeys: plan.toSet(),
+ recentKeys: _recentKeys,
);
- if (picked == null) return;
+ if (!mounted || picked == null) return;
+ _rest?.cancel();
+ restLeft.value = 0;
setState(() {
if (!plan.contains(picked)) plan.add(picked);
index = plan.indexOf(picked);
});
+ _persistPlan();
+ _seedFromHistory();
+ }
+
+ /// Replace an empty slot; once it has sets, switch/add instead so logged
+ /// sets keep their exercise.
+ Future changeExercise() async {
+ if (plan.isEmpty) return addExercise();
+ final oldIndex = index;
+ final mayReplace = setsHere.isEmpty;
+ final picked = await showExercisePicker(
+ context,
+ mode: ExercisePickMode.change,
+ selectedKeys: plan.toSet(),
+ recentKeys: _recentKeys,
+ );
+ if (!mounted || picked == null || picked == plan[oldIndex]) return;
+ _rest?.cancel();
+ restLeft.value = 0;
+ setState(() {
+ final existing = plan.indexOf(picked);
+ if (mayReplace) {
+ if (existing < 0) {
+ plan[oldIndex] = picked;
+ index = oldIndex;
+ } else {
+ plan.removeAt(oldIndex);
+ index = plan.indexOf(picked);
+ }
+ } else {
+ if (existing < 0) plan.add(picked);
+ index = plan.indexOf(picked);
+ }
+ });
+ _persistPlan();
_seedFromHistory();
}
+ List get _recentKeys {
+ final entries = widget.history.entries.toList()
+ ..sort((a, b) {
+ final aa = a.value.previous?.at.millisecondsSinceEpoch ?? 0;
+ final bb = b.value.previous?.at.millisecondsSinceEpoch ?? 0;
+ return bb.compareTo(aa);
+ });
+ return [for (final entry in entries) entry.key];
+ }
+
@override
Widget build(BuildContext c) {
final volume = log.volumeKg;
@@ -1386,7 +1430,15 @@ class _LiveStrengthState extends State {
widget.feed?.call() ?? LiveFeed.none, widget.weightKg, elapsed,
widget.private,
strength: log),
- footer: (ctx) => restLeft.value > 0
+ footer: (ctx) => plan.isEmpty
+ ? BigButton(
+ AppLocalizations.of(ctx)?.activityExercisePickerChooseTitle ??
+ 'Choose exercise',
+ icon: LucideIcons.search,
+ color: C.purple,
+ onTap: addExercise,
+ )
+ : restLeft.value > 0
? Row(children: [
Expanded(
child: BigButton('+30s',
@@ -1424,6 +1476,18 @@ class _LiveStrengthState extends State {
final p = P.of(c);
final l = AppLocalizations.of(c);
final u = unitsOf(c);
+ if (plan.isEmpty) {
+ return Column(children: [
+ StatusCard(
+ l?.activityExercisePickerChooseTitle ?? 'Choose exercise',
+ l?.activityLiveChooseExerciseBody ??
+ 'Pick the first exercise before logging a set.',
+ icon: LucideIcons.dumbbell,
+ ),
+ const SizedBox(height: S.x5),
+ LiveTick((_, _) => LiveHeart(widget.feed?.call() ?? LiveFeed.none)),
+ ]);
+ }
final volume = log.volumeKg;
final hist = widget.history[key];
return Column(children: [
@@ -1451,35 +1515,54 @@ class _LiveStrengthState extends State {
Row(children: [
Pressable(
semanticLabel: l?.activityLivePreviousExercise ?? 'Previous exercise',
- onTap: () => goExercise(index - 1),
+ onTap: index == 0 ? null : () => goExercise(index - 1),
child: Icon(LucideIcons.chevronLeft,
size: 20, color: index == 0 ? p.line : p.ink3),
),
Expanded(
- child: Column(children: [
- Text(
- l?.activityLiveExerciseOf(index + 1, plan.length) ??
- 'EXERCISE ${index + 1} OF ${plan.length}',
- style: F.over.copyWith(color: p.ink3)),
- const SizedBox(height: S.x1),
- Text(def?.label ?? key,
- textAlign: TextAlign.center,
- style: F.t2.copyWith(color: p.ink)),
- ]),
+ child: Pressable(
+ semanticLabel:
+ l?.activityLiveChangeExercise ?? 'Change exercise',
+ onTap: changeExercise,
+ child: Column(children: [
+ Text(
+ l?.activityLiveExerciseOf(index + 1, plan.length) ??
+ 'EXERCISE ${index + 1} OF ${plan.length}',
+ style: F.over.copyWith(color: p.ink3)),
+ const SizedBox(height: S.x1),
+ Row(mainAxisSize: MainAxisSize.min, children: [
+ Flexible(
+ child: Text(
+ def?.labelFor(
+ Localizations.localeOf(c).languageCode) ??
+ key,
+ textAlign: TextAlign.center,
+ style: F.t2.copyWith(color: p.ink)),
+ ),
+ const SizedBox(width: S.x2),
+ Icon(LucideIcons.chevronsUpDown, size: 15, color: p.ink3),
+ ]),
+ ]),
+ ),
),
Pressable(
semanticLabel: l?.activityLiveNextExercise ?? 'Next exercise',
- onTap: () => index == plan.length - 1
- ? addExercise()
- : goExercise(index + 1),
- child: Icon(
- index == plan.length - 1
- ? LucideIcons.plus
- : LucideIcons.chevronRight,
- size: 20,
- color: p.ink3),
+ onTap:
+ index == plan.length - 1 ? null : () => goExercise(index + 1),
+ child: Icon(LucideIcons.chevronRight,
+ size: 20, color: index == plan.length - 1 ? p.line : p.ink3),
),
]),
+ Pressable(
+ onTap: addExercise,
+ child: Row(mainAxisSize: MainAxisSize.min, children: [
+ Icon(LucideIcons.plus, size: 15, color: p.on(C.purple)),
+ const SizedBox(width: S.x1),
+ Text(l?.activityLiveAddExerciseTitle ?? 'Add exercise',
+ style: F.cap.copyWith(
+ color: p.on(C.purple), fontWeight: FontWeight.w600)),
+ ]),
+ ),
const SizedBox(height: S.x4),
// set dots — one per set logged for this exercise, plus the one in hand
diff --git a/lib/ui2/activity/summary.dart b/lib/ui2/activity/summary.dart
index d92c8ede5..ab5328bfb 100644
--- a/lib/ui2/activity/summary.dart
+++ b/lib/ui2/activity/summary.dart
@@ -1733,7 +1733,8 @@ class _ActivitySummaryState extends State {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- exerciseByKey(top.exerciseKey)?.label ??
+ exerciseByKey(top.exerciseKey)?.labelFor(
+ Localizations.localeOf(c).languageCode) ??
top.exerciseKey,
style: F.body.copyWith(
color: p.ink, fontWeight: FontWeight.w600)),
@@ -1952,7 +1953,9 @@ class _ActivitySummaryState extends State {
return [
for (final key in r.strength.exercises) ...[
Section(
- exerciseByKey(key)?.label ?? key,
+ exerciseByKey(key)
+ ?.labelFor(Localizations.localeOf(c).languageCode) ??
+ key,
Surface(
pad: const EdgeInsets.symmetric(horizontal: S.x4),
child: Column(children: [
@@ -2206,4 +2209,3 @@ class _ActivitySummaryState extends State {
];
}
}
-
diff --git a/lib/ui2/activity/wger_exercises.g.dart b/lib/ui2/activity/wger_exercises.g.dart
new file mode 100644
index 000000000..e6b7c866c
--- /dev/null
+++ b/lib/ui2/activity/wger_exercises.g.dart
@@ -0,0 +1,5477 @@
+// GENERATED FILE — DO NOT EDIT.
+// Refresh with: dart run tool/update_wger_exercises.dart
+// Source: https://wger.de/api/v2/exerciseinfo/?limit=200&language__code=en
+// Selection: tool/wger_weightlifting_selection.dart
+//
+// Base data and translations retain their Creative Commons
+// credits. See NOTICE.md and docs/notice.html.
+
+part of 'catalogue.dart';
+
+const _wgerExerciseLibrary = [
+ ExerciseDef(
+ "wger:a8a4ea81-9531-48e1-b6dc-f7621c7b9283",
+ "1-Arm Half-Kneeling Lat Pulldown",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "1-Arm Half-Kneeling Lat Pulldown"},
+ sourceId: "a8a4ea81-9531-48e1-b6dc-f7621c7b9283",
+ sourceUpdatedAt: "2026-07-23T12:36:32.061160+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:1b020b3a-3732-4c7e-92fd-a0cec90ed69b",
+ "2 Handed Kettlebell Swing",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: ["Abs", "Quads"],
+ category: "Abs",
+ equipment: ["Kettlebell"],
+ aliases: [],
+ localizedLabels: {"en": "2 Handed Kettlebell Swing"},
+ step: 2,
+ sourceId: "1b020b3a-3732-4c7e-92fd-a0cec90ed69b",
+ sourceUpdatedAt: "2026-07-23T12:24:53.482746+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "deusinvictus",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:eb9476ac-2c00-4f49-a40f-f81682161a75",
+ "Alternating dumbbell hammer curl",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hammercurl mit Kurzhanteln, abwechselnd",
+ "en": "Alternating dumbbell hammer curl",
+ "es": "Curl alterno de martillo con mancuernas",
+ "fr": "Curl marteau alterné avec haltères",
+ },
+ step: 2,
+ sourceId: "eb9476ac-2c00-4f49-a40f-f81682161a75",
+ sourceUpdatedAt: "2026-06-19T18:47:09.433435+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Mariano_O",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f24cb758-9c0d-42d4-ad9e-6025c527dd13",
+ "Arnold Shoulder Press",
+ ["Shoulders"],
+ secondaryMuscles: ["Trapezius", "Triceps"],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [
+ "Dumbbell Corkscrew Press",
+ "Palm-to-Palm Overhead Press",
+ "Rotational Dumbbell Press",
+ "Schwarzenegger Press",
+ ],
+ localizedLabels: {
+ "de": "Arnold Press",
+ "en": "Arnold Shoulder Press",
+ "es": "Press Arnold",
+ },
+ step: 2,
+ sourceId: "f24cb758-9c0d-42d4-ad9e-6025c527dd13",
+ sourceUpdatedAt: "2026-07-23T12:24:53.892041+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "trzr23",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:dae6f6ed-9408-4e62-a59a-1a33f4e8ab36",
+ "Barbell Hack Squats",
+ ["Quads"],
+ secondaryMuscles: ["Glutes", "Hamstrings"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "en": "Barbell Hack Squats",
+ "es": "Sentadilla Hack con Barra",
+ },
+ sourceId: "dae6f6ed-9408-4e62-a59a-1a33f4e8ab36",
+ sourceUpdatedAt: "2026-07-23T12:24:54.294120+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "BePieToday",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:04e7d7e4-f8d2-406d-97df-3df3bceec22c",
+ "Barbell Lunges Standing",
+ ["Quads"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Stehende Ausfallschritte mit Langhantel",
+ "en": "Barbell Lunges Standing",
+ "es": "Zancadas con Barra",
+ "fr": "Fentes debout à la barre",
+ },
+ sourceId: "04e7d7e4-f8d2-406d-97df-3df3bceec22c",
+ sourceUpdatedAt: "2026-06-19T18:56:45.225252+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Mikko Ruohola",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:072a9fa8-1028-47b3-b958-d8052c6b8661",
+ "Barbell Lunges Walking",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Calves", "Hamstrings"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "en": "Barbell Lunges Walking",
+ "es": "Zancadas Caminando con Barra",
+ },
+ sourceId: "072a9fa8-1028-47b3-b958-d8052c6b8661",
+ sourceUpdatedAt: "2026-07-23T12:25:00.768313+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:67c26c9b-32f3-4b8b-b13a-4d8be4803975",
+ "Barbell Reverse Wrist Curl",
+ [],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Barbell", "Bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Langhantel-Handgelenkcurl mit Untergriff",
+ "en": "Barbell Reverse Wrist Curl",
+ "es": "Curl de Muñeca Inverso con Barra",
+ "fr": "Curl inversé des poignets à la barre",
+ },
+ sourceId: "67c26c9b-32f3-4b8b-b13a-4d8be4803975",
+ sourceUpdatedAt: "2026-06-19T18:56:45.911487+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "burenl",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3c2b5e2d-bd9d-43e8-8b5f-e6363331faa1",
+ "Barbell Row (Underhand)",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Langhantelrudern (Untergriff)",
+ "en": "Barbell Row (Underhand)",
+ "es": "Remo con barra (agarre supino)",
+ "fr": "Rowing à la barre (prise en supination)",
+ },
+ sourceId: "3c2b5e2d-bd9d-43e8-8b5f-e6363331faa1",
+ sourceUpdatedAt: "2026-06-19T18:53:57.125874+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "daiben",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:928eeae3-d849-4605-a470-048ca259a8c5",
+ "Barbell Step Back Lunge",
+ ["Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: ["Barbell Reverse Lunge"],
+ localizedLabels: {
+ "de": "Ausfallschritt nach hinten mit Langhantel",
+ "en": "Barbell Step Back Lunge",
+ "es": "Zancada hacia atrás con barra",
+ "fr": "Fente arrière à la barre",
+ },
+ sourceId: "928eeae3-d849-4605-a470-048ca259a8c5",
+ sourceUpdatedAt: "2026-06-19T18:55:43.452552+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:13234b53-ea0c-41f1-9069-776865ea8eff",
+ "Barbell Triceps Extension",
+ ["Triceps"],
+ secondaryMuscles: ["Chest", "Shoulders"],
+ category: "Arms",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsdrücken KH Über Kopf",
+ "en": "Barbell Triceps Extension",
+ "es": "Extensión de triceps",
+ "fr": "Extension des triceps à la barre",
+ },
+ sourceId: "13234b53-ea0c-41f1-9069-776865ea8eff",
+ sourceUpdatedAt: "2026-06-19T18:46:16.135119+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "sevae",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:cfb6a754-57be-431b-b8d0-d9963120bd5a",
+ "Barbell Wrist Curl",
+ [],
+ secondaryMuscles: ["Biceps"],
+ category: "Arms",
+ equipment: ["Barbell", "Bench"],
+ aliases: ["Forearm Curls", "Kurzhantel-Unterarmcurl"],
+ localizedLabels: {
+ "de": "Handgelenkstreckung",
+ "en": "Barbell Wrist Curl",
+ "es": "Curl de Muñeca con Barra (Agarre Supino)",
+ "fr": "Curl des poignets à la barre",
+ },
+ sourceId: "cfb6a754-57be-431b-b8d0-d9963120bd5a",
+ sourceUpdatedAt: "2026-07-23T12:08:23.213596+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "burenl",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:06968051-a818-489c-9fbb-ddb94a001245",
+ "Bayesian Curl",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "Bayesian Curl"},
+ sourceId: "06968051-a818-489c-9fbb-ddb94a001245",
+ sourceUpdatedAt: "2026-07-23T12:36:35.382539+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "aahuja",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:57018827-f344-4627-88e5-03e4f2f49859",
+ "Behind the Back Cable Lateral Raise",
+ ["Shoulders"],
+ secondaryMuscles: ["Trapezius"],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Seitheben am Kabelzug hinter dem Rücken",
+ "en": "Behind the Back Cable Lateral Raise",
+ "es": "Elevación lateral en polea por detrás de la espalda",
+ "fr": "Élévation latérale à la poulie derrière le dos",
+ },
+ sourceId: "57018827-f344-4627-88e5-03e4f2f49859",
+ sourceUpdatedAt: "2026-07-23T12:36:50.053346+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "LEBRERO",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e9f3446f-ca98-4bf2-817c-0dacd123cecd",
+ "Belt Squat",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Hamstrings"],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Belt Squat",
+ "en": "Belt Squat",
+ "es": "Sentadilla con cinturón",
+ "fr": "Squat à la ceinture",
+ },
+ sourceId: "e9f3446f-ca98-4bf2-817c-0dacd123cecd",
+ sourceUpdatedAt: "2026-06-19T18:56:44.538394+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3db63138-a047-4a4d-b616-1a0b7dfca105",
+ "Bench Press Narrow Grip",
+ ["Triceps"],
+ secondaryMuscles: ["Chest", "Shoulders"],
+ category: "Arms",
+ equipment: ["Barbell", "Bench"],
+ aliases: ["Close-Grip Bench Press"],
+ localizedLabels: {
+ "de": "Bankdrücken Eng",
+ "en": "Bench Press Narrow Grip",
+ "es": "Press de Banca con Agarre Cerrado",
+ "fr": "Développé couché prise serrée",
+ },
+ sourceId: "3db63138-a047-4a4d-b616-1a0b7dfca105",
+ sourceUpdatedAt: "2026-07-02T16:14:26.402383+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:28321cf3-70e6-48a4-ade1-d11382180cb3",
+ "Benchpress Dumbbells",
+ ["Chest"],
+ secondaryMuscles: ["Shoulders", "Triceps"],
+ category: "Chest",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Bankdrücken KH",
+ "en": "Benchpress Dumbbells",
+ "es": "Press de banca con mancuernas",
+ "fr": "Développé couché avec haltères",
+ },
+ step: 2,
+ sourceId: "28321cf3-70e6-48a4-ade1-d11382180cb3",
+ sourceUpdatedAt: "2026-07-02T16:44:41.508287+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Garion",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:94a5c406-7bcd-47f3-9687-bdf92a763932",
+ "Bent Over Dumbbell Rows (Single Arm)",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: ["KH Rudern", "One Arm DB Rows"],
+ localizedLabels: {
+ "de": "Rudern Einarmig KH",
+ "en": "Bent Over Dumbbell Rows (Single Arm)",
+ "es": "Remo con mancuernas",
+ "fr": "Rowing avec haltères buste penché",
+ },
+ step: 2,
+ sourceId: "94a5c406-7bcd-47f3-9687-bdf92a763932",
+ sourceUpdatedAt: "2026-09-21T20:10:23.321565+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "sebk",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3642933c-d078-4fb6-a544-d1772fa0cc9d",
+ "Bent Over Dumbbell Rows (Two Arms)",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: ["Biceps", "Brachialis"],
+ category: "Back",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Bent Over Dumbbell Rows (Two Arms)"},
+ step: 2,
+ sourceId: "3642933c-d078-4fb6-a544-d1772fa0cc9d",
+ sourceUpdatedAt: "2026-09-21T20:27:48.081337+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "karthikrrk",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:563203ce-1867-4376-aa7f-a0356808c0ae",
+ "Bent-Over Dumbbell Triceps Extension",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Bent-Over Dumbbell Triceps Extension"},
+ step: 2,
+ sourceId: "563203ce-1867-4376-aa7f-a0356808c0ae",
+ sourceUpdatedAt: "2026-09-16T21:57:10.516376+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6f79b381-98a4-40d5-8a45-3bb0558be6fe",
+ "Bent-over Lateral Raises",
+ [],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Vorgebeugtes Seitheben",
+ "en": "Bent-over Lateral Raises",
+ "es": "Elevaciones Posteriores",
+ "fr": "Élévations latérales buste penché",
+ },
+ step: 2,
+ sourceId: "6f79b381-98a4-40d5-8a45-3bb0558be6fe",
+ sourceUpdatedAt: "2026-06-19T18:46:19.206526+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "apeschel",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e68f6d51-fe02-4175-b277-dec0c1521f36",
+ "Biceps Curl Machine",
+ ["Biceps"],
+ secondaryMuscles: ["Brachialis"],
+ category: "Arms",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Bizepscurl-Maschine",
+ "en": "Biceps Curl Machine",
+ "es": "Curl de bíceps sentado",
+ "fr": "Machine à curl biceps",
+ },
+ sourceId: "e68f6d51-fe02-4175-b277-dec0c1521f36",
+ sourceUpdatedAt: "2026-06-19T18:52:33.112552+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "hpmbala@gmail.com",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:42227131-9b1e-4220-b082-c523f0651057",
+ "Biceps Curls With SZ-bar",
+ ["Biceps"],
+ secondaryMuscles: ["Brachialis"],
+ category: "Arms",
+ equipment: ["SZ-Bar"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Bizeps Curls Mit SZ-Stange",
+ "en": "Biceps Curls With SZ-bar",
+ "es": "Curl de biceps con barra Z",
+ "fr": "Curl biceps avec barre SZ",
+ },
+ sourceId: "42227131-9b1e-4220-b082-c523f0651057",
+ sourceUpdatedAt: "2026-06-19T18:46:18.692960+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:60d2c34b-43a1-48a3-b43b-160e4c0157f2",
+ "Bulgarian Squat with Dumbbells",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Hamstrings"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Bulgarische Kniebeuge mit Kurzhanteln",
+ "en": "Bulgarian Squat with Dumbbells",
+ "es": "Sentadilla búlgara con mancuernas",
+ "fr": "Squats bulgares haltères",
+ },
+ step: 2,
+ sourceId: "60d2c34b-43a1-48a3-b43b-160e4c0157f2",
+ sourceUpdatedAt: "2026-06-19T18:54:06.798695+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:99449eae-0eae-44ab-a57f-fc094bbdda13",
+ "Butterfly",
+ ["Chest"],
+ secondaryMuscles: ["Shoulders"],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Butterfly",
+ "en": "Butterfly",
+ "es": "Aperturas en máquina",
+ "fr": "Butterfly",
+ },
+ sourceId: "99449eae-0eae-44ab-a57f-fc094bbdda13",
+ sourceUpdatedAt: "2026-06-19T18:46:27.982120+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:a6605e9c-887d-45e0-8282-617a8ec5fea2",
+ "Butterfly Reverse",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Butterfly Reverse",
+ "en": "Butterfly Reverse",
+ "es": "Pec-Deck Inverso",
+ },
+ sourceId: "a6605e9c-887d-45e0-8282-617a8ec5fea2",
+ sourceUpdatedAt: "2026-04-15T22:23:54.874667+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b2865908-b93e-4507-ada4-a5bb9317566c",
+ "Cable Chest Press - Decline",
+ ["Chest"],
+ secondaryMuscles: ["Triceps"],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Brustdrücken am Kabelzug - negativ geneigt",
+ "en": "Cable Chest Press - Decline",
+ "es": "Press de pecho en polea - declinado",
+ "fr": "Développé poitrine à la poulie, en décliné",
+ },
+ sourceId: "b2865908-b93e-4507-ada4-a5bb9317566c",
+ sourceUpdatedAt: "2026-07-23T12:36:39.040307+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Shiladree",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e22c4320-7753-49c6-b7f7-4be21b025fb6",
+ "Cable Chest Press - Incline",
+ ["Chest"],
+ secondaryMuscles: ["Triceps"],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Brustdrücken am Kabelzug auf der Schrägbank",
+ "en": "Cable Chest Press - Incline",
+ "es": "Press de pecho en polea inclinado",
+ "fr": "Développé pectoraux à la poulie - incliné",
+ },
+ sourceId: "e22c4320-7753-49c6-b7f7-4be21b025fb6",
+ sourceUpdatedAt: "2026-07-23T12:36:39.843118+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Shiladree",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b1bf02cf-17b2-4bfc-a5b0-4ff7a2768dbb",
+ "Cable Concentration Curl",
+ ["Biceps", "Brachialis"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Konzentrationscurl am Kabelzug",
+ "en": "Cable Concentration Curl",
+ "es": "Biceps concentrado",
+ "fr": "Curl concentration à la poulie",
+ },
+ sourceId: "b1bf02cf-17b2-4bfc-a5b0-4ff7a2768dbb",
+ sourceUpdatedAt: "2026-07-23T12:36:24.687380+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "cshep442",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b16e3e5d-8401-4d2b-919c-15b536f9ec5e",
+ "Cable Cross-over",
+ ["Chest"],
+ secondaryMuscles: ["Shoulders"],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: ["Kabelziehen Über Kreuz"],
+ localizedLabels: {
+ "de": "Kabelcross",
+ "en": "Cable Cross-over",
+ "es": "Aperturas en polea",
+ "fr": "Écarté à la poulie (cable cross-over)",
+ },
+ sourceId: "b16e3e5d-8401-4d2b-919c-15b536f9ec5e",
+ sourceUpdatedAt: "2026-07-23T12:36:16.446300+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:479ef6f0-5274-4da7-b5ad-67528ff3c8fd",
+ "Cable Curls",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "Cable Curls"},
+ sourceId: "479ef6f0-5274-4da7-b5ad-67528ff3c8fd",
+ sourceUpdatedAt: "2026-07-23T12:36:37.007514+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Brigade7938",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:ae695660-7cee-4601-8241-edd3e2a6d896",
+ "Cable Fly Lower Chest",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kabelzug-Fly für die untere Brust",
+ "en": "Cable Fly Lower Chest",
+ "es": "Aperturas en polea para el pecho inferior",
+ "fr": "Écarté à la poulie pour le bas des pectoraux",
+ },
+ sourceId: "ae695660-7cee-4601-8241-edd3e2a6d896",
+ sourceUpdatedAt: "2026-07-23T12:36:42.294200+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "daiben",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b16a28c4-40bf-42d4-94b3-bf3f56fab743",
+ "Cable Fly Upper Chest",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kabelzug-Fly für die obere Brust",
+ "en": "Cable Fly Upper Chest",
+ "es": "Aperturas en polea para el pecho superior",
+ "fr": "Écarté à la poulie pour le haut des pectoraux",
+ },
+ sourceId: "b16a28c4-40bf-42d4-94b3-bf3f56fab743",
+ sourceUpdatedAt: "2026-07-23T12:36:41.867060+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "daiben",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6e03ecb3-6bfe-4b34-be9d-e5c33e820cc6",
+ "Cable Front Raise with a small bar",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Frontheben am Kabelzug mit kurzer Stange",
+ "en": "Cable Front Raise with a small bar",
+ "es": "Elevación frontal en polea con barra",
+ "fr": "Élévation frontale à la poulie avec barre courte",
+ },
+ sourceId: "6e03ecb3-6bfe-4b34-be9d-e5c33e820cc6",
+ sourceUpdatedAt: "2026-07-23T12:36:47.197968+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:19a9f67b-60f5-4367-9134-b805f3e73956",
+ "Cable glute extension",
+ ["Glutes"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Gesäßstrecken am Kabelzug",
+ "en": "Cable glute extension",
+ "es": "Extensión de glúteos en polea",
+ "fr": "Extension des fessiers à la poulie",
+ },
+ sourceId: "19a9f67b-60f5-4367-9134-b805f3e73956",
+ sourceUpdatedAt: "2026-07-23T12:36:26.342568+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Franpol",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:81942f3d-d92c-431c-b9d0-0da12918d1e4",
+ "Cable Lateral Raises (Single Arm)",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Seitheben am Kabelzug (einarmig)",
+ "en": "Cable Lateral Raises (Single Arm)",
+ "es": "Elevaciones laterales en polea (a un brazo)",
+ "fr": "Élévations latérales à la poulie (un bras)",
+ },
+ sourceId: "81942f3d-d92c-431c-b9d0-0da12918d1e4",
+ sourceUpdatedAt: "2026-07-23T12:36:30.445670+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "carlos3c",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:821e289e-bd39-4410-b436-6f2a43bc3649",
+ "Cable pull through",
+ ["Glutes"],
+ secondaryMuscles: ["Calves", "Hamstrings", "Quads", "Soleus"],
+ category: "Legs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Cable Pull Through",
+ "en": "Cable pull through",
+ "es": "Cable pull through",
+ "fr": "Tirage à la poulie entre les jambes",
+ },
+ sourceId: "821e289e-bd39-4410-b436-6f2a43bc3649",
+ sourceUpdatedAt: "2026-07-23T12:36:47.683102+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "manuher89",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5d244235-cd56-472a-876e-6e530a899ef2",
+ "Cable Rear Delt Fly",
+ ["Shoulders", "Trapezius"],
+ secondaryMuscles: ["Triceps"],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: ["Oiseau"],
+ localizedLabels: {
+ "de": "Reverse Butterfly am Kabel",
+ "en": "Cable Rear Delt Fly",
+ "es": "Aperturas Posteriores en Polea",
+ "fr": "Oiseau à la poulie",
+ },
+ sourceId: "5d244235-cd56-472a-876e-6e530a899ef2",
+ sourceUpdatedAt: "2026-07-23T12:36:22.670251+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "cshep442",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:1ea73578-08a5-4f3e-9255-3c58b29b170d",
+ "Cable Tricep Kickback",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizeps-Kickback am Kabelzug",
+ "en": "Cable Tricep Kickback",
+ "es": "Patada de tríceps en polea",
+ "fr": "Extension des triceps à la poulie (kickback)",
+ },
+ sourceId: "1ea73578-08a5-4f3e-9255-3c58b29b170d",
+ sourceUpdatedAt: "2026-07-23T12:36:35.793372+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:c867836d-2929-4977-b23c-014bc21ec08d",
+ "Cable Woodchoppers",
+ ["Obliquus externus abdominis"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Holzhacker am Kabelzug",
+ "en": "Cable Woodchoppers",
+ "es": "Leñadores en Polea",
+ "fr": "Rotations du tronc à la poulie",
+ },
+ sourceId: "c867836d-2929-4977-b23c-014bc21ec08d",
+ sourceUpdatedAt: "2026-07-23T12:36:14.006304+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "robhoyt",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:0d79f259-3b28-4258-8a08-cffec062a710",
+ "Calf Press Using Leg Press Machine",
+ ["Calves", "Soleus"],
+ secondaryMuscles: [],
+ category: "Calves",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Wadendrücken an Beinpresse",
+ "en": "Calf Press Using Leg Press Machine",
+ "es": "Gemelos en prensa",
+ "fr": "Extension des mollets à la presse à cuisses",
+ },
+ sourceId: "0d79f259-3b28-4258-8a08-cffec062a710",
+ sourceUpdatedAt: "2026-06-19T18:46:26.446370+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "nate303303",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:95a9cc46-270e-45f2-8a17-5665a23ff70b",
+ "Calf Raises on Hackenschmitt Machine",
+ ["Calves"],
+ secondaryMuscles: ["Soleus"],
+ category: "Calves",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Wadenheben an Hackenschmidt",
+ "en": "Calf Raises on Hackenschmitt Machine",
+ "es": "Elevación de Pantorrillas en Hack",
+ "fr": "Extension des mollets à la machine Hackenschmidt",
+ },
+ sourceId: "95a9cc46-270e-45f2-8a17-5665a23ff70b",
+ sourceUpdatedAt: "2026-06-19T18:46:26.971016+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:25cbc266-aa84-44f5-989b-53c9cc8fdc77",
+ "Chest-Supported Rear Delt Raise",
+ ["Shoulders"],
+ secondaryMuscles: ["Trapezius"],
+ category: "Shoulders",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Reverse Flys für die hintere Schulter mit Brustauflage",
+ "en": "Chest-Supported Rear Delt Raise",
+ "es": "Elevación posterior de hombro con apoyo en el pecho",
+ "fr": "Élévation des deltoïdes postérieurs avec appui sur la poitrine",
+ },
+ step: 2,
+ sourceId: "25cbc266-aa84-44f5-989b-53c9cc8fdc77",
+ sourceUpdatedAt: "2026-06-19T18:55:36.365751+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "beagle",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:a8f76beb-b6d8-430b-8e45-cc03d4959043",
+ "Clean",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Abs", "Hamstrings", "Lats"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Clean"},
+ sourceId: "a8f76beb-b6d8-430b-8e45-cc03d4959043",
+ sourceUpdatedAt: "2026-04-15T22:23:53.154276+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "M9",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8c509754-c86f-4c1b-bfa9-29c906ffc409",
+ "Clean and Jerk OL",
+ ["Glutes", "Hamstrings", "Quads", "Shoulders"],
+ secondaryMuscles: ["Abs", "Lats", "Trapezius", "Triceps"],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Clean and Jerk OL"},
+ sourceId: "8c509754-c86f-4c1b-bfa9-29c906ffc409",
+ sourceUpdatedAt: "2026-04-15T22:23:53.156098+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "M9",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2a125f64-1d10-4926-95ad-33a48e61b3d6",
+ "Clean and Press",
+ ["Shoulders"],
+ secondaryMuscles: ["Quads"],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: [
+ "Barbell Clean and Press",
+ "Cargada y envión",
+ "Clean & Press",
+ "Olympic Clean and Press",
+ ],
+ localizedLabels: {
+ "de": "Umsetzen und Stoßen",
+ "en": "Clean and Press",
+ "es": "Cargada y press",
+ "fr": "Épaulé-développé (clean and press)",
+ },
+ sourceId: "2a125f64-1d10-4926-95ad-33a48e61b3d6",
+ sourceUpdatedAt: "2026-06-19T18:56:20.460798+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "nishant0712",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:63fbb8e5-6ebc-4def-8844-3e65e097213b",
+ "Close-grip Lat Pull Down",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: [],
+ aliases: ["Lat Pulldown Eng", "Lat ziehen eng"],
+ localizedLabels: {
+ "de": "Latzug Eng",
+ "en": "Close-grip Lat Pull Down",
+ "es": "Jalón al Pecho con Agarre Cerrado",
+ "fr": "Tirage vertical prise serrée",
+ },
+ sourceId: "63fbb8e5-6ebc-4def-8844-3e65e097213b",
+ sourceUpdatedAt: "2026-06-19T18:46:24.894703+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:4eb41bfb-cbeb-4683-b9c6-902ee7e028e0",
+ "Close-grip supinated lat pulldown",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Latzug mit engem Untergriff",
+ "en": "Close-grip supinated lat pulldown",
+ "es": "Jalón cerrado supino",
+ "fr": "Tirage vertical en prise serrée et supination",
+ },
+ sourceId: "4eb41bfb-cbeb-4683-b9c6-902ee7e028e0",
+ sourceUpdatedAt: "2026-07-23T12:36:25.919621+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Franpol",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:401989e0-64c3-459d-bc47-2c171ab4f41d",
+ "Crunches on Machine",
+ ["Abs"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: [],
+ aliases: ["crunch en máquina"],
+ localizedLabels: {
+ "de": "Crunches an Maschine",
+ "en": "Crunches on Machine",
+ "es": "Abdominales en Máquina",
+ "fr": "Crunchs à la machine",
+ },
+ sourceId: "401989e0-64c3-459d-bc47-2c171ab4f41d",
+ sourceUpdatedAt: "2026-06-19T18:46:28.487838+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:29175281-afaf-4ffd-85e8-cfc38493e304",
+ "Crunches With Cable",
+ ["Abs"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Crunches am Seil",
+ "en": "Crunches With Cable",
+ "es": "Encogimientos abdominales en polea",
+ "fr": "Crunchs à la poulie",
+ },
+ sourceId: "29175281-afaf-4ffd-85e8-cfc38493e304",
+ sourceUpdatedAt: "2026-07-23T12:36:14.430709+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:bcd801f0-9a38-4615-a91c-900153c8c234",
+ "Decline Bench Press Barbell",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Barbell", "Bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Negativ Bankdrücken",
+ "en": "Decline Bench Press Barbell",
+ "es": "Press de Banca Declinado con Barra",
+ "fr": "Développé couché décliné à la barre",
+ },
+ sourceId: "bcd801f0-9a38-4615-a91c-900153c8c234",
+ sourceUpdatedAt: "2026-07-23T12:08:35.224422+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:dda69c96-62d4-4690-aa07-a4a0f6ceb63a",
+ "Decline Bench Press Dumbbell",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Negativ Bankdrücken KH",
+ "en": "Decline Bench Press Dumbbell",
+ "es": "Press de Banca Declinado con Mancuernas",
+ "fr": "Développé couché décliné aux haltères",
+ },
+ step: 2,
+ sourceId: "dda69c96-62d4-4690-aa07-a4a0f6ceb63a",
+ sourceUpdatedAt: "2026-07-23T12:08:35.631556+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e7a964cd-e68e-4926-bc7c-577065137e18",
+ "Deficit Deadlift",
+ [],
+ secondaryMuscles: ["Abs", "Glutes", "Hamstrings", "Lats", "Soleus"],
+ category: "Back",
+ equipment: ["Barbell"],
+ aliases: ["Deficit deadlift"],
+ localizedLabels: {
+ "de": "Defizit Kreuzheben",
+ "en": "Deficit Deadlift",
+ "es": "Peso Muerto con Déficit",
+ "fr": "Soulevé de terre en déficit",
+ },
+ sourceId: "e7a964cd-e68e-4926-bc7c-577065137e18",
+ sourceUpdatedAt: "2026-06-19T18:46:30.033946+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "bobbyprince89",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fe249e2f-5856-420f-8a34-1b02c7fde141",
+ "Double Kettlebell Clean and Press",
+ ["Biceps", "Brachialis", "Quads", "Shoulders", "Trapezius", "Triceps"],
+ secondaryMuscles: ["Abs", "Glutes", "Hamstrings", "Lats"],
+ category: "Arms",
+ equipment: ["Kettlebell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Doppeltes Kettlebell Clean and Press",
+ "en": "Double Kettlebell Clean and Press",
+ "es": "Clean and press con doble kettlebell",
+ "fr": "Épaulé-développé avec deux kettlebells",
+ },
+ step: 2,
+ sourceId: "fe249e2f-5856-420f-8a34-1b02c7fde141",
+ sourceUpdatedAt: "2026-06-19T18:51:57.962481+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "m4k3r",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:a030a4e9-970c-4442-b0b3-e49b42143de0",
+ "Double Kettlebell Front Squat",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Abs", "Calves", "Hamstrings"],
+ category: "Legs",
+ equipment: ["Kettlebell"],
+ aliases: [],
+ localizedLabels: {"en": "Double Kettlebell Front Squat"},
+ step: 2,
+ sourceId: "a030a4e9-970c-4442-b0b3-e49b42143de0",
+ sourceUpdatedAt: "2026-04-15T22:23:53.461982+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "m4k3r",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:0f648dbf-9ddc-44d9-a10c-1aa97537ea91",
+ "Dumbbell Bent-Over Reverse Fly",
+ ["Trapezius"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Dumbbell Bent-Over Reverse Fly"},
+ step: 2,
+ sourceId: "0f648dbf-9ddc-44d9-a10c-1aa97537ea91",
+ sourceUpdatedAt: "2026-09-16T18:20:43.749123+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:456f0781-30c6-4f58-a5b4-3da8f4878065",
+ "Dumbbell Calf Raise",
+ ["Calves"],
+ secondaryMuscles: [],
+ category: "Calves",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Dumbbell Calf Raise"},
+ step: 2,
+ sourceId: "456f0781-30c6-4f58-a5b4-3da8f4878065",
+ sourceUpdatedAt: "2026-09-16T21:57:13.525405+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:428112f3-3918-45ba-870a-dc58cf3959cf",
+ "Dumbbell close grip bench press",
+ ["Chest", "Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kurzhantel-Bankdrücken im engen Griff",
+ "en": "Dumbbell close grip bench press",
+ "es": "Press de banca con agarre cerrado con mancuernas",
+ "fr": "Développé couché serré aux haltères",
+ },
+ step: 2,
+ sourceId: "428112f3-3918-45ba-870a-dc58cf3959cf",
+ sourceUpdatedAt: "2026-06-19T18:50:40.701113+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Lynn_McIntyre",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:46e0f60c-fdc0-489a-82e4-a5b7476a5c21",
+ "Dumbbell Concentration Curl",
+ ["Brachialis"],
+ secondaryMuscles: ["Biceps"],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Konzentrations-Curls",
+ "en": "Dumbbell Concentration Curl",
+ "es": "Curl de concentración con mancuerna",
+ "fr": "Curl biceps avec haltère",
+ },
+ step: 2,
+ sourceId: "46e0f60c-fdc0-489a-82e4-a5b7476a5c21",
+ sourceUpdatedAt: "2026-06-19T18:57:39.114487+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "http://www.exrx.net/WeightExercises/Brachialis/DBC",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:99846da5-5dc8-4de1-ba55-ae2b4c03c30d",
+ "Dumbbell Curl",
+ ["Biceps", "Brachialis"],
+ secondaryMuscles: ["Abs", "Shoulders"],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kurzhantel-Curl",
+ "en": "Dumbbell Curl",
+ "es": "Curl con mancuerna",
+ "fr": "Curl avec haltères",
+ },
+ step: 2,
+ sourceId: "99846da5-5dc8-4de1-ba55-ae2b4c03c30d",
+ sourceUpdatedAt: "2026-06-19T18:56:41.784168+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "amaesc",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b199baca-7353-4dee-bbcf-620f96dba5b4",
+ "Dumbbell Deadlift",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: ["Quads", "Trapezius"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Dumbbell Deadlift"},
+ step: 2,
+ sourceId: "b199baca-7353-4dee-bbcf-620f96dba5b4",
+ sourceUpdatedAt: "2026-07-23T12:25:13.206261+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Anastasious",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e4966cb8-9089-4595-9c78-99a27821e6ff",
+ "Dumbbell Floor Press",
+ ["Chest"],
+ secondaryMuscles: ["Biceps", "Shoulders", "Triceps"],
+ category: "Chest",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kurzhantel-Bodendrücken",
+ "en": "Dumbbell Floor Press",
+ "es": "Press de suelo con mancuernas",
+ "fr": "Développé au sol avec haltères",
+ },
+ step: 2,
+ sourceId: "e4966cb8-9089-4595-9c78-99a27821e6ff",
+ sourceUpdatedAt: "2026-06-19T18:57:47.486181+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:4621b41f-0d32-4033-acff-dc079c792606",
+ "Dumbbell Front Squat",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Abs", "Shoulders"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Frontkniebeuge mit Kurzhanteln",
+ "en": "Dumbbell Front Squat",
+ "es": "Sentadilla frontal con mancuernas",
+ "fr": "Squat avant avec haltères",
+ },
+ step: 2,
+ sourceId: "4621b41f-0d32-4033-acff-dc079c792606",
+ sourceUpdatedAt: "2026-06-19T18:49:28.824471+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b7c6a444-90ea-4f5b-9fea-748311606eaa",
+ "Dumbbell Goblet Squat",
+ ["Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Goblet Squat mit Kurzhantel",
+ "en": "Dumbbell Goblet Squat",
+ "es": "Sentadilla con disco",
+ "fr": "Squat goblet avec haltère",
+ },
+ step: 2,
+ sourceId: "b7c6a444-90ea-4f5b-9fea-748311606eaa",
+ sourceUpdatedAt: "2026-06-19T18:56:54.257029+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "ataraxie67",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f0f53b8e-0136-4195-baf3-781903651359",
+ "Dumbbell Hang Power Cleans",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: ["Calves", "Quads", "Trapezius"],
+ category: "Back",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hang Power Cleans mit Kurzhanteln",
+ "en": "Dumbbell Hang Power Cleans",
+ "es": "Cargada de potencia desde colgado con mancuernas",
+ "fr": "Épaulés-jetés en puissance avec haltères depuis la suspension",
+ },
+ step: 2,
+ sourceId: "f0f53b8e-0136-4195-baf3-781903651359",
+ sourceUpdatedAt: "2026-07-23T12:25:08.253641+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "philip",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:587af8f1-516b-44c3-8660-70f262e1bef8",
+ "Dumbbell Hex Press",
+ ["Chest", "Triceps"],
+ secondaryMuscles: ["Shoulders"],
+ category: "Chest",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hex Press mit Kurzhanteln",
+ "en": "Dumbbell Hex Press",
+ "es": "Press hex con mancuernas",
+ "fr": "Développé serré aux haltères (hex press)",
+ },
+ step: 2,
+ sourceId: "587af8f1-516b-44c3-8660-70f262e1bef8",
+ sourceUpdatedAt: "2026-07-23T12:25:12.368050+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Anastasious",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:af77220c-098c-47c2-9f8c-92a651998903",
+ "Dumbbell Hip Thrust",
+ ["Glutes", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hip Thrust mit Kurzhantel",
+ "en": "Dumbbell Hip Thrust",
+ "es": "Empuje de cadera con mancuerna",
+ "fr": "Hip Thrust avec haltère",
+ },
+ step: 2,
+ sourceId: "af77220c-098c-47c2-9f8c-92a651998903",
+ sourceUpdatedAt: "2026-06-19T18:49:31.505813+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:43e85cb8-51d0-4892-b1bf-80a3cb111ff6",
+ "Dumbbell Incline Curl",
+ ["Biceps"],
+ secondaryMuscles: ["Brachialis"],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Bizeps KH-Curls Schrägbank",
+ "en": "Dumbbell Incline Curl",
+ "es": "Curl Inclinado con Mancuernas",
+ "fr": "Curl incliné aux haltères",
+ },
+ step: 2,
+ sourceId: "43e85cb8-51d0-4892-b1bf-80a3cb111ff6",
+ sourceUpdatedAt: "2026-06-19T18:46:35.686758+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "ExRx",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:c8b182c3-9dfd-40ca-87b2-ef25f19aa181",
+ "Dumbbell Lateral Raise",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Dumbbell Lateral Raise"},
+ step: 2,
+ sourceId: "c8b182c3-9dfd-40ca-87b2-ef25f19aa181",
+ sourceUpdatedAt: "2026-09-16T20:51:30.167973+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2f1a2707-e7ff-46ac-9112-3e31e6e961ee",
+ "Dumbbell Lunges Standing",
+ ["Quads"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Ausfallschritte Stehend",
+ "en": "Dumbbell Lunges Standing",
+ "es": "Zancadas con Mancuernas",
+ },
+ step: 2,
+ sourceId: "2f1a2707-e7ff-46ac-9112-3e31e6e961ee",
+ sourceUpdatedAt: "2026-04-15T22:23:56.372270+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:dcc6e237-a8bb-4eca-bbc5-7fb852636f6a",
+ "Dumbbell Lunges Walking",
+ ["Quads"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: ["Walking Lunges"],
+ localizedLabels: {
+ "de": "Ausfallschritte im Gehen",
+ "en": "Dumbbell Lunges Walking",
+ "es": "Zancadas Caminando con Mancuernas",
+ "fr": "Fentes marchées avec haltères",
+ },
+ step: 2,
+ sourceId: "dcc6e237-a8bb-4eca-bbc5-7fb852636f6a",
+ sourceUpdatedAt: "2026-07-02T16:14:28.985601+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:adb6067f-fd48-4a25-a9b4-0793c5f158fa",
+ "Dumbbell rear delt row",
+ ["Brachialis", "Shoulders", "Trapezius"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kurzhantel-Rudern für die hintere Schulter",
+ "en": "Dumbbell rear delt row",
+ "es": "Remo para deltoides posterior con mancuernas",
+ "fr": "Rowing pour deltoïdes postérieurs aux haltères",
+ },
+ step: 2,
+ sourceId: "adb6067f-fd48-4a25-a9b4-0793c5f158fa",
+ sourceUpdatedAt: "2026-06-19T18:50:38.992200+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Lynn_McIntyre",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:75e34967-b0ec-40d5-811e-b474b234a7a9",
+ "Dumbbell Rear Lunge",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Soleus"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Rückwärtiger Ausfallschritt mit Kurzhanteln",
+ "en": "Dumbbell Rear Lunge",
+ "es": "Zancada hacia atrás con mancuernas",
+ "fr": "Fente arrière avec haltères",
+ },
+ step: 2,
+ sourceId: "75e34967-b0ec-40d5-811e-b474b234a7a9",
+ sourceUpdatedAt: "2026-06-19T18:49:37.661523+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:65d12ecf-54b8-466d-a412-e55c396cad69",
+ "Dumbbell Romanian Deadlift",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Rumänisches Kreuzheben mit Kurzhanteln",
+ "en": "Dumbbell Romanian Deadlift",
+ "es": "Peso muerto rumano con mancuernas",
+ "fr": "Soulevé de terre roumain avec haltères",
+ },
+ step: 2,
+ sourceId: "65d12ecf-54b8-466d-a412-e55c396cad69",
+ sourceUpdatedAt: "2026-06-19T18:49:43.204115+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3e9b3bb0-7958-4836-89a5-ed0c5f7eeb03",
+ "Dumbbell Side Bend",
+ ["Obliquus externus abdominis"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Seitneigung mit Kurzhantel",
+ "en": "Dumbbell Side Bend",
+ "es": "Flexión lateral con mancuerna",
+ "fr": "Flexion Latérale avec haltère",
+ },
+ step: 2,
+ sourceId: "3e9b3bb0-7958-4836-89a5-ed0c5f7eeb03",
+ sourceUpdatedAt: "2026-06-19T18:49:36.273254+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:7cc24acf-3fc9-4d14-a461-cd00d1d18f0e",
+ "Dumbbell Single-leg Hip Thrust",
+ ["Glutes"],
+ secondaryMuscles: ["Hamstrings", "Quads"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einbeiniger Hip Thrust mit Kurzhantel",
+ "en": "Dumbbell Single-leg Hip Thrust",
+ "es": "Empuje de cadera a una pierna con mancuerna",
+ "fr": "Hip thrust à une jambe avec haltère",
+ },
+ step: 2,
+ sourceId: "7cc24acf-3fc9-4d14-a461-cd00d1d18f0e",
+ sourceUpdatedAt: "2026-07-23T12:08:34.828878+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "carlos3c",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:51fe0871-1390-4755-bd04-01a93e32f533",
+ "dumbbell snatch",
+ ["Glutes", "Hamstrings", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kurzhantel-Reißen (Dumbbell Snatch)",
+ "en": "dumbbell snatch",
+ "es": "Arrancada con mancuerna (dumbbell snatch)",
+ "fr": "dumbbell snatch",
+ },
+ step: 2,
+ sourceId: "51fe0871-1390-4755-bd04-01a93e32f533",
+ sourceUpdatedAt: "2026-06-19T18:56:10.682974+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Aleid",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8a3e08ab-4e97-4aee-897a-60d8693b9b43",
+ "Dumbbell Split Squat",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Calves", "Hamstrings"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Dumbbell Split Squat",
+ "en": "Dumbbell Split Squat",
+ },
+ step: 2,
+ sourceId: "8a3e08ab-4e97-4aee-897a-60d8693b9b43",
+ sourceUpdatedAt: "2026-07-23T12:25:12.789068+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Anastasious",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:0c30c543-e2cc-499b-bc31-8c28db445ed2",
+ "Dumbbell sumo deadlift",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: ["Quads", "Trapezius"],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Sumo-Kreuzheben mit Kurzhantel",
+ "en": "Dumbbell sumo deadlift",
+ "es": "Peso muerto sumo con mancuerna",
+ "fr": "Soulevé de terre sumo avec haltère",
+ },
+ step: 2,
+ sourceId: "0c30c543-e2cc-499b-bc31-8c28db445ed2",
+ sourceUpdatedAt: "2026-07-23T12:25:08.665206+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "philip",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:d8bddb58-91b0-4d7b-8ec1-cd742584b607",
+ "Dumbbell Triceps Extension",
+ ["Triceps"],
+ secondaryMuscles: ["Chest", "Shoulders"],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsstrecken mit Kurzhantel",
+ "en": "Dumbbell Triceps Extension",
+ "es": "Press Francés con Mancuerna",
+ "fr": "Extension des triceps à l'haltère",
+ },
+ step: 2,
+ sourceId: "d8bddb58-91b0-4d7b-8ec1-cd742584b607",
+ sourceUpdatedAt: "2026-06-19T18:56:54.943487+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuninx",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2affa71f-7308-4448-af57-dc44b424090e",
+ "Dumbell Tate Press",
+ ["Triceps"],
+ secondaryMuscles: ["Chest", "Shoulders"],
+ category: "Arms",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Tate Press mit Kurzhanteln",
+ "en": "Dumbell Tate Press",
+ "es": "Tate press con mancuernas",
+ "fr": "Tate press avec haltères",
+ },
+ step: 2,
+ sourceId: "2affa71f-7308-4448-af57-dc44b424090e",
+ sourceUpdatedAt: "2026-06-19T18:55:35.012633+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "beagle",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:d7a418d4-d0cb-4f85-8a7c-1e9d97152cbd",
+ "Facepull",
+ ["Trapezius"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: ["Face pull"],
+ localizedLabels: {
+ "de": "Face Pulls",
+ "en": "Facepull",
+ "es": "Jalón a la Cara",
+ "fr": "Tirage à la face (Face pull)",
+ },
+ sourceId: "d7a418d4-d0cb-4f85-8a7c-1e9d97152cbd",
+ sourceUpdatedAt: "2026-07-23T12:36:14.828136+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "abeworld",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:03f7fc27-e64f-4417-ab0c-a52adac60fda",
+ "Flat Machine Press",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Brustdrücken an der Maschine (flach)",
+ "en": "Flat Machine Press",
+ "es": "Press de pecho en máquina (plano)",
+ "fr": "Développé à la machine sur plat",
+ },
+ sourceId: "03f7fc27-e64f-4417-ab0c-a52adac60fda",
+ sourceUpdatedAt: "2026-06-19T18:53:44.237251+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "daiben",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6b68043c-a900-41f4-89fb-ea5c8193f216",
+ "Floor Skull Crusher",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["SZ-Bar"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Floor Skull Crusher",
+ "en": "Floor Skull Crusher",
+ "es": "Press francés en el suelo",
+ "fr": "Barre au front au sol",
+ },
+ sourceId: "6b68043c-a900-41f4-89fb-ea5c8193f216",
+ sourceUpdatedAt: "2026-06-19T18:53:00.417610+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:95d226ad-3bf7-4cd6-aa64-2f26b526d8b6",
+ "Fly With Dumbbells",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Fliegende KH Flachbank",
+ "en": "Fly With Dumbbells",
+ "es": "Aperturas con Mancuernas",
+ "fr": "Écarté aux haltères",
+ },
+ step: 2,
+ sourceId: "95d226ad-3bf7-4cd6-aa64-2f26b526d8b6",
+ sourceUpdatedAt: "2026-06-19T18:46:35.176852+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:55d0a5ec-b147-40c3-aa77-314e61c93689",
+ "Fly With Dumbbells, Decline Bench",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Negativ Fliegende Mit KH",
+ "en": "Fly With Dumbbells, Decline Bench",
+ "es": "Aperturas con Mancuernas Declinadas",
+ "fr": "Écarté aux haltères sur banc décliné",
+ },
+ step: 2,
+ sourceId: "55d0a5ec-b147-40c3-aa77-314e61c93689",
+ sourceUpdatedAt: "2026-07-23T12:08:36.039336+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9c35594c-bbcc-4656-bdcb-376814c90e96",
+ "Front Raises",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Barbell", "Dumbbell"],
+ aliases: ["DB Front Raises", "dumbbell front raises"],
+ localizedLabels: {
+ "de": "Frontheben am Kabel",
+ "en": "Front Raises",
+ "es": "Elevaciones frontales",
+ "fr": "Élévations frontales",
+ },
+ sourceId: "9c35594c-bbcc-4656-bdcb-376814c90e96",
+ sourceUpdatedAt: "2026-06-19T18:46:37.707561+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "Manu, wikipedia",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:66ed6788-a1d8-49a2-af4d-397107fbf5fb",
+ "Glute Kickback (Machine)",
+ ["Glutes"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Glute Kickback (Machine)"},
+ sourceId: "66ed6788-a1d8-49a2-af4d-397107fbf5fb",
+ sourceUpdatedAt: "2026-04-15T22:23:54.079271+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "barry",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f462df26-3264-48c2-b5aa-2374d0670c24",
+ "Hack Squats",
+ ["Quads"],
+ secondaryMuscles: ["Calves", "Glutes", "Hamstrings"],
+ category: "Legs",
+ equipment: [],
+ aliases: ["Machine Squat"],
+ localizedLabels: {
+ "de": "Hack Squats",
+ "en": "Hack Squats",
+ "es": "Sentadillas Hack",
+ "fr": "Hack squats",
+ },
+ sourceId: "f462df26-3264-48c2-b5aa-2374d0670c24",
+ sourceUpdatedAt: "2026-06-19T18:52:31.727148+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "hpmbala@gmail.com",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:c0d9fe98-f4fe-49f3-8037-05e1984e7d2d",
+ "Hammer Curls",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hammercurls",
+ "en": "Hammer Curls",
+ "es": "Curl Martillo",
+ "fr": "Curls marteau",
+ },
+ step: 2,
+ sourceId: "c0d9fe98-f4fe-49f3-8037-05e1984e7d2d",
+ sourceUpdatedAt: "2026-06-19T18:46:39.253111+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:04365177-e078-489b-983a-8ac61b7346f1",
+ "Hammercurls on Cable",
+ ["Biceps"],
+ secondaryMuscles: ["Brachialis"],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hammercurls am Seil",
+ "en": "Hammercurls on Cable",
+ "es": "Biceps concentrado martillo con cable",
+ "fr": "Curl marteau à la poulie",
+ },
+ sourceId: "04365177-e078-489b-983a-8ac61b7346f1",
+ sourceUpdatedAt: "2026-07-23T12:36:15.643371+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:920692b2-0842-428a-b925-ba0daf6b41db",
+ "Hammerstrength Decline Chest Press",
+ ["Chest"],
+ secondaryMuscles: ["Shoulders", "Triceps"],
+ category: "Chest",
+ equipment: [],
+ aliases: ["Hammerstrength Decline Press"],
+ localizedLabels: {
+ "de": "Hammer-Strength-Bankdrücken negativ (Decline)",
+ "en": "Hammerstrength Decline Chest Press",
+ "es": "Press de pecho declinado en Hammer Strength",
+ "fr": "Développé décliné sur machine Hammer Strength",
+ },
+ sourceId: "920692b2-0842-428a-b925-ba0daf6b41db",
+ sourceUpdatedAt: "2026-06-19T18:55:44.799925+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f80468f2-7c5d-45c5-9470-46a88c1a75c2",
+ "High Row",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "High Row"},
+ sourceId: "f80468f2-7c5d-45c5-9470-46a88c1a75c2",
+ sourceUpdatedAt: "2026-07-23T12:36:34.970783+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "aahuja",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:ecba2395-f364-4e5f-a34c-04f178c8b822",
+ "High-Incline Smith Machine Press",
+ ["Chest", "Shoulders", "Triceps"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "High-Incline Smith Machine Press"},
+ sourceId: "ecba2395-f364-4e5f-a34c-04f178c8b822",
+ sourceUpdatedAt: "2026-04-15T22:23:54.166650+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:275fb49f-975c-4d6e-9d63-2c86ed740f40",
+ "Incline Bench Press - Barbell",
+ ["Chest"],
+ secondaryMuscles: ["Shoulders", "Triceps"],
+ category: "Chest",
+ equipment: ["Barbell", "Incline bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Schrägbankdrücken LH",
+ "en": "Incline Bench Press - Barbell",
+ "es": "Press de banca inclinado",
+ },
+ sourceId: "275fb49f-975c-4d6e-9d63-2c86ed740f40",
+ sourceUpdatedAt: "2026-04-15T22:23:55.484203+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:70c99a4e-3340-4993-a7f1-2d2709dada1a",
+ "Incline Bench Reverse Fly",
+ ["Shoulders", "Trapezius"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Dumbbell", "Incline bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Reverse Fly auf der Schrägbank",
+ "en": "Incline Bench Reverse Fly",
+ "es": "Aperturas Inversas",
+ "fr": "Oiseau sur banc incliné",
+ },
+ step: 2,
+ sourceId: "70c99a4e-3340-4993-a7f1-2d2709dada1a",
+ sourceUpdatedAt: "2026-07-23T12:08:36.445040+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "cshep442",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:51a2d520-b510-4b6e-bd65-9fc40365e8de",
+ "Incline Chest-Supported Dumbbell Row",
+ ["Biceps", "Lats", "Trapezius"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Schrägbank-Kurzhantelrudern mit Brustauflage",
+ "en": "Incline Chest-Supported Dumbbell Row",
+ "es": "Remo con mancuernas en banco inclinado con apoyo en el pecho",
+ "fr": "Rowing avec haltères incliné, poitrine soutenue",
+ },
+ step: 2,
+ sourceId: "51a2d520-b510-4b6e-bd65-9fc40365e8de",
+ sourceUpdatedAt: "2026-06-19T18:51:14.694651+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "carlos3c",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fca05168-7212-42ec-8927-efb1e8dd0971",
+ "Incline Close Grip Barbell Bench Press",
+ ["Triceps"],
+ secondaryMuscles: ["Chest", "Shoulders"],
+ category: "Arms",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Incline Close Grip Barbell Bench Press"},
+ sourceId: "fca05168-7212-42ec-8927-efb1e8dd0971",
+ sourceUpdatedAt: "2026-04-15T22:23:54.243669+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:55ff32e6-24ab-4303-9b50-176d60d48796",
+ "Incline Dumbbell Fly",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Dumbbell", "Incline bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Fliegende KH Schrägbank",
+ "en": "Incline Dumbbell Fly",
+ "es": "Aperturas con mancuernas en banco inclinado",
+ "fr": "Écarté incliné aux haltères",
+ },
+ step: 2,
+ sourceId: "55ff32e6-24ab-4303-9b50-176d60d48796",
+ sourceUpdatedAt: "2026-06-19T18:57:01.265947+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:27eb3d4a-8677-4c1f-bf34-251e2b4741cb",
+ "Incline Dumbbell Row",
+ [],
+ secondaryMuscles: ["Lats"],
+ category: "Back",
+ equipment: ["Dumbbell", "Incline bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Rudern KH Auf Schrägbank",
+ "en": "Incline Dumbbell Row",
+ "es": "Remo con mancuernas en banco inclinado",
+ "fr": "Rowing aux haltères sur banc incliné",
+ },
+ step: 2,
+ sourceId: "27eb3d4a-8677-4c1f-bf34-251e2b4741cb",
+ sourceUpdatedAt: "2026-06-19T18:48:52.840741+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "cal.zabel",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:85c28424-ab47-40d4-9a27-e1b9bfb01e7c",
+ "Jerk OL",
+ ["Shoulders"],
+ secondaryMuscles: ["Glutes", "Quads", "Triceps"],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Jerk (Stoßen)",
+ "en": "Jerk OL",
+ "es": "Jerk (envión)",
+ "fr": "Jeté — haltérophilie",
+ },
+ sourceId: "85c28424-ab47-40d4-9a27-e1b9bfb01e7c",
+ sourceUpdatedAt: "2026-06-19T18:52:46.580318+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "M9",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:67c2fe41-f1e9-4f75-b2cb-72d1f2e092f7",
+ "JM Press",
+ ["Triceps"],
+ secondaryMuscles: ["Biceps", "Chest"],
+ category: "Arms",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "JM Press",
+ "en": "JM Press",
+ "es": "JM Press",
+ "fr": "JM Press",
+ },
+ sourceId: "67c2fe41-f1e9-4f75-b2cb-72d1f2e092f7",
+ sourceUpdatedAt: "2026-06-19T18:51:30.374474+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Epiphany8424",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9b5f8c6e-2436-4ded-aea9-8c698b0c8768",
+ "Kettlebell deadlifts",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: ["Quads", "Trapezius"],
+ category: "Back",
+ equipment: ["Kettlebell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kettlebell Kreuzheben",
+ "en": "Kettlebell deadlifts",
+ "es": "Peso Muerto con Kettlebell",
+ "fr": "Haltères de kettlebell",
+ "zh": "提壶铃",
+ },
+ step: 2,
+ sourceId: "9b5f8c6e-2436-4ded-aea9-8c698b0c8768",
+ sourceUpdatedAt: "2026-07-23T12:25:06.546463+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:20b17b71-6d91-4b56-978a-04b7f94c04cc",
+ "Kettlebell One Legged Deadlift",
+ ["Glutes"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell", "Kettlebell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einbeiniges Kreuzheben mit Kettlebell",
+ "en": "Kettlebell One Legged Deadlift",
+ "es": "Peso muerto a una pierna con pesa rusa",
+ "fr": "Soulevé de terre unilatéral",
+ },
+ step: 2,
+ sourceId: "20b17b71-6d91-4b56-978a-04b7f94c04cc",
+ sourceUpdatedAt: "2026-06-19T18:49:30.169104+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5cbaa028-114d-4a15-9a95-70d9b3146f30",
+ "kettlebell sumo deadlift",
+ ["Glutes", "Hamstrings", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Kettlebell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Sumo-Kreuzheben mit Kettlebell",
+ "en": "kettlebell sumo deadlift",
+ "es": "Sentadilla con pesa rusa",
+ "fr": "Soulevé de terre sumo à la kettlebell",
+ },
+ step: 2,
+ sourceId: "5cbaa028-114d-4a15-9a95-70d9b3146f30",
+ sourceUpdatedAt: "2026-06-19T18:54:47.131884+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:107ea68a-d976-49ed-be75-3b0cf253c36a",
+ "Kickstand RDL",
+ ["Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Kickstand RDL"},
+ sourceId: "107ea68a-d976-49ed-be75-3b0cf253c36a",
+ sourceUpdatedAt: "2026-04-15T22:23:54.406427+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "eriktrinkle",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:91c47c8b-6290-4885-8921-df9aa7958c00",
+ "Landmine press",
+ ["Shoulders"],
+ secondaryMuscles: ["Chest", "Triceps"],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"de": "Landmine-Press, Einarmig", "en": "Landmine press"},
+ sourceId: "91c47c8b-6290-4885-8921-df9aa7958c00",
+ sourceUpdatedAt: "2026-04-15T22:23:54.495508+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Torsten Linnecke",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:73c1e130-fba9-473b-a093-a5712e2ae34d",
+ "Landmine Rotation",
+ ["Obliquus externus abdominis"],
+ secondaryMuscles: ["Abs", "Shoulders"],
+ category: "Abs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Landmine-Rotation",
+ "en": "Landmine Rotation",
+ "es": "Rotación con landmine",
+ "fr": "Rotation au landmine",
+ },
+ sourceId: "73c1e130-fba9-473b-a093-a5712e2ae34d",
+ sourceUpdatedAt: "2026-07-23T12:25:17.224907+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "aboksz",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:87f30f6d-dc8a-4b26-8c4f-640b66ecacf0",
+ "Larsen Press",
+ ["Chest"],
+ secondaryMuscles: ["Triceps"],
+ category: "Chest",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Larsen Press"},
+ sourceId: "87f30f6d-dc8a-4b26-8c4f-640b66ecacf0",
+ sourceUpdatedAt: "2026-04-15T22:23:54.499790+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:28d18142-8777-4dba-a3a1-6151cec94500",
+ "Lean-Away Cable Lateral Raise",
+ ["Shoulders"],
+ secondaryMuscles: ["Trapezius"],
+ category: "Shoulders",
+ equipment: ["Cable machine"],
+ aliases: ["Lean-To Cable Lateral Raise", "Leaning Cable Lateral Raise"],
+ localizedLabels: {"en": "Lean-Away Cable Lateral Raise"},
+ sourceId: "28d18142-8777-4dba-a3a1-6151cec94500",
+ sourceUpdatedAt: "2026-09-14T23:25:37.627892+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "trs1800",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3bc8b411-a28d-4e1c-a6d1-769e18fe9881",
+ "Leg Curls (laying)",
+ ["Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Beinbeuger Liegend",
+ "en": "Leg Curls (laying)",
+ "es": "Curl de piernas (tumbado)",
+ "fr": "Leg curl (allongé)",
+ },
+ sourceId: "3bc8b411-a28d-4e1c-a6d1-769e18fe9881",
+ sourceUpdatedAt: "2026-06-19T18:56:57.741764+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:440a5184-de58-4a86-a7ba-76ddeafaa855",
+ "Leg Curls (sitting)",
+ ["Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "en": "Leg Curls (sitting)",
+ "es": "Curl femoral sentado",
+ },
+ sourceId: "440a5184-de58-4a86-a7ba-76ddeafaa855",
+ sourceUpdatedAt: "2026-04-15T22:23:53.335983+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:49189e88-40b6-4c0d-a784-0cc3abbb8e75",
+ "Leg Curls (standing)",
+ ["Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"de": "Beinbeuger Stehend", "en": "Leg Curls (standing)"},
+ sourceId: "49189e88-40b6-4c0d-a784-0cc3abbb8e75",
+ sourceUpdatedAt: "2026-04-15T22:23:52.818788+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:62170477-90ec-463c-907e-9e523abc0a15",
+ "Leg Extension",
+ ["Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Beinstrecker (Leg Extension)",
+ "en": "Leg Extension",
+ "es": "Curl cuadriceps",
+ "fr": "Extension des jambes (leg extension)",
+ },
+ sourceId: "62170477-90ec-463c-907e-9e523abc0a15",
+ sourceUpdatedAt: "2026-06-19T18:50:33.557969+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "BFad07",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5f0b757c-0d6a-430b-9d5e-3bfba202878e",
+ "Leg Presses (narrow)",
+ ["Quads"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Beinpresse Eng",
+ "en": "Leg Presses (narrow)",
+ "es": "Prensa de piernas cerrada",
+ "fr": "Presse à cuisses (serrée)",
+ },
+ sourceId: "5f0b757c-0d6a-430b-9d5e-3bfba202878e",
+ sourceUpdatedAt: "2026-06-19T18:46:48.718551+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:661287d4-d1dc-485c-896b-73f90b000536",
+ "Leg Presses (wide)",
+ ["Quads"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Beinpresse (weit)",
+ "en": "Leg Presses (wide)",
+ "es": "Press de piernas abierto",
+ "fr": "Presse à cuisses (large)",
+ },
+ sourceId: "661287d4-d1dc-485c-896b-73f90b000536",
+ sourceUpdatedAt: "2026-06-19T18:57:08.953376+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fd595232-b9ad-4216-bba1-92298f993ea9",
+ "Leverage Machine Iso Row",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Leverage Machine Iso Row"},
+ sourceId: "fd595232-b9ad-4216-bba1-92298f993ea9",
+ sourceUpdatedAt: "2026-04-15T22:23:54.597126+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:183e7279-f052-4c70-8381-bbbda3bb9afe",
+ "Low Box Squat - Wide Stance",
+ ["Glutes", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Box Squat mit weitem Stand",
+ "en": "Low Box Squat - Wide Stance",
+ "es": "Sentadilla a cajón bajo con stance ancho",
+ "fr": "Box squat bas - position large",
+ },
+ sourceId: "183e7279-f052-4c70-8381-bbbda3bb9afe",
+ sourceUpdatedAt: "2026-06-19T18:57:08.264812+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "taylorbarbell",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8a356e9a-0058-4f60-b8d7-89147c3c371f",
+ "Lying Triceps Extensions",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Barbell", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Liegendes Trizepsstrecken",
+ "en": "Lying Triceps Extensions",
+ "es": "Extensiones de tríceps tumbado",
+ "fr": "Extensions des triceps allongé",
+ },
+ sourceId: "8a356e9a-0058-4f60-b8d7-89147c3c371f",
+ sourceUpdatedAt: "2026-06-19T18:53:09.218593+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Moffi",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:1e827ef8-0a01-4b26-9e26-eff9d200acca",
+ "Machine chest fly",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "en": "Machine chest fly",
+ "es": "Aperturas en Máquina para Pecho",
+ },
+ sourceId: "1e827ef8-0a01-4b26-9e26-eff9d200acca",
+ sourceUpdatedAt: "2026-05-27T01:00:07.405291+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "novadani",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fc9d4980-c6a1-4786-b982-7ff56ca773f9",
+ "Machine Chest Press",
+ ["Chest", "Triceps"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Machine Chest Press"},
+ sourceId: "fc9d4980-c6a1-4786-b982-7ff56ca773f9",
+ sourceUpdatedAt: "2026-09-16T01:47:35.152189+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b1eed626-112f-401c-a61d-bfe168fdedc7",
+ "Machine Dip or Triceps Extension",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Machine Dip or Triceps Extension"},
+ sourceId: "b1eed626-112f-401c-a61d-bfe168fdedc7",
+ sourceUpdatedAt: "2026-09-16T01:47:55.925079+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b5726562-82ed-451a-89d8-8c8b13c5019a",
+ "Machine Hip Abduction",
+ ["Glutes"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Hüftabduktion an der Maschine",
+ "en": "Machine Hip Abduction",
+ "es": "Abducción en máquina",
+ "fr": "Abduction des hanches à la machine",
+ },
+ sourceId: "b5726562-82ed-451a-89d8-8c8b13c5019a",
+ sourceUpdatedAt: "2026-06-19T18:55:12.873557+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fe0c2a17-fad9-4235-8786-84bb1de93530",
+ "Machine Incline Chest Press",
+ ["Chest", "Shoulders"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Machine Incline Chest Press"},
+ sourceId: "fe0c2a17-fad9-4235-8786-84bb1de93530",
+ sourceUpdatedAt: "2026-09-16T01:47:48.593077+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e6bf0364-ccce-4053-afe1-eef206a50291",
+ "Machine Lat Pulldown",
+ ["Biceps", "Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Machine Lat Pulldown"},
+ sourceId: "e6bf0364-ccce-4053-afe1-eef206a50291",
+ sourceUpdatedAt: "2026-09-16T01:47:45.154977+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3d2adf99-8fe9-45cb-b669-c5b18242e282",
+ "Machine Lateral Raise",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Gym mat"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Seitheben an der Maschine",
+ "en": "Machine Lateral Raise",
+ "es": "Elevación lateral en máquina",
+ "fr": "Élévation latérale à la machine",
+ },
+ sourceId: "3d2adf99-8fe9-45cb-b669-c5b18242e282",
+ sourceUpdatedAt: "2026-06-19T19:55:05.772016+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "roneydya",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8aae635b-cb15-4129-bc09-6951fa376274",
+ "Machine Seated Calf Raise",
+ ["Calves", "Soleus"],
+ secondaryMuscles: [],
+ category: "Calves",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Machine Seated Calf Raise"},
+ sourceId: "8aae635b-cb15-4129-bc09-6951fa376274",
+ sourceUpdatedAt: "2026-09-16T01:47:37.692348+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:28b65190-cae2-4d15-8f4e-dde0d9cc4d4b",
+ "Meadows Row",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: ["Abs", "Biceps"],
+ category: "Back",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Meadows Row",
+ "en": "Meadows Row",
+ "es": "Remo Meadows",
+ "fr": "Rowing Meadows",
+ },
+ sourceId: "28b65190-cae2-4d15-8f4e-dde0d9cc4d4b",
+ sourceUpdatedAt: "2026-06-19T18:51:33.034380+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "carlos3c",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:3cad7b92-a16b-45dc-a53e-d6a01a158f8e",
+ "Neutral Grip Lat Pulldown",
+ ["Lats"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "Neutral Grip Lat Pulldown"},
+ sourceId: "3cad7b92-a16b-45dc-a53e-d6a01a158f8e",
+ sourceUpdatedAt: "2026-07-23T12:36:36.193375+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5ed12527-c8be-4e86-8f2a-cf9dd4781f5b",
+ "One Arm Overhead Cable Tricep Extension",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einarmiges Trizepsstrecken über Kopf am Kabelzug",
+ "en": "One Arm Overhead Cable Tricep Extension",
+ "es": "Extensión de tríceps sobre la cabeza a una mano en polea",
+ "fr": "Extension Triceps Poulie Basse à Un Bras",
+ },
+ sourceId: "5ed12527-c8be-4e86-8f2a-cf9dd4781f5b",
+ sourceUpdatedAt: "2026-07-23T12:36:41.047712+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:c7058ff8-ca3b-4822-abdb-6341ce37c4d1",
+ "Overhand Cable Curl",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "Overhand Cable Curl"},
+ sourceId: "c7058ff8-ca3b-4822-abdb-6341ce37c4d1",
+ sourceUpdatedAt: "2026-07-23T12:36:18.078077+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "ThisGirl0819",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f2e17c8d-7285-4fd5-bfe8-26f407802850",
+ "Overhead Cable Tricep Extension",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsdrücken über Kopf am Kabelzug",
+ "en": "Overhead Cable Tricep Extension",
+ "es": "Extensión de tríceps sobre la cabeza en polea",
+ "fr": "Extension des triceps à la poulie au-dessus de la tête",
+ },
+ sourceId: "f2e17c8d-7285-4fd5-bfe8-26f407802850",
+ sourceUpdatedAt: "2026-07-23T12:36:36.598356+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Expenses7000",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:e56f2970-e4c9-45eb-8e8c-52abb590e7a6",
+ "Pallof Press",
+ [
+ "Abs",
+ "Glutes",
+ "Obliquus externus abdominis",
+ "Serratus anterior",
+ "Shoulders",
+ "Trapezius",
+ ],
+ secondaryMuscles: ["Chest", "Triceps"],
+ category: "Abs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Pallof Press",
+ "en": "Pallof Press",
+ "es": "Pallof Press",
+ "fr": "Pallof press",
+ },
+ sourceId: "e56f2970-e4c9-45eb-8e8c-52abb590e7a6",
+ sourceUpdatedAt: "2026-08-27T18:13:05.027882+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "prevail90",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6441ff9e-037e-48d9-8800-67b430dc8e37",
+ "Pause Bench",
+ ["Chest"],
+ secondaryMuscles: ["Triceps"],
+ category: "Chest",
+ equipment: ["Barbell", "Bench"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Pause Bench",
+ "en": "Pause Bench",
+ "es": "Press de banca con pausa",
+ "fr": "Développé couché avec pause",
+ },
+ sourceId: "6441ff9e-037e-48d9-8800-67b430dc8e37",
+ sourceUpdatedAt: "2026-09-18T13:06:07.622620+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "Mens Fitness",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:a4c97a23-f044-4095-a041-94d92ffe44bd",
+ "Paused Squats",
+ ["Quads"],
+ secondaryMuscles: ["Glutes", "Hamstrings"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"de": "Pausiertes Kniebeugen", "en": "Paused Squats"},
+ sourceId: "a4c97a23-f044-4095-a041-94d92ffe44bd",
+ sourceUpdatedAt: "2026-09-18T13:01:26.457048+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "bbk",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:748e9635-7958-4a41-b1ed-93de74c7ef72",
+ "Pendelay Rows",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: ["Biceps", "Triceps"],
+ category: "Back",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Pendelay Rows",
+ "en": "Pendelay Rows",
+ "es": "Remo Pendlay",
+ "fr": "Rowing Pendlay",
+ },
+ sourceId: "748e9635-7958-4a41-b1ed-93de74c7ef72",
+ sourceUpdatedAt: "2026-06-19T18:57:09.652116+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "Nallitnas",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:abd2854b-114a-4584-8746-30bd40127863",
+ "Pin Bench Press BB",
+ ["Chest", "Triceps"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Pin Bench Press LH",
+ "en": "Pin Bench Press BB",
+ "es": "Press de banca con pines (barra)",
+ "fr": "Développé couché aux barres d'arrêt (barre)",
+ },
+ sourceId: "abd2854b-114a-4584-8746-30bd40127863",
+ sourceUpdatedAt: "2026-06-19T18:52:38.483182+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "M9",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:96f9d74a-7bf4-4ec1-9cd6-c62c13cf7b67",
+ "Pin Squat",
+ ["Glutes", "Hamstrings", "Quads"],
+ secondaryMuscles: ["Abs"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Pin Squat",
+ "en": "Pin Squat",
+ "es": "Sentadilla con pines",
+ "fr": "Squat aux barres d'arrêt",
+ },
+ sourceId: "96f9d74a-7bf4-4ec1-9cd6-c62c13cf7b67",
+ sourceUpdatedAt: "2026-06-19T18:52:39.822376+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "M9",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5d5122e0-65f3-4afc-ac36-21955e2c7dde",
+ "Plate-Loaded Lat Pulldown",
+ ["Lats"],
+ secondaryMuscles: ["Biceps", "Brachialis", "Trapezius"],
+ category: "Back",
+ equipment: [],
+ aliases: ["Lever Lat Pulldown", "Plate Loaded Lat Pulldown"],
+ localizedLabels: {"en": "Plate-Loaded Lat Pulldown"},
+ sourceId: "5d5122e0-65f3-4afc-ac36-21955e2c7dde",
+ sourceUpdatedAt: "2026-09-14T23:25:37.108370+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "trs1800",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:52dec48d-25a4-4a78-b66b-ad6a773e143a",
+ "Power Clean",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"de": "Umsetzen", "en": "Power Clean"},
+ sourceId: "52dec48d-25a4-4a78-b66b-ad6a773e143a",
+ sourceUpdatedAt: "2026-04-15T22:23:56.195471+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:7eb52939-0e33-4319-829d-d8e142fc74d9",
+ "Preacher Curl Machine",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Preacher Curl Machine"},
+ sourceId: "7eb52939-0e33-4319-829d-d8e142fc74d9",
+ sourceUpdatedAt: "2026-09-16T01:47:51.467713+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:dd52fb99-9426-4a78-b446-20a8e3e4ec47",
+ "Preacher Curls",
+ ["Brachialis"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["SZ-Bar"],
+ aliases: ["Curl predicador", "Preacher Curls", "Scott Curls"],
+ localizedLabels: {
+ "de": "KH an Scottmaschine",
+ "en": "Preacher Curls",
+ "es": "Curl en banco Scott",
+ "fr": "Curls au pupitre",
+ },
+ sourceId: "dd52fb99-9426-4a78-b446-20a8e3e4ec47",
+ sourceUpdatedAt: "2026-07-23T12:08:30.792885+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "cgoob883",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:7f834f07-fa7b-46b6-8ffa-45930b0602db",
+ "Pull Ups on Machine",
+ ["Lats"],
+ secondaryMuscles: ["Biceps", "Shoulders", "Trapezius"],
+ category: "Back",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Klimmzüge an Maschine",
+ "en": "Pull Ups on Machine",
+ },
+ sourceId: "7f834f07-fa7b-46b6-8ffa-45930b0602db",
+ sourceUpdatedAt: "2026-04-15T22:23:55.167004+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9ccd53e0-8392-4e61-91aa-e4e5f4ea339c",
+ "Pullover",
+ ["Chest"],
+ secondaryMuscles: ["Lats"],
+ category: "Back",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"en": "Pullover"},
+ step: 2,
+ sourceId: "9ccd53e0-8392-4e61-91aa-e4e5f4ea339c",
+ sourceUpdatedAt: "2026-04-15T22:23:55.195453+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "3leh",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:ad20ef5e-e360-4859-9110-668ee0c24721",
+ "Pullover Machine",
+ ["Lats"],
+ secondaryMuscles: ["Chest", "Triceps"],
+ category: "Back",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Pullover Machine"},
+ sourceId: "ad20ef5e-e360-4859-9110-668ee0c24721",
+ sourceUpdatedAt: "2026-04-15T22:23:55.196893+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Paul@Chemistry",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2b6c09f7-dbf1-45ea-baf6-9a12e0b12396",
+ "Renegade Row",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Renegade Row",
+ "en": "Renegade Row",
+ "es": "Remo renegado",
+ "fr": "Renegade row",
+ },
+ step: 2,
+ sourceId: "2b6c09f7-dbf1-45ea-baf6-9a12e0b12396",
+ sourceUpdatedAt: "2026-06-19T18:57:17.325523+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "fletchgraham",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2cd5e3c6-a8c0-456a-ab47-5e7b3a435407",
+ "Reverse Curl",
+ ["Biceps", "Brachialis"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Barbell", "Dumbbell"],
+ aliases: ["Curl inversé"],
+ localizedLabels: {
+ "de": "Reverse Curls",
+ "en": "Reverse Curl",
+ "es": "Curl invertido",
+ "fr": "Curl en pronation (reverse curl)",
+ },
+ sourceId: "2cd5e3c6-a8c0-456a-ab47-5e7b3a435407",
+ sourceUpdatedAt: "2026-07-23T12:08:31.197212+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "BFad07",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:1b95d961-1d5c-4ded-b25e-c090295ffe37",
+ "Reverse Grip Bench Press",
+ ["Chest", "Triceps"],
+ secondaryMuscles: ["Shoulders"],
+ category: "Chest",
+ equipment: ["Barbell", "Bench"],
+ aliases: [],
+ localizedLabels: {"en": "Reverse Grip Bench Press"},
+ sourceId: "1b95d961-1d5c-4ded-b25e-c090295ffe37",
+ sourceUpdatedAt: "2026-04-15T22:23:55.328255+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "bl0sh",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:edc44b1a-35d7-4c37-ab20-05842fb40576",
+ "Reverse Preacher Curl (Close Grip)",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["SZ-Bar"],
+ aliases: [],
+ localizedLabels: {
+ "en": "Reverse Preacher Curl (Close Grip)",
+ "es": "Curl de Predicador Inverso",
+ },
+ sourceId: "edc44b1a-35d7-4c37-ab20-05842fb40576",
+ sourceUpdatedAt: "2026-04-15T22:23:55.337477+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "novadani",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:849f2fa6-42f9-4c59-90f6-275c8ada6725",
+ "Rotary Torso Machine",
+ ["Abs"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"de": "Bauch-Twist Gerät", "en": "Rotary Torso Machine"},
+ sourceId: "849f2fa6-42f9-4c59-90f6-275c8ada6725",
+ sourceUpdatedAt: "2026-04-15T22:23:55.387348+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "6LXBO",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:139772c6-e401-4281-8aa6-c7ff7fb49f6a",
+ "Seated Cable chest fly",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Butterfly am Kabelzug",
+ "en": "Seated Cable chest fly",
+ "es": "Aperturas de pecho en polea sentado",
+ "fr": "Écarté à la poulie assis (pectoraux)",
+ },
+ sourceId: "139772c6-e401-4281-8aa6-c7ff7fb49f6a",
+ sourceUpdatedAt: "2026-07-23T12:36:51.644035+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "shushu",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:0ecfec6f-5d0f-4e8a-8d37-f6203a8923b8",
+ "Seated Cable Row",
+ ["Lats"],
+ secondaryMuscles: ["Biceps", "Trapezius"],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Sitzendes Rudern am Kabelzug",
+ "en": "Seated Cable Row",
+ "es": "Remo gironda agarre cerrado",
+ "fr": "Rowing assis à la poulie",
+ },
+ sourceId: "0ecfec6f-5d0f-4e8a-8d37-f6203a8923b8",
+ sourceUpdatedAt: "2026-07-23T12:36:25.087001+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Franpol",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6995c30a-037f-4de3-ac4a-45384a1f38f9",
+ "Seated Dumbbell Calf Raise",
+ ["Calves"],
+ secondaryMuscles: [],
+ category: "Calves",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Sitzendes Wadenheben mit Kurzhantel",
+ "en": "Seated Dumbbell Calf Raise",
+ "es": "Elevación de gemelos sentado con mancuerna",
+ "fr": "Élévation des mollets haltères assis",
+ },
+ step: 2,
+ sourceId: "6995c30a-037f-4de3-ac4a-45384a1f38f9",
+ sourceUpdatedAt: "2026-06-19T18:54:53.981469+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:19fba43a-8363-4da3-82c6-d8a14628b5e7",
+ "Seated Dumbbell Curls",
+ ["Biceps"],
+ secondaryMuscles: ["Brachialis"],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Sitzende Kurzhantel-Curls",
+ "en": "Seated Dumbbell Curls",
+ "es": "Curl con mancuernas sentado",
+ "fr": "Curls avec haltères assis",
+ },
+ step: 2,
+ sourceId: "19fba43a-8363-4da3-82c6-d8a14628b5e7",
+ sourceUpdatedAt: "2026-06-19T18:51:20.831236+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "sTiKyt",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9bcc7b4f-4172-4db7-be95-d7b659c486f5",
+ "Seated Row (Machine)",
+ ["Lats"],
+ secondaryMuscles: ["Shoulders", "Trapezius"],
+ category: "Back",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Rudern im Sitzen (Maschine)",
+ "en": "Seated Row (Machine)",
+ "es": "Remo sentado (máquina)",
+ "fr": "Rowing assis (machine)",
+ },
+ sourceId: "9bcc7b4f-4172-4db7-be95-d7b659c486f5",
+ sourceUpdatedAt: "2026-06-19T18:54:46.441666+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "barry",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:cc5bf1a3-29b5-4c5a-8600-5e35c79ab4bf",
+ "Seated Triceps Press",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsdrücken im Sitzen",
+ "en": "Seated Triceps Press",
+ "es": "Press de tríceps sentado",
+ "fr": "Extension des triceps assis",
+ },
+ step: 2,
+ sourceId: "cc5bf1a3-29b5-4c5a-8600-5e35c79ab4bf",
+ sourceUpdatedAt: "2026-06-19T18:57:18.052455+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "richmr2174@gmail.com",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:87affa4b-395b-437c-9581-2bd20ea5aa7c",
+ "Shoulder Press, Dumbbells",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [
+ "Military Press KH",
+ "Military Press with Dumbbells",
+ "OHP Dumbbells",
+ "Overhead press Dumbbells",
+ ],
+ localizedLabels: {
+ "de": "Schulterdrücken KH",
+ "en": "Shoulder Press, Dumbbells",
+ "es": "Press Militar mancuerna",
+ "fr": "Développé épaules, haltères",
+ },
+ step: 2,
+ sourceId: "87affa4b-395b-437c-9581-2bd20ea5aa7c",
+ sourceUpdatedAt: "2026-06-19T18:46:58.504753+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5912d7ed-6a0e-4b4c-b30a-fc9f3f890fc1",
+ "Shoulder Press, on Machine",
+ ["Shoulders"],
+ secondaryMuscles: ["Triceps"],
+ category: "Shoulders",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Schultermaschine",
+ "en": "Shoulder Press, on Machine",
+ "es": "Press de hombro con maquina",
+ },
+ sourceId: "5912d7ed-6a0e-4b4c-b30a-fc9f3f890fc1",
+ sourceUpdatedAt: "2026-04-15T22:23:55.116420+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:141bc870-56be-4749-a3b9-e56d5d5618b4",
+ "Shoulder Press, on Multi Press",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: [],
+ aliases: ["Military Press MP", "Schulterdrücken MP"],
+ localizedLabels: {
+ "de": "Frontdrücken MP",
+ "en": "Shoulder Press, on Multi Press",
+ "es": "Press de hombros en multipower",
+ "fr": "Développé épaules à la multipresse",
+ },
+ sourceId: "141bc870-56be-4749-a3b9-e56d5d5618b4",
+ sourceUpdatedAt: "2026-06-19T18:57:22.262251+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6199b3c7-3ccb-47fa-89bb-ef7fef12c0e3",
+ "Shrugs on Multipress",
+ ["Shoulders"],
+ secondaryMuscles: ["Triceps"],
+ category: "Shoulders",
+ equipment: [],
+ aliases: ["Schulterheben MP"],
+ localizedLabels: {"de": "Shrugs an der MP", "en": "Shrugs on Multipress"},
+ sourceId: "6199b3c7-3ccb-47fa-89bb-ef7fef12c0e3",
+ sourceUpdatedAt: "2026-04-15T22:23:55.679163+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:270e108d-3cd2-45a9-807b-c357317eb15c",
+ "Shrugs, Barbells",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: ["Haussement d'épaules à la barre", "Schulterheben LH"],
+ localizedLabels: {
+ "de": "Shrugs LH",
+ "en": "Shrugs, Barbells",
+ "es": "Encogimientos de hombros con barra",
+ "fr": "Shrugs à la barre",
+ },
+ sourceId: "270e108d-3cd2-45a9-807b-c357317eb15c",
+ sourceUpdatedAt: "2026-06-19T18:57:27.795399+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:72a945ec-3a7f-424b-9a05-1616ef7dce91",
+ "Shrugs, Dumbbells",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: ["Haussement d'épaules aux haltères", "Schulterheben KH"],
+ localizedLabels: {
+ "de": "Shrugs KH",
+ "en": "Shrugs, Dumbbells",
+ "es": "Encogimientos de hombros con mancuernas",
+ "fr": "Shrugs aux haltères",
+ },
+ step: 2,
+ sourceId: "72a945ec-3a7f-424b-9a05-1616ef7dce91",
+ sourceUpdatedAt: "2026-06-19T18:57:28.480428+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:046fce45-69f2-46f7-a5b6-79a25a485af1",
+ "Single Leg Extension",
+ ["Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Beinstrecker Einbeinig",
+ "en": "Single Leg Extension",
+ "es": "Extensión de Cuádriceps a una Pierna",
+ },
+ sourceId: "046fce45-69f2-46f7-a5b6-79a25a485af1",
+ sourceUpdatedAt: "2026-04-15T22:23:53.834149+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9dfce8f0-39fc-4204-9f84-500ec42074e9",
+ "Single-arm dumbbell shoulder press",
+ ["Shoulders"],
+ secondaryMuscles: ["Abs", "Serratus anterior", "Trapezius", "Triceps"],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einarmiges Schulterdrücken mit Kurzhantel",
+ "en": "Single-arm dumbbell shoulder press",
+ "es": "Press de hombro con mancuerna a un brazo",
+ "fr": "Développé épaules unilatéral à l'haltère",
+ },
+ step: 2,
+ sourceId: "9dfce8f0-39fc-4204-9f84-500ec42074e9",
+ sourceUpdatedAt: "2026-06-19T18:48:21.547721+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "evtimovgeorg",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b86fd8df-e726-4998-a7ee-000de0f70bf0",
+ "Single-Arm Lat Pulldown",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: ["Biceps", "Brachialis"],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: ["Tirage vertical unilatéral"],
+ localizedLabels: {
+ "de": "Einarmiger Latzug",
+ "en": "Single-Arm Lat Pulldown",
+ "es": "Jalón al pecho a un brazo",
+ "fr": "Tirage vertical à un bras",
+ },
+ sourceId: "b86fd8df-e726-4998-a7ee-000de0f70bf0",
+ sourceUpdatedAt: "2026-07-23T12:36:53.693795+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "evtimovgeorg",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:c85e6137-2577-4e28-82c2-427407d534eb",
+ "Single-arm Preacher Curl",
+ ["Biceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einarmiger Scott-Curl",
+ "en": "Single-arm Preacher Curl",
+ "es": "Curl en banco Scott a un brazo",
+ "fr": "Curl au pupitre à un bras",
+ },
+ step: 2,
+ sourceId: "c85e6137-2577-4e28-82c2-427407d534eb",
+ sourceUpdatedAt: "2026-06-19T18:57:24.335413+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:a630de17-9170-434c-b486-96a6b8705506",
+ "Single-Leg Deadlift with Dumbbell",
+ ["Glutes", "Hamstrings"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Einbeiniges Kreuzheben mit Kurzhantel",
+ "en": "Single-Leg Deadlift with Dumbbell",
+ "es": "Peso muerto a una pierna con mancuerna",
+ "fr": "Soulevé de terre sur une jambe avec haltère",
+ },
+ step: 2,
+ sourceId: "a630de17-9170-434c-b486-96a6b8705506",
+ sourceUpdatedAt: "2026-06-19T18:55:12.191611+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:893c07ea-2e24-49b6-92e4-d0033eedec62",
+ "Skullcrusher Dumbbells",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Bench", "Dumbbell"],
+ aliases: ["French Press Dumbbells"],
+ localizedLabels: {
+ "de": "Frenchpress KH",
+ "en": "Skullcrusher Dumbbells",
+ "es": "Press Francés con Mancuernas",
+ "fr": "Barre au front aux haltères",
+ },
+ step: 2,
+ sourceId: "893c07ea-2e24-49b6-92e4-d0033eedec62",
+ sourceUpdatedAt: "2026-06-19T18:46:39.770835+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:95a7e546-e8f8-4521-a76b-983d94161b25",
+ "Skullcrusher SZ-bar",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Bench", "SZ-Bar"],
+ aliases: ["French Press SZ-bar"],
+ localizedLabels: {
+ "de": "Frenchpress SZ-Stange",
+ "en": "Skullcrusher SZ-bar",
+ "es": "Press Francés con Barra SZ",
+ "fr": "Barre au front à la barre EZ",
+ },
+ sourceId: "95a7e546-e8f8-4521-a76b-983d94161b25",
+ sourceUpdatedAt: "2026-06-19T18:46:40.278298+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:092d0fbb-0409-470e-a087-beb9a378f3f7",
+ "Smith Machine Close-grip Bench Press",
+ ["Triceps"],
+ secondaryMuscles: ["Chest"],
+ category: "Arms",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Enges Bankdrücken an der Multipresse",
+ "en": "Smith Machine Close-grip Bench Press",
+ "es": "Press de banca con agarre cerrado en multipower",
+ "fr": "Développé couché prise serrée à la machine Smith",
+ },
+ sourceId: "092d0fbb-0409-470e-a087-beb9a378f3f7",
+ sourceUpdatedAt: "2026-06-19T18:57:27.089692+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:ef2df6a2-41df-4a25-b2e0-4808dc2c3305",
+ "Smith Machine Slight Incline Press",
+ ["Chest"],
+ secondaryMuscles: [],
+ category: "Chest",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Schrägbankdrücken an der Smith-Maschine",
+ "en": "Smith Machine Slight Incline Press",
+ "es": "Press Inclinado Ligero en Máquina Smith",
+ "fr": "Développé incliné à la Smith machine",
+ },
+ sourceId: "ef2df6a2-41df-4a25-b2e0-4808dc2c3305",
+ sourceUpdatedAt: "2026-07-02T19:51:01.682867+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "novadani",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wgerjhn",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:b382dcb2-60a8-412d-b0ee-6afe9c9f36d7",
+ "Smith Machine Split Squat",
+ ["Hamstrings", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Split-Kniebeuge an der Smith-Maschine",
+ "en": "Smith Machine Split Squat",
+ "es": "Sentadilla split en máquina Smith",
+ "fr": "Split squat à la Smith machine",
+ },
+ sourceId: "b382dcb2-60a8-412d-b0ee-6afe9c9f36d7",
+ sourceUpdatedAt: "2026-06-19T18:54:31.067024+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "workout@rooven.anonaddy.me",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f3ef220b-a070-449c-a463-876d30cd9b14",
+ "Smith machine squat",
+ ["Glutes", "Quads"],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kniebeuge in der Multipresse",
+ "en": "Smith machine squat",
+ "es": "Sentadilla multipower",
+ "fr": "Squat à la machine Smith",
+ },
+ sourceId: "f3ef220b-a070-449c-a463-876d30cd9b14",
+ sourceUpdatedAt: "2026-06-19T18:55:08.100396+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Tierrasverdes",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8161de8b-7802-46c0-9bd4-db60f39b7677",
+ "Snatch",
+ ["Shoulders"],
+ secondaryMuscles: [],
+ category: "Shoulders",
+ equipment: ["Barbell"],
+ aliases: ["Snatch"],
+ localizedLabels: {
+ "de": "Reißen (Snatch)",
+ "en": "Snatch",
+ "es": "Arrancada (Snatch)",
+ "fr": "Arraché",
+ },
+ sourceId: "8161de8b-7802-46c0-9bd4-db60f39b7677",
+ sourceUpdatedAt: "2026-06-19T19:55:04.717633+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "Mens Fitness",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:93b9b84c-7b72-4712-a7c2-0e14a5fab32a",
+ "Standing Adduction (Cable)",
+ [],
+ secondaryMuscles: [],
+ category: "Legs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Adduktion im Stehen (Kabelzug)",
+ "en": "Standing Adduction (Cable)",
+ "es": "Aducción de pie (polea)",
+ "fr": "Adduction debout (poulie)",
+ },
+ sourceId: "93b9b84c-7b72-4712-a7c2-0e14a5fab32a",
+ sourceUpdatedAt: "2026-07-23T12:36:44.385178+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "barry",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:22c59ede-970b-43e1-bb51-ac1c2be0b0e0",
+ "Stiff-legged Deadlifts",
+ ["Hamstrings"],
+ secondaryMuscles: ["Glutes"],
+ category: "Legs",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "Stiff-legged Deadlifts"},
+ sourceId: "22c59ede-970b-43e1-bb51-ac1c2be0b0e0",
+ sourceUpdatedAt: "2026-04-15T22:23:55.956144+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:0f5fc602-afb6-4500-87da-f115f3ef3f47",
+ "Straight-arm Pull Down (bar Attachment)",
+ ["Lats"],
+ secondaryMuscles: ["Triceps"],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Lat-Ziehen Mit Gesteckten Armen Mit (Stange)",
+ "en": "Straight-arm Pull Down (bar Attachment)",
+ "es": "Jalón con brazos rectos (agarre de barra)",
+ "fr": "Tirage bras tendus (poignée barre)",
+ },
+ sourceId: "0f5fc602-afb6-4500-87da-f115f3ef3f47",
+ sourceUpdatedAt: "2026-07-23T12:36:18.888132+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2f7149c3-77ce-4313-a59c-aef82b5a730a",
+ "Straight-arm Pull Down (rope Attachment)",
+ ["Lats"],
+ secondaryMuscles: ["Triceps"],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: ["Lat Pulldown am Seil", "Lat ziehen am Seil"],
+ localizedLabels: {
+ "de": "Lat-Zug am Seil",
+ "en": "Straight-arm Pull Down (rope Attachment)",
+ "es": "Jalón con brazos rectos (agarre de cuerda)",
+ "fr": "Tirage bras tendus (poignée corde)",
+ },
+ sourceId: "2f7149c3-77ce-4313-a59c-aef82b5a730a",
+ sourceUpdatedAt: "2026-07-23T12:36:19.283497+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:1eeccede-29c5-4f38-9ba7-d77c7c47993d",
+ "T-Bar row",
+ ["Lats", "Trapezius"],
+ secondaryMuscles: [],
+ category: "Back",
+ equipment: ["Barbell"],
+ aliases: [],
+ localizedLabels: {"en": "T-Bar row"},
+ sourceId: "1eeccede-29c5-4f38-9ba7-d77c7c47993d",
+ sourceUpdatedAt: "2026-04-15T22:23:56.035568+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "novadani",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5915fabe-c941-4dac-b196-bc4e8c7ce57b",
+ "Tricep Dumbbell Kickback",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Kick-Backs",
+ "en": "Tricep Dumbbell Kickback",
+ "es": "Contragolpe de tríceps con mancuernas",
+ "fr": "Extension des avant-bras avec haltère",
+ },
+ step: 2,
+ sourceId: "5915fabe-c941-4dac-b196-bc4e8c7ce57b",
+ sourceUpdatedAt: "2026-04-15T22:23:56.145134+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "http://www.bodybuilding.com/",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Garion",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:fc608f0e-d754-4911-ae59-8856952e7064",
+ "Tricep Rope Pushdowns",
+ ["Triceps"],
+ secondaryMuscles: ["Lats"],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsdrücken am Seil",
+ "en": "Tricep Rope Pushdowns",
+ "es": "Extensiones de tríceps con cuerda en polea",
+ "fr": "Extensions triceps à la corde",
+ },
+ sourceId: "fc608f0e-d754-4911-ae59-8856952e7064",
+ sourceUpdatedAt: "2026-07-23T12:36:50.854048+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "nishant0712",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:6c671e43-497f-4863-8d78-b592f3b5e7c6",
+ "Triceps on Machine",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsmaschine",
+ "en": "Triceps on Machine",
+ "es": "Tríceps en máquina",
+ "fr": "Triceps à la machine",
+ },
+ sourceId: "6c671e43-497f-4863-8d78-b592f3b5e7c6",
+ sourceUpdatedAt: "2026-06-19T18:49:11.762462+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:19133a0d-e6b2-4d57-8a84-8aac3d962a99",
+ "Triceps Overhead (Dumbbell)",
+ ["Triceps"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Trizepsdrücken über Kopf (Kurzhantel)",
+ "en": "Triceps Overhead (Dumbbell)",
+ "es": "Extensión de tríceps sobre la cabeza (mancuerna)",
+ "fr": "Extension des triceps au-dessus de la tête (haltère)",
+ },
+ step: 2,
+ sourceId: "19133a0d-e6b2-4d57-8a84-8aac3d962a99",
+ sourceUpdatedAt: "2026-06-19T19:55:24.212769+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Anastasious",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:90e9f590-7d68-4afb-9f95-8429246ea4aa",
+ "Trunk Rotation With Cable",
+ ["Obliquus externus abdominis"],
+ secondaryMuscles: ["Lats"],
+ category: "Abs",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {"en": "Trunk Rotation With Cable"},
+ sourceId: "90e9f590-7d68-4afb-9f95-8429246ea4aa",
+ sourceUpdatedAt: "2026-07-23T12:36:20.551535+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "Robertcoop",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:309bfd2b-1af4-49db-b64b-d7d7c7dd39bb",
+ "Turkish Get-Up",
+ ["Glutes", "Obliquus externus abdominis", "Serratus anterior", "Shoulders"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {"de": "Turkish Get-Up", "en": "Turkish Get-Up"},
+ step: 2,
+ sourceId: "309bfd2b-1af4-49db-b64b-d7d7c7dd39bb",
+ sourceUpdatedAt: "2026-07-23T12:08:31.974627+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "dookie1481",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:01976085-6701-45ea-b152-5c46ba60550d",
+ "Upright Row w/ Dumbbells",
+ ["Shoulders", "Trapezius"],
+ secondaryMuscles: ["Biceps"],
+ category: "Shoulders",
+ equipment: ["Dumbbell"],
+ aliases: ["Remo vertical"],
+ localizedLabels: {
+ "de": "Aufrechtes Rudern mit Kurzhanteln",
+ "en": "Upright Row w/ Dumbbells",
+ "es": "Remo al mentón con mancuernas",
+ "fr": "Tirage menton avec haltères",
+ },
+ step: 2,
+ sourceId: "01976085-6701-45ea-b152-5c46ba60550d",
+ sourceUpdatedAt: "2026-06-19T18:57:37.753780+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "koreyhinton",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:738137c0-5387-4215-b457-ea7af113b3ba",
+ "Upright Row, on Multi Press",
+ ["Shoulders", "Trapezius"],
+ secondaryMuscles: ["Biceps"],
+ category: "Shoulders",
+ equipment: [],
+ aliases: ["Remo vertical", "Rowing menton"],
+ localizedLabels: {
+ "de": "Rudern Aufrecht MP",
+ "en": "Upright Row, on Multi Press",
+ "es": "Remo al mentón en multipower",
+ "fr": "Tirage menton, à la Smith machine",
+ },
+ sourceId: "738137c0-5387-4215-b457-ea7af113b3ba",
+ sourceUpdatedAt: "2026-06-19T18:57:38.427616+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:5d40c67d-be59-4092-9c9c-301ca5310e2b",
+ "Upright Row, SZ-bar",
+ ["Shoulders", "Trapezius"],
+ secondaryMuscles: ["Biceps"],
+ category: "Shoulders",
+ equipment: ["SZ-Bar"],
+ aliases: ["Rowing menton"],
+ localizedLabels: {
+ "de": "Rudern Aufrecht ß-Stange",
+ "en": "Upright Row, SZ-bar",
+ "es": "Remo al Menton",
+ "fr": "Tirage menton, barre EZ",
+ },
+ sourceId: "5d40c67d-be59-4092-9c9c-301ca5310e2b",
+ sourceUpdatedAt: "2026-06-19T18:47:06.353786+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "wger.de",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "admin",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:8d333b94-148c-48f8-9154-8b13279500d1",
+ "Weighted Crunch",
+ ["Obliquus externus abdominis"],
+ secondaryMuscles: [],
+ category: "Abs",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Crunch mit Zusatzgewicht",
+ "en": "Weighted Crunch",
+ "es": "Crunch con peso",
+ "fr": "Crunch lesté",
+ },
+ step: 2,
+ sourceId: "8d333b94-148c-48f8-9154-8b13279500d1",
+ sourceUpdatedAt: "2026-06-19T18:49:34.206768+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "AlucardEvil40",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:37abbe32-2fab-4818-8355-fffcff2dc3e3",
+ "Weighted push-ups",
+ ["Chest"],
+ secondaryMuscles: ["Triceps"],
+ category: "Chest",
+ equipment: ["none (bodyweight exercise)"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Liegestütze mit Zusatzgewicht",
+ "en": "Weighted push-ups",
+ "es": "Flexiones con peso",
+ "fr": "Pompes lestées",
+ },
+ sourceId: "37abbe32-2fab-4818-8355-fffcff2dc3e3",
+ sourceUpdatedAt: "2026-06-19T18:56:21.839768+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "nishant0712",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:591992cb-48ee-4b39-b790-e05b4a2c11e3",
+ "Weighted Step-ups",
+ ["Glutes", "Quads"],
+ secondaryMuscles: ["Calves", "Hamstrings"],
+ category: "Legs",
+ equipment: [],
+ aliases: [],
+ localizedLabels: {"en": "Weighted Step-ups"},
+ sourceId: "591992cb-48ee-4b39-b790-e05b4a2c11e3",
+ sourceUpdatedAt: "2026-07-23T12:24:59.911650+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC0",
+ "http://creativecommons.org/publicdomain/zero/1.0/",
+ "jigglychipmunk",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:55b9d286-c4cc-4a29-97ad-58cb13c2bb7e",
+ "Wide-grip Pulldown",
+ ["Lats"],
+ secondaryMuscles: [
+ "Brachialis",
+ "Serratus anterior",
+ "Shoulders",
+ "Trapezius",
+ ],
+ category: "Back",
+ equipment: ["Cable machine"],
+ aliases: [
+ "Jalón abierto",
+ "Lat Pulldown (Wide Grip)",
+ "Lat Pulldown Breit",
+ "Lat ziehen breit",
+ ],
+ localizedLabels: {
+ "de": "Latzug Breit",
+ "en": "Wide-grip Pulldown",
+ "es": "Jalón con agarre ancho",
+ "fr": "Tirage vertical en prise large",
+ },
+ sourceId: "55b9d286-c4cc-4a29-97ad-58cb13c2bb7e",
+ sourceUpdatedAt: "2026-07-23T12:36:21.397419+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 3",
+ "https://creativecommons.org/licenses/by-sa/3.0/deed.en",
+ "tuckerm",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:f6471f4a-0290-4a71-9580-bf3d046fdb9d",
+ "Wrist curl, cable",
+ [],
+ secondaryMuscles: ["Biceps"],
+ category: "Arms",
+ equipment: ["Cable machine"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Handgelenk-Curl am Kabelzug",
+ "en": "Wrist curl, cable",
+ "es": "Curl de muñeca en polea",
+ "fr": "Flexion des poignets à la poulie",
+ },
+ sourceId: "f6471f4a-0290-4a71-9580-bf3d046fdb9d",
+ sourceUpdatedAt: "2026-07-23T12:36:49.278870+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "eufvksruh",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:2cc92db8-b245-44b8-b1d6-bf91c78c2ab9",
+ "Wrist curl, dumbbells",
+ [],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Handgelenk-Curl mit Kurzhanteln",
+ "en": "Wrist curl, dumbbells",
+ "es": "Curl de muñeca con mancuernas",
+ "fr": "Curl des poignets, haltères",
+ },
+ step: 2,
+ sourceId: "2cc92db8-b245-44b8-b1d6-bf91c78c2ab9",
+ sourceUpdatedAt: "2026-06-19T18:50:20.629462+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wakanda90",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+ ExerciseDef(
+ "wger:9564ab2d-2534-4b47-bdf7-f2f653aae3de",
+ "Zottman curl",
+ ["Biceps", "Brachialis"],
+ secondaryMuscles: [],
+ category: "Arms",
+ equipment: ["Dumbbell"],
+ aliases: [],
+ localizedLabels: {
+ "de": "Zottman-Curl",
+ "en": "Zottman curl",
+ "es": "Curl Zottman",
+ "fr": "Curl Zottman",
+ },
+ step: 2,
+ sourceId: "9564ab2d-2534-4b47-bdf7-f2f653aae3de",
+ sourceUpdatedAt: "2026-06-19T18:52:03.524017+02:00",
+ sourceCredits: [
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "meldun",
+ ),
+ ExerciseCredit(
+ "CC-BY-SA 4",
+ "https://creativecommons.org/licenses/by-sa/4.0/deed.en",
+ "wger.de",
+ ),
+ ],
+ ),
+];
diff --git a/lib/ui2/screens/workout_screen.dart b/lib/ui2/screens/workout_screen.dart
index d0013a836..10dd8bd11 100644
--- a/lib/ui2/screens/workout_screen.dart
+++ b/lib/ui2/screens/workout_screen.dart
@@ -1366,22 +1366,26 @@ Future _finishSession(
return draft;
}
-/// Previous and best per lift, from this user's own log. One indexed query
-/// per exercise, fired in parallel.
+/// Previous and best per lift, from this user's own log.
Future> loadSetHistory() async {
final history = {};
try {
- await Future.wait([
- for (final e in exerciseLibrary)
- LocalDb.recentSetsFor(e.key, limit: 40).then((rows) {
- if (rows.isEmpty) return;
- final sets = _logFrom(rows).sets;
- history[e.key] = SetHistory(
- previous: sets.first, // recentSetsFor orders newest first
- best: StrengthLog(sets).topSet,
- );
- }),
- ]);
+ final candidates = await LocalDb.strengthHistoryCandidates();
+ final previous = {};
+ final best = {};
+ for (final row in candidates.previous) {
+ final key = row['exercise_key'] as String?;
+ if (key == null || previous.containsKey(key)) continue;
+ previous[key] = _logFrom([row]).sets.single;
+ }
+ for (final row in candidates.best) {
+ final key = row['exercise_key'] as String?;
+ if (key == null || best.containsKey(key)) continue;
+ best[key] = _logFrom([row]).sets.single;
+ }
+ for (final key in {...previous.keys, ...best.keys}) {
+ history[key] = SetHistory(previous: previous[key], best: best[key]);
+ }
} catch (_) {
// No history is the normal state on day one.
}
diff --git a/test/csv_export_test.dart b/test/csv_export_test.dart
index 1c1d035ec..af6d10864 100644
--- a/test/csv_export_test.dart
+++ b/test/csv_export_test.dart
@@ -337,5 +337,31 @@ void main() {
expect(failed.isEmpty, isTrue);
expect(failed.hasFailures, isTrue);
});
+
+ test('strength rows carry the exercise name, not just its key', () async {
+ final db = await LocalDb.instance;
+ for (final (seq, key) in [
+ (0, 'wger:eb9476ac-2c00-4f49-a40f-f81682161a75'),
+ (1, 'retired_lift'),
+ ]) {
+ await db.insert('strength_set', {
+ 'session_id': 's1',
+ 'seq': seq,
+ 'exercise_key': key,
+ 'set_index': 0,
+ 'reps': 8,
+ 'load_kg': 14.0,
+ 'at_ts': 1767225600 + seq,
+ });
+ }
+ final strength = kCsvExportSets.firstWhere((s) => s.name == 'strength');
+ final result = await exportCsvFiles([strength], now: DateTime(2026, 9, 3));
+ final lines =
+ (await File(result.paths.single).readAsString()).trim().split('\n');
+ expect(lines.first, startsWith('at_ts,session_id,exercise,exercise_name,'));
+ expect(lines[1], contains(',Alternating dumbbell hammer curl,'));
+ expect(lines[2], contains(',retired_lift,,'),
+ reason: 'an unknown key has no name, so the field is empty');
+ });
});
}
diff --git a/test/db_strength_schema_test.dart b/test/db_strength_schema_test.dart
index b6ec2a775..0886eb681 100644
--- a/test/db_strength_schema_test.dart
+++ b/test/db_strength_schema_test.dart
@@ -172,6 +172,62 @@ void main() {
expect(await LocalDb.recentSetsFor('bench_press'), isEmpty);
});
+ test('strength history candidates are newest and heaviest without N queries',
+ () async {
+ created.add('strength_history.db');
+ await databaseFactory.deleteDatabase(await _dbPath('strength_history.db'));
+ await _openThroughLocalDb('strength_history.db');
+
+ await LocalDb.saveStrengthSets('older', [
+ {
+ 'exercise_key': 'bench_press',
+ 'set_index': 1,
+ 'reps': 8,
+ 'load_kg': 80.0,
+ 'at_ts': 100,
+ },
+ {
+ 'exercise_key': 'bench_press',
+ 'set_index': 2,
+ 'reps': 10,
+ 'load_kg': 80.0,
+ 'at_ts': 200,
+ },
+ {
+ 'exercise_key': 'pull_up',
+ 'set_index': 1,
+ 'reps': 9,
+ 'at_ts': 250,
+ },
+ ]);
+ await LocalDb.saveStrengthSets('newer', [
+ {
+ 'exercise_key': 'bench_press',
+ 'set_index': 1,
+ 'reps': 12,
+ 'load_kg': 70.0,
+ 'at_ts': 300,
+ },
+ ]);
+
+ final candidates = await LocalDb.strengthHistoryCandidates();
+ final previousBench = candidates.previous
+ .firstWhere((row) => row['exercise_key'] == 'bench_press');
+ expect(previousBench['at_ts'], 300);
+ expect(previousBench['load_kg'], 70.0);
+
+ final bestBench = candidates.best
+ .firstWhere((row) => row['exercise_key'] == 'bench_press');
+ expect(bestBench['load_kg'], 80.0);
+ expect(bestBench['reps'], 10,
+ reason: 'more reps wins a tied best-load candidate');
+ expect(candidates.previous.any((row) => row['exercise_key'] == 'pull_up'),
+ isTrue);
+ expect(candidates.best.any((row) => row['exercise_key'] == 'pull_up'),
+ isFalse,
+ reason: 'bodyweight history has no fabricated zero-kilo best');
+ });
+
test('sessions carries the private flag, defaulting to not-private',
() async {
final db = await upgradeFrom(39, 'sessions_private_from_39.db');
diff --git a/test/ui2_activity_test.dart b/test/ui2_activity_test.dart
index a5f5efe7b..d0500160c 100644
--- a/test/ui2_activity_test.dart
+++ b/test/ui2_activity_test.dart
@@ -20,6 +20,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:openstrap_edge/data/local_repository.dart';
import 'package:openstrap_edge/gps/gps_source.dart';
+import 'package:openstrap_edge/l10n/app_localizations.dart';
import 'package:openstrap_edge/state/prefs.dart';
import 'package:openstrap_edge/state/units_controller.dart';
import 'package:openstrap_edge/ui2/activity/catalogue.dart';
@@ -36,6 +37,8 @@ import 'package:openstrap_edge/ui2/ui2.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import '../tool/wger_weightlifting_selection.dart';
+
// ── deterministic fixtures ─────────────────────────────────────────────────
final _start = DateTime(2026, 5, 20, 7, 15);
@@ -233,6 +236,84 @@ void main() {
expect(activityByName(null), isNull);
});
+ test('the offline strength catalogue keeps old keys and wger provenance',
+ () {
+ expect(coreExerciseCount, 18);
+ expect(wgerExerciseCount, wgerWeightliftingSelection.length);
+ expect(
+ {for (final e in exerciseLibrary.where((e) => e.fromWger))
+ e.sourceId: e.label},
+ wgerWeightliftingSelection,
+ reason: 'only explicitly reviewed lifting additions may ship');
+ expect(exerciseLibrary, hasLength(coreExerciseCount + wgerExerciseCount));
+ expect(exerciseLibrary.map((e) => e.key).toSet(),
+ hasLength(exerciseLibrary.length),
+ reason: 'a duplicate key would merge two exercises in history');
+ expect(exerciseByKey('bench_press')?.label, 'Bench press',
+ reason: 'existing set-history keys must remain stable');
+
+ final arnold = exerciseLibrary
+ .firstWhere((e) => e.label == 'Arnold Shoulder Press');
+ expect(arnold.fromWger, isTrue);
+ expect(arnold.matches('Schwarzenegger', 'en'), isTrue,
+ reason: 'search includes upstream aliases');
+ expect(arnold.labelFor('de'), 'Arnold Press');
+ expect(arnold.sourceId, isNotEmpty);
+ expect(arnold.sourceUrl,
+ 'https://wger.de/api/v2/exerciseinfo/?uuid=${arnold.sourceId}');
+ expect(arnold.sourceCredits.map((credit) => credit.author),
+ containsAll({'trzr23', 'wgerjhn'}),
+ reason: 'base data and translated labels have separate authors');
+ const allowedLicenses = {
+ 'CC-BY-SA 3':
+ 'https://creativecommons.org/licenses/by-sa/3.0/deed.en',
+ 'CC-BY-SA 4':
+ 'https://creativecommons.org/licenses/by-sa/4.0/deed.en',
+ 'CC0': 'http://creativecommons.org/publicdomain/zero/1.0/',
+ };
+ expect(
+ exerciseLibrary
+ .where((e) => e.fromWger)
+ .every((e) =>
+ e.sourceCredits.isNotEmpty &&
+ e.sourceCredits.every((credit) =>
+ allowedLicenses[credit.licenseName] ==
+ credit.licenseUrl)),
+ isTrue);
+ });
+
+ test('the core lifts are named and searchable in the shipped languages',
+ () {
+ for (final e in exerciseLibrary.take(coreExerciseCount)) {
+ for (final code in ['de', 'es', 'fr']) {
+ expect(e.localizedLabels[code], isNotEmpty,
+ reason: '${e.key} has no $code name');
+ }
+ }
+ final bench = exerciseByKey('bench_press')!;
+ expect(bench.labelFor('de'), 'Bankdrücken');
+ expect(bench.matches('Bankdrücken', 'de'), isTrue);
+ expect(bench.matches('soulevé', 'fr'), isFalse);
+ expect(exerciseByKey('deadlift')!.matches('soulevé', 'fr'), isTrue);
+ });
+
+ test('the lifting catalogue excludes other workouts and mobility drills', () {
+ final labels = exerciseLibrary.map((e) => e.label).toSet();
+ for (final excluded in [
+ 'Walking', 'Cycling', 'Zone 2 Running', 'Treadmill Cardio',
+ 'Cool-Down Swim', 'Guided or free meditation', '90/90 Breathing',
+ 'Cobra Stretch', 'Yoga exercise: Cow-cat',
+ ]) {
+ expect(labels, isNot(contains(excluded)), reason: excluded);
+ }
+ expect(exerciseLibrary.any((e) => e.category == 'Cardio'), isFalse);
+ expect(labels, containsAll([
+ 'Bench press', 'Back squat', 'Deadlift', 'Arnold Shoulder Press',
+ 'Dumbbell Romanian Deadlift', 'Leg Extension', 'Cable Curls',
+ 'Barbell Lunges Walking',
+ ]));
+ });
+
test('kcal is MET × 3.5 × kg / 200 × min, and null without a weight', () {
final run = activityByName('running')!;
// 9.8 × 3.5 × 70 / 200 × 30 = 360.15
@@ -277,6 +358,15 @@ void main() {
}
});
+ test('other workout types keep their own live screens', () {
+ expect(liveFor(activityByName('weight_training')!), isA());
+ expect(liveFor(activityByName('running')!), isA());
+ expect(liveFor(activityByName('cycling')!), isA());
+ expect(liveFor(activityByName('swimming')!), isA());
+ expect(liveFor(activityByName('yoga')!), isA());
+ expect(liveFor(activityByName('hiit')!), isA());
+ });
+
test('there is no power archetype, and the machines that claimed it are '
'heart-rate sessions', () {
expect([for (final a in Arch.values) a.name], isNot(contains('power')));
@@ -1139,10 +1229,15 @@ void main() {
tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
tester.view.devicePixelRatio = 3;
addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final activity = activityByName('weight_training')!;
+ LiveDraft.begin(activity, weightKg: 72.4);
+ LiveDraft.current!.put('exercise_plan', ['bench_press']);
ActivityResult? handed;
await tester.pumpWidget(_frame(
- liveFor(activityByName('weight_training')!,
+ liveFor(activity,
weightKg: 72.4,
host: ActivityHost(onFinish: (draft) async {
handed = draft;
@@ -1305,6 +1400,8 @@ void main() {
final a = activityByName('weight_training')!;
// What the setup screen does once the app accepts the session.
LiveDraft.begin(a, weightKg: 72.4);
+ LiveDraft.current!.put('exercise_plan', ['bench_press']);
+ LiveDraft.current!.put('exercise_index', 0);
await tester.pumpWidget(
_frame(liveFor(a, weightKg: 72.4), Brightness.light, 1.0));
@@ -1328,6 +1425,147 @@ void main() {
expect(tester.takeException(), isNull);
});
+ testWidgets('a lift can be chosen, replaced, and changed without retagging sets',
+ (tester) async {
+ tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
+ tester.view.devicePixelRatio = 3;
+ addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+ SharedPreferences.setMockInitialValues(const {});
+ await Prefs.ensureLoaded();
+
+ final a = activityByName('weight_training')!;
+ LiveDraft.begin(a, weightKg: 72.4);
+ await tester.pumpWidget(
+ _frame(liveFor(a, weightKg: 72.4), Brightness.light, 1.0));
+ await tester.pumpAndSettle();
+
+ expect(find.text('Choose exercise'), findsWidgets);
+ expect(find.text('Bench press'), findsNothing,
+ reason: 'a real session must not invent a first lift');
+ await tester.tap(find.widgetWithText(BigButton, 'Choose exercise'));
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Incline DB press'));
+ await tester.pumpAndSettle();
+ expect(LiveDraft.current!.data['exercise_plan'], ['incline_db_press']);
+
+ // The choice is part of the draft even before a set exists, so a
+ // minimise/restore cycle cannot lose an empty exercise slot.
+ await tester.pumpWidget(const SizedBox.shrink());
+ await tester.pumpAndSettle();
+ await tester.pumpWidget(
+ _frame(liveFor(a, weightKg: 72.4), Brightness.light, 1.0));
+ await tester.pumpAndSettle();
+ expect(find.text('Incline DB press'), findsOneWidget);
+
+ // No set belongs to this slot yet, so changing it replaces the slot.
+ await tester.tap(find.text('Incline DB press'));
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Back squat'));
+ await tester.pumpAndSettle();
+ expect(LiveDraft.current!.data['exercise_plan'], ['back_squat']);
+
+ // Once a set exists, changing exercises switches/adds instead of
+ // relabelling what the user already logged.
+ await tester.tap(find.text('Log set'));
+ await tester.pump();
+ await tester.tap(find.text('Back squat'));
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Bench press'));
+ await tester.pumpAndSettle();
+ expect(LiveDraft.current!.data['exercise_plan'],
+ ['back_squat', 'bench_press']);
+ final saved = LiveDraft.current!.data['sets']! as List;
+ expect((saved.single as Map)['k'], 'back_squat');
+ expect(tester.takeException(), isNull);
+ });
+
+ testWidgets('the exercise picker searches the offline catalogue and exposes '
+ 'per-entry credits', (tester) async {
+ tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
+ tester.view.devicePixelRatio = 3;
+ addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final a = activityByName('weight_training')!;
+ LiveDraft.begin(a, weightKg: 72.4);
+ await tester.pumpWidget(
+ _frame(liveFor(a, weightKg: 72.4), Brightness.light, 3.1));
+ await tester.pumpAndSettle();
+ await tester.tap(find.widgetWithText(BigButton, 'Choose exercise'));
+ await tester.pumpAndSettle();
+
+ expect(find.text('Cardio'), findsNothing);
+ for (final query in ['Treadmill Cardio', '90/90 Breathing', 'Cobra Stretch']) {
+ await tester.enterText(find.byType(TextField), query);
+ await tester.pumpAndSettle();
+ expect(find.text('No exercises match that'), findsOneWidget);
+ }
+ await tester.enterText(find.byType(TextField), 'Arnold Shoulder Press');
+ await tester.pumpAndSettle();
+ expect(find.text('Arnold Shoulder Press'), findsNWidgets(2),
+ reason: 'the search field and its offline result both show the name');
+ expect(
+ find.bySemanticsLabel(
+ 'View source and credits for Arnold Shoulder Press'),
+ findsOneWidget);
+ expect(tester.takeException(), isNull);
+ });
+
+ testWidgets('searching keeps the highlighted category', (tester) async {
+ tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
+ tester.view.devicePixelRatio = 3;
+ addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final a = activityByName('weight_training')!;
+ LiveDraft.begin(a, weightKg: 72.4);
+ await tester.pumpWidget(
+ _frame(liveFor(a, weightKg: 72.4), Brightness.light, 1.0));
+ await tester.pumpAndSettle();
+ await tester.tap(find.widgetWithText(BigButton, 'Choose exercise'));
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Abs'));
+ await tester.pumpAndSettle();
+ await tester.enterText(find.byType(TextField), 'raise');
+ await tester.pumpAndSettle();
+ expect(find.text('Hanging leg raise'), findsOneWidget);
+ expect(find.text('Front Raises'), findsNothing,
+ reason: 'a shoulders lift is not in the Abs filter');
+ });
+
+ testWidgets('search matches the translated category and equipment it shows',
+ (tester) async {
+ tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
+ tester.view.devicePixelRatio = 3;
+ addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final a = activityByName('weight_training')!;
+ LiveDraft.begin(a, weightKg: 72.4);
+ await tester.pumpWidget(MaterialApp(
+ locale: const Locale('de'),
+ localizationsDelegates: AppLocalizations.localizationsDelegates,
+ supportedLocales: AppLocalizations.supportedLocales,
+ theme: buildTheme(Brightness.light),
+ home: liveFor(a, weightKg: 72.4),
+ ));
+ await tester.pumpAndSettle();
+ await tester.tap(find.widgetWithText(BigButton, 'Übung auswählen'));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byType(TextField), 'Rücken');
+ await tester.pumpAndSettle();
+ expect(find.text('Langhantelrudern'), findsOneWidget,
+ reason: 'the chip says Rücken, so typing it finds Back lifts');
+ expect(find.text('Rücken · Langhantel'), findsWidgets);
+
+ await tester.enterText(find.byType(TextField), 'Kurzhantel');
+ await tester.pumpAndSettle();
+ expect(find.text('Schrägbankdrücken mit Kurzhanteln'), findsOneWidget);
+ expect(tester.takeException(), isNull);
+ });
+
testWidgets(
'interval work/rest/rounds are configurable, not the old 45/30/8',
(tester) async {
@@ -1513,12 +1751,13 @@ void main() {
reason: 'a fit instruction only makes sense for a band that is there');
});
- testWidgets('the strength screen still follows the band it does not tick '
- 'for', (tester) async {
+ testWidgets('a draftless strength screen chooses nothing and still follows '
+ 'the band', (tester) async {
tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
tester.view.devicePixelRatio = 3;
addTearDown(tester.view.reset);
addTearDown(LiveDraft.clear);
+ LiveDraft.clear();
var hr = 96;
await tester.pumpWidget(_frame(
@@ -1527,6 +1766,9 @@ void main() {
Brightness.light,
1.0));
await tester.pumpAndSettle();
+ expect(find.text('Choose exercise'), findsWidgets);
+ expect(find.text('Bench press'), findsNothing,
+ reason: 'no draft is not evidence that this was a bench press');
expect(find.text('96'), findsOneWidget);
// The body is built once and left alone by the 1 Hz tick — but the heart
@@ -1543,8 +1785,12 @@ void main() {
addTearDown(tester.view.reset);
addTearDown(LiveDraft.clear);
+ final activity = activityByName('weight_training')!;
+ LiveDraft.begin(activity);
+ LiveDraft.current!.put('exercise_plan', ['bench_press']);
+
await tester.pumpWidget(_frame(
- LiveStrength(activityByName('weight_training')!),
+ LiveStrength(activity),
Brightness.light,
1.0));
await tester.pumpAndSettle();
@@ -1725,9 +1971,14 @@ void main() {
tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
tester.view.devicePixelRatio = 3;
addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final activity = activityByName('weight_training')!;
+ LiveDraft.begin(activity, weightKg: 72.4);
+ LiveDraft.current!.put('exercise_plan', ['bench_press']);
await tester.pumpWidget(_frame(
- liveFor(activityByName('weight_training')!, weightKg: 72.4),
+ liveFor(activity, weightKg: 72.4),
Brightness.light,
1.0));
await tester.pumpAndSettle();
@@ -1748,12 +1999,16 @@ void main() {
tester.view.physicalSize = const Size(390 * 3, 2400 * 3);
tester.view.devicePixelRatio = 3;
addTearDown(tester.view.reset);
+ addTearDown(LiveDraft.clear);
+
+ final activity = activityByName('weight_training')!;
+ LiveDraft.begin(activity, weightKg: 72.4);
+ LiveDraft.current!.put('exercise_plan', ['bench_press']);
await tester.pumpWidget(_frame(
ChangeNotifierProvider.value(
value: UnitsController.seed(UnitSystem.imperial),
- child: liveFor(activityByName('weight_training')!,
- weightKg: 72.4)),
+ child: liveFor(activity, weightKg: 72.4)),
Brightness.light,
1.0));
await tester.pumpAndSettle();
diff --git a/test/wger_weightlifting_selection_test.dart b/test/wger_weightlifting_selection_test.dart
new file mode 100644
index 000000000..6ec74491e
--- /dev/null
+++ b/test/wger_weightlifting_selection_test.dart
@@ -0,0 +1,88 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:openstrap_edge/ui2/activity/catalogue.dart';
+
+import '../tool/update_wger_exercises.dart';
+import '../tool/wger_weightlifting_selection.dart';
+
+List> _reviewedRows() => [
+ for (final id in wgerWeightliftingSelection.keys)
+ {
+ 'uuid': id,
+ 'category': {'name': 'Arms'},
+ },
+];
+
+void main() {
+ test('refresh ignores unreviewed entries even in a lifting category', () {
+ final reviewed = _reviewedRows();
+ final selected = selectWeightliftingExercises([
+ ...reviewed,
+ {
+ 'uuid': 'unreviewed-cardio',
+ 'category': {'name': 'Cardio'},
+ },
+ {
+ 'uuid': 'unreviewed-breathing',
+ 'category': {'name': 'Abs'},
+ },
+ {
+ 'uuid': 'unreviewed-stretch',
+ 'category': {'name': 'Legs'},
+ },
+ {
+ 'uuid': 'unreviewed-lift',
+ 'category': {'name': 'Arms'},
+ },
+ ]);
+ expect(selected, reviewed);
+ });
+
+ test('refresh refuses to silently drop a reviewed exercise', () {
+ final rows = _reviewedRows()..removeLast();
+ expect(() => selectWeightliftingExercises(rows), throwsStateError);
+ });
+
+ test('refresh refuses duplicate reviewed UUIDs', () {
+ final rows = _reviewedRows();
+ rows.add(rows.first);
+ expect(() => selectWeightliftingExercises(rows), throwsStateError);
+ });
+
+ test('reviewed UUIDs cannot turn into cardio or an unknown category', () {
+ for (final category in ['Cardio', 'Yoga', '']) {
+ final rows = _reviewedRows();
+ rows.first['category'] = {'name': category};
+ expect(
+ () => selectWeightliftingExercises(rows),
+ throwsStateError,
+ reason: category,
+ );
+ }
+ });
+
+ test('a muscle with an empty English name keeps its Latin name', () {
+ expect(wgerMuscleName({'name': 'Trapezius', 'name_en': ''}), 'Trapezius');
+ expect(wgerMuscleName({'name': 'Biceps brachii', 'name_en': 'Biceps'}),
+ 'Biceps');
+ expect(
+ exerciseByKey('wger:d7a418d4-d0cb-4f85-8a7c-1e9d97152cbd')!
+ .matches('trapezius', 'en'),
+ isTrue);
+ });
+
+ test('dumbbell lifts step in 2 kg, bar lifts in 2.5', () {
+ expect(wgerLoadStep(['Dumbbell']), 2);
+ expect(wgerLoadStep(['Bench', 'Dumbbell']), 2);
+ expect(wgerLoadStep(['Barbell', 'Dumbbell']), 2.5);
+ expect(wgerLoadStep(['Cable machine']), 2.5);
+ // kettlebells come in 4 kg sizes, so 2.5 could never reach 8/12/16/24
+ expect(wgerLoadStep(['Kettlebell']), 2);
+ // the shipped snapshot was generated with the same rule
+ expect(
+ exerciseByKey('wger:eb9476ac-2c00-4f49-a40f-f81682161a75')!.step, 2);
+ for (final e in exerciseLibrary.where((e) => e.key.startsWith('wger:'))) {
+ expect(e.step, wgerLoadStep(e.equipment), reason: e.label);
+ }
+ });
+}
+
diff --git a/tool/update_wger_exercises.dart b/tool/update_wger_exercises.dart
new file mode 100644
index 000000000..88cd751ca
--- /dev/null
+++ b/tool/update_wger_exercises.dart
@@ -0,0 +1,478 @@
+// Refresh lib/ui2/activity/wger_exercises.g.dart from wger's public API.
+//
+// Maintainer tool, not app code. Run from the repo root:
+//
+// dart run tool/update_wger_exercises.dart
+//
+// Only names, aliases, muscles, equipment, categories and a load step are kept.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'wger_weightlifting_selection.dart';
+
+const _source =
+ 'https://wger.de/api/v2/exerciseinfo/?limit=200&language__code=en';
+const _licenseSource = 'https://wger.de/api/v2/license/?limit=100';
+const _output = 'lib/ui2/activity/wger_exercises.g.dart';
+
+// These wger records correspond to the original Edge catalogue. They stay out
+// of the generated tail so a user's existing strength_set.exercise_key keeps
+// resolving to the same stable key and the picker does not show duplicates.
+const _legacySourceIds = {
+ '3717d144-7815-4a97-9a56-956fb889c996', // bench_press
+ '57e17672-52b9-43cf-8d0d-4b3f06a0c0d0', // incline_db_press
+ '5b4fb3ec-53a1-4525-a58a-c070798ea86e', // cable_fly
+ 'f4467e9a-9bb1-4e93-bec6-10a5d7738ffb', // overhead_press
+ '6ebb138e-bb0a-402e-84e5-68fe0896e897', // triceps_pushdown
+ 'c5797bdf-1aa1-4d51-9775-c929ec5a2aaf', // overhead_extension
+ '6ce25688-ae91-4dc7-9b17-0b66a47151fa', // barbell_row
+ 'fff05d7a-f374-4c8a-9885-39f49076918f', // lat_pulldown
+ '8e420408-0682-4ab6-89f5-2681e54c7ce0', // pull_up
+ '7b99a081-6b1a-4aa5-b86a-5a935d083a35', // barbell_curl
+ '5d0e0a8b-1940-4034-b4ae-b965859f1ff0', // back_squat
+ 'd677de4c-5bd9-412a-91f1-857116a666a2', // front_squat
+ 'ee8e8db4-2d82-49e1-ab7f-891e9a354934', // deadlift
+ '2e7ffff9-e603-4b28-98c8-31d1a6ce8cd9', // romanian_deadlift
+ '19a289c0-33af-4055-bb34-3570c2975d3d', // hip_thrust
+ '66a42396-c207-44da-bc75-758a89d32404', // leg_press
+ 'c9e57bbe-e839-44c6-861d-1c8dd2845e36', // plank
+ '9b993e99-8701-43f0-84d6-689123183880', // hanging_leg_raise
+};
+
+const _languages = {1: 'de', 2: 'en', 4: 'es', 12: 'fr', 24: 'zh'};
+
+const _allowedLicenses = {
+ 'CC-BY-SA 3': 'https://creativecommons.org/licenses/by-sa/3.0/deed.en',
+ 'CC-BY-SA 4': 'https://creativecommons.org/licenses/by-sa/4.0/deed.en',
+ 'CC0': 'http://creativecommons.org/publicdomain/zero/1.0/',
+};
+
+final _uuid = RegExp(
+ r'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
+ caseSensitive: false,
+);
+
+/// Select only reviewed lifting movements. Categories alone are not sufficient:
+/// wger includes stretches, breathing and other workouts under muscle groups.
+/// Missing or reclassified records require review instead of a partial refresh.
+List> selectWeightliftingExercises(List raw) {
+ const categories = {
+ 'Abs',
+ 'Arms',
+ 'Back',
+ 'Calves',
+ 'Chest',
+ 'Legs',
+ 'Shoulders',
+ };
+ final selected = >[];
+ final seen = {};
+ for (final value in raw) {
+ final map = (value as Map).cast();
+ final id = _clean(map['uuid']).toLowerCase();
+ if (!wgerWeightliftingSelection.containsKey(id)) continue;
+ if (!seen.add(id)) throw StateError('Duplicate selected wger UUID: $id');
+ final category = _clean((map['category'] as Map?)?['name']);
+ if (!categories.contains(category)) {
+ throw StateError(
+ 'Review the category for selected wger exercise $id: $category',
+ );
+ }
+ selected.add(map);
+ }
+ final missing = wgerWeightliftingSelection.keys.toSet().difference(seen);
+ if (missing.isNotEmpty) {
+ throw StateError('Missing reviewed weightlifting exercises: $missing');
+ }
+ return selected;
+}
+
+/// Load increment for a generated row, matching the hand-written catalogue:
+/// a dumbbell or kettlebell moves in 2 kg, anything with a bar in 2.5.
+num wgerLoadStep(List equipment) =>
+ (equipment.contains('Dumbbell') || equipment.contains('Kettlebell')) &&
+ !equipment.contains('Barbell') &&
+ !equipment.contains('SZ-Bar')
+ ? 2
+ : 2.5;
+
+/// wger sends name_en as '' (not null) for some muscles, e.g. Trapezius, so
+/// fall back to the Latin name instead of dropping the muscle.
+Object? wgerMuscleName(Map muscle) =>
+ _clean(muscle['name_en']).isNotEmpty ? muscle['name_en'] : muscle['name'];
+
+Future main() async {
+ final client = HttpClient()
+ ..userAgent = 'OpenStrap exercise catalogue updater';
+ try {
+ final licenseRows = await _fetchAll(
+ client,
+ Uri.parse(_licenseSource),
+ minimumCount: 1,
+ );
+ final licenses = {};
+ for (final value in licenseRows) {
+ final license = _License.fromJson((value as Map).cast());
+ if (licenses.putIfAbsent(license.id, () => license) != license) {
+ throw StateError('Duplicate wger license id ${license.id}.');
+ }
+ }
+
+ // Follow wger's pagination instead of assuming the growing catalogue will
+ // remain below an arbitrary page size.
+ final raw = await _fetchAll(client, Uri.parse(_source), minimumCount: 800);
+
+ final rows = <_Exercise>[];
+ for (final map in selectWeightliftingExercises(raw)) {
+ final row = _Exercise.fromJson(map, licenses);
+ if (_legacySourceIds.contains(row.uuid)) continue;
+ if (row.label != wgerWeightliftingSelection[row.uuid]) {
+ throw StateError(
+ 'Review the renamed weightlifting exercise '
+ '${row.uuid}: ${row.label}',
+ );
+ }
+ rows.add(row);
+ }
+ rows.sort((a, b) {
+ final byName = a.label.toLowerCase().compareTo(b.label.toLowerCase());
+ return byName != 0 ? byName : a.uuid.compareTo(b.uuid);
+ });
+
+ final duplicateKeys = {};
+ final seenKeys = {};
+ for (final row in rows) {
+ if (!seenKeys.add(row.key)) duplicateKeys.add(row.key);
+ }
+ if (duplicateKeys.isNotEmpty) {
+ throw StateError('Duplicate generated keys: $duplicateKeys');
+ }
+
+ // strength_set stores the stable key, not a copied display label. If an
+ // upstream record disappears, silently dropping it here would turn old
+ // workouts into a raw `wger:` identifier. Stop the refresh so the
+ // maintainer can preserve that record as a retired/tombstoned definition.
+ final previousIds = await _existingSourceIds();
+ final currentIds = {for (final row in rows) row.uuid};
+ final removedIds = previousIds.difference(currentIds).toList()..sort();
+ if (removedIds.isNotEmpty) {
+ throw StateError(
+ 'Refusing to orphan existing strength history. Preserve these removed '
+ 'wger records as retired definitions before refreshing: $removedIds',
+ );
+ }
+
+ final sink = StringBuffer()
+ ..writeln('// GENERATED FILE — DO NOT EDIT.')
+ ..writeln('// Refresh with: dart run tool/update_wger_exercises.dart')
+ ..writeln('// Source: $_source')
+ ..writeln('// Selection: tool/wger_weightlifting_selection.dart')
+ ..writeln('//')
+ ..writeln('// Base data and translations retain their Creative Commons')
+ ..writeln('// credits. See NOTICE.md and docs/notice.html.')
+ ..writeln()
+ ..writeln("part of 'catalogue.dart';")
+ ..writeln()
+ ..writeln('const _wgerExerciseLibrary = [');
+ for (final row in rows) {
+ sink
+ ..writeln(' ExerciseDef(')
+ ..writeln(' ${_dart(row.key)},')
+ ..writeln(' ${_dart(row.label)},')
+ ..writeln(' ${_dart(row.primaryMuscles)},')
+ ..writeln(' secondaryMuscles: ${_dart(row.secondaryMuscles)},')
+ ..writeln(' category: ${_dart(row.category)},')
+ ..writeln(' equipment: ${_dart(row.equipment)},')
+ ..writeln(' aliases: ${_dart(row.aliases)},')
+ ..writeln(' localizedLabels: ${_dart(row.localizedLabels)},')
+ ..write(row.step == 2.5 ? '' : ' step: ${row.step},\n')
+ ..writeln(' sourceId: ${_dart(row.uuid)},')
+ ..writeln(' sourceUpdatedAt: ${_dart(row.updatedAt)},')
+ ..writeln(' sourceCredits: [');
+ for (final credit in row.credits) {
+ sink.writeln(
+ ' ExerciseCredit(${_dart(credit.licenseName)}, '
+ '${_dart(credit.licenseUrl)}, ${_dart(credit.author)}),',
+ );
+ }
+ sink
+ ..writeln(' ],')
+ ..writeln(' ),');
+ }
+ sink.writeln('];');
+ await File(_output).writeAsString(sink.toString());
+ final formatted = await Process.run(Platform.resolvedExecutable, [
+ 'format',
+ _output,
+ ]);
+ if (formatted.exitCode != 0) {
+ throw StateError('dart format failed: ${formatted.stderr}');
+ }
+ stdout.writeln(
+ 'Wrote ${rows.length} reviewed wger weightlifting exercises to $_output.',
+ );
+ } finally {
+ client.close(force: true);
+ }
+}
+
+Future> _existingSourceIds() async {
+ final file = File(_output);
+ if (!await file.exists()) return {};
+ final source = await file.readAsString();
+ final ids = {
+ for (final match in RegExp(
+ r'^\s+sourceId: "([0-9a-f-]{36})",$',
+ multiLine: true,
+ caseSensitive: false,
+ ).allMatches(source))
+ match.group(1)!.toLowerCase(),
+ };
+ if (source.contains('const _wgerExerciseLibrary') && ids.isEmpty) {
+ throw StateError('Could not read source IDs from the existing snapshot.');
+ }
+ return ids;
+}
+
+Future> _fetchAll(
+ HttpClient client,
+ Uri firstPage, {
+ required int minimumCount,
+}) async {
+ final rows = [];
+ int? expectedCount;
+ Uri? next = firstPage;
+ var pages = 0;
+ while (next != null) {
+ if (next.scheme != 'https' || next.host != 'wger.de') {
+ throw StateError('Refusing an unexpected wger pagination URL: $next');
+ }
+ if (++pages > 100) throw StateError('wger pagination did not terminate.');
+
+ final request = await client.getUrl(next);
+ // a redirect would skip the host check above; the non-200 check rejects it
+ request.followRedirects = false;
+ request.headers.set(HttpHeaders.acceptHeader, 'application/json');
+ final response = await request.close();
+ if (response.statusCode != HttpStatus.ok) {
+ throw HttpException(
+ 'wger returned HTTP ${response.statusCode}',
+ uri: next,
+ );
+ }
+ final body = await utf8.decoder.bind(response).join();
+ final decoded = (jsonDecode(body) as Map).cast();
+ final count = (decoded['count'] as num?)?.toInt();
+ if (count == null || count < minimumCount) {
+ throw StateError(
+ 'Expected at least $minimumCount wger rows; API reported $count.',
+ );
+ }
+ expectedCount ??= count;
+ if (count != expectedCount) {
+ throw StateError(
+ 'wger changed while paging: expected $expectedCount rows, now $count.',
+ );
+ }
+ final page = decoded['results'];
+ if (page is! List) throw FormatException('wger page has no results list.');
+ rows.addAll(page);
+
+ final nextValue = decoded['next'];
+ if (nextValue == null) {
+ next = null;
+ } else if (nextValue is String) {
+ next = Uri.parse(nextValue);
+ } else {
+ throw FormatException('wger returned a non-string next page URL.');
+ }
+ }
+ if (rows.length != expectedCount) {
+ throw StateError(
+ 'Incomplete wger pagination: expected $expectedCount rows, '
+ 'received ${rows.length}.',
+ );
+ }
+ return rows;
+}
+
+// JSON string syntax is almost Dart string syntax. The important exception is
+// `$`, which Dart would interpret as interpolation if a future upstream label
+// contains one.
+String _dart(Object? value) => jsonEncode(value).replaceAll(r'$', r'\$');
+
+String _clean(Object? value) =>
+ (value as String? ?? '').trim().replaceAll(RegExp(r'\s+'), ' ');
+
+List _strings(Iterable values) {
+ final out = {};
+ for (final value in values) {
+ final clean = _clean(value);
+ if (clean.isNotEmpty) out.add(clean);
+ }
+ return out.toList()..sort((a, b) => a.compareTo(b));
+}
+
+class _Exercise {
+ const _Exercise({
+ required this.uuid,
+ required this.label,
+ required this.localizedLabels,
+ required this.aliases,
+ required this.primaryMuscles,
+ required this.secondaryMuscles,
+ required this.category,
+ required this.equipment,
+ required this.updatedAt,
+ required this.credits,
+ });
+
+ final String uuid;
+ final String label;
+ final Map localizedLabels;
+ final List aliases;
+ final List primaryMuscles;
+ final List secondaryMuscles;
+ final String category;
+ final List equipment;
+ final String updatedAt;
+ final List<_Credit> credits;
+
+ String get key => 'wger:$uuid';
+ num get step => wgerLoadStep(equipment);
+
+ factory _Exercise.fromJson(
+ Map map,
+ Map licenses,
+ ) {
+ final uuid = _clean(map['uuid']).toLowerCase();
+ if (!_uuid.hasMatch(uuid)) {
+ throw FormatException('Exercise has an invalid UUID: $uuid');
+ }
+ final baseLicense = _License.fromJson(
+ ((map['license'] as Map?) ?? const {}).cast(),
+ );
+ final credits = {};
+
+ void addCredit(_License license, Object? authorValue) {
+ final expectedUrl = _allowedLicenses[license.name];
+ if (expectedUrl == null || license.url != expectedUrl) {
+ throw FormatException(
+ 'Exercise $uuid has unreviewed license '
+ '"${license.name}" (${license.url}).',
+ );
+ }
+ final author = _clean(authorValue);
+ final credit = _Credit(license.name, license.url, author);
+ credits.putIfAbsent(
+ jsonEncode([credit.licenseName, credit.licenseUrl, credit.author]),
+ () => credit,
+ );
+ }
+
+ // Muscles, category and equipment belong to the base exercise.
+ addCredit(baseLicense, map['license_author']);
+ final translations = ((map['translations'] as List?) ?? const [])
+ .whereType()
+ .map((e) => e.cast())
+ .toList();
+ final labels = {};
+ final aliases = [];
+ for (final translation in translations) {
+ final language = (translation['language'] as num?)?.toInt();
+ final code = _languages[language];
+ if (code == null) continue;
+ final name = _clean(translation['name']);
+ if (name.isNotEmpty) {
+ final previous = labels[code];
+ if (previous != null && previous != name) {
+ throw FormatException(
+ 'Exercise $uuid has two different $code labels.',
+ );
+ }
+ labels[code] = name;
+ }
+ var usesTranslation = name.isNotEmpty;
+ for (final alias in (translation['aliases'] as List?) ?? const []) {
+ if (alias is! Map) continue;
+ final value = _clean(alias['alias']);
+ if (value.isEmpty) continue;
+ aliases.add(value);
+ usesTranslation = true;
+ }
+ if (usesTranslation) {
+ final licenseId = (translation['license'] as num?)?.toInt();
+ final license = licenses[licenseId];
+ if (license == null) {
+ throw FormatException(
+ 'Exercise $uuid has an unknown $code translation license '
+ 'id $licenseId.',
+ );
+ }
+ addCredit(license, translation['license_author']);
+ }
+ }
+ final english = labels['en'];
+ if (english == null || english.isEmpty) {
+ throw FormatException('Exercise has no English label: $map');
+ }
+
+ List names(String field) => _strings(
+ ((map[field] as List?) ?? const []).whereType().map(wgerMuscleName),
+ );
+
+ final sortedLabels = Map.fromEntries(
+ labels.entries.toList()..sort((a, b) => a.key.compareTo(b.key)),
+ );
+ final sortedCredits = credits.values.toList()
+ ..sort((a, b) {
+ final byLicense = a.licenseName.compareTo(b.licenseName);
+ if (byLicense != 0) return byLicense;
+ final byAuthor = a.author.compareTo(b.author);
+ return byAuthor != 0 ? byAuthor : a.licenseUrl.compareTo(b.licenseUrl);
+ });
+ return _Exercise(
+ uuid: uuid,
+ label: english,
+ localizedLabels: Map.unmodifiable(sortedLabels),
+ aliases: _strings(aliases),
+ primaryMuscles: names('muscles'),
+ secondaryMuscles: names('muscles_secondary'),
+ category: _clean((map['category'] as Map?)?['name']),
+ equipment: _strings(
+ ((map['equipment'] as List?) ?? const []).whereType().map(
+ (e) => e['name'],
+ ),
+ ),
+ updatedAt: _clean(map['last_update_global'] ?? map['last_update']),
+ credits: List.unmodifiable(sortedCredits),
+ );
+ }
+}
+
+class _License {
+ const _License(this.id, this.name, this.url);
+
+ final int id;
+ final String name;
+ final String url;
+
+ factory _License.fromJson(Map map) {
+ final id = (map['id'] as num?)?.toInt();
+ final name = _clean(map['short_name']);
+ final url = _clean(map['url']);
+ if (id == null || name.isEmpty || url.isEmpty) {
+ throw FormatException('Incomplete wger license: $map');
+ }
+ return _License(id, name, url);
+ }
+}
+
+class _Credit {
+ const _Credit(this.licenseName, this.licenseUrl, this.author);
+
+ final String licenseName;
+ final String licenseUrl;
+ final String author;
+}
diff --git a/tool/wger_weightlifting_selection.dart b/tool/wger_weightlifting_selection.dart
new file mode 100644
index 000000000..25118c0cf
--- /dev/null
+++ b/tool/wger_weightlifting_selection.dart
@@ -0,0 +1,209 @@
+// Reviewed wger lifting exercises, uuid -> label. The updater refuses renamed
+// or missing records. Add sets-and-reps movements only; wger also files
+// stretches and breathing drills under muscle categories.
+const wgerWeightliftingSelection = {
+ "a8a4ea81-9531-48e1-b6dc-f7621c7b9283": "1-Arm Half-Kneeling Lat Pulldown",
+ "1b020b3a-3732-4c7e-92fd-a0cec90ed69b": "2 Handed Kettlebell Swing",
+ "eb9476ac-2c00-4f49-a40f-f81682161a75": "Alternating dumbbell hammer curl",
+ "f24cb758-9c0d-42d4-ad9e-6025c527dd13": "Arnold Shoulder Press",
+ "dae6f6ed-9408-4e62-a59a-1a33f4e8ab36": "Barbell Hack Squats",
+ "04e7d7e4-f8d2-406d-97df-3df3bceec22c": "Barbell Lunges Standing",
+ "072a9fa8-1028-47b3-b958-d8052c6b8661": "Barbell Lunges Walking",
+ "67c26c9b-32f3-4b8b-b13a-4d8be4803975": "Barbell Reverse Wrist Curl",
+ "3c2b5e2d-bd9d-43e8-8b5f-e6363331faa1": "Barbell Row (Underhand)",
+ "928eeae3-d849-4605-a470-048ca259a8c5": "Barbell Step Back Lunge",
+ "13234b53-ea0c-41f1-9069-776865ea8eff": "Barbell Triceps Extension",
+ "cfb6a754-57be-431b-b8d0-d9963120bd5a": "Barbell Wrist Curl",
+ "06968051-a818-489c-9fbb-ddb94a001245": "Bayesian Curl",
+ "57018827-f344-4627-88e5-03e4f2f49859": "Behind the Back Cable Lateral Raise",
+ "e9f3446f-ca98-4bf2-817c-0dacd123cecd": "Belt Squat",
+ "3db63138-a047-4a4d-b616-1a0b7dfca105": "Bench Press Narrow Grip",
+ "28321cf3-70e6-48a4-ade1-d11382180cb3": "Benchpress Dumbbells",
+ "94a5c406-7bcd-47f3-9687-bdf92a763932":
+ "Bent Over Dumbbell Rows (Single Arm)",
+ "3642933c-d078-4fb6-a544-d1772fa0cc9d": "Bent Over Dumbbell Rows (Two Arms)",
+ "563203ce-1867-4376-aa7f-a0356808c0ae":
+ "Bent-Over Dumbbell Triceps Extension",
+ "6f79b381-98a4-40d5-8a45-3bb0558be6fe": "Bent-over Lateral Raises",
+ "e68f6d51-fe02-4175-b277-dec0c1521f36": "Biceps Curl Machine",
+ "42227131-9b1e-4220-b082-c523f0651057": "Biceps Curls With SZ-bar",
+ "60d2c34b-43a1-48a3-b43b-160e4c0157f2": "Bulgarian Squat with Dumbbells",
+ "99449eae-0eae-44ab-a57f-fc094bbdda13": "Butterfly",
+ "a6605e9c-887d-45e0-8282-617a8ec5fea2": "Butterfly Reverse",
+ "b2865908-b93e-4507-ada4-a5bb9317566c": "Cable Chest Press - Decline",
+ "e22c4320-7753-49c6-b7f7-4be21b025fb6": "Cable Chest Press - Incline",
+ "b1bf02cf-17b2-4bfc-a5b0-4ff7a2768dbb": "Cable Concentration Curl",
+ "b16e3e5d-8401-4d2b-919c-15b536f9ec5e": "Cable Cross-over",
+ "479ef6f0-5274-4da7-b5ad-67528ff3c8fd": "Cable Curls",
+ "ae695660-7cee-4601-8241-edd3e2a6d896": "Cable Fly Lower Chest",
+ "b16a28c4-40bf-42d4-94b3-bf3f56fab743": "Cable Fly Upper Chest",
+ "6e03ecb3-6bfe-4b34-be9d-e5c33e820cc6": "Cable Front Raise with a small bar",
+ "19a9f67b-60f5-4367-9134-b805f3e73956": "Cable glute extension",
+ "81942f3d-d92c-431c-b9d0-0da12918d1e4": "Cable Lateral Raises (Single Arm)",
+ "821e289e-bd39-4410-b436-6f2a43bc3649": "Cable pull through",
+ "5d244235-cd56-472a-876e-6e530a899ef2": "Cable Rear Delt Fly",
+ "1ea73578-08a5-4f3e-9255-3c58b29b170d": "Cable Tricep Kickback",
+ "c867836d-2929-4977-b23c-014bc21ec08d": "Cable Woodchoppers",
+ "0d79f259-3b28-4258-8a08-cffec062a710": "Calf Press Using Leg Press Machine",
+ "95a9cc46-270e-45f2-8a17-5665a23ff70b":
+ "Calf Raises on Hackenschmitt Machine",
+ "25cbc266-aa84-44f5-989b-53c9cc8fdc77": "Chest-Supported Rear Delt Raise",
+ "a8f76beb-b6d8-430b-8e45-cc03d4959043": "Clean",
+ "8c509754-c86f-4c1b-bfa9-29c906ffc409": "Clean and Jerk OL",
+ "2a125f64-1d10-4926-95ad-33a48e61b3d6": "Clean and Press",
+ "63fbb8e5-6ebc-4def-8844-3e65e097213b": "Close-grip Lat Pull Down",
+ "4eb41bfb-cbeb-4683-b9c6-902ee7e028e0": "Close-grip supinated lat pulldown",
+ "401989e0-64c3-459d-bc47-2c171ab4f41d": "Crunches on Machine",
+ "29175281-afaf-4ffd-85e8-cfc38493e304": "Crunches With Cable",
+ "bcd801f0-9a38-4615-a91c-900153c8c234": "Decline Bench Press Barbell",
+ "dda69c96-62d4-4690-aa07-a4a0f6ceb63a": "Decline Bench Press Dumbbell",
+ "e7a964cd-e68e-4926-bc7c-577065137e18": "Deficit Deadlift",
+ "fe249e2f-5856-420f-8a34-1b02c7fde141": "Double Kettlebell Clean and Press",
+ "a030a4e9-970c-4442-b0b3-e49b42143de0": "Double Kettlebell Front Squat",
+ "0f648dbf-9ddc-44d9-a10c-1aa97537ea91": "Dumbbell Bent-Over Reverse Fly",
+ "456f0781-30c6-4f58-a5b4-3da8f4878065": "Dumbbell Calf Raise",
+ "428112f3-3918-45ba-870a-dc58cf3959cf": "Dumbbell close grip bench press",
+ "46e0f60c-fdc0-489a-82e4-a5b7476a5c21": "Dumbbell Concentration Curl",
+ "99846da5-5dc8-4de1-ba55-ae2b4c03c30d": "Dumbbell Curl",
+ "b199baca-7353-4dee-bbcf-620f96dba5b4": "Dumbbell Deadlift",
+ "e4966cb8-9089-4595-9c78-99a27821e6ff": "Dumbbell Floor Press",
+ "4621b41f-0d32-4033-acff-dc079c792606": "Dumbbell Front Squat",
+ "b7c6a444-90ea-4f5b-9fea-748311606eaa": "Dumbbell Goblet Squat",
+ "f0f53b8e-0136-4195-baf3-781903651359": "Dumbbell Hang Power Cleans",
+ "587af8f1-516b-44c3-8660-70f262e1bef8": "Dumbbell Hex Press",
+ "af77220c-098c-47c2-9f8c-92a651998903": "Dumbbell Hip Thrust",
+ "43e85cb8-51d0-4892-b1bf-80a3cb111ff6": "Dumbbell Incline Curl",
+ "c8b182c3-9dfd-40ca-87b2-ef25f19aa181": "Dumbbell Lateral Raise",
+ "2f1a2707-e7ff-46ac-9112-3e31e6e961ee": "Dumbbell Lunges Standing",
+ "dcc6e237-a8bb-4eca-bbc5-7fb852636f6a": "Dumbbell Lunges Walking",
+ "adb6067f-fd48-4a25-a9b4-0793c5f158fa": "Dumbbell rear delt row",
+ "75e34967-b0ec-40d5-811e-b474b234a7a9": "Dumbbell Rear Lunge",
+ "65d12ecf-54b8-466d-a412-e55c396cad69": "Dumbbell Romanian Deadlift",
+ "3e9b3bb0-7958-4836-89a5-ed0c5f7eeb03": "Dumbbell Side Bend",
+ "7cc24acf-3fc9-4d14-a461-cd00d1d18f0e": "Dumbbell Single-leg Hip Thrust",
+ "51fe0871-1390-4755-bd04-01a93e32f533": "dumbbell snatch",
+ "8a3e08ab-4e97-4aee-897a-60d8693b9b43": "Dumbbell Split Squat",
+ "0c30c543-e2cc-499b-bc31-8c28db445ed2": "Dumbbell sumo deadlift",
+ "d8bddb58-91b0-4d7b-8ec1-cd742584b607": "Dumbbell Triceps Extension",
+ "2affa71f-7308-4448-af57-dc44b424090e": "Dumbell Tate Press",
+ "d7a418d4-d0cb-4f85-8a7c-1e9d97152cbd": "Facepull",
+ "03f7fc27-e64f-4417-ab0c-a52adac60fda": "Flat Machine Press",
+ "6b68043c-a900-41f4-89fb-ea5c8193f216": "Floor Skull Crusher",
+ "95d226ad-3bf7-4cd6-aa64-2f26b526d8b6": "Fly With Dumbbells",
+ "55d0a5ec-b147-40c3-aa77-314e61c93689": "Fly With Dumbbells, Decline Bench",
+ "9c35594c-bbcc-4656-bdcb-376814c90e96": "Front Raises",
+ "66ed6788-a1d8-49a2-af4d-397107fbf5fb": "Glute Kickback (Machine)",
+ "f462df26-3264-48c2-b5aa-2374d0670c24": "Hack Squats",
+ "c0d9fe98-f4fe-49f3-8037-05e1984e7d2d": "Hammer Curls",
+ "04365177-e078-489b-983a-8ac61b7346f1": "Hammercurls on Cable",
+ "920692b2-0842-428a-b925-ba0daf6b41db": "Hammerstrength Decline Chest Press",
+ "f80468f2-7c5d-45c5-9470-46a88c1a75c2": "High Row",
+ "ecba2395-f364-4e5f-a34c-04f178c8b822": "High-Incline Smith Machine Press",
+ "275fb49f-975c-4d6e-9d63-2c86ed740f40": "Incline Bench Press - Barbell",
+ "70c99a4e-3340-4993-a7f1-2d2709dada1a": "Incline Bench Reverse Fly",
+ "51a2d520-b510-4b6e-bd65-9fc40365e8de":
+ "Incline Chest-Supported Dumbbell Row",
+ "fca05168-7212-42ec-8927-efb1e8dd0971":
+ "Incline Close Grip Barbell Bench Press",
+ "55ff32e6-24ab-4303-9b50-176d60d48796": "Incline Dumbbell Fly",
+ "27eb3d4a-8677-4c1f-bf34-251e2b4741cb": "Incline Dumbbell Row",
+ "85c28424-ab47-40d4-9a27-e1b9bfb01e7c": "Jerk OL",
+ "67c2fe41-f1e9-4f75-b2cb-72d1f2e092f7": "JM Press",
+ "9b5f8c6e-2436-4ded-aea9-8c698b0c8768": "Kettlebell deadlifts",
+ "20b17b71-6d91-4b56-978a-04b7f94c04cc": "Kettlebell One Legged Deadlift",
+ "5cbaa028-114d-4a15-9a95-70d9b3146f30": "kettlebell sumo deadlift",
+ "107ea68a-d976-49ed-be75-3b0cf253c36a": "Kickstand RDL",
+ "91c47c8b-6290-4885-8921-df9aa7958c00": "Landmine press",
+ "73c1e130-fba9-473b-a093-a5712e2ae34d": "Landmine Rotation",
+ "87f30f6d-dc8a-4b26-8c4f-640b66ecacf0": "Larsen Press",
+ "28d18142-8777-4dba-a3a1-6151cec94500": "Lean-Away Cable Lateral Raise",
+ "3bc8b411-a28d-4e1c-a6d1-769e18fe9881": "Leg Curls (laying)",
+ "440a5184-de58-4a86-a7ba-76ddeafaa855": "Leg Curls (sitting)",
+ "49189e88-40b6-4c0d-a784-0cc3abbb8e75": "Leg Curls (standing)",
+ "62170477-90ec-463c-907e-9e523abc0a15": "Leg Extension",
+ "5f0b757c-0d6a-430b-9d5e-3bfba202878e": "Leg Presses (narrow)",
+ "661287d4-d1dc-485c-896b-73f90b000536": "Leg Presses (wide)",
+ "fd595232-b9ad-4216-bba1-92298f993ea9": "Leverage Machine Iso Row",
+ "183e7279-f052-4c70-8381-bbbda3bb9afe": "Low Box Squat - Wide Stance",
+ "8a356e9a-0058-4f60-b8d7-89147c3c371f": "Lying Triceps Extensions",
+ "1e827ef8-0a01-4b26-9e26-eff9d200acca": "Machine chest fly",
+ "fc9d4980-c6a1-4786-b982-7ff56ca773f9": "Machine Chest Press",
+ "b1eed626-112f-401c-a61d-bfe168fdedc7": "Machine Dip or Triceps Extension",
+ "b5726562-82ed-451a-89d8-8c8b13c5019a": "Machine Hip Abduction",
+ "fe0c2a17-fad9-4235-8786-84bb1de93530": "Machine Incline Chest Press",
+ "e6bf0364-ccce-4053-afe1-eef206a50291": "Machine Lat Pulldown",
+ "3d2adf99-8fe9-45cb-b669-c5b18242e282": "Machine Lateral Raise",
+ "8aae635b-cb15-4129-bc09-6951fa376274": "Machine Seated Calf Raise",
+ "28b65190-cae2-4d15-8f4e-dde0d9cc4d4b": "Meadows Row",
+ "3cad7b92-a16b-45dc-a53e-d6a01a158f8e": "Neutral Grip Lat Pulldown",
+ "5ed12527-c8be-4e86-8f2a-cf9dd4781f5b":
+ "One Arm Overhead Cable Tricep Extension",
+ "c7058ff8-ca3b-4822-abdb-6341ce37c4d1": "Overhand Cable Curl",
+ "f2e17c8d-7285-4fd5-bfe8-26f407802850": "Overhead Cable Tricep Extension",
+ "e56f2970-e4c9-45eb-8e8c-52abb590e7a6": "Pallof Press",
+ "6441ff9e-037e-48d9-8800-67b430dc8e37": "Pause Bench",
+ "a4c97a23-f044-4095-a041-94d92ffe44bd": "Paused Squats",
+ "748e9635-7958-4a41-b1ed-93de74c7ef72": "Pendelay Rows",
+ "abd2854b-114a-4584-8746-30bd40127863": "Pin Bench Press BB",
+ "96f9d74a-7bf4-4ec1-9cd6-c62c13cf7b67": "Pin Squat",
+ "5d5122e0-65f3-4afc-ac36-21955e2c7dde": "Plate-Loaded Lat Pulldown",
+ "52dec48d-25a4-4a78-b66b-ad6a773e143a": "Power Clean",
+ "7eb52939-0e33-4319-829d-d8e142fc74d9": "Preacher Curl Machine",
+ "dd52fb99-9426-4a78-b446-20a8e3e4ec47": "Preacher Curls",
+ "7f834f07-fa7b-46b6-8ffa-45930b0602db": "Pull Ups on Machine",
+ "9ccd53e0-8392-4e61-91aa-e4e5f4ea339c": "Pullover",
+ "ad20ef5e-e360-4859-9110-668ee0c24721": "Pullover Machine",
+ "2b6c09f7-dbf1-45ea-baf6-9a12e0b12396": "Renegade Row",
+ "2cd5e3c6-a8c0-456a-ab47-5e7b3a435407": "Reverse Curl",
+ "1b95d961-1d5c-4ded-b25e-c090295ffe37": "Reverse Grip Bench Press",
+ "edc44b1a-35d7-4c37-ab20-05842fb40576": "Reverse Preacher Curl (Close Grip)",
+ "849f2fa6-42f9-4c59-90f6-275c8ada6725": "Rotary Torso Machine",
+ "139772c6-e401-4281-8aa6-c7ff7fb49f6a": "Seated Cable chest fly",
+ "0ecfec6f-5d0f-4e8a-8d37-f6203a8923b8": "Seated Cable Row",
+ "6995c30a-037f-4de3-ac4a-45384a1f38f9": "Seated Dumbbell Calf Raise",
+ "19fba43a-8363-4da3-82c6-d8a14628b5e7": "Seated Dumbbell Curls",
+ "9bcc7b4f-4172-4db7-be95-d7b659c486f5": "Seated Row (Machine)",
+ "cc5bf1a3-29b5-4c5a-8600-5e35c79ab4bf": "Seated Triceps Press",
+ "87affa4b-395b-437c-9581-2bd20ea5aa7c": "Shoulder Press, Dumbbells",
+ "5912d7ed-6a0e-4b4c-b30a-fc9f3f890fc1": "Shoulder Press, on Machine",
+ "141bc870-56be-4749-a3b9-e56d5d5618b4": "Shoulder Press, on Multi Press",
+ "6199b3c7-3ccb-47fa-89bb-ef7fef12c0e3": "Shrugs on Multipress",
+ "270e108d-3cd2-45a9-807b-c357317eb15c": "Shrugs, Barbells",
+ "72a945ec-3a7f-424b-9a05-1616ef7dce91": "Shrugs, Dumbbells",
+ "046fce45-69f2-46f7-a5b6-79a25a485af1": "Single Leg Extension",
+ "9dfce8f0-39fc-4204-9f84-500ec42074e9": "Single-arm dumbbell shoulder press",
+ "b86fd8df-e726-4998-a7ee-000de0f70bf0": "Single-Arm Lat Pulldown",
+ "c85e6137-2577-4e28-82c2-427407d534eb": "Single-arm Preacher Curl",
+ "a630de17-9170-434c-b486-96a6b8705506": "Single-Leg Deadlift with Dumbbell",
+ "893c07ea-2e24-49b6-92e4-d0033eedec62": "Skullcrusher Dumbbells",
+ "95a7e546-e8f8-4521-a76b-983d94161b25": "Skullcrusher SZ-bar",
+ "092d0fbb-0409-470e-a087-beb9a378f3f7":
+ "Smith Machine Close-grip Bench Press",
+ "ef2df6a2-41df-4a25-b2e0-4808dc2c3305": "Smith Machine Slight Incline Press",
+ "b382dcb2-60a8-412d-b0ee-6afe9c9f36d7": "Smith Machine Split Squat",
+ "f3ef220b-a070-449c-a463-876d30cd9b14": "Smith machine squat",
+ "8161de8b-7802-46c0-9bd4-db60f39b7677": "Snatch",
+ "93b9b84c-7b72-4712-a7c2-0e14a5fab32a": "Standing Adduction (Cable)",
+ "22c59ede-970b-43e1-bb51-ac1c2be0b0e0": "Stiff-legged Deadlifts",
+ "0f5fc602-afb6-4500-87da-f115f3ef3f47":
+ "Straight-arm Pull Down (bar Attachment)",
+ "2f7149c3-77ce-4313-a59c-aef82b5a730a":
+ "Straight-arm Pull Down (rope Attachment)",
+ "1eeccede-29c5-4f38-9ba7-d77c7c47993d": "T-Bar row",
+ "5915fabe-c941-4dac-b196-bc4e8c7ce57b": "Tricep Dumbbell Kickback",
+ "fc608f0e-d754-4911-ae59-8856952e7064": "Tricep Rope Pushdowns",
+ "6c671e43-497f-4863-8d78-b592f3b5e7c6": "Triceps on Machine",
+ "19133a0d-e6b2-4d57-8a84-8aac3d962a99": "Triceps Overhead (Dumbbell)",
+ "90e9f590-7d68-4afb-9f95-8429246ea4aa": "Trunk Rotation With Cable",
+ "309bfd2b-1af4-49db-b64b-d7d7c7dd39bb": "Turkish Get-Up",
+ "01976085-6701-45ea-b152-5c46ba60550d": "Upright Row w/ Dumbbells",
+ "738137c0-5387-4215-b457-ea7af113b3ba": "Upright Row, on Multi Press",
+ "5d40c67d-be59-4092-9c9c-301ca5310e2b": "Upright Row, SZ-bar",
+ "8d333b94-148c-48f8-9154-8b13279500d1": "Weighted Crunch",
+ "37abbe32-2fab-4818-8355-fffcff2dc3e3": "Weighted push-ups",
+ "591992cb-48ee-4b39-b790-e05b4a2c11e3": "Weighted Step-ups",
+ "55b9d286-c4cc-4a29-97ad-58cb13c2bb7e": "Wide-grip Pulldown",
+ "f6471f4a-0290-4a71-9580-bf3d046fdb9d": "Wrist curl, cable",
+ "2cc92db8-b245-44b8-b1d6-bf91c78c2ab9": "Wrist curl, dumbbells",
+ "9564ab2d-2534-4b47-bdf7-f2f653aae3de": "Zottman curl",
+};