From 3b33e6879afbb5e431729ec7ec5e90da3eb748d2 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sat, 23 May 2020 10:58:06 +0200 Subject: [PATCH] Add german translation --- lib/app.dart | 13 +++++++++ lib/backend.dart | 19 ++++++++----- lib/date_utils.dart | 14 ++++++---- lib/home_screen.dart | 15 ++++++---- lib/localizations.dart | 63 ++++++++++++++++++++++++++++++++++++++++++ lib/main.dart | 5 +--- lib/memo_editor.dart | 14 ++++++---- pubspec.yaml | 2 ++ 8 files changed, 117 insertions(+), 28 deletions(-) create mode 100644 lib/localizations.dart diff --git a/lib/app.dart b/lib/app.dart index 16cd1e3..5449d2d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'backend.dart'; import 'home_screen.dart'; +import 'localizations.dart'; /// A simple reminder app. /// @@ -9,6 +12,15 @@ class MemorApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( + localizationsDelegates: const [ + MemorLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en'), + Locale('de'), + ], theme: ThemeData( primaryColor: Colors.black, accentColor: Colors.amber, @@ -21,6 +33,7 @@ class MemorApp extends StatelessWidget { ), fontFamily: 'Libertinus Sans', ), + builder: (context, child) => MemorBackend(child: child), home: HomeScreen(), ); } diff --git a/lib/backend.dart b/lib/backend.dart index b8dd6f2..674a192 100644 --- a/lib/backend.dart +++ b/lib/backend.dart @@ -8,6 +8,7 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart' as pp; import 'package:rxdart/rxdart.dart'; +import 'localizations.dart'; import 'memo.dart'; /// Widget for managing resources and state for Memor. @@ -40,16 +41,12 @@ class MemorBackendState extends State { final memos = BehaviorSubject.seeded([]); /// Whether the backend is currently loading. - /// + /// /// If this is true, the UI should not call backend methods. bool loading = true; static const _fileName = 'memos.json'; - static const _notificationDetails = NotificationDetails( - AndroidNotificationDetails('memor', 'Memor', 'Memor reminders'), - IOSNotificationDetails()); - final _notifications = FlutterLocalNotificationsPlugin(); File _file; @@ -133,12 +130,20 @@ class MemorBackendState extends State { /// Schedule a notification for a memo. Future _schedule(Memo memo) async { + final l10n = MemorLocalizations.of(context); _notifications.schedule( memo.id, - 'Reminder', + l10n.reminder, memo.text, memo.scheduled, - _notificationDetails, + NotificationDetails( + AndroidNotificationDetails( + 'memor', + 'Memor', + l10n.notificationDescription, + ), + IOSNotificationDetails(), + ), androidAllowWhileIdle: true, ); } diff --git a/lib/date_utils.dart b/lib/date_utils.dart index d75daa9..eb18af0 100644 --- a/lib/date_utils.dart +++ b/lib/date_utils.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'localizations.dart'; + /// Utilities for handling DateTime objects. extension DateUtils on DateTime { /// Create a new instance with identical values. @@ -21,18 +23,20 @@ extension DateUtils on DateTime { time.minute, ); - /// Get a string representation of the represented day suitable for display. - String get dateString { + /// Get a localized string representation of the represented day suitable for + /// display. + String dateString(BuildContext context) { + final l10n = MemorLocalizations.of(context); final now = DateTime.now(); if (this.year == now.year && this.month == now.month) { if (this.day == now.day) { - return 'Today'; + return l10n.today; } else if (this.day == now.day + 1) { - return 'Tomorrow'; + return l10n.tomorrow; } } - final format = DateFormat.yMd(); + final format = DateFormat.yMd(l10n.languageCode); return format.format(this); } diff --git a/lib/home_screen.dart b/lib/home_screen.dart index fabf8c5..e0e9ba1 100644 --- a/lib/home_screen.dart +++ b/lib/home_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'backend.dart'; import 'date_utils.dart'; +import 'localizations.dart'; import 'memo.dart'; import 'memo_editor.dart'; @@ -25,10 +26,11 @@ class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { final backend = MemorBackend.of(context); + final l10n = MemorLocalizations.of(context); return Scaffold( appBar: AppBar( - title: Text('Memor'), + title: Text(l10n.title), ), body: backend.loading ? Center( @@ -46,7 +48,7 @@ class HomeScreen extends StatelessWidget { itemBuilder: (context, index) { final memo = memos[index]; final scheduled = memo.scheduled; - final dateString = scheduled.dateString; + final dateString = scheduled.dateString(context); final timeOfDayString = scheduled.timeOfDay.format(context); @@ -66,7 +68,8 @@ class HomeScreen extends StatelessWidget { ), child: ListTile( title: Text(memo.text), - subtitle: Text('$dateString at $timeOfDayString'), + subtitle: Text( + l10n.scheduled(dateString, timeOfDayString)), onTap: () async { final result = await _showMemoEditor(context, memo); @@ -79,9 +82,9 @@ class HomeScreen extends StatelessWidget { await backend.deleteMemo(index); Scaffold.of(context).showSnackBar( SnackBar( - content: Text('Deleted "${memo.text}"'), + content: Text(l10n.deleted(memo.text)), action: SnackBarAction( - label: 'UNDO', + label: l10n.undo, onPressed: () async { await backend.addMemo(memo); }, @@ -95,7 +98,7 @@ class HomeScreen extends StatelessWidget { } else { return Center( child: Text( - 'No reminders scheduled', + l10n.noReminders, style: Theme.of(context).textTheme.headline6.copyWith( color: Colors.grey, ), diff --git a/lib/localizations.dart b/lib/localizations.dart new file mode 100644 index 0000000..c44f8d8 --- /dev/null +++ b/lib/localizations.dart @@ -0,0 +1,63 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +class MemorLocalizations { + static const delegate = MemorLocalizationsDelegate(); + + static MemorLocalizations of(BuildContext context) => + Localizations.of(context, MemorLocalizations); + + final String languageCode; + + MemorLocalizations(this.languageCode); + + bool get de => languageCode == 'de'; + + // Home screen + + String get title => 'Memor'; + String scheduled(String date, String time) => + de ? '$date um $time' : '$date at $time'; + String deleted(String msg) => de ? '"$msg" gelöscht' : 'Deleted "$msg"'; + String get undo => de ? 'RÜCKGÄNGIG' : 'UNDO'; + String get noReminders => + de ? 'Keine Erinnerungen eingerichtet' : 'No reminders scheduled'; + + // Memo editor + + String get editTitle => de ? 'Memo bearbeiten' : 'Edit memo'; + String get addTitle => de ? 'Memo erstellen' : 'Create memo'; + String get save => de ? 'SPEICHERN' : 'SAVE'; + String get create => de ? 'ERSTELLEN' : 'CREATE'; + String get memo => 'Memo'; + String get date => de ? 'Datum' : 'Date'; + String get time => de ? 'Zeit' : 'Time'; + + // Backend + + String get notificationDescription => + de ? 'Memors Erinnerungen' : 'Memor reminders'; + String get reminder => de ? 'Erinnerung' : 'Reminder'; + + // Date utils + + String get today => de ? 'Heute' : 'Today'; + String get tomorrow => de ? 'Morgen' : 'Tomorrow'; +} + +class MemorLocalizationsDelegate + extends LocalizationsDelegate { + const MemorLocalizationsDelegate(); + + @override + bool isSupported(Locale locale) => ['en', 'de'].contains(locale.languageCode); + + @override + Future load(Locale locale) { + return SynchronousFuture( + MemorLocalizations(locale.languageCode)); + } + + @override + bool shouldReload(MemorLocalizationsDelegate old) => false; +} diff --git a/lib/main.dart b/lib/main.dart index ed4b460..7077a90 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,9 @@ import 'package:flutter/widgets.dart'; -import 'backend.dart'; import 'app.dart'; void main() { runApp( - MemorBackend( - child: MemorApp(), - ), + MemorApp(), ); } diff --git a/lib/memo_editor.dart b/lib/memo_editor.dart index ed46f82..be3d399 100644 --- a/lib/memo_editor.dart +++ b/lib/memo_editor.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'localizations.dart'; import 'memo.dart'; import 'date_utils.dart'; @@ -46,15 +47,16 @@ class _MemoEditorState extends State { @override Widget build(BuildContext context) { + final l10n = MemorLocalizations.of(context); final theme = Theme.of(context); return Scaffold( appBar: AppBar( - title: Text(widget.memo != null ? 'Edit memo' : 'Add memo'), + title: Text(widget.memo != null ? l10n.editTitle : l10n.addTitle), actions: [ FlatButton( child: Text( - widget.memo != null ? 'SAVE' : 'CREATE', + widget.memo != null ? l10n.save : l10n.create, style: theme.textTheme.button.copyWith( color: theme.colorScheme.onPrimary, ), @@ -80,13 +82,13 @@ class _MemoEditorState extends State { maxLines: null, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Memo', + labelText: l10n.memo, ), ), ), ListTile( - title: Text('Date'), - subtitle: Text(_date.dateString), + title: Text(l10n.date), + subtitle: Text(_date.dateString(context)), onTap: () async { final result = await showDatePicker( context: context, @@ -103,7 +105,7 @@ class _MemoEditorState extends State { }, ), ListTile( - title: Text('Time'), + title: Text(l10n.time), subtitle: Text(_time.format(context)), onTap: () async { final result = await showTimePicker( diff --git a/pubspec.yaml b/pubspec.yaml index 636a580..bccb255 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter flutter_local_notifications: intl: meta: