feat(train): the body's figures in a sealed, synced profile (ADR 0030, D33, D34)

A person's profile, one row per person (its ID derived from theirs),
sealed like a workout: sex, birth date, height, weight, resting and
maximum heart rate, VO2max, training background, typical day and sleep
goal, each optional. "Your body" in the account sheet edits it; the
Loop's setup is to reuse its form. The migration only adds the synced
table `profile`, deleted with the person's sync space.

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 18:02:04 +01:00
commit de6fe5c15a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 20185 additions and 9 deletions

View file

@ -153,5 +153,40 @@
"workoutFromList": "von {app}",
"workoutFrom": "Von {app}, über Health Connect",
"workoutBringTrack": "Strecke holen",
"workoutNoTrack": "Health Connect hat keine Strecke dazu gegeben."
"workoutNoTrack": "Health Connect hat keine Strecke dazu gegeben.",
"profileTitle": "Dein Körper",
"profileHint": "Woraus dein Armband und deine Herzfrequenzzonen rechnen",
"profileIntro": "Jede Angabe ist freiwillig und mit deinem Schlüssel versiegelt, wie deine Trainings; deine Instanz kann sie nicht lesen.",
"profileLocked": "Dein Profil ist mit einem Schlüssel versiegelt, den dieses Gerät nicht hat.",
"profileSex": "Geschlecht",
"profileSexHint": "Wie Polars Formeln es nehmen: das, dem die Werte deines Körpers folgen",
"profileSexFemale": "Weiblich",
"profileSexMale": "Männlich",
"profileNotGiven": "Keine Angabe",
"profileBirthDate": "Geburtsdatum",
"profileHeight": "Größe",
"profileHeightUnit": "cm",
"profileWeight": "Gewicht",
"profileWeightUnit": "kg",
"profileRestingHeartRate": "Ruhepuls",
"profileMaxHeartRate": "Höchste Herzfrequenz",
"profileBpmUnit": "S/min",
"profileVo2Max": "VO2max",
"profileVo2MaxUnit": "ml/kg/min",
"profileTrainingBackground": "Trainingshintergrund",
"trainingOccasional": "Gelegentlich",
"trainingRegular": "Regelmäßig",
"trainingFrequent": "Häufig",
"trainingHeavy": "Intensiv",
"trainingSemiPro": "Halbprofi",
"trainingPro": "Profi",
"profileTypicalDay": "Typischer Tag",
"typicalDaySitting": "Meist sitzend",
"typicalDayStanding": "Meist stehend",
"typicalDayMoving": "Meist in Bewegung",
"profileSleepGoal": "Schlafziel",
"profileSave": "Speichern",
"profileSaved": "Gespeichert",
"profileInvalid": "Prüfe die markierten Angaben."
}

View file

@ -203,5 +203,41 @@
"@workoutFrom": {"description": "On an imported workout's page.", "placeholders": {"app": {"type": "String", "example": "Gadgetbridge"}}},
"workoutBringTrack": "Bring in the track",
"@workoutBringTrack": {"description": "On an imported workout's page: asks Health Connect for its route."},
"workoutNoTrack": "Health Connect gave no track for it."
"workoutNoTrack": "Health Connect gave no track for it.",
"profileTitle": "Your body",
"profileHint": "What your band and your heart-rate zones work from",
"@profileHint": {"description": "Under Your body in the account sheet."},
"profileIntro": "Each figure is optional and sealed with your key, as your workouts are; your instance can't read them.",
"profileLocked": "Your profile is sealed with a key this device doesn't hold.",
"profileSex": "Sex",
"profileSexHint": "As Polar's formulas take it: the one your body's figures follow",
"profileSexFemale": "Female",
"profileSexMale": "Male",
"profileNotGiven": "Not given",
"profileBirthDate": "Birth date",
"profileHeight": "Height",
"profileHeightUnit": "cm",
"profileWeight": "Weight",
"profileWeightUnit": "kg",
"profileRestingHeartRate": "Resting heart rate",
"profileMaxHeartRate": "Highest heart rate",
"profileBpmUnit": "bpm",
"profileVo2Max": "VO2max",
"profileVo2MaxUnit": "ml/kg/min",
"profileTrainingBackground": "Training background",
"trainingOccasional": "Occasional",
"trainingRegular": "Regular",
"trainingFrequent": "Frequent",
"trainingHeavy": "Heavy",
"trainingSemiPro": "Semi-pro",
"trainingPro": "Pro",
"profileTypicalDay": "Typical day",
"typicalDaySitting": "Mostly sitting",
"typicalDayStanding": "Mostly standing",
"typicalDayMoving": "Mostly moving",
"profileSleepGoal": "Sleep goal",
"profileSave": "Save",
"profileSaved": "Saved",
"profileInvalid": "Check the figures marked."
}

