feat(train): interval plans, synced and sealed, with an editor and templates (ADR 0030, D42)

Train's server gains the synced `plan` table, a sealed payload alone;
the migration `plans` only adds it, its space index and the foreign key
to its sync space, and a person's deletion takes it with their space.

The app keeps plans as `plan` rows sealed for their row: a name, what
they are for, whether a step off target is told, and blocks of steps
repeated, each ending after a time, a distance or a lap tap and aiming
at a pace, speed, heart rate or zone, or the rower's split, watts or
stroke rate. Home's Plans tab lists them, a new one starts from nothing
or from a template (4×4 minutes, Tabata, 10×400 m), the editor changes
steps on a sheet, and a deletion asks first and can be undone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-28 22:35:22 +01:00
commit f406ddcc59
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 23608 additions and 10 deletions

View file

@ -241,6 +241,76 @@
"loopFormSave": "Band einrichten",
"homeWorkouts": "Workouts",
"homeDays": "Tage",
"homePlans": "Pläne",
"plansNew": "Neuer Plan",
"plansNewEmpty": "Leer",
"plansEmptyTitle": "Noch keine Pläne",
"plansEmptyHint": "Die Schritte eines Plans führen dich beim Aufzeichnen durch Intervalle, mit Ton, Vibration und Stimme bei jedem Wechsel. Beginne mit einer Vorlage oder leer.",
"planLocked": "Ein Plan, der sich hier nicht öffnet",
"planTemplateFourByFour": "4×4 Minuten",
"planTemplateTabata": "Tabata",
"planTemplateFourHundreds": "10×400 m",
"planTitle": "Plan",
"planNewTitle": "Neuer Plan",
"planName": "Name",
"planActivity": "Für",
"planActivityAny": "Jede Aktivität",
"planOffTarget": "Sagen, wenn ich vom Ziel abweiche",
"planOffTargetHint": "Ein kurzer Hinweis, höchstens alle 30 Sekunden.",
"planSave": "Speichern",
"planNotSaved": "Der Plan wurde nicht gespeichert.",
"planAddStep": "Schritt hinzufügen",
"planAddRepeat": "Wiederholung hinzufügen",
"planRepeat": "{count}×",
"planFewer": "Weniger Runden",
"planMore": "Mehr Runden",
"planBlockMenu": "Mehr",
"planMoveUp": "Nach oben",
"planMoveDown": "Nach unten",
"planRemove": "Entfernen",
"planDelete": "Löschen",
"planDeleteTitle": "Diesen Plan löschen?",
"planDeleteMessage": "Er wird auf all deinen Geräten gelöscht; Trainings, die mit ihm aufgezeichnet wurden, behalten ihre Schritte.",
"planDeleted": "Plan gelöscht",
"planSteps": "{count, plural, =1{1 Schritt} other{{count} Schritte}}",
"stepTitle": "Schritt",
"stepKind": "Art",
"stepWarmUp": "Aufwärmen",
"stepWork": "Belastung",
"stepRecovery": "Erholung",
"stepCoolDown": "Auslaufen",
"stepEnds": "Endet",
"stepEndTime": "Nach einer Zeit",
"stepEndDistance": "Nach einer Strecke",
"stepEndLap": "Mit der Rundentaste",
"stepTimeLabel": "Zeit (min:s)",
"stepMetresLabel": "Meter",
"stepTarget": "Ziel",
"stepZone": "Zone",
"stepDone": "Fertig",
"stepMinutes": "{minutes} min",
"stepSeconds": "{seconds} s",
"stepMinutesSeconds": "{minutes} min {seconds} s",
"stepMetres": "{metres} m",
"stepKilometres": "{km} km",
"stepUntilLap": "bis zur Rundentaste",
"targetNone": "Keins",
"targetFrom": "Von",
"targetTo": "Bis",
"targetKindPace": "Tempo (pro km)",
"targetKindSpeed": "Geschwindigkeit (km/h)",
"targetKindHeartRate": "Herzfrequenz (Schläge/min)",
"targetKindHeartZone": "Herzfrequenzzone",
"targetKindSplit": "Split (pro 500 m)",
"targetKindWatts": "Leistung (W)",
"targetKindStrokeRate": "Schlagzahl (pro min)",
"targetPace": "{from}–{to} pro km",
"targetSpeed": "{from}–{to} km/h",
"targetHeartRate": "{from}–{to} Schläge/min",
"targetZone": "Zone {zone}",
"targetSplit": "{from}–{to} pro 500 m",
"targetWatts": "{from}–{to} W",
"targetStrokeRate": "{from}–{to} Schläge/min",
"daysEmptyTitle": "Noch keine Tage",
"daysEmptyHint": "Die Tage und Nächte, die dein Polar Loop zählt, erscheinen hier.",
"daysSteps": "{steps, plural, =1{1 Schritt} other{{steps} Schritte}}",

