feat(train): Halkyon Train's shell, Todo's copied and renamed (ADR 0030, D12)
apps/train with halkyon_train_server, _client and _flutter, as Todo's:
halkyon_base configured by trainBase(), its own register alone and a
member of the instance's where the operator names one, the app on
halkyon_app's gates with Train's frame, account sheet and About, texts
in British English and German that speak of workouts, app.halkyon.train
on Android, iOS, Linux, macOS and Windows.
Where it differs from Todo: development ports of its own (8180 to 8190),
so it runs beside Learn's server; one initial migration with the schema
Todo's migrations end at (the same tables, compared line by line), not
Todo's seventeen; and Todo's icon as a placeholder until the icon tool
of ADR 0031 draws Train's bike. The workspace, the licences, the READMEs
and AGENTS.md name it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 10:26:49 +01:00
import ' dart:async ' ;
import ' package:flutter/foundation.dart ' ;
import ' package:flutter/widgets.dart ' ;
import ' package:flutter_localizations/flutter_localizations.dart ' ;
import ' package:intl/intl.dart ' as intl ;
import ' app_localizations_de.dart ' ;
import ' app_localizations_en.dart ' ;
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of AppLocalizations
/// returned by `AppLocalizations.of(context)`.
///
/// Applications need to include `AppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
/// supportedLocales: AppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ ll need to edit this
/// file.
///
/// First, open your project’ s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’ s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations ( String locale )
: localeName = intl . Intl . canonicalizedLocale ( locale . toString ( ) ) ;
final String localeName ;
static AppLocalizations of ( BuildContext context ) {
return Localizations . of < AppLocalizations > ( context , AppLocalizations ) ! ;
}
static const LocalizationsDelegate < AppLocalizations > delegate =
_AppLocalizationsDelegate ( ) ;
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List < LocalizationsDelegate < dynamic > > localizationsDelegates =
< LocalizationsDelegate < dynamic > > [
delegate ,
GlobalMaterialLocalizations . delegate ,
GlobalCupertinoLocalizations . delegate ,
GlobalWidgetsLocalizations . delegate ,
] ;
/// A list of this localizations delegate's supported locales.
static const List < Locale > supportedLocales = < Locale > [
Locale ( ' de ' ) ,
Locale ( ' en ' ) ,
Locale ( ' en ' , ' GB ' ) ,
Locale ( ' en ' , ' US ' ) ,
] ;
/// The app's name: the window's title, and where halkyon_app's texts name the app.
///
/// In en, this message translates to:
/// **'Halkyon Train'**
String get appTitle ;
/// The app's word in its wordmark, under "Halkyon".
///
/// In en, this message translates to:
/// **'Train'**
String get launchProduct ;
/// No description provided for @instanceFirstStartSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance to keep your workouts on. Someone runs each one; you can add others later.'**
String get instanceFirstStartSubtitle ;
/// No description provided for @homeEmptyTitle.
///
/// In en, this message translates to:
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// **'No workouts yet'**
feat(train): Halkyon Train's shell, Todo's copied and renamed (ADR 0030, D12)
apps/train with halkyon_train_server, _client and _flutter, as Todo's:
halkyon_base configured by trainBase(), its own register alone and a
member of the instance's where the operator names one, the app on
halkyon_app's gates with Train's frame, account sheet and About, texts
in British English and German that speak of workouts, app.halkyon.train
on Android, iOS, Linux, macOS and Windows.
Where it differs from Todo: development ports of its own (8180 to 8190),
so it runs beside Learn's server; one initial migration with the schema
Todo's migrations end at (the same tables, compared line by line), not
Todo's seventeen; and Todo's icon as a placeholder until the icon tool
of ADR 0031 draws Train's bike. The workspace, the licences, the READMEs
and AGENTS.md name it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 10:26:49 +01:00
String get homeEmptyTitle ;
/// No description provided for @homeEmptyHint.
///
/// In en, this message translates to:
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// **'Workouts recorded on your phone show here.'**
feat(train): Halkyon Train's shell, Todo's copied and renamed (ADR 0030, D12)
apps/train with halkyon_train_server, _client and _flutter, as Todo's:
halkyon_base configured by trainBase(), its own register alone and a
member of the instance's where the operator names one, the app on
halkyon_app's gates with Train's frame, account sheet and About, texts
in British English and German that speak of workouts, app.halkyon.train
on Android, iOS, Linux, macOS and Windows.
Where it differs from Todo: development ports of its own (8180 to 8190),
so it runs beside Learn's server; one initial migration with the schema
Todo's migrations end at (the same tables, compared line by line), not
Todo's seventeen; and Todo's icon as a placeholder until the icon tool
of ADR 0031 draws Train's bike. The workspace, the licences, the READMEs
and AGENTS.md name it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 10:26:49 +01:00
String get homeEmptyHint ;
/// No description provided for @accountTitle.
///
/// In en, this message translates to:
/// **'Account'**
String get accountTitle ;
/// No description provided for @accountDeleteMessage.
///
/// In en, this message translates to:
/// **'Your account is deleted on the server and on all your devices. This can\'t be undone.'**
String get accountDeleteMessage ;
/// No description provided for @aboutTitle.
///
/// In en, this message translates to:
feat(train): Settings over the tabs from a cog on Home and Profile, the account near its foot (ADR 0030, D60)
As Learn's: a cog atop Home and Profile, with the count of accounts
waiting for whoever operates the instance, opens Settings over the tabs,
where an account sheet had held the account and the training's screens.
Grouped in cards: the operator's card, the training (your body, Health
Connect, the Polar Loop, music, those the device has), the account near
the foot, About Halkyon Train last, and the version small at the very
bottom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 00:16:01 +01:00
/// **'About Halkyon Train'**
feat(train): Halkyon Train's shell, Todo's copied and renamed (ADR 0030, D12)
apps/train with halkyon_train_server, _client and _flutter, as Todo's:
halkyon_base configured by trainBase(), its own register alone and a
member of the instance's where the operator names one, the app on
halkyon_app's gates with Train's frame, account sheet and About, texts
in British English and German that speak of workouts, app.halkyon.train
on Android, iOS, Linux, macOS and Windows.
Where it differs from Todo: development ports of its own (8180 to 8190),
so it runs beside Learn's server; one initial migration with the schema
Todo's migrations end at (the same tables, compared line by line), not
Todo's seventeen; and Todo's icon as a placeholder until the icon tool
of ADR 0031 draws Train's bike. The workspace, the licences, the READMEs
and AGENTS.md name it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 10:26:49 +01:00
String get aboutTitle ;
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.
The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.
Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 13:24:56 +01:00
/// No description provided for @homeRecordHint.
///
/// In en, this message translates to:
2026-10-31 12:20:39 +01:00
/// **'Record one from the Record tab.'**
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.
The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.
Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 13:24:56 +01:00
String get homeRecordHint ;
/// No description provided for @recordBack.
///
/// In en, this message translates to:
/// **'Back to the recording'**
String get recordBack ;
/// No description provided for @activityRunning.
///
/// In en, this message translates to:
/// **'Running'**
String get activityRunning ;
/// No description provided for @activityWalking.
///
/// In en, this message translates to:
/// **'Walking'**
String get activityWalking ;
/// No description provided for @activityHiking.
///
/// In en, this message translates to:
/// **'Hiking'**
String get activityHiking ;
/// No description provided for @activityCycling.
///
/// In en, this message translates to:
/// **'Cycling'**
String get activityCycling ;
feat(train): strength plans and sessions (ADR 0035)
Strength plans of exercises from a catalogue or the person's own, in
blocks of one exercise or several done in turn (supersets, circuits),
each with its sets, reps or seconds, weights and rests; each a sealed
`strength_plan` row. Plans are marked with what the place chosen lacks,
and a session offers swaps moving the same way that the place allows.
A session runs on every device, set by set with the rest counted down,
each weight made up from the place's plates, bars and fixed weights with
the plate changes between sets, kept in strength_session.json after each
change so one cut off is carried on, and saved as a workout of the new
activity Strength with its sets, listed on its page; strength training
in Health Connect and FIT, no GPX.
Migration 20261102113104496-strength-plans is purely additive: it
creates the table strength_plan (id uuid v7, spaceId, keyId, the sealed
payload and its nonce), its index on spaceId, and a foreign key to
offline_sync_spaces with ON DELETE CASCADE, so deleting a person's sync
spaces deletes their plans; then the modules' version rows. The
gen_random_uuid_v7 function is created or replaced as in every
migration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 13:18:12 +01:00
/// No description provided for @activityStrength.
///
/// In en, this message translates to:
/// **'Strength training'**
String get activityStrength ;
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.
The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.
Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 13:24:56 +01:00
/// No description provided for @activityIndoorRowing.
///
/// In en, this message translates to:
/// **'Rowing indoors'**
String get activityIndoorRowing ;
feat(train): the Loop's training sessions as workouts (ADR 0030, D39)
Each read of the band lists the sessions it holds, reads those from the
read's first day on (`LoopDays.kt`, one line per exercise: its start
with the band's offset, length, Polar's sport ID, figures and heart
rate at its interval), and deletes those up to the day kept, as the
days. The app keeps each as a workout of origin Polar Loop with its
heart rate (`takeLoopSessions`), Polar's running and cycling as
Train's and every other sport as the new `Activity.other`, which a
workout's edit sheet now changes; a session overlapping a workout Train
recorded gives it its heart rate where it has none instead. The
sessions taken are remembered in `loop.json`, so one deleted in Train
isn't brought back. Health Connect writes the Loop's workouts as
Train's own.
The SDK gives sessions as its protobuf messages: protobuf-javalite
3.20.0, the SDK's own, is named for compiling only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 20:22:23 +01:00
/// No description provided for @activityOther.
///
/// In en, this message translates to:
/// **'Other workout'**
String get activityOther ;
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.
The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.
Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 13:24:56 +01:00
/// The switch for auto-pause, as the recording starts.
///
/// In en, this message translates to:
/// **'Pause by itself'**
String get recordAutoPause ;
/// No description provided for @recordAutoPauseHint.
///
/// In en, this message translates to:
/// **'When you stand still, and going on as you move again'**
String get recordAutoPauseHint ;
/// No description provided for @recordStart.
///
/// In en, this message translates to:
/// **'Start'**
String get recordStart ;
feat(train): a recording runs an interval plan, with cues and Lap (ADR 0030, D42)
The plan chosen on the Record sheet goes to the recording service spelled
out: each step's end, its target (zones as the heart rates they span for
the profile's highest rate) and its spoken line in the app's language.
PlanRunner.kt moves through the steps on the moving time and distance,
counts a timed step's last seconds down and cues off target from a 30 s
window; PlanCues.kt gives a tone, a vibration and the voice, ducking
music. The file's plan and step lines bring it back, and the recording
screen shows the step, what is left of it, its target, the next step and
Lap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 22:59:48 +01:00
/// No description provided for @recordPlan.
///
/// In en, this message translates to:
/// **'Plan'**
String get recordPlan ;
/// No description provided for @recordNoPlan.
///
/// In en, this message translates to:
/// **'None'**
String get recordNoPlan ;
/// No description provided for @recordingLap.
///
/// In en, this message translates to:
/// **'Lap'**
String get recordingLap ;
/// No description provided for @recordingPlanDone.
///
/// In en, this message translates to:
/// **'Plan done'**
String get recordingPlanDone ;
/// No description provided for @recordingStepOf.
///
/// In en, this message translates to:
/// **'Step {index} of {count}'**
String recordingStepOf ( int index , int count ) ;
/// No description provided for @recordingNextStep.
///
/// In en, this message translates to:
/// **'Next: {step}'**
String recordingNextStep ( String step ) ;
/// No description provided for @recordingStepTapLap.
///
/// In en, this message translates to:
/// **'Until you tap Lap'**
String get recordingStepTapLap ;
feat(train): recording a workout on Android (ADR 0030, D13, D16, D19, D21)
RecorderService, a foreground service of the types location and health
started from the open app, takes fixes from LocationManager's GPS and
steps from the step detector, pauses by itself (AutoPause, tested on the
JVM) and appends each sample to the person's recording file, flushed
every five seconds. MainActivity asks for location, notifications and,
for auto-pause on foot, physical activity, never background location
(D19), and bridges the service to the app.
The app follows the file's lines with RecordingBloc and shows the moving
time, the distance, the pace or speed and GPS's state; the notification
shows the time and the distance with Pause or Resume. Stop is held down;
a stopped recording is saved as a workout and its series, or discarded
after asking, and one cut off or left unsaved is offered as the app
opens. Home has the Record button where the device records.
Three choices of the implementation's are recorded under D21 for review:
rowing waits for 5f, auto-pause on foot without steps goes by GPS speed
(below 2 km/h, on above 3 km/h), and Stop's hint shows above the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 13:24:56 +01:00
/// No description provided for @recordLocationRefused.
///
/// In en, this message translates to:
/// **'Train needs your location to record this. You can allow it in the system\'s settings.'**
String get recordLocationRefused ;
/// No description provided for @recordingWaitingForGps.
///
/// In en, this message translates to:
/// **'Waiting for GPS'**
String get recordingWaitingForGps ;
/// Before a fix precise enough: how far off the last one may be.
///
/// In en, this message translates to:
/// **'Waiting for GPS · ±{metres} m'**
String recordingWaitingForGpsAccuracy ( int metres ) ;
/// How far off the last fix may be.
///
/// In en, this message translates to:
/// **'GPS ±{metres} m'**
String recordingGpsAccuracy ( int metres ) ;
/// Under the time, lower case: the time moved, pauses left out.
///
/// In en, this message translates to:
/// **'moving time'**
String get recordingMovingTime ;
/// No description provided for @recordingDistance.
///
/// In en, this message translates to:
/// **'distance'**
String get recordingDistance ;
/// No description provided for @recordingPace.
///
/// In en, this message translates to:
/// **'pace'**
String get recordingPace ;
/// No description provided for @recordingSpeed.
///
/// In en, this message translates to:
/// **'speed'**
String get recordingSpeed ;
/// No description provided for @recordingDistanceValue.
///
/// In en, this message translates to:
/// **'{km} km'**
String recordingDistanceValue ( String km ) ;
/// Minutes and seconds a kilometre takes.
///
/// In en, this message translates to:
/// **'{pace} /km'**
String recordingPaceValue ( String pace ) ;
/// No description provided for @recordingSpeedValue.
///
/// In en, this message translates to:
/// **'{kmh} km/h'**
String recordingSpeedValue ( String kmh ) ;
/// Train paused the recording by itself (auto-pause).
///
/// In en, this message translates to:
/// **'Paused: not moving'**
String get recordingPausedAuto ;
/// No description provided for @recordingPausedByHand.
///
/// In en, this message translates to:
/// **'Paused'**
String get recordingPausedByHand ;
/// No description provided for @recordingPause.
///
/// In en, this message translates to:
/// **'Pause'**
String get recordingPause ;
/// No description provided for @recordingResume.
///
/// In en, this message translates to:
/// **'Resume'**
String get recordingResume ;
/// No description provided for @recordingStop.
///
/// In en, this message translates to:
/// **'Stop'**
String get recordingStop ;
/// No description provided for @recordingHoldToStop.
///
/// In en, this message translates to:
/// **'Hold Stop to end the workout'**
String get recordingHoldToStop ;
/// No description provided for @recordingStoppedTitle.
///
/// In en, this message translates to:
/// **'Save this workout?'**
String get recordingStoppedTitle ;
/// No description provided for @recordingSave.
///
/// In en, this message translates to:
/// **'Save'**
String get recordingSave ;
/// No description provided for @recordingDiscard.
///
/// In en, this message translates to:
/// **'Discard'**
String get recordingDiscard ;
/// No description provided for @recordingDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard this workout?'**
String get recordingDiscardTitle ;
/// No description provided for @recordingDiscardMessage.
///
/// In en, this message translates to:
/// **'Its time and track are deleted from this device. This can\'t be undone.'**
String get recordingDiscardMessage ;
/// No description provided for @recordingSaveFailed.
///
/// In en, this message translates to:
/// **'It can\'t be saved on this device, which doesn\'t hold your account\'s keys. It stays here until it can be.'**
String get recordingSaveFailed ;
/// No description provided for @recordingCutOffTitle.
///
/// In en, this message translates to:
/// **'A recording was cut off'**
String get recordingCutOffTitle ;
/// A recording found unfinished as the app opened: when it started and its last sample.
///
/// In en, this message translates to:
/// **'A recording from {start} was cut off. Save it up to {end}, or discard it?'**
String recordingCutOffMessage ( String start , String end ) ;
/// The name of the recording's notification channel, in the system's settings.
///
/// In en, this message translates to:
/// **'Recording'**
String get recordingNotificationChannel ;
/// The notification's title while paused.
///
/// In en, this message translates to:
/// **'{activity} · paused'**
String recordingNotificationPaused ( String activity ) ;
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// A workout's name where the person gave none: the time of day it started (morning 5– 12, afternoon 12– 17, evening 17– 22, night) and its activity, as Strava names them.
///
/// In en, this message translates to:
feat(train): strength plans and sessions (ADR 0035)
Strength plans of exercises from a catalogue or the person's own, in
blocks of one exercise or several done in turn (supersets, circuits),
each with its sets, reps or seconds, weights and rests; each a sealed
`strength_plan` row. Plans are marked with what the place chosen lacks,
and a session offers swaps moving the same way that the place allows.
A session runs on every device, set by set with the rest counted down,
each weight made up from the place's plates, bars and fixed weights with
the plate changes between sets, kept in strength_session.json after each
change so one cut off is carried on, and saved as a workout of the new
activity Strength with its sets, listed on its page; strength training
in Health Connect and FIT, no GPX.
Migration 20261102113104496-strength-plans is purely additive: it
creates the table strength_plan (id uuid v7, spaceId, keyId, the sealed
payload and its nonce), its index on spaceId, and a foreign key to
offline_sync_spaces with ON DELETE CASCADE, so deleting a person's sync
spaces deletes their plans; then the modules' version rows. The
gen_random_uuid_v7 function is created or replaced as in every
migration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 13:18:12 +01:00
/// **'{when, select, morning_strength{Morning strength} afternoon_strength{Afternoon strength} evening_strength{Evening strength} night_strength{Night strength} morning_running{Morning run} morning_walking{Morning walk} morning_hiking{Morning hike} morning_cycling{Morning ride} morning_indoorRowing{Morning row} afternoon_running{Afternoon run} afternoon_walking{Afternoon walk} afternoon_hiking{Afternoon hike} afternoon_cycling{Afternoon ride} afternoon_indoorRowing{Afternoon row} evening_running{Evening run} evening_walking{Evening walk} evening_hiking{Evening hike} evening_cycling{Evening ride} evening_indoorRowing{Evening row} night_running{Night run} night_walking{Night walk} night_hiking{Night hike} night_cycling{Night ride} night_indoorRowing{Night row} other{Workout}}'**
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
String workoutName ( String when ) ;
/// No description provided for @workoutLocked.
///
/// In en, this message translates to:
/// **'Locked workout'**
String get workoutLocked ;
/// Under a workout this device can't open (its key is missing here).
///
/// In en, this message translates to:
/// **'This device doesn\'t hold the key it\'s sealed with.'**
String get workoutLockedHint ;
/// No description provided for @workoutEdit.
///
/// In en, this message translates to:
/// **'Edit'**
String get workoutEdit ;
/// No description provided for @workoutEditTitle.
///
/// In en, this message translates to:
/// **'Edit workout'**
String get workoutEditTitle ;
/// No description provided for @workoutNameLabel.
///
/// In en, this message translates to:
/// **'Name'**
String get workoutNameLabel ;
feat(train): the Loop's training sessions as workouts (ADR 0030, D39)
Each read of the band lists the sessions it holds, reads those from the
read's first day on (`LoopDays.kt`, one line per exercise: its start
with the band's offset, length, Polar's sport ID, figures and heart
rate at its interval), and deletes those up to the day kept, as the
days. The app keeps each as a workout of origin Polar Loop with its
heart rate (`takeLoopSessions`), Polar's running and cycling as
Train's and every other sport as the new `Activity.other`, which a
workout's edit sheet now changes; a session overlapping a workout Train
recorded gives it its heart rate where it has none instead. The
sessions taken are remembered in `loop.json`, so one deleted in Train
isn't brought back. Health Connect writes the Loop's workouts as
Train's own.
The SDK gives sessions as its protobuf messages: protobuf-javalite
3.20.0, the SDK's own, is named for compiling only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 20:22:23 +01:00
/// No description provided for @workoutActivityLabel.
///
/// In en, this message translates to:
/// **'Activity'**
String get workoutActivityLabel ;
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// No description provided for @workoutNotesLabel.
///
/// In en, this message translates to:
/// **'Notes'**
String get workoutNotesLabel ;
/// No description provided for @workoutSave.
///
/// In en, this message translates to:
/// **'Save'**
String get workoutSave ;
/// No description provided for @workoutDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get workoutDelete ;
/// No description provided for @workoutDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this workout?'**
String get workoutDeleteTitle ;
/// No description provided for @workoutDeleteMessage.
///
/// In en, this message translates to:
/// **'It\'s deleted on all your devices.'**
String get workoutDeleteMessage ;
/// No description provided for @workoutDeleted.
///
/// In en, this message translates to:
/// **'Workout deleted'**
String get workoutDeleted ;
/// No description provided for @workoutMovingTime.
///
/// In en, this message translates to:
/// **'moving time'**
String get workoutMovingTime ;
/// Lower case, under a figure: from start to end, pauses included.
///
/// In en, this message translates to:
/// **'whole time'**
String get workoutTotalTime ;
/// No description provided for @workoutDistance.
///
/// In en, this message translates to:
/// **'distance'**
String get workoutDistance ;
/// No description provided for @workoutPace.
///
/// In en, this message translates to:
/// **'pace'**
String get workoutPace ;
/// No description provided for @workoutSpeed.
///
/// In en, this message translates to:
/// **'speed'**
String get workoutSpeed ;
/// Lower case, under a figure: the elevation gained.
///
/// In en, this message translates to:
/// **'climbed'**
String get workoutClimb ;
/// No description provided for @workoutClimbValue.
///
/// In en, this message translates to:
/// **'{metres} m'**
String workoutClimbValue ( int metres ) ;
/// No description provided for @workoutSplits.
///
/// In en, this message translates to:
/// **'Splits'**
String get workoutSplits ;
feat(train): a workout keeps its plan's steps as laps, shown on its page (ADR 0030, D43)
As a recording with a plan is saved, each step run becomes a lap in the
workout's sealed payload: the step, its round, when it started and ended,
its moving time and distance, with the highest heart rate the zones ran
with. The page lists the steps with their pace, speed or split, stroke
rate, watts and heart rate, marks whether each held its target, and the
charts shade the work steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 23:12:34 +01:00
/// No description provided for @workoutSteps.
///
/// In en, this message translates to:
/// **'Steps of {plan}'**
String workoutSteps ( String plan ) ;
/// No description provided for @workoutOnTarget.
///
/// In en, this message translates to:
/// **'On target'**
String get workoutOnTarget ;
/// No description provided for @workoutOffTarget.
///
/// In en, this message translates to:
/// **'Off target'**
String get workoutOffTarget ;
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// A kilometre's split, its number.
///
/// In en, this message translates to:
/// **'km {number}'**
String workoutSplitKm ( int number ) ;
/// A cycling split of 5 km: where it starts and ends.
///
/// In en, this message translates to:
/// **'{from}– {to} km'**
String workoutSplitFiveKm ( int from , int to ) ;
/// No description provided for @workoutPaceChart.
///
/// In en, this message translates to:
/// **'Pace'**
String get workoutPaceChart ;
/// No description provided for @workoutSpeedChart.
///
/// In en, this message translates to:
/// **'Speed'**
String get workoutSpeedChart ;
/// No description provided for @workoutElevationChart.
///
/// In en, this message translates to:
/// **'Elevation'**
String get workoutElevationChart ;
/// Under a workout's map: what the map's source sees.
///
/// In en, this message translates to:
/// **'Map by {name}: it sees your device\'s address and the area shown, not your track.'**
String workoutMapSource ( String name ) ;
2026-10-29 16:04:05 +01:00
/// Under a workout's map drawn from the instance's own tiles (D50): what it sees.
///
/// In en, this message translates to:
/// **'Map by your instance, {name}, from OpenStreetMap: it keeps nothing of the area shown.'**
String workoutMapOwn ( String name ) ;
feat(train): the workouts listed, and each one's page with its map (ADR 0030, D22-D24)
Home lists the person's workouts newest first under their months, each
with its activity's icon, its name (else the time of day's and the
activity's), when it started, its distance, moving time and pace or
speed; those this device can't open come last, locked. A workout's page
shows its track on the instance's map source (flutter_map with
vector_map_tiles, pinned at 9.0.0-beta.13), in pieces between its
pauses, start and end marked; its totals with the elevation gained;
its splits per kilometre on foot, per 5 km cycling; pace or speed and
elevation over the distance, drawn by the app's own painter; and what
the map's source sees. Its name and notes are edited there; deleting
asks first and can be undone for a moment from home (halkyon_app's
DelayedDeletions, TrainPerson.deletions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 14:29:39 +01:00
/// A link to the map source's page about what it keeps.
///
/// In en, this message translates to:
/// **'What {name} keeps'**
String workoutMapSourceAbout ( String name ) ;
/// No description provided for @workoutMapUnavailable.
///
/// In en, this message translates to:
/// **'The map can\'t be loaded now.'**
String get workoutMapUnavailable ;
/// No description provided for @workoutNotFound.
///
/// In en, this message translates to:
/// **'This workout is gone.'**
String get workoutNotFound ;
feat(train): the heart-rate strap, recorded by the service itself (ADR 0030, D25-D27)
The Record sheet gains a Heart rate row: a sheet looks for straps
advertising the Heart Rate Service, the nearest first, once nearby
devices are allowed, and the one chosen is remembered in the person's
folder; it records with every workout unless switched off for one.
The recorder service connects to it with Android's own Bluetooth API,
no package (D26), parses Heart Rate Measurement in Kotlin (tested on
the JVM), writes each rate and the strap's state to the recording
file, and asks for a lost strap again for as long as it records. The
recording screen shows the heart rate, and "Strap lost" while it is
gone; a workout's page shows the heart's average and highest rate,
each split's average and its chart.
iOS's recording waits for a Mac (D25), after the rower.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 15:50:36 +01:00
/// No description provided for @recordHeartRate.
///
/// In en, this message translates to:
/// **'Heart rate'**
String get recordHeartRate ;
/// On the Record sheet, under Heart rate, where no strap is chosen.
///
/// In en, this message translates to:
/// **'No strap'**
String get recordNoStrap ;
2026-10-28 21:32:13 +01:00
/// No description provided for @recordHeartLoop.
///
/// In en, this message translates to:
/// **'From the Polar Loop'**
String get recordHeartLoop ;
/// No description provided for @recordHeartStrapThenLoop.
///
/// In en, this message translates to:
/// **'{strap}, the Loop while it is lost'**
String recordHeartStrapThenLoop ( String strap ) ;
feat(train): the heart-rate strap, recorded by the service itself (ADR 0030, D25-D27)
The Record sheet gains a Heart rate row: a sheet looks for straps
advertising the Heart Rate Service, the nearest first, once nearby
devices are allowed, and the one chosen is remembered in the person's
folder; it records with every workout unless switched off for one.
The recorder service connects to it with Android's own Bluetooth API,
no package (D26), parses Heart Rate Measurement in Kotlin (tested on
the JVM), writes each rate and the strap's state to the recording
file, and asks for a lost strap again for as long as it records. The
recording screen shows the heart rate, and "Strap lost" while it is
gone; a workout's page shows the heart's average and highest rate,
each split's average and its chart.
iOS's recording waits for a Mac (D25), after the rower.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 15:50:36 +01:00
/// No description provided for @strapSheetTitle.
///
/// In en, this message translates to:
/// **'Heart-rate strap'**
String get strapSheetTitle ;
/// No description provided for @strapSearching.
///
/// In en, this message translates to:
/// **'Looking for straps nearby…'**
String get strapSearching ;
/// No description provided for @strapSearchingHint.
///
/// In en, this message translates to:
/// **'Put the strap on: most wake up only once they touch skin.'**
String get strapSearchingHint ;
/// Under a strap found: its signal is strong.
///
/// In en, this message translates to:
/// **'close by'**
String get strapNear ;
/// Under a strap found: its signal is weak.
///
/// In en, this message translates to:
/// **'further off'**
String get strapFar ;
/// A strap that sends no name.
///
/// In en, this message translates to:
/// **'Unnamed strap'**
String get strapUnnamed ;
/// No description provided for @strapForget.
///
/// In en, this message translates to:
/// **'Forget strap'**
String get strapForget ;
/// No description provided for @strapRefused.
///
/// In en, this message translates to:
/// **'Train needs nearby devices to find your strap. You can allow it in the system\'s settings.'**
String get strapRefused ;
/// No description provided for @strapBluetoothOff.
///
/// In en, this message translates to:
feat(train): rowing on the WP 5000 over FTMS (ADR 0030, D28)
Rowing indoors is offered again, with the rower chosen on the Record
sheet: a sheet looks for Fitness Machine Service rowers, and the one
chosen is remembered in the person's folder. The recorder service
connects to it as to the strap (one NotifyLink for both), parses
FTMS's Rower Data in Kotlin (tested on the JVM on frames written from
the specification and D8's), counts its totals from its first frame,
pauses 5 s after the last stroke, and gets the connectedDevice type. A
row's screen shows its metres, split per 500 m, stroke rate and watts;
its page its average split, stroke rate and power, the highest power,
its strokes, splits per 500 m and charts of the split, stroke rate and
power. Not yet tried on the WP 5000.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 16:20:37 +01:00
/// **'Bluetooth is off. Turn it on to look for devices nearby.'**
feat(train): the heart-rate strap, recorded by the service itself (ADR 0030, D25-D27)
The Record sheet gains a Heart rate row: a sheet looks for straps
advertising the Heart Rate Service, the nearest first, once nearby
devices are allowed, and the one chosen is remembered in the person's
folder; it records with every workout unless switched off for one.
The recorder service connects to it with Android's own Bluetooth API,
no package (D26), parses Heart Rate Measurement in Kotlin (tested on
the JVM), writes each rate and the strap's state to the recording
file, and asks for a lost strap again for as long as it records. The
recording screen shows the heart rate, and "Strap lost" while it is
gone; a workout's page shows the heart's average and highest rate,
each split's average and its chart.
iOS's recording waits for a Mac (D25), after the rower.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 15:50:36 +01:00
String get strapBluetoothOff ;
/// Under the heart rate, lower case.
///
/// In en, this message translates to:
/// **'heart rate'**
String get recordingHeartRate ;
/// While recording: the strap is out of reach or off; Train keeps asking for it.
///
/// In en, this message translates to:
/// **'Strap lost'**
String get recordingStrapLost ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'average heart rate'**
String get workoutAverageHeartRate ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'highest heart rate'**
String get workoutMaxHeartRate ;
/// No description provided for @workoutBpmValue.
///
/// In en, this message translates to:
/// **'{bpm} bpm'**
String workoutBpmValue ( int bpm ) ;
/// No description provided for @workoutHeartRateChart.
///
/// In en, this message translates to:
/// **'Heart rate'**
String get workoutHeartRateChart ;
feat(train): rowing on the WP 5000 over FTMS (ADR 0030, D28)
Rowing indoors is offered again, with the rower chosen on the Record
sheet: a sheet looks for Fitness Machine Service rowers, and the one
chosen is remembered in the person's folder. The recorder service
connects to it as to the strap (one NotifyLink for both), parses
FTMS's Rower Data in Kotlin (tested on the JVM on frames written from
the specification and D8's), counts its totals from its first frame,
pauses 5 s after the last stroke, and gets the connectedDevice type. A
row's screen shows its metres, split per 500 m, stroke rate and watts;
its page its average split, stroke rate and power, the highest power,
its strokes, splits per 500 m and charts of the split, stroke rate and
power. Not yet tried on the WP 5000.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 16:20:37 +01:00
/// No description provided for @recordRower.
///
/// In en, this message translates to:
/// **'Rowing machine'**
String get recordRower ;
/// No description provided for @recordNoRower.
///
/// In en, this message translates to:
/// **'Choose your rowing machine'**
String get recordNoRower ;
/// No description provided for @recordRowerRefused.
///
/// In en, this message translates to:
/// **'Train needs nearby devices to record from your rowing machine. You can allow it in the system\'s settings.'**
String get recordRowerRefused ;
/// No description provided for @rowerSheetTitle.
///
/// In en, this message translates to:
/// **'Rowing machine'**
String get rowerSheetTitle ;
/// No description provided for @rowerSearching.
///
/// In en, this message translates to:
/// **'Looking for rowing machines nearby…'**
String get rowerSearching ;
/// No description provided for @rowerSearchingHint.
///
/// In en, this message translates to:
/// **'Wake the console: most show once you pull or press a button. One another app holds (Christopeit Connect, Kinomap) can\'t be found: close that app first.'**
String get rowerSearchingHint ;
/// No description provided for @rowerUnnamed.
///
/// In en, this message translates to:
/// **'Unnamed rowing machine'**
String get rowerUnnamed ;
/// No description provided for @rowerForget.
///
/// In en, this message translates to:
/// **'Forget rowing machine'**
String get rowerForget ;
/// No description provided for @rowerRefused.
///
/// In en, this message translates to:
/// **'Train needs nearby devices to find your rowing machine. You can allow it in the system\'s settings.'**
String get rowerRefused ;
/// While recording a row: the rower isn't connected yet. Another app may hold it.
///
/// In en, this message translates to:
/// **'Looking for the rowing machine'**
String get recordingRowerConnecting ;
/// While recording a row: the rower is out of reach or off; Train keeps asking for it.
///
/// In en, this message translates to:
/// **'Rowing machine lost'**
String get recordingRowerLost ;
/// Lower case, under a figure: how long 500 m take now.
///
/// In en, this message translates to:
/// **'split'**
String get recordingSplit ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'strokes a minute'**
String get recordingStrokeRate ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'watts'**
String get recordingWatts ;
/// Minutes and seconds 500 m take.
///
/// In en, this message translates to:
/// **'{split} /500 m'**
String workoutSplitValue ( String split ) ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'average split'**
String get workoutAverageSplit ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'average stroke rate'**
String get workoutAverageStrokeRate ;
/// Strokes a minute.
///
/// In en, this message translates to:
/// **'{rate} /min'**
String workoutStrokeRateValue ( String rate ) ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'average power'**
String get workoutAveragePower ;
/// Lower case, under a figure.
///
/// In en, this message translates to:
/// **'highest power'**
String get workoutMaxPower ;
/// No description provided for @workoutWattsValue.
///
/// In en, this message translates to:
/// **'{watts} W'**
String workoutWattsValue ( int watts ) ;
/// Lower case, under a figure: how many strokes.
///
/// In en, this message translates to:
/// **'strokes'**
String get workoutStrokes ;
/// A rowing split of 500 m: where it starts and ends.
///
/// In en, this message translates to:
/// **'{from}– {to} m'**
String workoutSplitMetres ( int from , int to ) ;
/// No description provided for @workoutSplitChart.
///
/// In en, this message translates to:
/// **'Split'**
String get workoutSplitChart ;
/// No description provided for @workoutStrokeRateChart.
///
/// In en, this message translates to:
/// **'Stroke rate'**
String get workoutStrokeRateChart ;
/// No description provided for @workoutPowerChart.
///
/// In en, this message translates to:
/// **'Power'**
String get workoutPowerChart ;
/// No description provided for @recordAutoPauseRowingHint.
///
/// In en, this message translates to:
/// **'When you stop rowing, going on with the next stroke'**
String get recordAutoPauseRowingHint ;
/// No description provided for @recordingMetres.
///
/// In en, this message translates to:
/// **'metres'**
String get recordingMetres ;
feat(train): workouts written to Health Connect (ADR 0030, D31)
A Health Connect row in the account sheet opens its screen: Write my
workouts, off until turned on, Health Connect asking as it is. Then
every workout this phone holds is written there (its session with its
pauses and route, heart rate, speed and the rower's watts as samples,
distance and elevation gained), and written again or deleted as it
changes, here or through a sync, and as the app opens. Client IDs come
from the workout's ID, a version counts up with each change; what was
written is kept in the person's health_connect.json, never synced. A
permission taken back in Health Connect turns writing off and says so;
Health Connect's link to Train's rationale opens a screen of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:06:31 +01:00
/// No description provided for @healthTitle.
///
/// In en, this message translates to:
/// **'Health Connect'**
String get healthTitle ;
/// Under Health Connect in the account sheet.
///
/// In en, this message translates to:
/// **'Share workouts with other apps on this phone'**
String get healthHint ;
/// No description provided for @healthUnavailable.
///
/// In en, this message translates to:
/// **'Health Connect isn\'t on this phone. Android 14 and later have it built in; before 14, it\'s an app of its own.'**
String get healthUnavailable ;
/// No description provided for @healthNeedsUpdate.
///
/// In en, this message translates to:
/// **'Update the Health Connect app to use it with Train.'**
String get healthNeedsUpdate ;
/// No description provided for @healthWrite.
///
/// In en, this message translates to:
/// **'Write my workouts'**
String get healthWrite ;
/// No description provided for @healthWriteHint.
///
/// In en, this message translates to:
/// **'Every workout on this phone, kept up to date as you edit or delete it'**
String get healthWriteHint ;
feat(train): other apps' workouts read from Health Connect (ADR 0030, D31)
Read from other apps, off until turned on, asks for reading with the
history; the apps that wrote sessions are listed, each off. One turned
on brings its sessions, earlier ones at once and later ones through the
changes token as the app opens and comes to the front: each a workout
of Train's own store, sealed and synced, its origin in its payload, its
heart rate, pauses and a row's power as series, named by its app in the
list and on its page. A double of a workout Train has brings at most its
heart rate; one deleted in Train stays out; a track comes only when
asked for on the page, Health Connect asking for consent. What came from
Health Connect is never written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:17:59 +01:00
/// No description provided for @healthTakenBack.
feat(train): workouts written to Health Connect (ADR 0030, D31)
A Health Connect row in the account sheet opens its screen: Write my
workouts, off until turned on, Health Connect asking as it is. Then
every workout this phone holds is written there (its session with its
pauses and route, heart rate, speed and the rower's watts as samples,
distance and elevation gained), and written again or deleted as it
changes, here or through a sync, and as the app opens. Client IDs come
from the workout's ID, a version counts up with each change; what was
written is kept in the person's health_connect.json, never synced. A
permission taken back in Health Connect turns writing off and says so;
Health Connect's link to Train's rationale opens a screen of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:06:31 +01:00
///
/// In en, this message translates to:
/// **'Health Connect took this back. Allow it there to turn it on again.'**
feat(train): other apps' workouts read from Health Connect (ADR 0030, D31)
Read from other apps, off until turned on, asks for reading with the
history; the apps that wrote sessions are listed, each off. One turned
on brings its sessions, earlier ones at once and later ones through the
changes token as the app opens and comes to the front: each a workout
of Train's own store, sealed and synced, its origin in its payload, its
heart rate, pauses and a row's power as series, named by its app in the
list and on its page. A double of a workout Train has brings at most its
heart rate; one deleted in Train stays out; a track comes only when
asked for on the page, Health Connect asking for consent. What came from
Health Connect is never written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:17:59 +01:00
String get healthTakenBack ;
feat(train): workouts written to Health Connect (ADR 0030, D31)
A Health Connect row in the account sheet opens its screen: Write my
workouts, off until turned on, Health Connect asking as it is. Then
every workout this phone holds is written there (its session with its
pauses and route, heart rate, speed and the rower's watts as samples,
distance and elevation gained), and written again or deleted as it
changes, here or through a sync, and as the app opens. Client IDs come
from the workout's ID, a version counts up with each change; what was
written is kept in the person's health_connect.json, never synced. A
permission taken back in Health Connect turns writing off and says so;
Health Connect's link to Train's rationale opens a screen of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:06:31 +01:00
/// No description provided for @healthRefused.
///
/// In en, this message translates to:
/// **'Health Connect didn\'t allow it. You can allow it in its settings.'**
String get healthRefused ;
/// No description provided for @healthOpenSettings.
///
/// In en, this message translates to:
/// **'Open Health Connect'**
String get healthOpenSettings ;
/// No description provided for @healthWhy.
///
/// In en, this message translates to:
/// **'Why Train asks'**
String get healthWhy ;
/// No description provided for @healthRationaleTitle.
///
/// In en, this message translates to:
/// **'Train and Health Connect'**
String get healthRationaleTitle ;
/// No description provided for @healthRationaleWrite.
///
/// In en, this message translates to:
/// **'Train writes your workouts to Health Connect only once you turn it on: each one with its route, heart rate, speed, power, distance and climb, so that other apps on this phone can use them. A workout edited or deleted in Train is edited or deleted there too.'**
String get healthRationaleWrite ;
/// No description provided for @healthRationaleRead.
///
/// In en, this message translates to:
/// **'Train reads other apps\' workouts only once you turn it on, and only from the apps you choose: their heart rate, distance and power come into Train as workouts, sealed with your key before they\'re synced to your instance, which can\'t read them.'**
String get healthRationaleRead ;
/// No description provided for @healthRationaleNothingElse.
///
/// In en, this message translates to:
/// **'What Train reads there is shown to you and nobody else, and isn\'t used for anything else. Turning a switch off, or taking a permission back in Health Connect, stops it.'**
String get healthRationaleNothingElse ;
feat(train): other apps' workouts read from Health Connect (ADR 0030, D31)
Read from other apps, off until turned on, asks for reading with the
history; the apps that wrote sessions are listed, each off. One turned
on brings its sessions, earlier ones at once and later ones through the
changes token as the app opens and comes to the front: each a workout
of Train's own store, sealed and synced, its origin in its payload, its
heart rate, pauses and a row's power as series, named by its app in the
list and on its page. A double of a workout Train has brings at most its
heart rate; one deleted in Train stays out; a track comes only when
asked for on the page, Health Connect asking for consent. What came from
Health Connect is never written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:17:59 +01:00
/// No description provided for @healthRead.
///
/// In en, this message translates to:
/// **'Read from other apps'**
String get healthRead ;
/// No description provided for @healthReadHint.
///
/// In en, this message translates to:
/// **'Their workouts come into Train, from the apps you choose below'**
String get healthReadHint ;
feat(train): weight, entered on Progress and read from Health Connect, sealed and synced (ADR 0030, D63)
A person's weights are each a sealed `body_weight` row, as the days and
the places: the weight in grams, when it was measured (UTC), where it
came from and, for Health Connect's, the record's id and app are the
payload; the server sees the space and the ciphertext alone.
Progress gets a Weight card after Your body: the latest and its day,
its line over time (held, each weight and its day), the plus to enter
one (a comma taken as a point, 20 to 400 kg, when measured, today by
default) and All, the list to delete one. The latest weight is also
Your body's, which the band's and the zones' figures work from. In
kilograms alone: Train is metric throughout and has no units setting
yet.
Health Connect's weight records are read in when switched on in its
screen (READ_WEIGHT, asked as the others): each record once, its id kept
in health_connect.json also after it is deleted here, never Train's own,
and none within 10 minutes and 0.1 kg of a weight entered in Train.
The migration (20261103204323683-body-weights):
- creates (or replaces, unchanged) `gen_random_uuid_v7()`, the
default of every UUID id, as each of Serverpod's migrations does;
- CREATE TABLE "body_weight": "id" uuid primary key defaulting to a
UUID v7, "spaceId" bigint (the sync space), "keyId" text (the key
that sealed it), "payload" bytea NOT NULL (the sealed content),
"nonce" bytea;
- CREATE INDEX "body_weight_space_idx" on "spaceId", for a space's
rows as the sync reads them;
- a foreign key from "spaceId" to "offline_sync_spaces"("id"),
ON DELETE CASCADE, so that an account's deletion takes its weights
with its space;
- the version rows of Train's and every module's migrations.
Nothing existing changes, so nothing is lost.
Tests: the row written and read back in its owner's space, and the
denials: another person reads none of them, and writing one into
another's space throws OfflineSyncSpaceMembershipException and leaves
it empty; an account's deletion takes them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 22:11:57 +01:00
/// No description provided for @healthReadWeight.
///
/// In en, this message translates to:
/// **'Read my weight'**
String get healthReadWeight ;
/// No description provided for @healthReadWeightHint.
///
/// In en, this message translates to:
/// **'Weights other apps wrote, a scale\'s say, come into Train'**
String get healthReadWeightHint ;
/// No description provided for @healthRationaleWeight.
///
/// In en, this message translates to:
/// **'Train reads the weights other apps wrote only once you turn it on: each comes into Train once, sealed with your key before it\'s synced to your instance, which can\'t read it.'**
String get healthRationaleWeight ;
feat(train): other apps' workouts read from Health Connect (ADR 0030, D31)
Read from other apps, off until turned on, asks for reading with the
history; the apps that wrote sessions are listed, each off. One turned
on brings its sessions, earlier ones at once and later ones through the
changes token as the app opens and comes to the front: each a workout
of Train's own store, sealed and synced, its origin in its payload, its
heart rate, pauses and a row's power as series, named by its app in the
list and on its page. A double of a workout Train has brings at most its
heart rate; one deleted in Train stays out; a track comes only when
asked for on the page, Health Connect asking for consent. What came from
Health Connect is never written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 17:17:59 +01:00
/// Over the apps that wrote workouts to Health Connect, each with its switch.
///
/// In en, this message translates to:
/// **'Apps'**
String get healthApps ;
/// No description provided for @healthAppsEmpty.
///
/// In en, this message translates to:
/// **'No app has written workouts to Health Connect yet.'**
String get healthAppsEmpty ;
/// In the list, after when an imported workout was: the app it came from.
///
/// In en, this message translates to:
/// **'from {app}'**
String workoutFromList ( String app ) ;
/// On an imported workout's page.
///
/// In en, this message translates to:
/// **'From {app}, through Health Connect'**
String workoutFrom ( String app ) ;
/// On an imported workout's page: asks Health Connect for its route.
///
/// In en, this message translates to:
/// **'Bring in the track'**
String get workoutBringTrack ;
/// No description provided for @workoutNoTrack.
///
/// In en, this message translates to:
/// **'Health Connect gave no track for it.'**
String get workoutNoTrack ;
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>
2026-10-28 18:02:04 +01:00
/// 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 ;
feat(train): setting up and keeping the Polar Loop (ADR 0030, D34–D36)
On Android, the Loop through Polar's BLE SDK 8.3.0 (from JitPack, asked
for Polar's group alone, its files pinned by SHA-256 in Gradle's
verification metadata): LoopBridge.kt over app.halkyon.train/loop, the
first-time use's figures mapped and checked in LoopSetup.kt, tested on
the JVM. PolarLoop per person remembers the band in loop.json, connects
while the app is open, sets its clock at each connection, keeps what it
tells (battery, charging, firmware), offers a changed profile and sends
it only when asked (D35: first-time use writes a new user identifier
each time), and lets the band go by a factory reset after asking. The
setup sheet finds Loops nearest first and asks only for what the
profile lacks; Polar's defaults (60, 220 less the age, a VO2max from
Polar's fitness tables) go to the band alone.
Polar's SDK is not free software (D36, correcting D32): the app's AGPL
gains a section 7 permission for linking it, LICENSE.md names it, and
its licence and bundled notices join the licence list on Android.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 18:58:02 +01:00
/// No description provided for @profileNeeded.
///
/// In en, this message translates to:
/// **'Needed for your band'**
String get profileNeeded ;
/// No description provided for @loopTitle.
///
/// In en, this message translates to:
/// **'Polar Loop'**
String get loopTitle ;
/// Under Polar Loop in the account sheet.
///
/// In en, this message translates to:
/// **'Your band: its setup, battery and clock'**
String get loopHint ;
/// No description provided for @loopNone.
///
/// In en, this message translates to:
/// **'Train sets up a Polar Loop and keeps it itself, without Polar Flow or a Polar account. A band used with Polar Flow needs a factory reset first.'**
String get loopNone ;
/// No description provided for @loopSetUp.
///
/// In en, this message translates to:
/// **'Set up a Loop'**
String get loopSetUp ;
/// No description provided for @loopSetUpAgain.
///
/// In en, this message translates to:
/// **'Set up again'**
String get loopSetUpAgain ;
/// No description provided for @loopLetGo.
///
/// In en, this message translates to:
/// **'Let the band go'**
String get loopLetGo ;
/// No description provided for @loopLetGoTitle.
///
/// In en, this message translates to:
/// **'Let the band go?'**
String get loopLetGoTitle ;
/// No description provided for @loopLetGoText.
///
/// In en, this message translates to:
/// **'The band is reset to the factory\'s state, and what it holds that hasn\'t reached Train is lost. Afterwards it can be paired with another phone, or with Polar Flow.'**
String get loopLetGoText ;
/// No description provided for @loopLetGoConfirm.
///
/// In en, this message translates to:
/// **'Reset the band'**
String get loopLetGoConfirm ;
/// No description provided for @loopConnected.
///
/// In en, this message translates to:
/// **'Connected'**
String get loopConnected ;
/// No description provided for @loopLastSeen.
///
/// In en, this message translates to:
/// **'Last seen {when}'**
String loopLastSeen ( String when ) ;
/// No description provided for @loopNotInReach.
///
/// In en, this message translates to:
/// **'Not in reach'**
String get loopNotInReach ;
/// No description provided for @loopBattery.
///
/// In en, this message translates to:
/// **'Battery {level} %'**
String loopBattery ( int level ) ;
/// No description provided for @loopCharging.
///
/// In en, this message translates to:
/// **'Battery {level} %, charging'**
String loopCharging ( int level ) ;
/// No description provided for @loopFirmware.
///
/// In en, this message translates to:
/// **'Firmware {version}'**
String loopFirmware ( String version ) ;
feat(train): the Loop's firmware updates, directly or through the instance (ADR 0030, D10, D40)
Train's server relays Polar's firmware service for the people signed in
(`firmware`: offered, check, download), a fetched file kept in memory a
day per hardware and version, off with LOOP_FIRMWARE_RELAY=off. The app
asks on the day's first connection and on the band's screen, from Polar
or the instance as the person chose, never Polar in the instance's
place; an update runs only after asking, reads and keeps the band
first, and sets it up again with its figures and clock after.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 20:52:53 +01:00
/// No description provided for @loopFirmwareCheck.
///
/// In en, this message translates to:
/// **'Check for a firmware update'**
String get loopFirmwareCheck ;
/// No description provided for @loopFirmwareCurrent.
///
/// In en, this message translates to:
/// **'The band\'s firmware is up to date.'**
String get loopFirmwareCurrent ;
/// No description provided for @loopFirmwareOffered.
///
/// In en, this message translates to:
/// **'Firmware {version} available'**
String loopFirmwareOffered ( String version ) ;
/// No description provided for @loopFirmwareViaPolar.
///
/// In en, this message translates to:
/// **'Fetched from Polar directly: Polar learns this phone\'s address (or its VPN\'s), with the band\'s firmware version and hardware code.'**
String get loopFirmwareViaPolar ;
/// No description provided for @loopFirmwareViaInstance.
///
/// In en, this message translates to:
/// **'Fetched through your instance: Polar learns the instance\'s address, not this phone\'s, with the band\'s firmware version and hardware code.'**
String get loopFirmwareViaInstance ;
/// No description provided for @loopFirmwareErases.
///
/// In en, this message translates to:
/// **'The update erases the band. Train first keeps what is on it, then sets it up again with your profile.'**
String get loopFirmwareErases ;
/// No description provided for @loopFirmwareUpdate.
///
/// In en, this message translates to:
/// **'Update'**
String get loopFirmwareUpdate ;
/// No description provided for @loopFirmwareUpdateTitle.
///
/// In en, this message translates to:
/// **'Update the band\'s firmware?'**
String get loopFirmwareUpdateTitle ;
/// No description provided for @loopFirmwareUpdateText.
///
/// In en, this message translates to:
/// **'Train first keeps what is on the band, as the update erases it. The band restarts, which takes a few minutes, and Train then sets it up again with your profile. Keep it near the phone until then.'**
String get loopFirmwareUpdateText ;
/// No description provided for @loopFirmwareStage.
///
/// In en, this message translates to:
/// **'{stage, select, reading{Keeping what is on the band…} fetching{Fetching the firmware…} preparing{Preparing the band…} writing{Writing the firmware…} finalizing{Finishing the update…} done{Written} restarting{The band restarts…} other{Updating…}}'**
String loopFirmwareStage ( String stage ) ;
/// No description provided for @loopFirmwareStageHint.
///
/// In en, this message translates to:
/// **'Keep the band near the phone, and Train open.'**
String get loopFirmwareStageHint ;
/// No description provided for @loopFirmwareUpdated.
///
/// In en, this message translates to:
/// **'Firmware updated; the band is set up again.'**
String get loopFirmwareUpdated ;
/// No description provided for @loopFirmwareThroughInstance.
///
/// In en, this message translates to:
/// **'Firmware through your instance'**
String get loopFirmwareThroughInstance ;
/// No description provided for @loopFirmwareThroughInstanceHint.
///
/// In en, this message translates to:
/// **'Polar then learns the instance\'s address rather than this phone\'s. Off, the phone asks Polar directly.'**
String get loopFirmwareThroughInstanceHint ;
feat(train): setting up and keeping the Polar Loop (ADR 0030, D34–D36)
On Android, the Loop through Polar's BLE SDK 8.3.0 (from JitPack, asked
for Polar's group alone, its files pinned by SHA-256 in Gradle's
verification metadata): LoopBridge.kt over app.halkyon.train/loop, the
first-time use's figures mapped and checked in LoopSetup.kt, tested on
the JVM. PolarLoop per person remembers the band in loop.json, connects
while the app is open, sets its clock at each connection, keeps what it
tells (battery, charging, firmware), offers a changed profile and sends
it only when asked (D35: first-time use writes a new user identifier
each time), and lets the band go by a factory reset after asking. The
setup sheet finds Loops nearest first and asks only for what the
profile lacks; Polar's defaults (60, 220 less the age, a VO2max from
Polar's fitness tables) go to the band alone.
Polar's SDK is not free software (D36, correcting D32): the app's AGPL
gains a section 7 permission for linking it, LICENSE.md names it, and
its licence and bundled notices join the licence list on Android.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 18:58:02 +01:00
/// No description provided for @loopProfileChanged.
///
/// In en, this message translates to:
/// **'Your body changed since the band was set up'**
String get loopProfileChanged ;
/// No description provided for @loopProfileChangedHint.
///
/// In en, this message translates to:
/// **'Sending sets the band up again with your figures as they are now.'**
String get loopProfileChangedHint ;
/// No description provided for @loopSend.
///
/// In en, this message translates to:
/// **'Send to the band'**
String get loopSend ;
/// No description provided for @loopSent.
///
/// In en, this message translates to:
/// **'Sent to the band.'**
String get loopSent ;
/// No description provided for @loopFailed.
///
/// In en, this message translates to:
/// **'The band didn\'t answer. Keep it close, and try again.'**
String get loopFailed ;
/// No description provided for @loopSearching.
///
/// In en, this message translates to:
/// **'Looking for Loops nearby'**
String get loopSearching ;
/// No description provided for @loopSearchingHint.
///
/// In en, this message translates to:
/// **'Keep the band close; tap it to wake it up.'**
String get loopSearchingHint ;
/// No description provided for @loopRefused.
///
/// In en, this message translates to:
/// **'Train can\'t look for your band without nearby devices. Allow them in Android\'s settings.'**
String get loopRefused ;
/// No description provided for @loopUnnamed.
///
/// In en, this message translates to:
/// **'Polar Loop'**
String get loopUnnamed ;
/// No description provided for @loopSettingUp.
///
/// In en, this message translates to:
/// **'Setting up the band…'**
String get loopSettingUp ;
/// No description provided for @loopSettingUpHint.
///
/// In en, this message translates to:
/// **'Android may ask you to pair it.'**
String get loopSettingUpHint ;
/// No description provided for @loopSetUpDone.
///
/// In en, this message translates to:
/// **'The band is set up.'**
String get loopSetUpDone ;
/// No description provided for @loopFormTitle.
///
/// In en, this message translates to:
/// **'For your band'**
String get loopFormTitle ;
/// No description provided for @loopFormIntro.
///
/// In en, this message translates to:
/// **'Your band needs these. They\'re kept in Your body, sealed with your key. For a resting or highest heart rate or a VO2max you haven\'t given, the band takes Polar\'s defaults, and Your body stays as it is.'**
String get loopFormIntro ;
/// No description provided for @loopFormSave.
///
/// In en, this message translates to:
/// **'Set up the band'**
String get loopFormSave ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
/// No description provided for @homeWorkouts.
///
/// In en, this message translates to:
/// **'Workouts'**
String get homeWorkouts ;
feat(ui): tabs folding into the title, in Learn's and Train's Profile (ADR 0030, D60)
halkyon_ui's FoldingTabs: the tab row under the app bar's title at the
top, scrolling away with the content, the chosen tab's name then small
under the title, which, tapped, scrolls back up to the row, as Strava's.
Learn's Profile (Profile, Statistics) is built on it, and Train's
Profile in tabs: Progress (your body, the last 12 weeks, this year),
Days, and Plans where the device doesn't record.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:12:11 +01:00
/// No description provided for @profileTabProgress.
///
/// In en, this message translates to:
/// **'Progress'**
String get profileTabProgress ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
/// No description provided for @homeDays.
///
/// In en, this message translates to:
/// **'Days'**
String get homeDays ;
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>
2026-10-28 22:35:22 +01:00
/// 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 ) ;
feat(train): a recording runs an interval plan, with cues and Lap (ADR 0030, D42)
The plan chosen on the Record sheet goes to the recording service spelled
out: each step's end, its target (zones as the heart rates they span for
the profile's highest rate) and its spoken line in the app's language.
PlanRunner.kt moves through the steps on the moving time and distance,
counts a timed step's last seconds down and cues off target from a 30 s
window; PlanCues.kt gives a tone, a vibration and the voice, ducking
music. The file's plan and step lines bring it back, and the recording
screen shows the step, what is left of it, its target, the next step and
Lap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 22:59:48 +01:00
/// No description provided for @planSayRound.
///
/// In en, this message translates to:
/// **'{round} of {rounds}'**
String planSayRound ( int round , int rounds ) ;
/// No description provided for @planSayMinutes.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 minute} other{{count} minutes}}'**
String planSayMinutes ( int count ) ;
/// No description provided for @planSaySeconds.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 second} other{{count} seconds}}'**
String planSaySeconds ( int count ) ;
/// No description provided for @planSayMetres.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 metre} other{{count} metres}}'**
String planSayMetres ( int count ) ;
/// No description provided for @planSayKilometres.
///
/// In en, this message translates to:
/// **'{km} kilometres'**
String planSayKilometres ( String km ) ;
/// No description provided for @planSayUntilLap.
///
/// In en, this message translates to:
/// **'until you tap Lap'**
String get planSayUntilLap ;
/// No description provided for @planSayPace.
///
/// In en, this message translates to:
/// **'{from} to {to} a kilometre'**
String planSayPace ( String from , String to ) ;
/// No description provided for @planSaySpeed.
///
/// In en, this message translates to:
/// **'{from} to {to} kilometres an hour'**
String planSaySpeed ( String from , String to ) ;
/// No description provided for @planSayHeartRate.
///
/// In en, this message translates to:
/// **'{from} to {to} beats a minute'**
String planSayHeartRate ( String from , String to ) ;
/// No description provided for @planSayZone.
///
/// In en, this message translates to:
/// **'zone {zone}'**
String planSayZone ( int zone ) ;
/// No description provided for @planSaySplit.
///
/// In en, this message translates to:
/// **'{from} to {to} per 500 metres'**
String planSaySplit ( String from , String to ) ;
/// No description provided for @planSayWatts.
///
/// In en, this message translates to:
/// **'{from} to {to} watts'**
String planSayWatts ( String from , String to ) ;
/// No description provided for @planSayStrokeRate.
///
/// In en, this message translates to:
/// **'{from} to {to} strokes a minute'**
String planSayStrokeRate ( String from , String to ) ;
/// No description provided for @planSayHarder.
///
/// In en, this message translates to:
/// **'Push a little'**
String get planSayHarder ;
/// No description provided for @planSayEasier.
///
/// In en, this message translates to:
/// **'Ease off a little'**
String get planSayEasier ;
/// No description provided for @planSayDone.
///
/// In en, this message translates to:
/// **'Plan done'**
String get planSayDone ;
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>
2026-10-28 22:35:22 +01:00
/// 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 ) ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
/// No description provided for @daysEmptyTitle.
///
/// In en, this message translates to:
/// **'No days yet'**
String get daysEmptyTitle ;
/// No description provided for @daysEmptyHint.
///
/// In en, this message translates to:
/// **'The days and nights your Polar Loop counts show here.'**
String get daysEmptyHint ;
/// No description provided for @daysSteps.
///
/// In en, this message translates to:
/// **'{steps, plural, =1{1 step} other{{steps} steps}}'**
String daysSteps ( int steps ) ;
/// No description provided for @daysActive.
///
/// In en, this message translates to:
/// **'{time} active'**
String daysActive ( String time ) ;
/// No description provided for @daysAsleep.
///
/// In en, this message translates to:
/// **'{time} asleep'**
String daysAsleep ( String time ) ;
/// No description provided for @daysRecharge.
///
/// In en, this message translates to:
/// **'Recharge {level} of 6'**
String daysRecharge ( int level ) ;
/// No description provided for @dayHoursMinutes.
///
/// In en, this message translates to:
/// **'{hours} h {minutes} min'**
String dayHoursMinutes ( int hours , int minutes ) ;
/// No description provided for @dayMinutes.
///
/// In en, this message translates to:
/// **'{minutes} min'**
String dayMinutes ( int minutes ) ;
/// No description provided for @dayCalories.
///
/// In en, this message translates to:
/// **'{calories} kcal'**
String dayCalories ( int calories ) ;
/// No description provided for @dayGoal.
///
/// In en, this message translates to:
/// **'{percent} % of the day\'s goal'**
String dayGoal ( int percent ) ;
/// No description provided for @dayNothing.
///
/// In en, this message translates to:
/// **'The band has given nothing for this day yet.'**
String get dayNothing ;
/// No description provided for @dayTimes.
///
/// In en, this message translates to:
/// **'The day\'s time'**
String get dayTimes ;
/// No description provided for @dayTimeNonWear.
///
/// In en, this message translates to:
/// **'Not worn'**
String get dayTimeNonWear ;
/// No description provided for @dayTimeSleep.
///
/// In en, this message translates to:
/// **'Asleep'**
String get dayTimeSleep ;
/// No description provided for @dayTimeSedentary.
///
/// In en, this message translates to:
/// **'Sitting still'**
String get dayTimeSedentary ;
/// No description provided for @dayTimeLight.
///
/// In en, this message translates to:
/// **'Light activity'**
String get dayTimeLight ;
/// No description provided for @dayTimeModerate.
///
/// In en, this message translates to:
/// **'Moderate activity'**
String get dayTimeModerate ;
/// No description provided for @dayTimeVigorous.
///
/// In en, this message translates to:
/// **'Vigorous activity'**
String get dayTimeVigorous ;
/// No description provided for @dayHeartRate.
///
/// In en, this message translates to:
/// **'Heart rate'**
String get dayHeartRate ;
/// No description provided for @dayBpm.
///
/// In en, this message translates to:
/// **'{bpm} bpm'**
String dayBpm ( int bpm ) ;
/// No description provided for @dayNight.
///
/// In en, this message translates to:
/// **'The night'**
String get dayNight ;
/// No description provided for @dayNightSpan.
///
/// In en, this message translates to:
/// **'{from} – {to}'**
String dayNightSpan ( String from , String to ) ;
/// No description provided for @daySleepWake.
///
/// In en, this message translates to:
/// **'Awake'**
String get daySleepWake ;
/// No description provided for @daySleepRem.
///
/// In en, this message translates to:
/// **'REM'**
String get daySleepRem ;
/// No description provided for @daySleepLight.
///
/// In en, this message translates to:
/// **'Light'**
String get daySleepLight ;
/// No description provided for @daySleepDeep.
///
/// In en, this message translates to:
/// **'Deep'**
String get daySleepDeep ;
/// No description provided for @dayRatingPoorly.
///
/// In en, this message translates to:
/// **'You slept poorly'**
String get dayRatingPoorly ;
/// No description provided for @dayRatingSomewhatPoorly.
///
/// In en, this message translates to:
/// **'You slept somewhat poorly'**
String get dayRatingSomewhatPoorly ;
/// No description provided for @dayRatingNeither.
///
/// In en, this message translates to:
/// **'You slept neither poorly nor well'**
String get dayRatingNeither ;
/// No description provided for @dayRatingSomewhatWell.
///
/// In en, this message translates to:
/// **'You slept somewhat well'**
String get dayRatingSomewhatWell ;
/// No description provided for @dayRatingWell.
///
/// In en, this message translates to:
/// **'You slept well'**
String get dayRatingWell ;
/// No description provided for @dayRecharge.
///
/// In en, this message translates to:
/// **'Nightly Recharge'**
String get dayRecharge ;
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// No description provided for @dayRecovery.
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
///
/// In en, this message translates to:
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// **'Recovery'**
String get dayRecovery ;
/// No description provided for @dayRecoveryOf.
///
/// In en, this message translates to:
/// **'{level} of 6'**
String dayRecoveryOf ( int level ) ;
/// No description provided for @dayAns.
///
/// In en, this message translates to:
/// **'Autonomic nervous system'**
String get dayAns ;
/// No description provided for @dayHrvName.
///
/// In en, this message translates to:
/// **'Heart rate variability'**
String get dayHrvName ;
/// No description provided for @dayBreathingName.
///
/// In en, this message translates to:
/// **'Breathing'**
String get dayBreathingName ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// No description provided for @dayPerMinute.
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
///
/// In en, this message translates to:
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// **'{rate} a minute'**
String dayPerMinute ( String rate ) ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// No description provided for @dayMs.
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
///
/// In en, this message translates to:
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// **'{ms} ms'**
String dayMs ( int ms ) ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// Below a figure of the night: the person's own usual, as Polar learnt it.
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
///
/// In en, this message translates to:
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// **'usually {value}'**
String dayUsual ( String value ) ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// No description provided for @dayRechargeHelp.
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
///
/// In en, this message translates to:
fix(train): a day's page of its own, in cards, its night and Recharge drawn in place
The night's chart threw mid-paint where Polar's last phase starts at the
night's end or after it: its right edge was clamped between left + 1 and
the row's width, the lower bound then above the upper (an ArgumentError).
A throw mid-paint leaves the canvas moved, so the rows below and the charts
after them were drawn out of place, the skin temperature's among them. Each
phase now stays within its row, a pixel wide at least.
Nightly Recharge showed Polar's marks for a figure it has none of (-1 for
the recovery and the intervals, -100 for the nervous system's status, 0 for
a baseline not yet learnt) as the person's own. Recharge.known leaves them
out, on the page and in the days' list; the figures are rows, each with the
person's usual below it, and Polar's explanation of them (the status from
-10 to +10, what each measures) is behind halkyon_ui's question mark.
The page is a screen of its own over the tabs, without the bar and with the
way back at its top left, its parts in cards (the developer's review of
3 Nov 2026; D60 had it inside Profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 21:36:34 +01:00
/// **'Nightly Recharge is Polar\'s measure of how your body recovered in the first hours of sleep, against your own usual. Recovery runs from 1 to 6. The autonomic nervous system\'s status runs from − 10 to +10: above 0, it settled better than usual. Heart rate variability is how much the time between heartbeats varies; higher than usual tends to mean a better recovery. Breathing counts your breaths a minute. A figure the band has none of yet is left out.'**
String get dayRechargeHelp ;
feat(train): Days, the Loop's days and nights kept sealed (ADR 0030, D37, D38)
What the band read brings, from the bridge or the background job's
file (`LoopLines`), is kept by `DayRepository`: one sealed `day` and
`night` a date and the day's heart rate in chunks of three hours, every
ID made from the person and the date, a later read replacing what it
brought and keeping the rest, nothing rewritten unchanged; versioned,
gzipped JSON (`day_payload.dart`). `PolarLoop` reads the band as each
connection is ready (every half hour at most, or when asked), takes in
the background file as the app opens, and remembers up to which day
the days are sealed, which the next read then deletes on the band;
it keeps the background job on the band and from the right day, and
`Let the band go` stops it.
Home gains a Days tab, each day with its steps, active time, sleep and
Nightly Recharge, pulled to read the band; a day's page shows its
figures, its time in each class, its heart rate, the night's phases
and rating, Nightly Recharge and skin temperature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 19:53:12 +01:00
/// No description provided for @daySkin.
///
/// In en, this message translates to:
/// **'Skin temperature'**
String get daySkin ;
/// No description provided for @dayCelsius.
///
/// In en, this message translates to:
/// **'{value} °C'**
String dayCelsius ( String value ) ;
/// No description provided for @daySkinDeviation.
///
/// In en, this message translates to:
/// **'{value} °C from your usual at night'**
String daySkinDeviation ( String value ) ;
feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.
Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 00:37:44 +01:00
/// No description provided for @homeRoutes.
///
/// In en, this message translates to:
/// **'Routes'**
String get homeRoutes ;
/// No description provided for @routesNew.
///
/// In en, this message translates to:
/// **'New route'**
String get routesNew ;
/// No description provided for @routesEmptyTitle.
///
/// In en, this message translates to:
/// **'No routes yet'**
String get routesEmptyTitle ;
/// No description provided for @routesEmptyHint.
///
/// In en, this message translates to:
/// **'Tap the map to set waypoints, and your instance plans the way between them on paths and roads, with its climb and surfaces.'**
String get routesEmptyHint ;
/// No description provided for @routeLocked.
///
/// In en, this message translates to:
/// **'A route that doesn\'t open here'**
String get routeLocked ;
/// No description provided for @routeTitle.
///
/// In en, this message translates to:
/// **'Route'**
String get routeTitle ;
/// No description provided for @routeNewTitle.
///
/// In en, this message translates to:
/// **'New route'**
String get routeNewTitle ;
/// No description provided for @routeName.
///
/// In en, this message translates to:
/// **'Name'**
String get routeName ;
/// No description provided for @routeActivity.
///
/// In en, this message translates to:
/// **'For'**
String get routeActivity ;
/// No description provided for @routeActivityAny.
///
/// In en, this message translates to:
/// **'Any activity'**
String get routeActivityAny ;
/// No description provided for @routeProfile.
///
/// In en, this message translates to:
/// **'Planned for'**
String get routeProfile ;
/// No description provided for @routeProfileRun.
///
/// In en, this message translates to:
/// **'Running'**
String get routeProfileRun ;
/// No description provided for @routeProfileHike.
///
/// In en, this message translates to:
/// **'Hiking'**
String get routeProfileHike ;
/// No description provided for @routeProfileRoadBike.
///
/// In en, this message translates to:
/// **'Road bike'**
String get routeProfileRoadBike ;
/// No description provided for @routeProfileTrekkingBike.
///
/// In en, this message translates to:
/// **'Trekking bike'**
String get routeProfileTrekkingBike ;
/// No description provided for @routeProfileGravel.
///
/// In en, this message translates to:
/// **'Gravel bike'**
String get routeProfileGravel ;
/// No description provided for @routeProfileMtb.
///
/// In en, this message translates to:
/// **'Mountain bike'**
String get routeProfileMtb ;
/// No description provided for @routeTapHint.
///
/// In en, this message translates to:
2026-10-30 23:01:04 +01:00
/// **'Tap the map to add a waypoint, tap one to take it off. Hold one to drag it, or hold the line to pull out another.'**
feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.
Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 00:37:44 +01:00
String get routeTapHint ;
2026-10-30 23:01:04 +01:00
/// The menu of a waypoint tapped on the planner's map (D59).
///
/// In en, this message translates to:
/// **'Take this waypoint off'**
String get routeWaypointRemove ;
feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.
Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 00:37:44 +01:00
/// No description provided for @routeUndo.
///
/// In en, this message translates to:
/// **'Take the last waypoint off'**
String get routeUndo ;
2026-10-29 16:20:36 +01:00
/// The planner's search field over the map (D50).
///
/// In en, this message translates to:
/// **'Search for a place'**
String get routeSearchHint ;
/// No description provided for @routeSearchClear.
///
/// In en, this message translates to:
/// **'Clear the search'**
String get routeSearchClear ;
/// Under the planner's search: the instance found no place.
///
/// In en, this message translates to:
/// **'Nothing found.'**
String get routeSearchNothing ;
/// No description provided for @routeSearchTooOften.
///
/// In en, this message translates to:
/// **'Too many searches for now; try again in a minute.'**
String get routeSearchTooOften ;
/// No description provided for @routeSearchUnreachable.
///
/// In en, this message translates to:
/// **'The search can\'t be reached now.'**
String get routeSearchUnreachable ;
feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.
Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 00:37:44 +01:00
/// No description provided for @routePlanning.
///
/// In en, this message translates to:
/// **'Planning…'**
String get routePlanning ;
/// No description provided for @routeRefusedInvalid.
///
/// In en, this message translates to:
/// **'A waypoint too many, or one off the map.'**
String get routeRefusedInvalid ;
/// No description provided for @routeRefusedTooOften.
///
/// In en, this message translates to:
/// **'Planned too often: try again in a minute.'**
String get routeRefusedTooOften ;
/// No description provided for @routeRefusedNoRoute.
///
/// In en, this message translates to:
/// **'No way found there: a waypoint may be off the paths, or outside the region your instance plans in.'**
String get routeRefusedNoRoute ;
/// No description provided for @routeRefusedUnreachable.
///
/// In en, this message translates to:
/// **'Your instance can\'t be reached to plan the way.'**
String get routeRefusedUnreachable ;
/// No description provided for @routeNotOffered.
///
/// In en, this message translates to:
/// **'Your instance doesn\'t plan routes.'**
String get routeNotOffered ;
/// No description provided for @routeAscent.
///
/// In en, this message translates to:
/// **'{metres} m up'**
String routeAscent ( int metres ) ;
/// No description provided for @routeDescent.
///
/// In en, this message translates to:
/// **'{metres} m down'**
String routeDescent ( int metres ) ;
/// A route in the list: its distance and climb.
///
/// In en, this message translates to:
/// **'{distance} · {ascent}'**
String routeSummary ( String distance , String ascent ) ;
2026-10-29 10:44:47 +01:00
/// In the Routes tab: a route brought in from a GPX file (ADR 0030, D45).
///
/// In en, this message translates to:
/// **'Bring in a GPX file'**
String get routesBringIn ;
/// No description provided for @routeBringInTitle.
///
/// In en, this message translates to:
/// **'Bring in {name}'**
String routeBringInTitle ( String name ) ;
/// No description provided for @routeBringInSnap.
///
/// In en, this message translates to:
/// **'Plan it along the paths'**
String get routeBringInSnap ;
/// No description provided for @routeBringInSnapHint.
///
/// In en, this message translates to:
/// **'Through its points, with the profile you choose; its turns told'**
String get routeBringInSnapHint ;
/// No description provided for @routeBringInDrawn.
///
/// In en, this message translates to:
/// **'Keep it as drawn'**
String get routeBringInDrawn ;
/// No description provided for @routeBringInDrawnHint.
///
/// In en, this message translates to:
/// **'Its way as the file has it, no turns told'**
String get routeBringInDrawnHint ;
/// No description provided for @routeNotGpx.
///
/// In en, this message translates to:
/// **'This file holds no route Train can read'**
String get routeNotGpx ;
/// No description provided for @routeShareGpx.
///
/// In en, this message translates to:
/// **'Share as GPX'**
String get routeShareGpx ;
/// No description provided for @routeSaveGpx.
///
/// In en, this message translates to:
/// **'Save as GPX'**
String get routeSaveGpx ;
2026-10-29 10:28:36 +01:00
/// A switch on a saved route: its map kept on this device (ADR 0030, D45).
///
/// In en, this message translates to:
/// **'Keep the map offline'**
String get routeOffline ;
/// No description provided for @routeOfflineOff.
///
/// In en, this message translates to:
/// **'Its map along the way, 2 km either side, on this device for when there\'s no network'**
String get routeOfflineOff ;
/// No description provided for @routeOfflineLoading.
///
/// In en, this message translates to:
/// **'Keeping the map: {done} of {total} tiles'**
String routeOfflineLoading ( int done , int total ) ;
/// No description provided for @routeOfflineKept.
///
/// In en, this message translates to:
/// **'Kept on this device: {size} MB'**
String routeOfflineKept ( String size ) ;
/// No description provided for @routeOfflineMissing.
///
/// In en, this message translates to:
/// **'{missing} of {total} tiles didn\'t come: asked for again when the app next opens online'**
String routeOfflineMissing ( int missing , int total ) ;
/// No description provided for @routeOfflineNoMap.
///
/// In en, this message translates to:
/// **'The map can\'t be reached now: try again online'**
String get routeOfflineNoMap ;
/// No description provided for @routeOfflineSaveFirst.
///
/// In en, this message translates to:
/// **'Save the route to keep its map offline'**
String get routeOfflineSaveFirst ;
/// No description provided for @routeKeptOffline.
///
/// In en, this message translates to:
/// **'Map kept offline'**
String get routeKeptOffline ;
feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.
Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 00:37:44 +01:00
/// No description provided for @routeSurfaces.
///
/// In en, this message translates to:
/// **'Surfaces'**
String get routeSurfaces ;
/// No description provided for @surfacePaved.
///
/// In en, this message translates to:
/// **'Paved'**
String get surfacePaved ;
/// No description provided for @surfaceCompacted.
///
/// In en, this message translates to:
/// **'Compacted'**
String get surfaceCompacted ;
/// No description provided for @surfaceGravel.
///
/// In en, this message translates to:
/// **'Gravel'**
String get surfaceGravel ;
/// No description provided for @surfaceNatural.
///
/// In en, this message translates to:
/// **'Natural ground'**
String get surfaceNatural ;
/// No description provided for @surfaceUnknown.
///
/// In en, this message translates to:
/// **'Not known'**
String get surfaceUnknown ;
/// No description provided for @routeSave.
///
/// In en, this message translates to:
/// **'Save'**
String get routeSave ;
/// No description provided for @routeNotSaved.
///
/// In en, this message translates to:
/// **'The route wasn\'t saved.'**
String get routeNotSaved ;
/// No description provided for @routeDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get routeDelete ;
/// No description provided for @routeDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete this route?'**
String get routeDeleteTitle ;
/// No description provided for @routeDeleteMessage.
///
/// In en, this message translates to:
/// **'It\'s deleted on all your devices.'**
String get routeDeleteMessage ;
/// No description provided for @routeDeleted.
///
/// In en, this message translates to:
/// **'Route deleted'**
String get routeDeleted ;
2026-10-29 08:27:27 +01:00
/// No description provided for @turnStraight.
///
/// In en, this message translates to:
/// **'Go straight on'**
String get turnStraight ;
/// No description provided for @turnLeft.
///
/// In en, this message translates to:
/// **'Turn left'**
String get turnLeft ;
/// No description provided for @turnSlightLeft.
///
/// In en, this message translates to:
/// **'Bear left'**
String get turnSlightLeft ;
/// No description provided for @turnSharpLeft.
///
/// In en, this message translates to:
/// **'Turn sharp left'**
String get turnSharpLeft ;
/// No description provided for @turnRight.
///
/// In en, this message translates to:
/// **'Turn right'**
String get turnRight ;
/// No description provided for @turnSlightRight.
///
/// In en, this message translates to:
/// **'Bear right'**
String get turnSlightRight ;
/// No description provided for @turnSharpRight.
///
/// In en, this message translates to:
/// **'Turn sharp right'**
String get turnSharpRight ;
/// No description provided for @turnKeepLeft.
///
/// In en, this message translates to:
/// **'Keep left'**
String get turnKeepLeft ;
/// No description provided for @turnKeepRight.
///
/// In en, this message translates to:
/// **'Keep right'**
String get turnKeepRight ;
/// No description provided for @turnAround.
///
/// In en, this message translates to:
/// **'Turn around'**
String get turnAround ;
/// No description provided for @turnRoundabout.
///
/// In en, this message translates to:
/// **'At the roundabout, take exit {exit}'**
String turnRoundabout ( int exit ) ;
/// No description provided for @turnExitLeft.
///
/// In en, this message translates to:
/// **'Take the exit on the left'**
String get turnExitLeft ;
/// No description provided for @turnExitRight.
///
/// In en, this message translates to:
/// **'Take the exit on the right'**
String get turnExitRight ;
/// Spoken before a turn while following a route.
///
/// In en, this message translates to:
/// **'In {metres} metres: {turn}'**
String routeSayIn ( int metres , String turn ) ;
/// Spoken off the route; the recorder puts the figures in.
///
/// In en, this message translates to:
/// **'Off the route. It lies {metres} metres {direction}.'**
String routeSayOff ( String metres , String direction ) ;
/// No description provided for @routeSayOn.
///
/// In en, this message translates to:
/// **'Back on the route'**
String get routeSayOn ;
/// No description provided for @routeSayArrived.
///
/// In en, this message translates to:
/// **'End of the route'**
String get routeSayArrived ;
/// No description provided for @directionNorth.
///
/// In en, this message translates to:
/// **'to the north'**
String get directionNorth ;
/// No description provided for @directionNorthEast.
///
/// In en, this message translates to:
/// **'to the north-east'**
String get directionNorthEast ;
/// No description provided for @directionEast.
///
/// In en, this message translates to:
/// **'to the east'**
String get directionEast ;
/// No description provided for @directionSouthEast.
///
/// In en, this message translates to:
/// **'to the south-east'**
String get directionSouthEast ;
/// No description provided for @directionSouth.
///
/// In en, this message translates to:
/// **'to the south'**
String get directionSouth ;
/// No description provided for @directionSouthWest.
///
/// In en, this message translates to:
/// **'to the south-west'**
String get directionSouthWest ;
/// No description provided for @directionWest.
///
/// In en, this message translates to:
/// **'to the west'**
String get directionWest ;
/// No description provided for @directionNorthWest.
///
/// In en, this message translates to:
/// **'to the north-west'**
String get directionNorthWest ;
/// No description provided for @recordRoute.
///
/// In en, this message translates to:
/// **'Route'**
String get recordRoute ;
/// No description provided for @recordNoRoute.
///
/// In en, this message translates to:
/// **'None'**
String get recordNoRoute ;
/// No description provided for @recordingNextTurn.
///
/// In en, this message translates to:
/// **'In {distance}'**
String recordingNextTurn ( String distance ) ;
/// No description provided for @recordingOffRoute.
///
/// In en, this message translates to:
/// **'Off the route: {distance} {direction}'**
String recordingOffRoute ( String distance , String direction ) ;
/// No description provided for @recordingRouteEnd.
///
/// In en, this message translates to:
/// **'End of the route'**
String get recordingRouteEnd ;
/// No description provided for @recordingFollowRoute.
///
/// In en, this message translates to:
/// **'Following {name}'**
String recordingFollowRoute ( String name ) ;
/// No description provided for @recordingMapHint.
///
/// In en, this message translates to:
/// **'Swipe for the map'**
String get recordingMapHint ;
/// No description provided for @recordingFiguresHint.
///
/// In en, this message translates to:
/// **'Swipe back for the figures'**
String get recordingFiguresHint ;
/// No description provided for @recordingNorthUp.
///
/// In en, this message translates to:
/// **'North up'**
String get recordingNorthUp ;
/// No description provided for @recordingHeadingUp.
///
/// In en, this message translates to:
/// **'Heading up'**
String get recordingHeadingUp ;
2026-10-29 12:36:32 +01:00
/// The music screen's title, and its tile in the account sheet.
///
/// In en, this message translates to:
/// **'Music'**
String get musicTitle ;
/// Under Music in the account sheet.
///
/// In en, this message translates to:
/// **'Jellyfin playlists to run to'**
String get musicHint ;
/// No description provided for @musicIntro.
///
/// In en, this message translates to:
/// **'Train plays music from your Jellyfin server, choosing tracks whose beat fits your steps. Your account and music stay on this phone.'**
String get musicIntro ;
/// The label of the server's address.
///
/// In en, this message translates to:
/// **'Jellyfin server'**
String get musicServer ;
/// No description provided for @musicConnect.
///
/// In en, this message translates to:
/// **'Connect'**
String get musicConnect ;
/// No description provided for @musicBadAddress.
///
/// In en, this message translates to:
/// **'Enter the server\'s address, such as jellyfin.example.org.'**
String get musicBadAddress ;
2026-10-30 12:55:59 +01:00
/// No description provided for @musicInClear.
///
/// In en, this message translates to:
/// **'Train connects to Jellyfin over https only: http would send the password unencrypted. Use the server\'s https address.'**
String get musicInClear ;
2026-10-29 12:36:32 +01:00
/// No description provided for @musicUnreachable.
///
/// In en, this message translates to:
/// **'The server can\'t be reached. Check the address and the connection.'**
String get musicUnreachable ;
/// No description provided for @musicNotJellyfin.
///
/// In en, this message translates to:
/// **'There is no Jellyfin server at this address.'**
String get musicNotJellyfin ;
/// No description provided for @musicRefused.
///
/// In en, this message translates to:
/// **'Jellyfin refused. Check the user name and password.'**
String get musicRefused ;
/// No description provided for @musicCodeExpired.
///
/// In en, this message translates to:
/// **'The code has expired.'**
String get musicCodeExpired ;
/// No description provided for @musicFailed.
///
/// In en, this message translates to:
/// **'Jellyfin answered with an error. Try again later.'**
String get musicFailed ;
/// No description provided for @musicQuickConnect.
///
/// In en, this message translates to:
/// **'To sign in to {server}, enter this code in Jellyfin on another device, under Settings, Quick Connect.'**
String musicQuickConnect ( String server ) ;
/// No description provided for @musicNewCode.
///
/// In en, this message translates to:
/// **'New code'**
String get musicNewCode ;
/// No description provided for @musicBack.
///
/// In en, this message translates to:
/// **'Back'**
String get musicBack ;
/// No description provided for @musicUsePassword.
///
/// In en, this message translates to:
/// **'Use a password'**
String get musicUsePassword ;
/// No description provided for @musicPasswordIntro.
///
/// In en, this message translates to:
/// **'Sign in to {server} with your Jellyfin user name and password.'**
String musicPasswordIntro ( String server ) ;
/// No description provided for @musicUserName.
///
/// In en, this message translates to:
/// **'User name'**
String get musicUserName ;
/// No description provided for @musicPassword.
///
/// In en, this message translates to:
/// **'Password'**
String get musicPassword ;
/// No description provided for @musicSignIn.
///
/// In en, this message translates to:
/// **'Sign in'**
String get musicSignIn ;
/// No description provided for @musicSignedInAs.
///
/// In en, this message translates to:
/// **'Signed in as {name}'**
String musicSignedInAs ( String name ) ;
/// No description provided for @musicSignOut.
///
/// In en, this message translates to:
/// **'Sign out'**
String get musicSignOut ;
/// No description provided for @musicPlaylists.
///
/// In en, this message translates to:
/// **'Playlists for running'**
String get musicPlaylists ;
/// No description provided for @musicPlaylistsHint.
///
/// In en, this message translates to:
/// **'Train chooses from the tracks of the playlists you mark, and keeps them on this phone.'**
String get musicPlaylistsHint ;
/// No description provided for @musicNoPlaylists.
///
/// In en, this message translates to:
/// **'There are no music playlists on this server yet.'**
String get musicNoPlaylists ;
/// No description provided for @musicRetry.
///
/// In en, this message translates to:
/// **'Try again'**
String get musicRetry ;
/// No description provided for @musicTracks.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 track} other{{count} tracks}}'**
String musicTracks ( int count ) ;
2026-10-29 12:48:53 +01:00
/// No description provided for @musicKept.
///
/// In en, this message translates to:
/// **'On this phone'**
String get musicKept ;
/// No description provided for @musicKeptWaiting.
///
/// In en, this message translates to:
/// **'{done} of {total} tracks: the rest waits for Wi-Fi'**
String musicKeptWaiting ( int done , int total ) ;
/// No description provided for @musicKeptLoading.
///
/// In en, this message translates to:
/// **'Downloading: {done} of {total} tracks'**
String musicKeptLoading ( int done , int total ) ;
/// No description provided for @musicKeptDone.
///
/// In en, this message translates to:
/// **'{count} tracks, {size} MB, {tempo} with a tempo'**
String musicKeptDone ( int count , String size , int tempo ) ;
/// No description provided for @musicKeptDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get musicKeptDelete ;
/// No description provided for @musicKeptDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete the music on this phone?'**
String get musicKeptDeleteTitle ;
/// No description provided for @musicKeptDeleteText.
///
/// In en, this message translates to:
/// **'Every playlist is unmarked, and its tracks deleted from this phone. They stay on your Jellyfin server.'**
String get musicKeptDeleteText ;
feat(train): music by cadence in a recording, or the metronome (ADR 0030, D48)
A workout keeps its cadence as a series of its own (steps a minute, every
10 s while moving). The Record sheet offers Music, Metronome or Off on
foot, remembered; Music once a kept track has a tempo. The recorder
measures the cadence over the last 30 s and plays a fitting track (its
tempo, half or double, within 6 %) through Media3's ExoPlayer, retuning
the track playing where it still fits, else crossfading over 3 s; a
plan's new step picks anew. A run starts at the cadence learnt from the
person's runs, which also gives a plan's steps their cadence.
Media3 1.10.1, pinned by Gradle's checksums (trusted on first download
from Google's Maven, 29 Oct 2026), for review:
media3-common aar 39686f54ff6baf8cbc841199afd55619533eaef2ab6cd0efc32b8a3c55dd88c3
module a81b2f0ebc256fe0e09f05d93c5259ade5bf44707748b9295343bee67575d9c2
media3-container aar a198b62c577fa434941282fbfa31eb66ff0ff308a6573edf0b5dd01f4e38445d
module e2a1c62c7e5d1013b906bc6ff5056db3e797cda4505e2d7d229524b24ca67a0e
media3-database aar 520e6f814e42a7d428db187ac073b453c0fcba0f4f5f4edd1cf33fafd2cfb740
module c380fea161e80d8074215a20e86e3dc2c2d825de964ca87aee66c8331f5ae252
media3-datasource aar e22f9f779047ce951152c793af094593e7cbb52475a82d56fa4f869fd319e700
module 9d32d2ec6a983ee9f418bcc05777aa23eee4db37bf794f44fb66a05d6f22e963
media3-decoder aar a9fb2b8fa1d54b4a8d6bda98a1c655b04d59c74b3ace7e2503e9e39830f13aee
module 388d5226366edd1facf8eec42cb5420871a46032d545286e90eaea8454789580
media3-exoplayer aar 19090f829afbbb595d67618f59c212f72b433ac01dc72b47594cf42552c8ed61
module ba0b9ef325fa114baa0f2fbdb4caf54029606924be1999f4af02bed5f079756b
media3-extractor aar 3c823279356b64f4215a8c6cb1e6bfa60c533351a651786ad6fa69ad001ef1b1
module b3eb755d7373e4fd199ff80814005f3b303693dfb8ace16b5b9482655d49d493
Not tried on a phone yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 13:31:41 +01:00
2026-10-29 19:09:06 +01:00
/// No description provided for @musicSmaller.
///
/// In en, this message translates to:
/// **'Smaller copies'**
String get musicSmaller ;
/// No description provided for @musicSmallerHint.
///
/// In en, this message translates to:
/// **'Lossless files such as FLAC kept as Opus at 128 kbps, which your server makes. MP3 and AAC stay as they are.'**
String get musicSmallerHint ;
feat(train): music by cadence in a recording, or the metronome (ADR 0030, D48)
A workout keeps its cadence as a series of its own (steps a minute, every
10 s while moving). The Record sheet offers Music, Metronome or Off on
foot, remembered; Music once a kept track has a tempo. The recorder
measures the cadence over the last 30 s and plays a fitting track (its
tempo, half or double, within 6 %) through Media3's ExoPlayer, retuning
the track playing where it still fits, else crossfading over 3 s; a
plan's new step picks anew. A run starts at the cadence learnt from the
person's runs, which also gives a plan's steps their cadence.
Media3 1.10.1, pinned by Gradle's checksums (trusted on first download
from Google's Maven, 29 Oct 2026), for review:
media3-common aar 39686f54ff6baf8cbc841199afd55619533eaef2ab6cd0efc32b8a3c55dd88c3
module a81b2f0ebc256fe0e09f05d93c5259ade5bf44707748b9295343bee67575d9c2
media3-container aar a198b62c577fa434941282fbfa31eb66ff0ff308a6573edf0b5dd01f4e38445d
module e2a1c62c7e5d1013b906bc6ff5056db3e797cda4505e2d7d229524b24ca67a0e
media3-database aar 520e6f814e42a7d428db187ac073b453c0fcba0f4f5f4edd1cf33fafd2cfb740
module c380fea161e80d8074215a20e86e3dc2c2d825de964ca87aee66c8331f5ae252
media3-datasource aar e22f9f779047ce951152c793af094593e7cbb52475a82d56fa4f869fd319e700
module 9d32d2ec6a983ee9f418bcc05777aa23eee4db37bf794f44fb66a05d6f22e963
media3-decoder aar a9fb2b8fa1d54b4a8d6bda98a1c655b04d59c74b3ace7e2503e9e39830f13aee
module 388d5226366edd1facf8eec42cb5420871a46032d545286e90eaea8454789580
media3-exoplayer aar 19090f829afbbb595d67618f59c212f72b433ac01dc72b47594cf42552c8ed61
module ba0b9ef325fa114baa0f2fbdb4caf54029606924be1999f4af02bed5f079756b
media3-extractor aar 3c823279356b64f4215a8c6cb1e6bfa60c533351a651786ad6fa69ad001ef1b1
module b3eb755d7373e4fd199ff80814005f3b303693dfb8ace16b5b9482655d49d493
Not tried on a phone yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 13:31:41 +01:00
/// No description provided for @recordMusic.
///
/// In en, this message translates to:
/// **'Music'**
String get recordMusic ;
/// The segment playing the kept tracks by cadence.
///
/// In en, this message translates to:
/// **'Music'**
String get recordMusicOn ;
/// No description provided for @recordMetronome.
///
/// In en, this message translates to:
/// **'Metronome'**
String get recordMetronome ;
/// No description provided for @recordMusicOff.
///
/// In en, this message translates to:
/// **'Off'**
String get recordMusicOff ;
2026-10-29 14:25:13 +01:00
/// No description provided for @recordAnnounce.
///
/// In en, this message translates to:
/// **'Announcements'**
String get recordAnnounce ;
/// The switch's hint on foot (D49).
///
/// In en, this message translates to:
/// **'Every kilometre: time, pace and heart rate'**
String get recordAnnounceFoot ;
/// The switch's hint on a bike (D49).
///
/// In en, this message translates to:
/// **'Every 5 km: time, speed and heart rate'**
String get recordAnnounceBike ;
/// No description provided for @announceKilometres.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 kilometre} other{{count} kilometres}}'**
String announceKilometres ( int count ) ;
/// No description provided for @announceHours.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 hour} other{{count} hours}}'**
String announceHours ( int count ) ;
/// No description provided for @announceTime.
///
/// In en, this message translates to:
/// **'Time {time}'**
String announceTime ( String time ) ;
/// No description provided for @announcePace.
///
/// In en, this message translates to:
/// **'Pace {pace} a kilometre'**
String announcePace ( String pace ) ;
/// No description provided for @announceSpeed.
///
/// In en, this message translates to:
/// **'Speed {speed} kilometres an hour'**
String announceSpeed ( String speed ) ;
/// No description provided for @announceHeartRate.
///
/// In en, this message translates to:
/// **'Heart rate {bpm}'**
String announceHeartRate ( String bpm ) ;
feat(train): a workout out as GPX, TCX or FIT (ADR 0030, D49)
11b: Export on a workout's page hands it to the share sheet as GPX (with
a track only; heart rate and cadence in Garmin's TrackPointExtension),
TCX (laps, heart rate, cadence, watts, a row's distance) or FIT (file
ID, records, timer events at the pauses, laps with their intensities,
session, activity), all from the same points: the precise fixes while
moving, else the rower's frames, else the heart rates; a plan's steps
are the laps. The FIT writer is our own (protocol 1.0, CRC-16/ARC),
tested by a reader written from the specification.
Not tried yet with Strava, intervals.icu or Garmin Connect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 14:37:48 +01:00
/// No description provided for @workoutExport.
///
/// In en, this message translates to:
/// **'Export'**
String get workoutExport ;
/// No description provided for @workoutExportTitle.
///
/// In en, this message translates to:
/// **'Export the workout'**
String get workoutExportTitle ;
/// No description provided for @workoutExportGpxHint.
///
/// In en, this message translates to:
/// **'The track, for maps and other apps'**
String get workoutExportGpxHint ;
/// No description provided for @workoutExportTcxHint.
///
/// In en, this message translates to:
/// **'Laps, heart rate and cadence, for most training sites'**
String get workoutExportTcxHint ;
/// No description provided for @workoutExportFitHint.
///
/// In en, this message translates to:
/// **'What Strava, Garmin and intervals.icu read best'**
String get workoutExportFitHint ;
feat(train): a ride reads the bike's speed and cadence sensor and power meter (ADR 0030, D49)
11d, its sensors: a ride's Record sheet offers the CSC sensor (0x1816)
and the power meter (0x1818), found nearby and remembered as the strap
is; the person's devices are now one map of DeviceKind to DeviceStore.
The service parses CSC and Cycling Power measurements (JVM-tested,
counters' rollover, coasting), writes the wheel's revolutions, the
crank's cadence and the watts to the file and to series of their own.
The recording screen shows cadence and watts; the page the power's
chart, average, highest and normalised power and the cadence's chart and
average; GPX, TCX and FIT carry them. A plan's watts target reads the
power meter too.
The wheel's speed and distance follow. Not tried with a real sensor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-29 15:11:20 +01:00
/// No description provided for @recordSpeedSensor.
///
/// In en, this message translates to:
/// **'Speed and cadence sensor'**
String get recordSpeedSensor ;
/// No description provided for @recordNoSpeedSensor.
///
/// In en, this message translates to:
/// **'None chosen'**
String get recordNoSpeedSensor ;
/// No description provided for @recordPowerMeter.
///
/// In en, this message translates to:
/// **'Power meter'**
String get recordPowerMeter ;
/// No description provided for @recordNoPowerMeter.
///
/// In en, this message translates to:
/// **'None chosen'**
String get recordNoPowerMeter ;
/// No description provided for @speedSensorSheetTitle.
///
/// In en, this message translates to:
/// **'Speed and cadence sensor'**
String get speedSensorSheetTitle ;
/// No description provided for @speedSensorSearching.
///
/// In en, this message translates to:
/// **'Looking for speed and cadence sensors nearby…'**
String get speedSensorSearching ;
/// No description provided for @speedSensorSearchingHint.
///
/// In en, this message translates to:
/// **'Turn the wheel or the crank to wake it. One another app holds can\'t be found: close that app first.'**
String get speedSensorSearchingHint ;
/// No description provided for @speedSensorUnnamed.
///
/// In en, this message translates to:
/// **'Unnamed sensor'**
String get speedSensorUnnamed ;
/// No description provided for @speedSensorForget.
///
/// In en, this message translates to:
/// **'Forget sensor'**
String get speedSensorForget ;
/// No description provided for @speedSensorRefused.
///
/// In en, this message translates to:
/// **'Train needs nearby devices to find your sensor. You can allow it in the system\'s settings.'**
String get speedSensorRefused ;
/// No description provided for @powerMeterSheetTitle.
///
/// In en, this message translates to:
/// **'Power meter'**
String get powerMeterSheetTitle ;
/// No description provided for @powerMeterSearching.
///
/// In en, this message translates to:
/// **'Looking for power meters nearby…'**
String get powerMeterSearching ;
/// No description provided for @powerMeterSearchingHint.
///
/// In en, this message translates to:
/// **'Turn the crank to wake it. One another app holds can\'t be found: close that app first.'**
String get powerMeterSearchingHint ;
/// No description provided for @powerMeterUnnamed.
///
/// In en, this message translates to:
/// **'Unnamed power meter'**
String get powerMeterUnnamed ;
/// No description provided for @powerMeterForget.
///
/// In en, this message translates to:
/// **'Forget power meter'**
String get powerMeterForget ;
/// No description provided for @powerMeterRefused.
///
/// In en, this message translates to:
/// **'Train needs nearby devices to find your power meter. You can allow it in the system\'s settings.'**
String get powerMeterRefused ;
/// No description provided for @recordingCadence.
///
/// In en, this message translates to:
/// **'turns a minute'**
String get recordingCadence ;
/// No description provided for @workoutPedalling.
///
/// In en, this message translates to:
/// **'Cadence'**
String get workoutPedalling ;
/// No description provided for @workoutAverageCadence.
///
/// In en, this message translates to:
/// **'average cadence'**
String get workoutAverageCadence ;
/// No description provided for @workoutNormalisedPower.
///
/// In en, this message translates to:
/// **'normalised power'**
String get workoutNormalisedPower ;
/// No description provided for @workoutRpmValue.
///
/// In en, this message translates to:
/// **'{rpm} rpm'**
String workoutRpmValue ( int rpm ) ;
2026-10-29 15:22:48 +01:00
/// No description provided for @recordWheel.
///
/// In en, this message translates to:
/// **'Wheel circumference'**
String get recordWheel ;
/// No description provided for @recordWheelMeasured.
///
/// In en, this message translates to:
/// **'{mm} mm, measured by GPS'**
String recordWheelMeasured ( int mm ) ;
/// No description provided for @recordWheelTyped.
///
/// In en, this message translates to:
/// **'{mm} mm, as you typed it'**
String recordWheelTyped ( int mm ) ;
/// No description provided for @recordWheelDefault.
///
/// In en, this message translates to:
/// **'{mm} mm until GPS has measured it on a ride of 2 km'**
String recordWheelDefault ( int mm ) ;
/// No description provided for @recordWheelHint.
///
/// In en, this message translates to:
/// **'In millimetres, as the tyre says (700× 25C: 2105); empty to have GPS measure it'**
String get recordWheelHint ;
/// No description provided for @recordWheelSave.
///
/// In en, this message translates to:
/// **'Save'**
String get recordWheelSave ;
feat(train): Home, Maps, Record, Groups and Profile in Learn's frame, washed in carrot (ADR 0030, D60)
Train's tabs are Strava's five where one screen with four tabs at its
top had held workouts, days, plans and routes: Home the workouts, Maps
the routes, Record recording and the interval plans, Groups a screen
saying they come later, Profile the days and the way to the account.
A device that doesn't record has no Record tab, its plans beside the
days in Profile.
The frame is halkyon_ui's NavigationFrame, driven by go_router as
Learn's: a workout's page opens in Home and a day's in Profile with the
tabs beside them; the route and plan editors and the recording cover
them, their content handed over as it is. Every tab washes down from the
top in carrot, the warm end of Train's icon, as Learn's in cobalt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 23:52:48 +01:00
/// No description provided for @shellTabHome.
///
/// In en, this message translates to:
/// **'Home'**
String get shellTabHome ;
/// No description provided for @shellTabMaps.
///
/// In en, this message translates to:
/// **'Maps'**
String get shellTabMaps ;
/// No description provided for @shellTabRecord.
///
/// In en, this message translates to:
/// **'Record'**
String get shellTabRecord ;
/// No description provided for @shellTabGroups.
///
/// In en, this message translates to:
/// **'Groups'**
String get shellTabGroups ;
/// No description provided for @shellTabProfile.
///
/// In en, this message translates to:
/// **'Profile'**
String get shellTabProfile ;
/// No description provided for @groupsLaterTitle.
///
/// In en, this message translates to:
/// **'Groups come later'**
String get groupsLaterTitle ;
/// No description provided for @groupsLaterHint.
///
/// In en, this message translates to:
/// **'Training with others, in groups of your own, comes in a later version of Train.'**
String get groupsLaterHint ;
feat(train): Settings over the tabs from a cog on Home and Profile, the account near its foot (ADR 0030, D60)
As Learn's: a cog atop Home and Profile, with the count of accounts
waiting for whoever operates the instance, opens Settings over the tabs,
where an account sheet had held the account and the training's screens.
Grouped in cards: the operator's card, the training (your body, Health
Connect, the Polar Loop, music, those the device has), the account near
the foot, About Halkyon Train last, and the version small at the very
bottom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 00:16:01 +01:00
/// No description provided for @settingsTitle.
///
/// In en, this message translates to:
/// **'Settings'**
String get settingsTitle ;
/// No description provided for @settingsTraining.
///
/// In en, this message translates to:
/// **'Training'**
String get settingsTraining ;
/// No description provided for @settingsVersion.
///
/// In en, this message translates to:
/// **'Version {version} (build {build})'**
String settingsVersion ( String version , String build ) ;
2026-10-31 12:12:55 +01:00
/// No description provided for @recordingBarStopped.
///
/// In en, this message translates to:
/// **'Stopped: save or discard it'**
String get recordingBarStopped ;
/// No description provided for @recordingBarDistance.
///
/// In en, this message translates to:
/// **'{km} km'**
String recordingBarDistance ( String km ) ;
2026-10-31 12:20:39 +01:00
/// No description provided for @homeWeekTitle.
///
/// In en, this message translates to:
/// **'This week'**
String get homeWeekTitle ;
/// No description provided for @homeWeekTime.
///
/// In en, this message translates to:
/// **'Time'**
String get homeWeekTime ;
/// No description provided for @homeWeekDistance.
///
/// In en, this message translates to:
/// **'Distance'**
String get homeWeekDistance ;
/// No description provided for @homeWeekWorkouts.
///
/// In en, this message translates to:
/// **'Workouts'**
String get homeWeekWorkouts ;
2026-10-31 18:00:45 +01:00
/// No description provided for @recordPlansHeading.
///
/// In en, this message translates to:
/// **'Interval plans'**
String get recordPlansHeading ;
2026-10-31 18:46:26 +01:00
/// No description provided for @profileWeeksTitle.
///
/// In en, this message translates to:
/// **'Last 12 weeks'**
String get profileWeeksTitle ;
/// No description provided for @profileWeekOf.
///
/// In en, this message translates to:
/// **'Week of {date}'**
String profileWeekOf ( String date ) ;
/// No description provided for @profileWorkouts.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 workout} other{{count} workouts}}'**
String profileWorkouts ( int count ) ;
/// No description provided for @profileYearEmpty.
///
/// In en, this message translates to:
/// **'No workouts this year yet.'**
String get profileYearEmpty ;
/// No description provided for @bodyWeight.
///
/// In en, this message translates to:
/// **'{kg} kg'**
String bodyWeight ( String kg ) ;
feat(train): weight, entered on Progress and read from Health Connect, sealed and synced (ADR 0030, D63)
A person's weights are each a sealed `body_weight` row, as the days and
the places: the weight in grams, when it was measured (UTC), where it
came from and, for Health Connect's, the record's id and app are the
payload; the server sees the space and the ciphertext alone.
Progress gets a Weight card after Your body: the latest and its day,
its line over time (held, each weight and its day), the plus to enter
one (a comma taken as a point, 20 to 400 kg, when measured, today by
default) and All, the list to delete one. The latest weight is also
Your body's, which the band's and the zones' figures work from. In
kilograms alone: Train is metric throughout and has no units setting
yet.
Health Connect's weight records are read in when switched on in its
screen (READ_WEIGHT, asked as the others): each record once, its id kept
in health_connect.json also after it is deleted here, never Train's own,
and none within 10 minutes and 0.1 kg of a weight entered in Train.
The migration (20261103204323683-body-weights):
- creates (or replaces, unchanged) `gen_random_uuid_v7()`, the
default of every UUID id, as each of Serverpod's migrations does;
- CREATE TABLE "body_weight": "id" uuid primary key defaulting to a
UUID v7, "spaceId" bigint (the sync space), "keyId" text (the key
that sealed it), "payload" bytea NOT NULL (the sealed content),
"nonce" bytea;
- CREATE INDEX "body_weight_space_idx" on "spaceId", for a space's
rows as the sync reads them;
- a foreign key from "spaceId" to "offline_sync_spaces"("id"),
ON DELETE CASCADE, so that an account's deletion takes its weights
with its space;
- the version rows of Train's and every module's migrations.
Nothing existing changes, so nothing is lost.
Tests: the row written and read back in its owner's space, and the
denials: another person reads none of them, and writing one into
another's space throws OfflineSyncSpaceMembershipException and leaves
it empty; an account's deletion takes them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 22:11:57 +01:00
/// No description provided for @weightTitle.
///
/// In en, this message translates to:
/// **'Weight'**
String get weightTitle ;
/// No description provided for @weightAdd.
///
/// In en, this message translates to:
/// **'Add weight'**
String get weightAdd ;
/// No description provided for @weightAll.
///
/// In en, this message translates to:
/// **'All'**
String get weightAll ;
/// No description provided for @weightEmpty.
///
/// In en, this message translates to:
/// **'Add your weight to follow it here; the latest is also your body\'s weight.'**
String get weightEmpty ;
/// No description provided for @weightOverTime.
///
/// In en, this message translates to:
/// **'Over time'**
String get weightOverTime ;
/// No description provided for @weightField.
///
/// In en, this message translates to:
/// **'Weight'**
String get weightField ;
/// No description provided for @weightInvalid.
///
/// In en, this message translates to:
/// **'A weight between 20 and 400 kg'**
String get weightInvalid ;
/// No description provided for @weightWhen.
///
/// In en, this message translates to:
/// **'Measured'**
String get weightWhen ;
/// No description provided for @weightDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get weightDelete ;
/// A weight read from Health Connect: the package of the app that wrote it.
///
/// In en, this message translates to:
/// **'from {app}'**
String weightFromHealth ( String app ) ;
2026-10-31 18:46:26 +01:00
/// No description provided for @bodyResting.
///
/// In en, this message translates to:
/// **'resting {bpm} bpm'**
String bodyResting ( int bpm ) ;
/// No description provided for @bodyMax.
///
/// In en, this message translates to:
/// **'max {bpm} bpm'**
String bodyMax ( int bpm ) ;
2026-10-31 18:52:32 +01:00
/// No description provided for @homeToday.
///
/// In en, this message translates to:
/// **'Today'**
String get homeToday ;
feat(train): equipment by place and a plate calculator (ADR 0034)
The places a person trains at (home, a gym), each a sealed, synced
`training_place` row with its equipment: bars and handles with their own
weight, plates by weight and count, dumbbells and kettlebells, the rest
by count, the person's own by name. A new place starts from what a home
or a gym usually has. The place last chosen is remembered on the device,
never its location.
The plate calculator loads each weight both sides alike, the heaviest
load not above it with the fewest plates, heavier inside, and over a
series of sets picks the loads making the fewest plate moves, listing
what comes off and goes on. Equipment is in Settings; strength plans
are to build on it.
The migration only creates the table, its space index and its cascade
to the sync engine's spaces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 11:59:24 +01:00
/// Settings' entry and the screen of the places the person trains at, with what each has (ADR 0034).
///
/// In en, this message translates to:
/// **'Equipment'**
String get equipmentTitle ;
/// No description provided for @equipmentHint.
///
/// In en, this message translates to:
/// **'Where you train, and what\'s there'**
String get equipmentHint ;
/// No description provided for @equipmentEmptyTitle.
///
/// In en, this message translates to:
/// **'No places yet'**
String get equipmentEmptyTitle ;
/// No description provided for @equipmentEmptyHint.
///
/// In en, this message translates to:
/// **'Add the places you train at, such as your home and your gym, with what each has. Start from what a home or a gym usually has, or from nothing.'**
String get equipmentEmptyHint ;
/// No description provided for @equipmentAddPlace.
///
/// In en, this message translates to:
/// **'Add a place'**
String get equipmentAddPlace ;
/// No description provided for @equipmentItems.
///
/// In en, this message translates to:
/// **'{count, plural, =0{Nothing yet} =1{1 piece of equipment} other{{count} pieces of equipment}}'**
String equipmentItems ( int count ) ;
/// Marks the place chosen, which the plate calculator and plans use (ADR 0034, D4).
///
/// In en, this message translates to:
/// **'Where you train now'**
String get equipmentChosen ;
/// No description provided for @equipmentChoose.
///
/// In en, this message translates to:
/// **'Train here'**
String get equipmentChoose ;
/// No description provided for @equipmentLocked.
///
/// In en, this message translates to:
/// **'Can\'t be opened on this device'**
String get equipmentLocked ;
/// No description provided for @placeNewTitle.
///
/// In en, this message translates to:
/// **'New place'**
String get placeNewTitle ;
/// No description provided for @placeName.
///
/// In en, this message translates to:
/// **'Name'**
String get placeName ;
/// No description provided for @placeStartFrom.
///
/// In en, this message translates to:
/// **'Start from'**
String get placeStartFrom ;
/// No description provided for @placeStartHome.
///
/// In en, this message translates to:
/// **'A home'**
String get placeStartHome ;
/// No description provided for @placeStartGym.
///
/// In en, this message translates to:
/// **'A gym'**
String get placeStartGym ;
/// No description provided for @placeStartNothing.
///
/// In en, this message translates to:
/// **'Nothing'**
String get placeStartNothing ;
/// No description provided for @placeDefaultHome.
///
/// In en, this message translates to:
/// **'Home'**
String get placeDefaultHome ;
/// No description provided for @placeDefaultGym.
///
/// In en, this message translates to:
/// **'Gym'**
String get placeDefaultGym ;
/// No description provided for @placeAdd.
///
/// In en, this message translates to:
/// **'Add'**
String get placeAdd ;
/// No description provided for @placeRename.
///
/// In en, this message translates to:
/// **'Rename'**
String get placeRename ;
/// No description provided for @placeSave.
///
/// In en, this message translates to:
/// **'Save'**
String get placeSave ;
/// No description provided for @placeDelete.
///
/// In en, this message translates to:
/// **'Delete place'**
String get placeDelete ;
/// No description provided for @placeDeleteMessage.
///
/// In en, this message translates to:
/// **'The place and its equipment are deleted on all your devices.'**
String get placeDeleteMessage ;
/// No description provided for @placeAddEquipment.
///
/// In en, this message translates to:
/// **'Add equipment'**
String get placeAddEquipment ;
/// No description provided for @placeEmptyHint.
///
/// In en, this message translates to:
/// **'Nothing here yet. Add the equipment this place has.'**
String get placeEmptyHint ;
/// No description provided for @equipmentKind.
///
/// In en, this message translates to:
/// **'{kind, select, barbell{Barbell} ezBar{EZ bar} trapBar{Trap bar} dumbbellHandle{Dumbbell handles} plates{Weight plates} fixedDumbbells{Dumbbells} kettlebells{Kettlebells} bench{Bench} rack{Rack} pullUpBar{Pull-up bar} dipBars{Dip bars} cableMachine{Cable machine} bands{Resistance bands} other{Your own}}'**
String equipmentKind ( String kind ) ;
/// No description provided for @itemName.
///
/// In en, this message translates to:
/// **'What it is'**
String get itemName ;
/// No description provided for @itemHowMany.
///
/// In en, this message translates to:
/// **'How many'**
String get itemHowMany ;
/// No description provided for @itemOwnWeight.
///
/// In en, this message translates to:
/// **'Its own weight'**
String get itemOwnWeight ;
/// No description provided for @itemWeights.
///
/// In en, this message translates to:
/// **'Weights'**
String get itemWeights ;
/// No description provided for @itemAddWeight.
///
/// In en, this message translates to:
/// **'Add a weight'**
String get itemAddWeight ;
/// No description provided for @itemDone.
///
/// In en, this message translates to:
/// **'Done'**
String get itemDone ;
/// No description provided for @itemRemove.
///
/// In en, this message translates to:
/// **'Remove'**
String get itemRemove ;
/// No description provided for @itemWeightInvalid.
///
/// In en, this message translates to:
/// **'A weight in kilograms, above 0'**
String get itemWeightInvalid ;
/// No description provided for @weightKg.
///
/// In en, this message translates to:
/// **'{weight} kg'**
String weightKg ( String weight ) ;
/// No description provided for @weightCount.
///
/// In en, this message translates to:
/// **'{count} × {weight} kg'**
String weightCount ( int count , String weight ) ;
/// No description provided for @plateCalculatorTitle.
///
/// In en, this message translates to:
/// **'Plate calculator'**
String get plateCalculatorTitle ;
/// No description provided for @plateCalculatorHint.
///
/// In en, this message translates to:
/// **'What to load, and what changes between sets'**
String get plateCalculatorHint ;
/// No description provided for @platePlace.
///
/// In en, this message translates to:
/// **'Place'**
String get platePlace ;
/// No description provided for @plateBar.
///
/// In en, this message translates to:
/// **'Bar'**
String get plateBar ;
/// No description provided for @plateDumbbellPair.
///
/// In en, this message translates to:
/// **'Two dumbbell handles'**
String get plateDumbbellPair ;
/// No description provided for @plateTargets.
///
/// In en, this message translates to:
/// **'Weights, kg'**
String get plateTargets ;
/// No description provided for @plateTargetsHelper.
///
/// In en, this message translates to:
/// **'One for each set, such as 60, 80, 100'**
String get plateTargetsHelper ;
/// No description provided for @plateNoPlace.
///
/// In en, this message translates to:
/// **'Add a place you train at, with its plates, first.'**
String get plateNoPlace ;
/// No description provided for @plateNoBar.
///
/// In en, this message translates to:
/// **'This place has nothing that takes plates. Add a barbell or dumbbell handles to it.'**
String get plateNoBar ;
/// No description provided for @plateBelowBar.
///
/// In en, this message translates to:
/// **'Below the bar\'s {weight} kg'**
String plateBelowBar ( String weight ) ;
/// No description provided for @plateShort.
///
/// In en, this message translates to:
/// **'{weight} kg below what you asked for: the plates make no closer weight'**
String plateShort ( String weight ) ;
/// No description provided for @plateSide.
///
/// In en, this message translates to:
/// **'Each side, from the inside: {plates}'**
String plateSide ( String plates ) ;
/// No description provided for @plateBarAlone.
///
/// In en, this message translates to:
/// **'The bar alone'**
String get plateBarAlone ;
/// No description provided for @plateOff.
///
/// In en, this message translates to:
/// **'Take off {plates}'**
String plateOff ( String plates ) ;
/// No description provided for @plateOn.
///
/// In en, this message translates to:
/// **'Put on {plates}'**
String plateOn ( String plates ) ;
/// No description provided for @plateNoChange.
///
/// In en, this message translates to:
/// **'Nothing changes'**
String get plateNoChange ;
/// No description provided for @plateMoves.
///
/// In en, this message translates to:
/// **'{count, plural, =0{No plate moves} =1{1 plate moves on each side} other{{count} plates move on each side}}'**
String plateMoves ( int count ) ;
/// No description provided for @plateSet.
///
/// In en, this message translates to:
/// **'Set {number}'**
String plateSet ( int number ) ;
feat(train): strength plans and sessions (ADR 0035)
Strength plans of exercises from a catalogue or the person's own, in
blocks of one exercise or several done in turn (supersets, circuits),
each with its sets, reps or seconds, weights and rests; each a sealed
`strength_plan` row. Plans are marked with what the place chosen lacks,
and a session offers swaps moving the same way that the place allows.
A session runs on every device, set by set with the rest counted down,
each weight made up from the place's plates, bars and fixed weights with
the plate changes between sets, kept in strength_session.json after each
change so one cut off is carried on, and saved as a workout of the new
activity Strength with its sets, listed on its page; strength training
in Health Connect and FIT, no GPX.
Migration 20261102113104496-strength-plans is purely additive: it
creates the table strength_plan (id uuid v7, spaceId, keyId, the sealed
payload and its nonce), its index on spaceId, and a foreign key to
offline_sync_spaces with ON DELETE CASCADE, so deleting a person's sync
spaces deletes their plans; then the modules' version rows. The
gen_random_uuid_v7 function is created or replaced as in every
migration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 13:18:12 +01:00
/// A catalogue exercise's name, by its id (ADR 0035, D1).
///
/// In en, this message translates to:
/// **'{id, select, backSquat{Back squat} frontSquat{Front squat} gobletSquat{Goblet squat} kettlebellGobletSquat{Kettlebell goblet squat} bodyweightSquat{Bodyweight squat} lunge{Lunge} bulgarianSplitSquat{Bulgarian split squat} bodyweightLunge{Bodyweight lunge} deadlift{Deadlift} romanianDeadlift{Romanian deadlift} trapBarDeadlift{Trap bar deadlift} dumbbellRomanianDeadlift{Dumbbell Romanian deadlift} kettlebellSwing{Kettlebell swing} hipThrust{Hip thrust} gluteBridge{Glute bridge} benchPress{Bench press} inclineBenchPress{Incline bench press} dumbbellBenchPress{Dumbbell bench press} pushUp{Push-up} dip{Dip} overheadPress{Overhead press} dumbbellShoulderPress{Dumbbell shoulder press} pikePushUp{Pike push-up} barbellRow{Barbell row} dumbbellRow{One-arm dumbbell row} cableRow{Seated cable row} invertedRow{Inverted row} bandPullApart{Band pull-apart} facePull{Face pull} pullUp{Pull-up} chinUp{Chin-up} latPulldown{Lat pulldown} bandPulldown{Band pulldown} barbellCurl{Barbell curl} ezBarCurl{EZ bar curl} dumbbellCurl{Dumbbell curl} hammerCurl{Hammer curl} tricepsPushdown{Triceps pushdown} skullCrusher{Skull crusher} overheadTricepsExtension{Overhead triceps extension} benchDip{Bench dip} lateralRaise{Lateral raise} cableLateralRaise{Cable lateral raise} calfRaise{Calf raise} plank{Plank} sidePlank{Side plank} hangingLegRaise{Hanging leg raise} crunch{Crunch} farmersCarry{Farmer\'s carry} other{Exercise}}'**
String exerciseName ( String id ) ;
/// What an exercise needs, as in "Missing here: a rack, dip bars".
///
/// In en, this message translates to:
/// **'{need, select, barbell{a barbell} ezBar{an EZ bar} trapBar{a trap bar} dumbbells{dumbbells} kettlebell{a kettlebell} bench{a bench} rack{a rack} pullUpBar{a pull-up bar} dipBars{dip bars} cableMachine{a cable machine} bands{bands} other{something}}'**
String exerciseNeed ( String need ) ;
/// No description provided for @exercisePattern.
///
/// In en, this message translates to:
/// **'{pattern, select, squat{Squats} lunge{Lunges} hinge{Hinges} horizontalPush{Pushes} verticalPush{Pushes overhead} horizontalPull{Rows} verticalPull{Pull-ups and pulldowns} curl{Curls} extension{Triceps} raise{Raises} calf{Calves} core{Core} carry{Carries} other{Other}}'**
String exercisePattern ( String pattern ) ;
/// No description provided for @exerciseLoad.
///
/// In en, this message translates to:
/// **'{load, select, barbell{Barbell with plates} ezBar{EZ bar with plates} trapBar{Trap bar with plates} dumbbellPair{A dumbbell in each hand} dumbbell{One dumbbell} kettlebell{Kettlebell} cable{Cable stack} band{Band} body{Body weight} other{Other}}'**
String exerciseLoad ( String load ) ;
/// No description provided for @activityStrengthCard.
///
/// In en, this message translates to:
/// **'Strength'**
String get activityStrengthCard ;
/// No description provided for @strengthPlansHeading.
///
/// In en, this message translates to:
/// **'Strength plans'**
String get strengthPlansHeading ;
/// No description provided for @strengthPlansNew.
///
/// In en, this message translates to:
/// **'New strength plan'**
String get strengthPlansNew ;
/// No description provided for @strengthPlansEmpty.
///
/// In en, this message translates to:
/// **'Strength plans list exercises with their sets, repetitions, weights and rests, alone or as supersets and circuits.'**
String get strengthPlansEmpty ;
/// No description provided for @strengthPlanLocked.
///
/// In en, this message translates to:
/// **'Can\'t be opened on this device'**
String get strengthPlanLocked ;
/// No description provided for @strengthPlanDeleted.
///
/// In en, this message translates to:
/// **'Strength plan deleted'**
String get strengthPlanDeleted ;
/// No description provided for @strengthStart.
///
/// In en, this message translates to:
/// **'Start'**
String get strengthStart ;
/// No description provided for @strengthChoosePlan.
///
/// In en, this message translates to:
/// **'Which plan?'**
String get strengthChoosePlan ;
/// No description provided for @strengthNoPlans.
///
/// In en, this message translates to:
/// **'Make a strength plan first: the list below has New strength plan.'**
String get strengthNoPlans ;
/// No description provided for @strengthPlanTitle.
///
/// In en, this message translates to:
/// **'Strength plan'**
String get strengthPlanTitle ;
/// No description provided for @strengthPlanName.
///
/// In en, this message translates to:
/// **'Name'**
String get strengthPlanName ;
/// No description provided for @strengthPlanSave.
///
/// In en, this message translates to:
/// **'Save'**
String get strengthPlanSave ;
/// No description provided for @strengthPlanDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get strengthPlanDelete ;
/// No description provided for @strengthAddExercise.
///
/// In en, this message translates to:
/// **'Add exercise'**
String get strengthAddExercise ;
/// No description provided for @strengthAddToBlock.
///
/// In en, this message translates to:
/// **'Add an exercise to do in turn'**
String get strengthAddToBlock ;
/// No description provided for @strengthOwnExercise.
///
/// In en, this message translates to:
/// **'Your own exercise'**
String get strengthOwnExercise ;
/// No description provided for @strengthOwnExercises.
///
/// In en, this message translates to:
/// **'Your own'**
String get strengthOwnExercises ;
/// No description provided for @strengthExerciseName.
///
/// In en, this message translates to:
/// **'What it is'**
String get strengthExerciseName ;
/// No description provided for @strengthExerciseNeeds.
///
/// In en, this message translates to:
/// **'What it needs'**
String get strengthExerciseNeeds ;
/// No description provided for @strengthExerciseLoad.
///
/// In en, this message translates to:
/// **'What it is done with'**
String get strengthExerciseLoad ;
/// No description provided for @strengthExercisePattern.
///
/// In en, this message translates to:
/// **'How it moves'**
String get strengthExercisePattern ;
/// No description provided for @strengthExerciseTimed.
///
/// In en, this message translates to:
/// **'Held for a time'**
String get strengthExerciseTimed ;
/// No description provided for @strengthExerciseDone.
///
/// In en, this message translates to:
/// **'Done'**
String get strengthExerciseDone ;
/// No description provided for @strengthAddSet.
///
/// In en, this message translates to:
/// **'Add a set'**
String get strengthAddSet ;
/// No description provided for @strengthReps.
///
/// In en, this message translates to:
/// **'Reps'**
String get strengthReps ;
/// No description provided for @strengthSeconds.
///
/// In en, this message translates to:
/// **'Seconds'**
String get strengthSeconds ;
/// No description provided for @strengthKg.
///
/// In en, this message translates to:
/// **'kg'**
String get strengthKg ;
/// No description provided for @strengthRemoveExercise.
///
/// In en, this message translates to:
/// **'Remove exercise'**
String get strengthRemoveExercise ;
/// No description provided for @strengthRemoveSet.
///
/// In en, this message translates to:
/// **'Remove set'**
String get strengthRemoveSet ;
/// No description provided for @strengthMoveUp.
///
/// In en, this message translates to:
/// **'Move up'**
String get strengthMoveUp ;
/// No description provided for @strengthMoveDown.
///
/// In en, this message translates to:
/// **'Move down'**
String get strengthMoveDown ;
/// No description provided for @strengthRest.
///
/// In en, this message translates to:
/// **'Rest'**
String get strengthRest ;
/// No description provided for @strengthRestBetween.
///
/// In en, this message translates to:
/// **'Rest between exercises'**
String get strengthRestBetween ;
/// No description provided for @strengthDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard changes?'**
String get strengthDiscardTitle ;
/// No description provided for @strengthDiscard.
///
/// In en, this message translates to:
/// **'Discard'**
String get strengthDiscard ;
/// No description provided for @strengthDiscardMessage.
///
/// In en, this message translates to:
/// **'What you changed in this plan is not kept.'**
String get strengthDiscardMessage ;
/// No description provided for @strengthAbandon.
///
/// In en, this message translates to:
/// **'End without saving'**
String get strengthAbandon ;
/// No description provided for @strengthAbandonMessage.
///
/// In en, this message translates to:
/// **'The sets done so far are deleted from this device. This can\'t be undone.'**
String get strengthAbandonMessage ;
/// No description provided for @strengthFinish.
///
/// In en, this message translates to:
/// **'Finish and save'**
String get strengthFinish ;
/// No description provided for @strengthAllDone.
///
/// In en, this message translates to:
/// **'Every set is through.'**
String get strengthAllDone ;
/// No description provided for @strengthSetDone.
///
/// In en, this message translates to:
/// **'Done'**
String get strengthSetDone ;
/// No description provided for @strengthSkip.
///
/// In en, this message translates to:
/// **'Skip set'**
String get strengthSkip ;
/// No description provided for @strengthSwap.
///
/// In en, this message translates to:
/// **'Swap exercise'**
String get strengthSwap ;
/// No description provided for @strengthSwapTitle.
///
/// In en, this message translates to:
/// **'Do instead'**
String get strengthSwapTitle ;
/// No description provided for @strengthNoSwaps.
///
/// In en, this message translates to:
/// **'Nothing else here moves this way.'**
String get strengthNoSwaps ;
/// No description provided for @strengthRestSkip.
///
/// In en, this message translates to:
/// **'Skip rest'**
String get strengthRestSkip ;
/// No description provided for @strengthResting.
///
/// In en, this message translates to:
/// **'Rest'**
String get strengthResting ;
/// No description provided for @strengthCarryOn.
///
/// In en, this message translates to:
/// **'A session is under way'**
String get strengthCarryOn ;
/// No description provided for @strengthCarryOnAction.
///
/// In en, this message translates to:
/// **'Carry on'**
String get strengthCarryOnAction ;
/// No description provided for @strengthWeightNotMadeUp.
///
/// In en, this message translates to:
/// **'Nothing here makes this weight up.'**
String get strengthWeightNotMadeUp ;
/// No description provided for @strengthFixed.
///
/// In en, this message translates to:
/// **'{weight} kg, nearest at or below'**
String strengthFixed ( Object weight ) ;
/// No description provided for @strengthNoPlace.
///
/// In en, this message translates to:
/// **'No place chosen: weights as planned.'**
String get strengthNoPlace ;
/// No description provided for @strengthTrainsAt.
///
/// In en, this message translates to:
/// **'At {place}'**
String strengthTrainsAt ( Object place ) ;
/// No description provided for @strengthWorkoutVolume.
///
/// In en, this message translates to:
/// **'{weight} kg moved'**
String strengthWorkoutVolume ( Object weight ) ;
/// No description provided for @strengthWorkoutSets.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 set} other{{count} sets}}'**
String strengthWorkoutSets ( num count ) ;
/// No description provided for @strengthMissing.
///
/// In en, this message translates to:
/// **'Missing here: {needs}'**
String strengthMissing ( String needs ) ;
/// No description provided for @strengthPlanSummary.
///
/// In en, this message translates to:
/// **'{exercises, plural, =1{1 exercise} other{{exercises} exercises}} · {sets, plural, =1{1 set} other{{sets} sets}}'**
String strengthPlanSummary ( int exercises , int sets ) ;
/// No description provided for @strengthBlockKind.
///
/// In en, this message translates to:
/// **'{kind, select, superset{Superset} circuit{Circuit} other{Exercise}}'**
String strengthBlockKind ( String kind ) ;
/// No description provided for @strengthSetOf.
///
/// In en, this message translates to:
/// **'Set {set} of {sets}'**
String strengthSetOf ( int set , int sets ) ;
/// No description provided for @strengthRoundOf.
///
/// In en, this message translates to:
/// **'Round {round} of {rounds}'**
String strengthRoundOf ( int round , int rounds ) ;
/// No description provided for @strengthTarget.
///
/// In en, this message translates to:
/// **'{reps} × {weight} kg'**
String strengthTarget ( int reps , String weight ) ;
/// No description provided for @strengthTargetReps.
///
/// In en, this message translates to:
/// **'{reps, plural, =1{1 rep} other{{reps} reps}}'**
String strengthTargetReps ( int reps ) ;
/// No description provided for @strengthTargetSeconds.
///
/// In en, this message translates to:
/// **'{seconds} s'**
String strengthTargetSeconds ( int seconds ) ;
/// No description provided for @strengthNextUp.
///
/// In en, this message translates to:
/// **'Next: {exercise}'**
String strengthNextUp ( String exercise ) ;
/// No description provided for @strengthBarAndPlates.
///
/// In en, this message translates to:
/// **'{bar} kg bar, each side: {plates}'**
String strengthBarAndPlates ( String bar , String plates ) ;
/// No description provided for @strengthHandlesAndPlates.
///
/// In en, this message translates to:
/// **'{bar} kg handles, each side: {plates}'**
String strengthHandlesAndPlates ( String bar , String plates ) ;
/// No description provided for @strengthWeightShort.
///
/// In en, this message translates to:
/// **'{weight} kg below what was planned'**
String strengthWeightShort ( String weight ) ;
feat(train): workouts' weather from Open-Meteo, through the instance (ADR 0036)
A workout with a track gets the weather over it as its page opens, once
while it has none: the hours from the one it started in to the one it
ended in, kept in its sealed payload (temperature, feels-like, humidity,
precipitation, the WMO code, the wind, its gusts and direction) and shown
under its totals with the link to Open-Meteo its licence asks for.
Train's server asks Open-Meteo in the person's place (the `weather`
endpoint), so Open-Meteo never sees their address: the place rounded to
two decimals (also on the phone), a span of at most 48 hours, the
forecast API for recent spans and the archive for older ones, for people
signed in, at most 30 requests a person a minute, nothing kept or
logged. On by default; `WEATHER=off` switches it off, and
`OPEN_METEO_API_KEY` moves it to the paid hosts for an instance used
commercially.
Health Connect's fingerprint leaves the weather out, so keeping it
writes nothing again. No table and no migration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 13:59:41 +01:00
/// No description provided for @weatherHeading.
///
/// In en, this message translates to:
/// **'Weather'**
String get weatherHeading ;
/// What the sky did over a workout, from Open-Meteo's weather code.
///
/// In en, this message translates to:
/// **'{conditions, select, clear{Clear} partlyCloudy{Partly cloudy} cloudy{Overcast} fog{Fog} drizzle{Drizzle} rain{Rain} freezingRain{Freezing rain} snow{Snow} rainShowers{Showers} snowShowers{Snow showers} thunderstorm{Thunderstorm} other{Weather}}'**
String weatherConditions ( String conditions ) ;
/// No description provided for @weatherTemperature.
///
/// In en, this message translates to:
/// **'{temperature} °C'**
String weatherTemperature ( String temperature ) ;
/// No description provided for @weatherTemperatureRange.
///
/// In en, this message translates to:
/// **'{low}– {high} °C'**
String weatherTemperatureRange ( String low , String high ) ;
/// No description provided for @weatherFeelsLike.
///
/// In en, this message translates to:
/// **'Felt like {temperature} °C'**
String weatherFeelsLike ( String temperature ) ;
/// No description provided for @weatherWind.
///
/// In en, this message translates to:
/// **'Wind {speed} km/h from the {direction}'**
String weatherWind ( String speed , String direction ) ;
/// No description provided for @weatherWindNoDirection.
///
/// In en, this message translates to:
/// **'Wind {speed} km/h'**
String weatherWindNoDirection ( String speed ) ;
/// No description provided for @weatherCalm.
///
/// In en, this message translates to:
/// **'Hardly any wind'**
String get weatherCalm ;
/// No description provided for @weatherGusts.
///
/// In en, this message translates to:
/// **'gusts up to {speed} km/h'**
String weatherGusts ( String speed ) ;
/// Where the wind came from, as in "from the south-west".
///
/// In en, this message translates to:
/// **'{direction, select, n{north} ne{north-east} e{east} se{south-east} s{south} sw{south-west} w{west} nw{north-west} other{wind}}'**
String weatherDirection ( String direction ) ;
/// No description provided for @weatherPrecipitation.
///
/// In en, this message translates to:
/// **'{amount} mm fell'**
String weatherPrecipitation ( String amount ) ;
/// No description provided for @weatherDry.
///
/// In en, this message translates to:
/// **'Nothing fell'**
String get weatherDry ;
/// No description provided for @weatherBy.
///
/// In en, this message translates to:
/// **'Weather data by Open-Meteo.com'**
String get weatherBy ;
feat(train): reminders to move and workouts suggested from the steps (ADR 0037)
Settings' Moving more, on Android: a reminder to get up after an idle
time, and a suggested walk or run, each off until turned on (then every
30 minutes, weekdays 9:00 to 18:00, not 12:00 to 13:00, each value the
person's), turning on asking for the activity and notification
permissions.
A WorkManager job every 15 minutes reads the phone's step counter and
keeps two hours of readings. It reminds where fewer than 50 steps came
over the idle time, all of it within the hours and outside the break,
on the person's days, never while Train records or the phone is in a
car (Android's car mode, or a car's Bluetooth audio or hands-free). It
suggests a walk or a run where the last span averaged a walking or
running cadence, once an hour; tapped, the Record sheet for it opens.
The decisions are pure Kotlin (Movement.kt), tested on the JVM; the
notifications' words are the app's, written into the job's
configuration as the person's app opens.
Polar's SDK sets neither the band's inactivity alert nor tells of one
without an all-day connection, hence the phone's steps (ADR 0037).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 14:32:02 +01:00
/// No description provided for @movementTitle.
///
/// In en, this message translates to:
/// **'Moving more'**
String get movementTitle ;
/// No description provided for @movementHint.
///
/// In en, this message translates to:
/// **'Reminders to get up, and workouts suggested as you move'**
String get movementHint ;
/// No description provided for @movementRemind.
///
/// In en, this message translates to:
/// **'Remind me to move'**
String get movementRemind ;
/// No description provided for @movementRemindHint.
///
/// In en, this message translates to:
/// **'When the phone counted hardly a step for a while; never while recording or in a car.'**
String get movementRemindHint ;
/// No description provided for @movementIdle.
///
/// In en, this message translates to:
/// **'After'**
String get movementIdle ;
/// No description provided for @movementIdleMinutes.
///
/// In en, this message translates to:
/// **'{minutes} min still'**
String movementIdleMinutes ( int minutes ) ;
/// No description provided for @movementHours.
///
/// In en, this message translates to:
/// **'Hours'**
String get movementHours ;
/// No description provided for @movementBreak.
///
/// In en, this message translates to:
/// **'Break'**
String get movementBreak ;
/// No description provided for @movementBreakHint.
///
/// In en, this message translates to:
/// **'No reminders over it.'**
String get movementBreakHint ;
/// No description provided for @movementDays.
///
/// In en, this message translates to:
/// **'Days'**
String get movementDays ;
/// No description provided for @movementSuggest.
///
/// In en, this message translates to:
/// **'Suggest workouts'**
String get movementSuggest ;
/// No description provided for @movementSuggestHint.
///
/// In en, this message translates to:
/// **'When your steps look like a walk or a run and nothing records: tap to record the rest of it. Your Loop\'s own detection is on its screen.'**
String get movementSuggestHint ;
/// No description provided for @movementUnavailable.
///
/// In en, this message translates to:
/// **'This phone doesn\'t count steps, so it can neither remind nor suggest.'**
String get movementUnavailable ;
/// No description provided for @movementRefused.
///
/// In en, this message translates to:
/// **'Train needs the physical activity and notification permissions for this.'**
String get movementRefused ;
/// No description provided for @movementLate.
///
/// In en, this message translates to:
/// **'The steps are counted every 15 minutes, so a reminder can come a little late.'**
String get movementLate ;
/// No description provided for @movementChannel.
///
/// In en, this message translates to:
/// **'Moving more'**
String get movementChannel ;
/// No description provided for @movementRemindTitle.
///
/// In en, this message translates to:
/// **'Time to move'**
String get movementRemindTitle ;
/// No description provided for @movementRemindText.
///
/// In en, this message translates to:
/// **'{minutes} minutes with hardly a step. Up for a short walk?'**
String movementRemindText ( int minutes ) ;
/// No description provided for @movementWalkingTitle.
///
/// In en, this message translates to:
/// **'Out for a walk?'**
String get movementWalkingTitle ;
/// No description provided for @movementRunningTitle.
///
/// In en, this message translates to:
/// **'Out for a run?'**
String get movementRunningTitle ;
/// No description provided for @movementSuggestText.
///
/// In en, this message translates to:
/// **'Tap to record the rest of it.'**
String get movementSuggestText ;
2026-11-02 14:49:48 +01:00
/// No description provided for @loopDetection.
///
/// In en, this message translates to:
/// **'Detect training by itself'**
String get loopDetection ;
/// No description provided for @loopDetectionHint.
///
/// In en, this message translates to:
/// **'The band notices a session you didn\'t record, and it comes in as a workout.'**
String get loopDetectionHint ;
/// No description provided for @loopDetectionMinimum.
///
/// In en, this message translates to:
/// **'Keeps sessions from'**
String get loopDetectionMinimum ;
/// No description provided for @loopDetectionMinutes.
///
/// In en, this message translates to:
/// **'{minutes} min'**
String loopDetectionMinutes ( int minutes ) ;
feat(train): Halkyon Train's shell, Todo's copied and renamed (ADR 0030, D12)
apps/train with halkyon_train_server, _client and _flutter, as Todo's:
halkyon_base configured by trainBase(), its own register alone and a
member of the instance's where the operator names one, the app on
halkyon_app's gates with Train's frame, account sheet and About, texts
in British English and German that speak of workouts, app.halkyon.train
on Android, iOS, Linux, macOS and Windows.
Where it differs from Todo: development ports of its own (8180 to 8190),
so it runs beside Learn's server; one initial migration with the schema
Todo's migrations end at (the same tables, compared line by line), not
Todo's seventeen; and Todo's icon as a placeholder until the icon tool
of ADR 0031 draws Train's bike. The workspace, the licences, the READMEs
and AGENTS.md name it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-28 10:26:49 +01:00
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate < AppLocalizations > {
const _AppLocalizationsDelegate ( ) ;
@ override
Future < AppLocalizations > load ( Locale locale ) {
return SynchronousFuture < AppLocalizations > ( lookupAppLocalizations ( locale ) ) ;
}
@ override
bool isSupported ( Locale locale ) = >
< String > [ ' de ' , ' en ' ] . contains ( locale . languageCode ) ;
@ override
bool shouldReload ( _AppLocalizationsDelegate old ) = > false ;
}
AppLocalizations lookupAppLocalizations ( Locale locale ) {
// Lookup logic when language+country codes are specified.
switch ( locale . languageCode ) {
case ' en ' :
{
switch ( locale . countryCode ) {
case ' GB ' :
return AppLocalizationsEnGb ( ) ;
case ' US ' :
return AppLocalizationsEnUs ( ) ;
}
break ;
}
}
// Lookup logic when only language code is specified.
switch ( locale . languageCode ) {
case ' de ' :
return AppLocalizationsDe ( ) ;
case ' en ' :
return AppLocalizationsEn ( ) ;
}
throw FlutterError (
' AppLocalizations.delegate failed to load unsupported locale " $ locale ". This is likely '
' an issue with the localizations generation tool. Please file an issue '
' on GitHub with a reproducible sample app and the gen-l10n configuration '
' that was used. ' ,
) ;
}