View file

@ -957,6 +957,210 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Health Connect gave no track for it.'**
String get workoutNoTrack;
/// No description provided for @profileTitle.
///
/// In en, this message translates to:
/// **'Your body'**
String get profileTitle;
/// Under Your body in the account sheet.
///
/// In en, this message translates to:
/// **'What your band and your heart-rate zones work from'**
String get profileHint;
/// No description provided for @profileIntro.
///
/// In en, this message translates to:
/// **'Each figure is optional and sealed with your key, as your workouts are; your instance can\'t read them.'**
String get profileIntro;
/// No description provided for @profileLocked.
///
/// In en, this message translates to:
/// **'Your profile is sealed with a key this device doesn\'t hold.'**
String get profileLocked;
/// No description provided for @profileSex.
///
/// In en, this message translates to:
/// **'Sex'**
String get profileSex;
/// No description provided for @profileSexHint.
///
/// In en, this message translates to:
/// **'As Polar\'s formulas take it: the one your body\'s figures follow'**
String get profileSexHint;
/// No description provided for @profileSexFemale.
///
/// In en, this message translates to:
/// **'Female'**
String get profileSexFemale;
/// No description provided for @profileSexMale.
///
/// In en, this message translates to:
/// **'Male'**
String get profileSexMale;
/// No description provided for @profileNotGiven.
///
/// In en, this message translates to:
/// **'Not given'**
String get profileNotGiven;
/// No description provided for @profileBirthDate.
///
/// In en, this message translates to:
/// **'Birth date'**
String get profileBirthDate;
/// No description provided for @profileHeight.
///
/// In en, this message translates to:
/// **'Height'**
String get profileHeight;
/// No description provided for @profileHeightUnit.
///
/// In en, this message translates to:
/// **'cm'**
String get profileHeightUnit;
/// No description provided for @profileWeight.
///
/// In en, this message translates to:
/// **'Weight'**
String get profileWeight;
/// No description provided for @profileWeightUnit.
///
/// In en, this message translates to:
/// **'kg'**
String get profileWeightUnit;
/// No description provided for @profileRestingHeartRate.
///
/// In en, this message translates to:
/// **'Resting heart rate'**
String get profileRestingHeartRate;
/// No description provided for @profileMaxHeartRate.
///
/// In en, this message translates to:
/// **'Highest heart rate'**
String get profileMaxHeartRate;
/// No description provided for @profileBpmUnit.
///
/// In en, this message translates to:
/// **'bpm'**
String get profileBpmUnit;
/// No description provided for @profileVo2Max.
///
/// In en, this message translates to:
/// **'VO2max'**
String get profileVo2Max;
/// No description provided for @profileVo2MaxUnit.
///
/// In en, this message translates to:
/// **'ml/kg/min'**
String get profileVo2MaxUnit;
/// No description provided for @profileTrainingBackground.
///
/// In en, this message translates to:
/// **'Training background'**
String get profileTrainingBackground;
/// No description provided for @trainingOccasional.
///
/// In en, this message translates to:
/// **'Occasional'**
String get trainingOccasional;
/// No description provided for @trainingRegular.
///
/// In en, this message translates to:
/// **'Regular'**
String get trainingRegular;
/// No description provided for @trainingFrequent.
///
/// In en, this message translates to:
/// **'Frequent'**
String get trainingFrequent;
/// No description provided for @trainingHeavy.
///
/// In en, this message translates to:
/// **'Heavy'**
String get trainingHeavy;
/// No description provided for @trainingSemiPro.
///
/// In en, this message translates to:
/// **'Semi-pro'**
String get trainingSemiPro;
/// No description provided for @trainingPro.
///
/// In en, this message translates to:
/// **'Pro'**
String get trainingPro;
/// No description provided for @profileTypicalDay.
///
/// In en, this message translates to:
/// **'Typical day'**
String get profileTypicalDay;
/// No description provided for @typicalDaySitting.
///
/// In en, this message translates to:
/// **'Mostly sitting'**
String get typicalDaySitting;
/// No description provided for @typicalDayStanding.
///
/// In en, this message translates to:
/// **'Mostly standing'**
String get typicalDayStanding;
/// No description provided for @typicalDayMoving.
///
/// In en, this message translates to:
/// **'Mostly moving'**
String get typicalDayMoving;
/// No description provided for @profileSleepGoal.
///
/// In en, this message translates to:
/// **'Sleep goal'**
String get profileSleepGoal;
/// No description provided for @profileSave.
///
/// In en, this message translates to:
/// **'Save'**
String get profileSave;
/// No description provided for @profileSaved.
///
/// In en, this message translates to:
/// **'Saved'**
String get profileSaved;
/// No description provided for @profileInvalid.
///
/// In en, this message translates to:
/// **'Check the figures marked.'**
String get profileInvalid;
}
class _AppLocalizationsDelegate