View file

@ -300,6 +300,90 @@
"loopFormSave": "Set up the band",
"homeWorkouts": "Workouts",
"homeDays": "Days",
"homePlans": "Plans",
"plansNew": "New plan",
"plansNewEmpty": "From nothing",
"plansEmptyTitle": "No plans yet",
"plansEmptyHint": "A plan's steps lead you through intervals as you record, with a tone, a vibration and a voice at each change. Start from a template, or from nothing.",
"planLocked": "A plan that doesn't open here",
"planTemplateFourByFour": "4×4 minutes",
"planTemplateTabata": "Tabata",
"planTemplateFourHundreds": "10×400 m",
"planTitle": "Plan",
"planNewTitle": "New plan",
"planName": "Name",
"planActivity": "For",
"planActivityAny": "Any activity",
"planOffTarget": "Tell me when I'm off target",
"planOffTargetHint": "A short cue at most every 30 seconds.",
"planSave": "Save",
"planNotSaved": "The plan wasn't saved.",
"planAddStep": "Add a step",
"planAddRepeat": "Add a repeat",
"planRepeat": "{count}×",
"@planRepeat": {"placeholders": {"count": {"type": "int"}}},
"planFewer": "Fewer rounds",
"planMore": "More rounds",
"planBlockMenu": "More",
"planMoveUp": "Move up",
"planMoveDown": "Move down",
"planRemove": "Remove",
"planDelete": "Delete",
"planDeleteTitle": "Delete this plan?",
"planDeleteMessage": "It's deleted on all your devices; workouts recorded with it keep their steps.",
"planDeleted": "Plan deleted",
"planSteps": "{count, plural, =1{1 step} other{{count} steps}}",
"@planSteps": {"placeholders": {"count": {"type": "int"}}},
"stepTitle": "Step",
"stepKind": "Kind",
"stepWarmUp": "Warm-up",
"stepWork": "Work",
"stepRecovery": "Recovery",
"stepCoolDown": "Cool-down",
"stepEnds": "Ends",
"stepEndTime": "After a time",
"stepEndDistance": "After a distance",
"stepEndLap": "With a lap tap",
"stepTimeLabel": "Time (min:s)",
"stepMetresLabel": "Metres",
"stepTarget": "Target",
"stepZone": "Zone",
"stepDone": "Done",
"stepMinutes": "{minutes} min",
"@stepMinutes": {"placeholders": {"minutes": {"type": "int"}}},
"stepSeconds": "{seconds} s",
"@stepSeconds": {"placeholders": {"seconds": {"type": "int"}}},
"stepMinutesSeconds": "{minutes} min {seconds} s",
"@stepMinutesSeconds": {"placeholders": {"minutes": {"type": "int"}, "seconds": {"type": "int"}}},
"stepMetres": "{metres} m",
"@stepMetres": {"placeholders": {"metres": {"type": "int"}}},
"stepKilometres": "{km} km",
"@stepKilometres": {"placeholders": {"km": {"type": "String"}}},
"stepUntilLap": "until a lap tap",
"targetNone": "None",
"targetFrom": "From",
"targetTo": "To",
"targetKindPace": "Pace (per km)",
"targetKindSpeed": "Speed (km/h)",
"targetKindHeartRate": "Heart rate (bpm)",
"targetKindHeartZone": "Heart-rate zone",
"targetKindSplit": "Split (per 500 m)",
"targetKindWatts": "Power (W)",
"targetKindStrokeRate": "Stroke rate (per min)",
"targetPace": "{from}–{to} per km",
"@targetPace": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"targetSpeed": "{from}–{to} km/h",
"@targetSpeed": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"targetHeartRate": "{from}–{to} bpm",
"@targetHeartRate": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"targetZone": "Zone {zone}",
"@targetZone": {"placeholders": {"zone": {"type": "int"}}},
"targetSplit": "{from}–{to} per 500 m",
"@targetSplit": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"targetWatts": "{from}–{to} W",
"@targetWatts": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"targetStrokeRate": "{from}–{to} strokes/min",
"@targetStrokeRate": {"placeholders": {"from": {"type": "String"}, "to": {"type": "String"}}},
"daysEmptyTitle": "No days yet",
"daysEmptyHint": "The days and nights your Polar Loop counts show here.",
"daysSteps": "{steps, plural, =1{1 step} other{{steps} steps}}",

