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:
/// **'About'**
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:
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
/// **'Record one with the button below.'**
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 @recordAction.
///
/// In en, this message translates to:
/// **'Record'**
String get recordAction ;
/// No description provided for @recordBack.
///
/// In en, this message translates to:
/// **'Back to the recording'**
String get recordBack ;
/// No description provided for @recordSheetTitle.
///
/// In en, this message translates to:
/// **'Record a workout'**
String get recordSheetTitle ;
/// 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 ;
/// 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 ;
/// 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:
/// **'{when, select, 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}}'**
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 ;
/// 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 ) ;
/// 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 ;
/// 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 ;
/// No description provided for @homeDays.
///
/// In en, this message translates to:
/// **'Days'**
String get homeDays ;
/// 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 ;
/// No description provided for @dayRechargeLevel.
///
/// In en, this message translates to:
/// **'Recovery {level} of 6'**
String dayRechargeLevel ( int level ) ;
/// No description provided for @dayAnsStatus.
///
/// In en, this message translates to:
/// **'Autonomic nervous system {value}, from − 10 to +10'**
String dayAnsStatus ( String value ) ;
/// No description provided for @dayNightHeart.
///
/// In en, this message translates to:
/// **'Heart rate {bpm} bpm, your usual {usual} bpm'**
String dayNightHeart ( int bpm , int usual ) ;
/// No description provided for @dayHrv.
///
/// In en, this message translates to:
/// **'Heart rate variability {ms} ms, your usual {usual} ms'**
String dayHrv ( int ms , int usual ) ;
/// No description provided for @dayBreathing.
///
/// In en, this message translates to:
/// **'Breathing {rate} a minute'**
String dayBreathing ( String rate ) ;
/// 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): 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. ' ,
) ;
}