View file

@ -531,4 +531,110 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get workoutNoTrack => 'Health Connect hat keine Strecke dazu gegeben.';
@override
String get profileTitle => 'Dein Körper';
@override
String get profileHint =>
'Woraus dein Armband und deine Herzfrequenzzonen rechnen';
@override
String get profileIntro =>
'Jede Angabe ist freiwillig und mit deinem Schlüssel versiegelt, wie deine Trainings; deine Instanz kann sie nicht lesen.';
@override
String get profileLocked =>
'Dein Profil ist mit einem Schlüssel versiegelt, den dieses Gerät nicht hat.';
@override
String get profileSex => 'Geschlecht';
@override
String get profileSexHint =>
'Wie Polars Formeln es nehmen: das, dem die Werte deines Körpers folgen';
@override
String get profileSexFemale => 'Weiblich';
@override
String get profileSexMale => 'Männlich';
@override
String get profileNotGiven => 'Keine Angabe';
@override
String get profileBirthDate => 'Geburtsdatum';
@override
String get profileHeight => 'Größe';
@override
String get profileHeightUnit => 'cm';
@override
String get profileWeight => 'Gewicht';
@override
String get profileWeightUnit => 'kg';
@override
String get profileRestingHeartRate => 'Ruhepuls';
@override
String get profileMaxHeartRate => 'Höchste Herzfrequenz';
@override
String get profileBpmUnit => 'S/min';
@override
String get profileVo2Max => 'VO2max';
@override
String get profileVo2MaxUnit => 'ml/kg/min';
@override
String get profileTrainingBackground => 'Trainingshintergrund';
@override
String get trainingOccasional => 'Gelegentlich';
@override
String get trainingRegular => 'Regelmäßig';
@override
String get trainingFrequent => 'Häufig';
@override
String get trainingHeavy => 'Intensiv';
@override
String get trainingSemiPro => 'Halbprofi';
@override
String get trainingPro => 'Profi';
@override
String get profileTypicalDay => 'Typischer Tag';
@override
String get typicalDaySitting => 'Meist sitzend';
@override
String get typicalDayStanding => 'Meist stehend';
@override
String get typicalDayMoving => 'Meist in Bewegung';
@override
String get profileSleepGoal => 'Schlafziel';
@override
String get profileSave => 'Speichern';
@override
String get profileSaved => 'Gespeichert';
@override
String get profileInvalid => 'Prüfe die markierten Angaben.';
}

View file

@ -527,6 +527,112 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get workoutNoTrack => 'Health Connect gave no track for it.';
@override
String get profileTitle => 'Your body';
@override
String get profileHint =>
'What your band and your heart-rate zones work from';
@override
String get profileIntro =>
'Each figure is optional and sealed with your key, as your workouts are; your instance can\'t read them.';
@override
String get profileLocked =>
'Your profile is sealed with a key this device doesn\'t hold.';
@override
String get profileSex => 'Sex';
@override
String get profileSexHint =>
'As Polar\'s formulas take it: the one your body\'s figures follow';
@override
String get profileSexFemale => 'Female';
@override
String get profileSexMale => 'Male';
@override
String get profileNotGiven => 'Not given';
@override
String get profileBirthDate => 'Birth date';
@override
String get profileHeight => 'Height';
@override
String get profileHeightUnit => 'cm';
@override
String get profileWeight => 'Weight';
@override
String get profileWeightUnit => 'kg';
@override
String get profileRestingHeartRate => 'Resting heart rate';
@override
String get profileMaxHeartRate => 'Highest heart rate';
@override
String get profileBpmUnit => 'bpm';
@override
String get profileVo2Max => 'VO2max';
@override
String get profileVo2MaxUnit => 'ml/kg/min';
@override
String get profileTrainingBackground => 'Training background';
@override
String get trainingOccasional => 'Occasional';
@override
String get trainingRegular => 'Regular';
@override
String get trainingFrequent => 'Frequent';
@override
String get trainingHeavy => 'Heavy';
@override
String get trainingSemiPro => 'Semi-pro';
@override
String get trainingPro => 'Pro';
@override
String get profileTypicalDay => 'Typical day';
@override
String get typicalDaySitting => 'Mostly sitting';
@override
String get typicalDayStanding => 'Mostly standing';
@override
String get typicalDayMoving => 'Mostly moving';
@override
String get profileSleepGoal => 'Sleep goal';
@override
String get profileSave => 'Save';
@override
String get profileSaved => 'Saved';
@override
String get profileInvalid => 'Check the figures marked.';
}
/// The translations for English, as used in the United Kingdom (`en_GB`).