View file

@ -1468,6 +1468,426 @@ abstract class AppLocalizations {
/// **'Days'**
String get homeDays;
/// No description provided for @homePlans.
///
/// In en, this message translates to:
/// **'Plans'**
String get homePlans;
/// No description provided for @plansNew.
///
/// In en, this message translates to:
/// **'New plan'**
String get plansNew;
/// No description provided for @plansNewEmpty.
///
/// In en, this message translates to:
/// **'From nothing'**
String get plansNewEmpty;
/// No description provided for @plansEmptyTitle.
///
/// In en, this message translates to:
/// **'No plans yet'**
String get plansEmptyTitle;
/// No description provided for @plansEmptyHint.
///
/// In en, this message translates to:
/// **'A plan\'s steps lead you through intervals as you record, with a tone, a vibration and a voice at each change. Start from a template, or from nothing.'**
String get plansEmptyHint;
/// No description provided for @planLocked.
///
/// In en, this message translates to:
/// **'A plan that doesn\'t open here'**
String get planLocked;
/// No description provided for @planTemplateFourByFour.
///
/// In en, this message translates to:
/// **'4×4 minutes'**
String get planTemplateFourByFour;
/// No description provided for @planTemplateTabata.
///
/// In en, this message translates to:
/// **'Tabata'**
String get planTemplateTabata;
/// No description provided for @planTemplateFourHundreds.
///
/// In en, this message translates to:
/// **'10×400 m'**
String get planTemplateFourHundreds;
/// No description provided for @planTitle.
///
/// In en, this message translates to:
/// **'Plan'**
String get planTitle;
/// No description provided for @planNewTitle.
///
/// In en, this message translates to:
/// **'New plan'**
String get planNewTitle;
/// No description provided for @planName.
///
/// In en, this message translates to:
/// **'Name'**
String get planName;
/// No description provided for @planActivity.
///
/// In en, this message translates to:
/// **'For'**
String get planActivity;
/// No description provided for @planActivityAny.
///
/// In en, this message translates to:
/// **'Any activity'**
String get planActivityAny;
/// No description provided for @planOffTarget.
///
/// In en, this message translates to:
/// **'Tell me when I\'m off target'**
String get planOffTarget;
/// No description provided for @planOffTargetHint.
///
/// In en, this message translates to:
/// **'A short cue at most every 30 seconds.'**
String get planOffTargetHint;
/// No description provided for @planSave.
///
/// In en, this message translates to:
/// **'Save'**
String get planSave;
/// No description provided for @planNotSaved.
///
/// In en, this message translates to:
/// **'The plan wasn\'t saved.'**
String get planNotSaved;
/// No description provided for @planAddStep.
///
/// In en, this message translates to:
/// **'Add a step'**
String get planAddStep;
/// No description provided for @planAddRepeat.
///
/// In en, this message translates to:
/// **'Add a repeat'**
String get planAddRepeat;
/// No description provided for @planRepeat.
///
/// In en, this message translates to:
/// **'{count}×'**
String planRepeat(int count);
/// No description provided for @planFewer.
///
/// In en, this message translates to:
/// **'Fewer rounds'**
String get planFewer;
/// No description provided for @planMore.
///
/// In en, this message translates to:
/// **'More rounds'**
String get planMore;
/// No description provided for @planBlockMenu.
///
/// In en, this message translates to:
/// **'More'**
String get planBlockMenu;
/// No description provided for @planMoveUp.
///
/// In en, this message translates to:
/// **'Move up'**
String get planMoveUp;
/// No description provided for @planMoveDown.
///
/// In en, this message translates to:
/// **'Move down'**
String get planMoveDown;
/// No description provided for @planRemove.
///
/// In en, this message translates to:
/// **'Remove'**
String get planRemove;
/// No description provided for @planDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get planDelete;
/// No description provided for @planDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this plan?'**
String get planDeleteTitle;
/// No description provided for @planDeleteMessage.
///
/// In en, this message translates to:
/// **'It\'s deleted on all your devices; workouts recorded with it keep their steps.'**
String get planDeleteMessage;
/// No description provided for @planDeleted.
///
/// In en, this message translates to:
/// **'Plan deleted'**
String get planDeleted;
/// No description provided for @planSteps.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 step} other{{count} steps}}'**
String planSteps(int count);
/// No description provided for @stepTitle.
///
/// In en, this message translates to:
/// **'Step'**
String get stepTitle;
/// No description provided for @stepKind.
///
/// In en, this message translates to:
/// **'Kind'**
String get stepKind;
/// No description provided for @stepWarmUp.
///
/// In en, this message translates to:
/// **'Warm-up'**
String get stepWarmUp;
/// No description provided for @stepWork.
///
/// In en, this message translates to:
/// **'Work'**
String get stepWork;
/// No description provided for @stepRecovery.
///
/// In en, this message translates to:
/// **'Recovery'**
String get stepRecovery;
/// No description provided for @stepCoolDown.
///
/// In en, this message translates to:
/// **'Cool-down'**
String get stepCoolDown;
/// No description provided for @stepEnds.
///
/// In en, this message translates to:
/// **'Ends'**
String get stepEnds;
/// No description provided for @stepEndTime.
///
/// In en, this message translates to:
/// **'After a time'**
String get stepEndTime;
/// No description provided for @stepEndDistance.
///
/// In en, this message translates to:
/// **'After a distance'**
String get stepEndDistance;
/// No description provided for @stepEndLap.
///
/// In en, this message translates to:
/// **'With a lap tap'**
String get stepEndLap;
/// No description provided for @stepTimeLabel.
///
/// In en, this message translates to:
/// **'Time (min:s)'**
String get stepTimeLabel;
/// No description provided for @stepMetresLabel.
///
/// In en, this message translates to:
/// **'Metres'**
String get stepMetresLabel;
/// No description provided for @stepTarget.
///
/// In en, this message translates to:
/// **'Target'**
String get stepTarget;
/// No description provided for @stepZone.
///
/// In en, this message translates to:
/// **'Zone'**
String get stepZone;
/// No description provided for @stepDone.
///
/// In en, this message translates to:
/// **'Done'**
String get stepDone;
/// No description provided for @stepMinutes.
///
/// In en, this message translates to:
/// **'{minutes} min'**
String stepMinutes(int minutes);
/// No description provided for @stepSeconds.
///
/// In en, this message translates to:
/// **'{seconds} s'**
String stepSeconds(int seconds);
/// No description provided for @stepMinutesSeconds.
///
/// In en, this message translates to:
/// **'{minutes} min {seconds} s'**
String stepMinutesSeconds(int minutes, int seconds);
/// No description provided for @stepMetres.
///
/// In en, this message translates to:
/// **'{metres} m'**
String stepMetres(int metres);
/// No description provided for @stepKilometres.
///
/// In en, this message translates to:
/// **'{km} km'**
String stepKilometres(String km);
/// No description provided for @stepUntilLap.
///
/// In en, this message translates to:
/// **'until a lap tap'**
String get stepUntilLap;
/// No description provided for @targetNone.
///
/// In en, this message translates to:
/// **'None'**
String get targetNone;
/// No description provided for @targetFrom.
///
/// In en, this message translates to:
/// **'From'**
String get targetFrom;
/// No description provided for @targetTo.
///
/// In en, this message translates to:
/// **'To'**
String get targetTo;
/// No description provided for @targetKindPace.
///
/// In en, this message translates to:
/// **'Pace (per km)'**
String get targetKindPace;
/// No description provided for @targetKindSpeed.
///
/// In en, this message translates to:
/// **'Speed (km/h)'**
String get targetKindSpeed;
/// No description provided for @targetKindHeartRate.
///
/// In en, this message translates to:
/// **'Heart rate (bpm)'**
String get targetKindHeartRate;
/// No description provided for @targetKindHeartZone.
///
/// In en, this message translates to:
/// **'Heart-rate zone'**
String get targetKindHeartZone;
/// No description provided for @targetKindSplit.
///
/// In en, this message translates to:
/// **'Split (per 500 m)'**
String get targetKindSplit;
/// No description provided for @targetKindWatts.
///
/// In en, this message translates to:
/// **'Power (W)'**
String get targetKindWatts;
/// No description provided for @targetKindStrokeRate.
///
/// In en, this message translates to:
/// **'Stroke rate (per min)'**
String get targetKindStrokeRate;
/// No description provided for @targetPace.
///
/// In en, this message translates to:
/// **'{from}–{to} per km'**
String targetPace(String from, String to);
/// No description provided for @targetSpeed.
///
/// In en, this message translates to:
/// **'{from}–{to} km/h'**
String targetSpeed(String from, String to);
/// No description provided for @targetHeartRate.
///
/// In en, this message translates to:
/// **'{from}–{to} bpm'**
String targetHeartRate(String from, String to);
/// No description provided for @targetZone.
///
/// In en, this message translates to:
/// **'Zone {zone}'**
String targetZone(int zone);
/// No description provided for @targetSplit.
///
/// In en, this message translates to:
/// **'{from}–{to} per 500 m'**
String targetSplit(String from, String to);
/// No description provided for @targetWatts.
///
/// In en, this message translates to:
/// **'{from}–{to} W'**
String targetWatts(String from, String to);
/// No description provided for @targetStrokeRate.
///
/// In en, this message translates to:
/// **'{from}–{to} strokes/min'**
String targetStrokeRate(String from, String to);
/// No description provided for @daysEmptyTitle.
///
/// In en, this message translates to:

View file

@ -834,6 +834,253 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get homeDays => 'Tage';
@override
String get homePlans => 'Pläne';
@override
String get plansNew => 'Neuer Plan';
@override
String get plansNewEmpty => 'Leer';
@override
String get plansEmptyTitle => 'Noch keine Pläne';
@override
String get plansEmptyHint =>
'Die Schritte eines Plans führen dich beim Aufzeichnen durch Intervalle, mit Ton, Vibration und Stimme bei jedem Wechsel. Beginne mit einer Vorlage oder leer.';
@override
String get planLocked => 'Ein Plan, der sich hier nicht öffnet';
@override
String get planTemplateFourByFour => '4×4 Minuten';
@override
String get planTemplateTabata => 'Tabata';
@override
String get planTemplateFourHundreds => '10×400 m';
@override
String get planTitle => 'Plan';
@override
String get planNewTitle => 'Neuer Plan';
@override
String get planName => 'Name';
@override
String get planActivity => 'Für';
@override
String get planActivityAny => 'Jede Aktivität';
@override
String get planOffTarget => 'Sagen, wenn ich vom Ziel abweiche';
@override
String get planOffTargetHint =>
'Ein kurzer Hinweis, höchstens alle 30 Sekunden.';
@override
String get planSave => 'Speichern';
@override
String get planNotSaved => 'Der Plan wurde nicht gespeichert.';
@override
String get planAddStep => 'Schritt hinzufügen';
@override
String get planAddRepeat => 'Wiederholung hinzufügen';
@override
String planRepeat(int count) {
return '$count×';
}
@override
String get planFewer => 'Weniger Runden';
@override
String get planMore => 'Mehr Runden';
@override
String get planBlockMenu => 'Mehr';
@override
String get planMoveUp => 'Nach oben';
@override
String get planMoveDown => 'Nach unten';
@override
String get planRemove => 'Entfernen';
@override
String get planDelete => 'Löschen';
@override
String get planDeleteTitle => 'Diesen Plan löschen?';
@override
String get planDeleteMessage =>
'Er wird auf all deinen Geräten gelöscht; Trainings, die mit ihm aufgezeichnet wurden, behalten ihre Schritte.';
@override
String get planDeleted => 'Plan gelöscht';
@override
String planSteps(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count Schritte',
one: '1 Schritt',
);
return '$_temp0';
}
@override
String get stepTitle => 'Schritt';
@override
String get stepKind => 'Art';
@override
String get stepWarmUp => 'Aufwärmen';
@override
String get stepWork => 'Belastung';
@override
String get stepRecovery => 'Erholung';
@override
String get stepCoolDown => 'Auslaufen';
@override
String get stepEnds => 'Endet';
@override
String get stepEndTime => 'Nach einer Zeit';
@override
String get stepEndDistance => 'Nach einer Strecke';
@override
String get stepEndLap => 'Mit der Rundentaste';
@override
String get stepTimeLabel => 'Zeit (min:s)';
@override
String get stepMetresLabel => 'Meter';
@override
String get stepTarget => 'Ziel';
@override
String get stepZone => 'Zone';
@override
String get stepDone => 'Fertig';
@override
String stepMinutes(int minutes) {
return '$minutes min';
}
@override
String stepSeconds(int seconds) {
return '$seconds s';
}
@override
String stepMinutesSeconds(int minutes, int seconds) {
return '$minutes min $seconds s';
}
@override
String stepMetres(int metres) {
return '$metres m';
}
@override
String stepKilometres(String km) {
return '$km km';
}
@override
String get stepUntilLap => 'bis zur Rundentaste';
@override
String get targetNone => 'Keins';
@override
String get targetFrom => 'Von';
@override
String get targetTo => 'Bis';
@override
String get targetKindPace => 'Tempo (pro km)';
@override
String get targetKindSpeed => 'Geschwindigkeit (km/h)';
@override
String get targetKindHeartRate => 'Herzfrequenz (Schläge/min)';
@override
String get targetKindHeartZone => 'Herzfrequenzzone';
@override
String get targetKindSplit => 'Split (pro 500 m)';
@override
String get targetKindWatts => 'Leistung (W)';
@override
String get targetKindStrokeRate => 'Schlagzahl (pro min)';
@override
String targetPace(String from, String to) {
return '$from–$to pro km';
}
@override
String targetSpeed(String from, String to) {
return '$from–$to km/h';
}
@override
String targetHeartRate(String from, String to) {
return '$from–$to Schläge/min';
}
@override
String targetZone(int zone) {
return 'Zone $zone';
}
@override
String targetSplit(String from, String to) {
return '$from–$to pro 500 m';
}
@override
String targetWatts(String from, String to) {
return '$from–$to W';
}
@override
String targetStrokeRate(String from, String to) {
return '$from–$to Schläge/min';
}
@override
String get daysEmptyTitle => 'Noch keine Tage';

