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 ;
/// 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 ;
/// 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 ;
/// 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): 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. ' ,
) ;
}