diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index abf4e7c..19b404c 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -25,7 +25,10 @@ jobs: uses: subosito/flutter-action@v2 with: channel: "stable" - - run: flutter pub get + - name: Get flutter packages + run: flutter pub get + - name: Generate localization + run: flutter gen-l10n - name: Analyze project source run: dart analyze - name: Run tests diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fac8444..75f933c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -8,7 +8,7 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - sqflite (0.0.2): + - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) - Toast (4.0.0) @@ -37,9 +37,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 - fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 + fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/lib/l10n.yaml b/lib/l10n.yaml new file mode 100644 index 0000000..15338f2 --- /dev/null +++ b/lib/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb new file mode 100644 index 0000000..e93a620 --- /dev/null +++ b/lib/l10n/app_de.arb @@ -0,0 +1,22 @@ +{ + "bottomBarDepartures": "Abfahrten", + "bottomBarNews": "Neuigkeiten", + "bottomBarSettings": "Einstellungen", + "departureEmptyText": "Noch keine Stationen augewählt.", + "dialogDone": "Fertig", + "favoriteAppBarAddTooltip": "Favorisierte Station hinzufügen", + "favoriteAppBarTitle": "Favoriten hinzufügen", + "favoriteDestinationSelection": "Wähle bevorzugte Ziele aus:", + "favoriteOriginSelectionError": "Für den angegebenen Namen wurde keine Station gefunden.", + "favoriteOriginSelectionLabel": "Name der Startstation", + "favoriteTransportationFilterWait": "Es wurden noch keine Transportarten gefunden.", + "favoriteTransportationTypeFilter": "Transport-Typ Filter", + "settingsDepartureCountDescription": "Legt fest, wie viele Abfahrtspositionen pro Ziel auf dem Startbildschirm angezeigt werden.", + "settingsDepartureCountSliderTitle": "Anzahl der Abfahrten", + "settingsDepartureCountTitle": "Anzahl der Abfahrten", + "settingsLanguageTitle": "Sprache", + "settingsLanguageValue": "Deutsch", + "settingsSectionCommon": "Allgemein", + "settingsSectionDeparture": "Abfahrten", + "settingsThemeSwitchTitle": "Farbschema Wechseln" +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 0000000..d4683ab --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,22 @@ +{ + "bottomBarDepartures": "Departures", + "bottomBarNews": "News", + "bottomBarSettings": "Settings", + "departureEmptyText": "No stations defined yet.", + "dialogDone": "Done", + "favoriteAppBarAddTooltip": "Add favorite station", + "favoriteAppBarTitle": "Add favorite", + "favoriteDestinationSelection": "Select preferred destinations:", + "favoriteOriginSelectionError": "No station found for provided name.", + "favoriteOriginSelectionLabel": "Name of the origin station", + "favoriteTransportationFilterWait": "No transport types found yet.", + "favoriteTransportationTypeFilter": "Filter for type of transportation", + "settingsDepartureCountDescription": "Defines how manydeparture items per destination are displayed on the start screen.", + "settingsDepartureCountSliderTitle": "Departure count", + "settingsDepartureCountTitle": "Departure Count", + "settingsLanguageTitle": "Language", + "settingsLanguageValue": "English", + "settingsSectionCommon": "Common", + "settingsSectionDeparture": "Departures", + "settingsThemeSwitchTitle": "Switch Theme" +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 46fd152..2f86adf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logger/logger.dart'; import 'package:track_your_stop/routing/router.dart'; @@ -70,7 +71,9 @@ class HaltestellenTrackerState extends ConsumerState @override Widget build(BuildContext context) { return MaterialApp.router( - title: 'Haltestellen Tracker', + title: "TrackYourStop", + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, debugShowCheckedModeBanner: false, themeMode: ref.watch(themeProvider), darkTheme: darkTheme, diff --git a/lib/modules/departure/views/departure_page.dart b/lib/modules/departure/views/departure_page.dart index 94d32ec..08e61a2 100644 --- a/lib/modules/departure/views/departure_page.dart +++ b/lib/modules/departure/views/departure_page.dart @@ -4,6 +4,7 @@ import 'package:track_your_stop/modules/settings/provider/departure_settings_pro import 'package:track_your_stop/utils/app_theme.dart'; import 'package:track_your_stop/utils/transportation_type.util.dart'; import 'package:flutter/material.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:track_your_stop/constants/colors.dart'; import 'package:track_your_stop/constants/departure_card_choices.dart'; @@ -172,8 +173,9 @@ class DeparturePage extends HookConsumerWidget { final Map> stationMap = Map.from(snapshot.data); if (stationMap.isEmpty) { - return const Center( - child: Text("No stations defined yet.")); + return Center( + child: Text( + AppLocalizations.of(context)!.departureEmptyText)); } return buildListView(context, stationMap); } diff --git a/lib/modules/favorites/ui/favorite_app_bar.dart b/lib/modules/favorites/ui/favorite_app_bar.dart index d4c6261..91662fa 100644 --- a/lib/modules/favorites/ui/favorite_app_bar.dart +++ b/lib/modules/favorites/ui/favorite_app_bar.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:track_your_stop/modules/favorites/database/favorites_database.dart'; import 'package:track_your_stop/modules/favorites/models/favorite.model.dart'; @@ -31,7 +32,7 @@ class FavoriteAppBar extends ConsumerWidget implements PreferredSizeWidget { } return AppBar( - title: const Text('Add favorite'), + title: Text(AppLocalizations.of(context)!.favoriteAppBarTitle), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { @@ -41,7 +42,7 @@ class FavoriteAppBar extends ConsumerWidget implements PreferredSizeWidget { actions: [ IconButton( icon: const Icon(Icons.add_location), - tooltip: 'Add favorite station', + tooltip: AppLocalizations.of(context)!.favoriteAppBarAddTooltip, onPressed: () { final selectedOrigin = ref.watch(selectedOriginProvider); final selectedTransportationTypes = diff --git a/lib/modules/favorites/views/favorite_page.dart b/lib/modules/favorites/views/favorite_page.dart index bb9cb83..b9a6eab 100644 --- a/lib/modules/favorites/views/favorite_page.dart +++ b/lib/modules/favorites/views/favorite_page.dart @@ -1,6 +1,7 @@ import 'package:track_your_stop/utils/transportation_type.util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_image_stack/flutter_image_stack.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -87,11 +88,11 @@ class FavoritePage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(bottom: 8.0), child: TypeAheadField( - noItemsFoundBuilder: (context) => const SizedBox( + noItemsFoundBuilder: (context) => SizedBox( height: 50.0, child: Center( - child: - Text('No station found for provided name.'))), + child: Text(AppLocalizations.of(context)! + .favoriteOriginSelectionError))), hideSuggestionsOnKeyboardHide: false, hideKeyboardOnDrag: true, debounceDuration: const Duration(milliseconds: 1000), @@ -119,10 +120,11 @@ class FavoritePage extends HookConsumerWidget { }, textFieldConfiguration: TextFieldConfiguration( controller: ref.watch(stationControllerProvider), - decoration: const InputDecoration( - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.search), - labelText: 'Name of the origin station', + decoration: InputDecoration( + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.search), + labelText: AppLocalizations.of(context)! + .favoriteOriginSelectionLabel, )), onSuggestionSelected: (StationResponse? selection) { logger.d('Selected station: ${selection!.toJson()}'); @@ -143,10 +145,11 @@ class FavoritePage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(bottom: 8.0), child: TypeAheadField( - noItemsFoundBuilder: (context) => const SizedBox( + noItemsFoundBuilder: (context) => SizedBox( height: 50.0, child: Center( - child: Text('No transport types found yet.'))), + child: Text(AppLocalizations.of(context)! + .favoriteTransportationFilterWait))), hideSuggestionsOnKeyboardHide: false, hideKeyboardOnDrag: true, suggestionsCallback: (input) { @@ -186,10 +189,11 @@ class FavoritePage extends HookConsumerWidget { }, textFieldConfiguration: TextFieldConfiguration( controller: transportationTypeController, - decoration: const InputDecoration( - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.train), - labelText: 'Filter for type of transportation', + decoration: InputDecoration( + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.train), + labelText: AppLocalizations.of(context)! + .favoriteTransportationTypeFilter, )))), // Chip list containing selected transportation types Container( @@ -205,7 +209,7 @@ class FavoritePage extends HookConsumerWidget { Align( alignment: AlignmentDirectional.center, child: Text( - 'Select preferred destinations:', + AppLocalizations.of(context)!.favoriteDestinationSelection, style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.start, ), @@ -268,7 +272,8 @@ class FavoritePage extends HookConsumerWidget { return Scaffold( appBar: FavoriteAppBar(), body: Scrollable( - viewportBuilder: (BuildContext context, ViewportOffset position) => buildBody(), + viewportBuilder: (BuildContext context, ViewportOffset position) => + buildBody(), ), ); } diff --git a/lib/modules/settings/views/settings_page.dart b/lib/modules/settings/views/settings_page.dart index d3a2f2d..e85ca0b 100644 --- a/lib/modules/settings/views/settings_page.dart +++ b/lib/modules/settings/views/settings_page.dart @@ -1,11 +1,12 @@ -import 'package:track_your_stop/modules/settings/provider/departure_settings_provider.dart'; -import 'package:track_your_stop/modules/settings/ui/slider_selection.dart'; -import 'package:track_your_stop/utils/app_theme.dart'; -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:settings_ui/settings_ui.dart'; -import 'package:track_your_stop/shared/ui/bottom_app_bar.dart'; -import 'package:track_your_stop/utils/logger.dart'; +import "package:track_your_stop/modules/settings/provider/departure_settings_provider.dart"; +import "package:track_your_stop/modules/settings/ui/slider_selection.dart"; +import "package:track_your_stop/utils/app_theme.dart"; +import "package:flutter/material.dart"; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:settings_ui/settings_ui.dart"; +import "package:track_your_stop/shared/ui/bottom_app_bar.dart"; +import "package:track_your_stop/utils/logger.dart"; final logger = getLogger("SettingsPage"); @@ -23,7 +24,8 @@ class SettingsPage extends HookConsumerWidget { context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Departure count'), + title: Text(AppLocalizations.of(context)! + .settingsDepartureCountSliderTitle), content: SingleChildScrollView( child: ListBody( children: [ @@ -36,7 +38,7 @@ class SettingsPage extends HookConsumerWidget { onPressed: () { Navigator.of(context).pop(); }, - child: const Text('Done'), + child: Text(AppLocalizations.of(context)!.dialogDone), ), ], ); @@ -52,12 +54,14 @@ class SettingsPage extends HookConsumerWidget { lightTheme: lightSettings, sections: [ SettingsSection( - title: const Text('Common'), + title: Text(AppLocalizations.of(context)!.settingsSectionCommon), tiles: [ - SettingsTile.navigation( + SettingsTile( leading: const Icon(Icons.language), - title: const Text('Language'), - value: const Text('English'), + title: + Text(AppLocalizations.of(context)!.settingsLanguageTitle), + value: + Text(AppLocalizations.of(context)!.settingsLanguageValue), ), SettingsTile.switchTile( activeSwitchColor: @@ -65,17 +69,20 @@ class SettingsPage extends HookConsumerWidget { onToggle: (isActive) => _switchTheme(ref, isActive), initialValue: isDark, leading: Icon(isDark ? Icons.brightness_2 : Icons.wb_sunny), - title: const Text('Switch Theme'), + title: Text( + AppLocalizations.of(context)!.settingsThemeSwitchTitle), ), ], ), SettingsSection( - title: const Text('Departures'), + title: + Text(AppLocalizations.of(context)!.settingsSectionDeparture), tiles: [ SettingsTile( - title: const Text('Departure Count'), - description: const Text( - 'Defines how manydeparture items per destination are displayed on the start screen.'), + title: Text(AppLocalizations.of(context)! + .settingsDepartureCountTitle), + description: Text(AppLocalizations.of(context)! + .settingsDepartureCountDescription), value: Text(ref.watch(departureSettingsProvider).toString()), leading: const Icon(Icons.train), onPressed: (BuildContext context) { diff --git a/lib/shared/ui/bottom_app_bar.dart b/lib/shared/ui/bottom_app_bar.dart index 34836fd..baf62d5 100644 --- a/lib/shared/ui/bottom_app_bar.dart +++ b/lib/shared/ui/bottom_app_bar.dart @@ -1,6 +1,7 @@ import 'package:track_your_stop/routing/router.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:track_your_stop/utils/logger.dart'; import 'package:track_your_stop/shared/provider/app_bar_selection_provider.dart'; @@ -35,18 +36,18 @@ class BottomAppNavigationBar extends ConsumerWidget { final currentIndex = ref.watch(appBarSelectionProvider); logger.d("Current Index: $currentIndex"); return BottomNavigationBar( - items: const [ + items: [ BottomNavigationBarItem( - icon: Icon(Icons.train), - label: 'Departures', + icon: const Icon(Icons.train), + label: AppLocalizations.of(context)!.bottomBarDepartures, ), BottomNavigationBarItem( - icon: Icon(Icons.newspaper), - label: 'News', + icon: const Icon(Icons.newspaper), + label: AppLocalizations.of(context)!.bottomBarNews, ), BottomNavigationBarItem( - icon: Icon(Icons.settings), - label: 'Settings', + icon: const Icon(Icons.settings), + label: AppLocalizations.of(context)!.bottomBarSettings, ), ], currentIndex: currentIndex, diff --git a/pubspec.lock b/pubspec.lock index d9bed9d..2fb5ccf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" auto_route: dependency: "direct main" description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.2" convert: dependency: transitive description: @@ -318,6 +318,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_riverpod: dependency: transitive description: @@ -444,10 +449,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -500,26 +505,26 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -540,10 +545,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" petitparser: dependency: transitive description: @@ -657,10 +662,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sqflite: dependency: "direct main" description: @@ -737,10 +742,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.6.0" timing: dependency: transitive description: @@ -773,6 +778,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -799,4 +812,4 @@ packages: version: "3.1.1" sdks: dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index af453dc..2e08520 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # Logging logger: ^1.1.0 # Date formatting - intl: ^0.18.0 + intl: any # State management hooks_riverpod: ^2.3.8 # Routing @@ -33,6 +33,8 @@ dependencies: settings_ui: ^2.0.2 # UI Image bubbles flutter_image_stack: ^0.0.6 + flutter_localizations: + sdk: flutter dev_dependencies: # Testing @@ -53,6 +55,7 @@ dev_dependencies: # Flutter related packages flutter: + generate: true # Enable material design uses-material-design: true # Add assets