View file

@ -828,6 +828,252 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get homeDays => 'Days';
@override
String get homePlans => 'Plans';
@override
String get plansNew => 'New plan';
@override
String get plansNewEmpty => 'From nothing';
@override
String get plansEmptyTitle => 'No plans yet';
@override
String get plansEmptyHint =>
'A plan\'s steps lead you through intervals as you record, with a tone, a vibration and a voice at each change. Start from a template, or from nothing.';
@override
String get planLocked => 'A plan that doesn\'t open here';
@override
String get planTemplateFourByFour => '4×4 minutes';
@override
String get planTemplateTabata => 'Tabata';
@override
String get planTemplateFourHundreds => '10×400 m';
@override
String get planTitle => 'Plan';
@override
String get planNewTitle => 'New plan';
@override
String get planName => 'Name';
@override
String get planActivity => 'For';
@override
String get planActivityAny => 'Any activity';
@override
String get planOffTarget => 'Tell me when I\'m off target';
@override
String get planOffTargetHint => 'A short cue at most every 30 seconds.';
@override
String get planSave => 'Save';
@override
String get planNotSaved => 'The plan wasn\'t saved.';
@override
String get planAddStep => 'Add a step';
@override
String get planAddRepeat => 'Add a repeat';
@override
String planRepeat(int count) {
return '$count×';
}
@override
String get planFewer => 'Fewer rounds';
@override
String get planMore => 'More rounds';
@override
String get planBlockMenu => 'More';
@override
String get planMoveUp => 'Move up';
@override
String get planMoveDown => 'Move down';
@override
String get planRemove => 'Remove';
@override
String get planDelete => 'Delete';
@override
String get planDeleteTitle => 'Delete this plan?';
@override
String get planDeleteMessage =>
'It\'s deleted on all your devices; workouts recorded with it keep their steps.';
@override
String get planDeleted => 'Plan deleted';
@override
String planSteps(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count steps',
one: '1 step',
);
return '$_temp0';
}
@override
String get stepTitle => 'Step';
@override
String get stepKind => 'Kind';
@override
String get stepWarmUp => 'Warm-up';
@override
String get stepWork => 'Work';
@override
String get stepRecovery => 'Recovery';
@override
String get stepCoolDown => 'Cool-down';
@override
String get stepEnds => 'Ends';
@override
String get stepEndTime => 'After a time';
@override
String get stepEndDistance => 'After a distance';
@override
String get stepEndLap => 'With a lap tap';
@override
String get stepTimeLabel => 'Time (min:s)';
@override
String get stepMetresLabel => 'Metres';
@override
String get stepTarget => 'Target';
@override
String get stepZone => 'Zone';
@override
String get stepDone => 'Done';
@override
String stepMinutes(int minutes) {
return '$minutes min';
}
@override
String stepSeconds(int seconds) {
return '$seconds s';
}
@override
String stepMinutesSeconds(int minutes, int seconds) {
return '$minutes min $seconds s';
}
@override
String stepMetres(int metres) {
return '$metres m';
}
@override
String stepKilometres(String km) {
return '$km km';
}
@override
String get stepUntilLap => 'until a lap tap';
@override
String get targetNone => 'None';
@override
String get targetFrom => 'From';
@override
String get targetTo => 'To';
@override
String get targetKindPace => 'Pace (per km)';
@override
String get targetKindSpeed => 'Speed (km/h)';
@override
String get targetKindHeartRate => 'Heart rate (bpm)';
@override
String get targetKindHeartZone => 'Heart-rate zone';
@override
String get targetKindSplit => 'Split (per 500 m)';
@override
String get targetKindWatts => 'Power (W)';
@override
String get targetKindStrokeRate => 'Stroke rate (per min)';
@override
String targetPace(String from, String to) {
return '$from–$to per km';
}
@override
String targetSpeed(String from, String to) {
return '$from–$to km/h';
}
@override
String targetHeartRate(String from, String to) {
return '$from–$to bpm';
}
@override
String targetZone(int zone) {
return 'Zone $zone';
}
@override
String targetSplit(String from, String to) {
return '$from–$to per 500 m';
}
@override
String targetWatts(String from, String to) {
return '$from–$to W';
}
@override
String targetStrokeRate(String from, String to) {
return '$from–$to strokes/min';
}
@override
String get daysEmptyTitle => 'No days yet';