diff --git a/covas_mobile/l10n.yaml b/covas_mobile/l10n.yaml new file mode 100644 index 0000000..1437ccc --- /dev/null +++ b/covas_mobile/l10n.yaml @@ -0,0 +1,4 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations diff --git a/covas_mobile/lib/classes/MyDrawer.dart b/covas_mobile/lib/classes/MyDrawer.dart index 27cfe5e..8fcf62c 100644 --- a/covas_mobile/lib/classes/MyDrawer.dart +++ b/covas_mobile/lib/classes/MyDrawer.dart @@ -13,6 +13,10 @@ import '../pages/LoginDemo.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv import 'package:encrypt_shared_preferences/provider.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + class MyDrawer extends StatelessWidget with ShowAlertDialog { Future logout(BuildContext context) async { SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -87,6 +91,8 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog { @override Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final localeProvider = Provider.of(context); return Drawer( child: ListView( padding: EdgeInsets.zero, @@ -107,7 +113,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog { // Drawer Items ListTile( leading: Icon(Icons.home), - title: Text('Home'), + title: Text(loc?.home ?? "Home"), onTap: () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => ListItemMenu())); @@ -117,7 +123,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog { ), ListTile( leading: Icon(Icons.settings), - title: Text('Settings'), + title: Text(loc?.settings ?? 'Settings'), onTap: () { Navigator.pushReplacement( context, @@ -127,7 +133,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog { ), ListTile( leading: Icon(Icons.account_circle), - title: Text('Update profile'), + title: Text(loc?.update_profile ?? 'Update profile'), onTap: () { Navigator.pushReplacement( context, @@ -136,16 +142,61 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog { }, ), ListTile( - leading: Icon(Icons.info), - title: Text('About'), + leading: Icon(Icons.language), + title: Text(loc?.language ?? 'Language'), onTap: () { - showAlertDialog( - context, 'About', "Version 0.0.1"); // Close the drawer + showDialog( + context: context, + builder: (_) => AlertDialog( + title: Text(loc?.select_language ?? 'Select Language'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: Icon(Icons.flag), + title: Text(loc?.french ?? 'Français'), + onTap: () { + Provider.of(context, listen: false) + .setLocale(const Locale('fr')); + Navigator.of(context).pop(); + }, + ), + ListTile( + leading: Icon(Icons.flag_outlined), + title: Text(loc?.english ?? 'English'), + onTap: () { + Provider.of(context, listen: false) + .setLocale(const Locale('en')); + Navigator.of(context).pop(); + }, + ), + ListTile( + leading: Icon(Icons.flag_outlined), + title: Text(loc?.german ?? 'German'), + onTap: () { + Provider.of(context, listen: false) + .setLocale(const Locale('de')); + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ); + }, + ), + + ListTile( + leading: Icon(Icons.info), + title: Text(loc?.about ?? 'About'), + onTap: () { + showAlertDialog(context, loc?.about ?? 'About', + "Version 0.0.1"); // Close the drawer }, ), ListTile( leading: Icon(Icons.logout), - title: Text('Log out'), + title: Text(loc?.log_out ?? 'Log out'), onTap: () async { logout(context); // Close the drawer diff --git a/covas_mobile/lib/l10n/app_de.arb b/covas_mobile/lib/l10n/app_de.arb new file mode 100644 index 0000000..9194d0a --- /dev/null +++ b/covas_mobile/lib/l10n/app_de.arb @@ -0,0 +1,134 @@ +{ + "@@locale": "de", + "menu_list": "Veranstaltungsmenü", + "language": "Sprache", + "home": "Startseite", + "settings": "Einstellungen", + "update_profile": "Profil aktualisieren", + "about": "Über", + "log_out": "Abmelden", + "french": "Französisch", + "english": "Englisch", + "german": "Deutsch", + "select_language": "Sprache auswählen", + "search_item": "Nach Element suchen", + "search_tag": "Nach Schlagwörtern suchen", + "search_geographical": "Nach geografischer Zone suchen", + "show_date_field": "Datumsfelder anzeigen", + "hide_date_field": "Datumsfelder ausblenden", + "no_data": "Keine Daten verfügbar", + "search": "Suchen", + "no_events": "Keine Veranstaltungen für diesen Ort verfügbar.", + "start_date": "Anfangsdatum", + "end_date": "Enddatum", + "failed_suggestions": "Vorschläge konnten nicht geladen werden", + "error": "Fehler", + "password_different": "Ein anderes Passwort eingeben", + "create": "Erstellung", + "user_create": "Benutzer wurde erstellt", + "user_update": "Benutzer wurde aktualisiert", + "request_error": "Fehlerhafte Anfrage", + "incorrect_password": "Falsches Passwort", + "unknown_user": "Unbekannter Benutzer", + "disabled_user": "Benutzer deaktiviert", + "invalid_token": "Ungültiger Token", + "internal_error_server": "Interner Serverfehler", + "unknown_error_auth": "Unbekannter Authentifizierungsfehler", + "required_input": "Pflichtfeld", + "create_profile": "Profil erstellen", + "edit_pseudo": "Benutzernamen bearbeiten", + "password": "Passwort", + "enter_password": "Passwort eingeben", + "password_confirmed": "Passwort bestätigt", + "last_name": "Nachname", + "first_name": "Vorname", + "email": "E-Mail", + "edit_last_name": "Nachnamen bearbeiten", + "edit_first_name": "Vornamen bearbeiten", + "edit_email": "E-Mail-Adresse bearbeiten", + "birth_date": "Geburtsdatum", + "edit_birth": "Geburtsdatum bearbeiten", + "create_profile_button": "Profil erstellen", + "take_picture": "Foto aufnehmen", + "error_ia": "Google KI konnte das Bild nicht analysieren. Bitte ein anderes versuchen.", + "no_data_geo": "Keine geografischen Daten", + "response_status_update": "Statuscode-Antwort aktualisieren", + "error_token": "Token-Fehler", + "error_format": "Vom KI geliefertes Datenformat ist fehlerhaft", + "display_picture": "Bild anzeigen", + "analyze_image": "Bildanalyse läuft", + "loading_progress": "Ladefortschritt", + "error_event": "Veranstaltungsfehler", + "no_future_event": "Keine zukünftigen Veranstaltungen", + "error_user": "Benutzerfehler", + "empty_input": "Eingabefeld leer", + "info_event": "Veranstaltungsinfo", + "event_already": "Veranstaltung existiert bereits", + "picture_error": "Bildfehler", + "no_picture_published": "Kein Bild veröffentlicht", + "event_update": "Veranstaltung aktualisiert", + "location": "Ort", + "add_event": "Veranstaltung hinzufügen oder aktualisieren", + "edit_image": "Bilder bearbeiten", + "name": "Name", + "edit_event_name": "Veranstaltungsname bearbeiten", + "start_time": "Startzeit", + "end_time": "Endzeit", + "select_date": "Zum Auswählen eines Datums klicken", + "select_time": "Zum Auswählen einer Uhrzeit klicken", + "tag": "Schlagwörter", + "already_tag": "Dieses Schlagwort ist bereits vorhanden", + "enter_tag": "Ein Schlagwort eingeben", + "organizer": "Veranstalter", + "already_organiser": "Veranstalter bereits vorhanden", + "enter_organizer": "Veranstalter eingeben", + "description": "Beschreibung", + "describe_event": "Veranstaltung beschreiben", + "add": "Hinzufügen", + "different_password_error": "Passwörter stimmen nicht überein", + "update": "Aktualisieren", + "updated": "Aktualisiert", + "settings_updated": "Einstellungen aktualisiert", + "define_kilometer": "Kilometer definieren", + "email_sent": "E-Mail wurde gesendet", + "forgot_password": "Passwort vergessen", + "enter_email": "E-Mail eingeben", + "send_email": "E-Mail senden", + "invalid_cache": "Ungültiger Cache", + "item_date": "Datum : ", + "item_maps": "Karte : ", + "item_organizer": "Veranstalter : ", + "item_description": "Beschreibung : ", + "item_tags": "Schlagwörter : ", + "failed_auth": "Authentifizierung fehlgeschlagen", + "login_page": "Anmeldeseite", + "pseudo": "Benutzername", + "enter_existing_pseudo": "Vorhandenen Benutzernamen eingeben", + "remembr_me": "Angemeldet bleiben", + "new_user": "Neuer Benutzer? Konto erstellen", + "sign_in": "Anmelden", + "map_token": "Mapbox-Zugangstoken ist nicht verfügbar", + "geo_disabled": "Standortdienste sind deaktiviert.", + "permission_denied": "Standortberechtigungen wurden verweigert.", + "enable_permission": "Standortberechtigungen dauerhaft verweigert. Bitte in den Einstellungen aktivieren.", + "no_last_position": "Keine letzte bekannte Position verfügbar.", + "failed_location": "Standort konnte nicht ermittelt werden", + "failed_fetch": "Route konnte nicht abgerufen werden", + "invalid_coordinates_symbol": "Ungültige Koordinaten, Symbol kann nicht hinzugefügt werden.", + "error_symbol": "Fehler beim Hinzufügen des Symbols.", + "position_not_init": "Benutzerposition noch nicht initialisiert. Erneut versuchen.", + "invalid_coordinates": "Ungültige Koordinaten.", + "walking": "Zu Fuß", + "cycling": "Mit dem Fahrrad", + "driving": "Mit dem Auto", + "get_direction": "Wegbeschreibung und Markierungen anzeigen", + "missing_token": "Zugangstoken fehlt", + "geocoding_error": "Fehler bei der Geokodierung", + "no_found_place": "Kein Ort gefunden", + "upload_error": "Fehler beim Hochladen des Bildes", + "event_added": "Veranstaltung hinzugefügt", + "unknown_error": "Unbekannter Fehler", + "app_error": "Anwendungsfehler", + "at": "um", + "to_date": "bis" +} diff --git a/covas_mobile/lib/l10n/app_en.arb b/covas_mobile/lib/l10n/app_en.arb new file mode 100644 index 0000000..2f29651 --- /dev/null +++ b/covas_mobile/lib/l10n/app_en.arb @@ -0,0 +1,136 @@ +{ + "@@locale": "en", +"menu_list": "Event list menu", +"language": "Language", +"home": "Home", +"settings": "Settings", +"update_profile": "Update profile", +"about": "About", +"log_out": "Log out", +"french": "French", +"english": "English", +"german": "German", +"select_language": "Select language", +"search_item": "Search by item", +"search_tag": "Search by tags", +"search_geographical": "Search by geographical zone", +"show_date_field": "Show Date Fields", +"hide_date_field": "Hide Date Fields", +"no_data": "No data available", +"search": "Search", +"no_events": "No events available for this location.", +"start_date": "Start date", +"end_date": "End date", +"failed_suggestions": "Failed to load suggestions", +"error":"Error", +"password_different":"Must write a different password", +"create": "Creation", +"user_create": "Your user created", +"user_update": "Your user updated", +"request_error": "Poorly constructed query", +"incorrect_password": "Incorrect password", +"unknown_user": "Unknown user", +"disabled_user": "User disabled", +"invalid_token": "Invalid token", +"internal_error_server": "Internal error server", +"unknown_error_auth": "Unknown error authentification", +"required_input": "Required input", +"create_profile": "Create profile", +"edit_pseudo": "Edit pseudo", +"password":"Password", +"enter_password": "Enter the passord", + "password_confirmed": "Password confirmed", + "last_name": "Last name", +"first_name": "First name", +"email": "Mail", +"edit_last_name": "Edit name", +"edit_first_name": "Edit first name", +"edit_email": "Edit email address", +"birth_date": "Birth date", +"edit_birth": "Edit birth date", +"create_profile_button": "Create profile", +"take_picture": "Take a picture", +"error_ia": "Google AI failed to analyze picture. Retry with another one", +"no_data_geo": "No geographical data", +"response_status_update": "response status code update", +"error_token": "Token error", +"error_format": "Data format error given by AI", +"display_picture": "Display the Picture", +"analyze_image": "Image Analyze in progress", +"loading_progress": "Loading progress", +"error_event": "Event error", +"no_future_event": "No future event", +"error_user": "Error user", +"empty_input": "Empty input", +"info_event": "Event info", +"event_already": "Event already exists", +"picture_error": "Picture error", +"no_picture_published": "No picture published", +"event_update": "Event updated", +"location": "Location", +"add_event": "Add or Update a event", +"edit_image": "Edit pictures", +"name": "Name", +"edit_event_name": "Edit event name", +"start_time": "Start time", +"end_time": "End time", +"select_date": "Click to select a date", +"select_time": "Click to select a time", +"tag": "Tags", +"already_tag": "You have already this tags", +"enter_tag": "Enter a tag", +"organizer": "Organizer", +"already_organiser": "You have already a organizer", +"enter_organizer": "Enter a organizer", +"description": "Description", +"describe_event": "Describe event", +"add": "Add", +"update_profile": "Update profile", +"different_password_error": "Different password", +"update": "Update", +"updated": "Updated", +"settings_updated": "Settings updated", +"define_kilometer": "Define Kilometer", +"settings": "Settings", +"email_sent": "Email has been sent", +"forgot_password": "Forgot password", +"enter_email": "Enter the email", +"send_email": "Send email", +"invalid_cache": "Invalid cache", +"item_date": "Date : ", +"item_maps": "Maps : ", +"item_organizer": "Organizer : ", +"item_description": "Description : ", +"item_tags": "Tags : ", +"failed_auth": "Authentification failed", +"login_page": "Login page", +"pseudo": "Pseudo", +"enter_existing_pseudo": "Enter a existing pseudo", +"remembr_me": "Remember me", +"new_user": "New User? Create Account", +"sign_in": "Sign in", +"map_token": "Mapbox Access Token is not available", +"geo_disabled": "Location services are disabled.", +"permission_denied":"Location permissions are denied.", +"enable_permission": "Location permissions are permanently denied. Enable them in settings.", +"no_last_position": "No last known position available.", +"failed_location": "Failed to get user location", +"failed_fetch": "Failed to fetch the route", +"invalid_coordinates_symbol": "Invalid coordinates, cannot add symbol.", +"error_symbol": "Error when adding symbol.", +"position_not_init": "User position is not yet initialized. Try again.", +"invalid_coordinates": "Invalid coordinates.", +"walking": "Walking", +"cycling": "Cycling", +"driving": "Driving", +"get_direction": "Get Directions and Markers", +"missing_token": "Missing access token", +"geocoding_error": "Error when geocoding", +"no_found_place": "No found place", +"upload_error": "Error when image uploading", +"event_added": "Event added", +"unknown_error": "Unknown error", +"app_error": "Application error", +"at": "at", +"to_date": "to" +} \ No newline at end of file diff --git a/covas_mobile/lib/l10n/app_fr.arb b/covas_mobile/lib/l10n/app_fr.arb new file mode 100644 index 0000000..c3dd1fd --- /dev/null +++ b/covas_mobile/lib/l10n/app_fr.arb @@ -0,0 +1,137 @@ +{ + "@@locale": "fr", +"menu_list": "Liste d'évènement", +"language": "Langue", +"home": "Accueil", +"settings": "Paramètres", +"update_profile": "Modifier profil", +"about": "À propos", +"log_out": "Se déconnecter", +"french": "Français", +"english": "Anglais", +"german": "Allemand", +"select_language": "Selectionne la langue", +"search_item": "Recherche par item", +"search_tag": "Recherche par tags", +"search_geographical": "Recherche par zone géographique", +"show_date_field": "Afficher champ date", +"hide_date_field": "Cacher Date Fields", +"no_data": "Aucune donnée disponible", +"search": "Recherche", +"no_events": "Pas d'évènements dans cette localisation", +"start_date": "Date de début", +"end_date": "Date de fin", +"failed_suggestions": "Echec de chargement des suggestions", +"error":"Erreur", +"password_different":"Tu dois écrire un mot de passe different", +"create": "Création", +"user_create": "Votre utilisateur a été créé", +"user_update": "Votre utilisateur a été modifié", +"request_error": "Requête mal construite", +"incorrect_password": "Mot de passe incorrect", +"unknown_user": "Utilisateur inconnu", +"disabled_user": "Utilisateur désactivé", +"invalid_token": "Token invalide", +"internal_error_server": "Erreur interne de serveur", +"unknown_error_auth": "Problème d'authentification inconnu", +"required_input": "Champ requis", +"create_profile": "Creation profil", +"edit_pseudo": "Modifier le pseudo", +"password":"Mot de passe", +"enter_password": "Entrez le password", +"password_confirmed": "Confirmez le mot de passe", +"last_name": "Nom", +"first_name": "Prénom", +"email": "Email", +"edit_last_name": "Modifier le nom", +"edit_first_name": "Modifier le prénom", +"edit_email": "Modifier l'email", +"birth_date": "Date de naissance", +"edit_birth": "Modifier la date de naissance", +"create_profile_button": "Créer le profil", +"take_picture": "Take a picture", +"error_ia": "L'IA de Google n'a pas su analyser l'image. Recommencer avec une autre", +"no_data_geo": "Aucune donnée géographique", +"response_status_update": "Code du statut de réponse de la modification", +"error_token": "Erreur de token", +"error_format": "Erreur de format de donnée fourni par l'IA", +"display_picture": "Display the Picture", +"analyze_image": "Analyse de l'image en cours", +"loading_progress": "Chargement en cours", +"error_event": "Erreur de l'évènement", +"no_future_event": "Évènement non futur", +"error_user": "Erreur de l'utilisateur", +"empty_input": "Champ vide", +"info_event": "Event info", +"event_already": "Event already exists", +"picture_error": "Erreur image", +"no_picture_published": "Image non publiée", +"event_update": "Évènement modifié", +"location": "Lieu", +"add_event": "Ajouter ou modifier un évènement", +"edit_image": "Changer la photo", +"name": "Nom", +"edit_event_name": "Changer le nom de l'évènement", +"start_time": "Heure de début", +"end_time": "Heure de fin", +"select_date": "Cliquer pour selectionner une date", +"select_time": "Cliquer pour selectionner une heure", +"tag": "Tags", +"already_tag": "Tu as déjà entré ce tag", +"enter_tag": "Entrer un tag", +"organizer": "Organisateur", +"already_organiser": "Tu as déjà rentré cet organisateur", +"enter_organizer": "Entrer un organisateur", +"description": "Description", +"describe_event": "Décrire l'évènement", +"add": "Ajouter", +"update_profile": "Modifier le profil", +"different_password_error": "Mot de passe différent", +"update": "Mettre à jour", +"updated": "Mis à jour", +"settings_updated": "Paramètre mis à jour", +"define_kilometer": "Definir un kilomètre", +"settings": "Paramètres", +"email_sent": "Email a été envoyé", +"forgot_password": "Mot de passe oublié", +"enter_email": "Entrez l'email", +"send_email": "Send email", +"invalid_cache": "Cache invalide", +"item_date": "Date : ", +"item_maps": "Carte : ", +"item_organizer": "Organisateurs : ", +"item_description": "Description : ", +"item_tags": "Tags : ", +"failed_auth": "Échec de l'authenticaton", +"login_page": "Page d'authentification", +"pseudo": "Pseudo", +"enter_existing_pseudo": "Entrez un pseudo existant", +"remembr_me": "Se souvenir de moi", +"new_user": "Nouvel utilisateur ? Créer un compte", +"sign_in": "Se connecter", +"map_token": "Token d'accès de Mapbox n'est pas disponible", +"geo_disabled": "Les services de localisation sont désactivés.", +"permission_denied":"Les permissions de localisation sont refusées.", +"enable_permission": "Les permissions de localisation sont toujours désactivés. Il faut les désactiver", +"no_last_position": "Aucune position n'est pas disponible.", +"failed_location": "Échec de récupération des données geographique", +"failed_fetch": "Échec de récupération des routes", +"invalid_coordinates_symbol": "Coordonnées invalides. On ne peut pas ajouter le symbole", +"error_symbol": "Erreur lors de l'ajout du symbole", +"position_not_init": "Coordonnées non initialisées. Essaye encore.", +"invalid_coordinates": "Coordonnées invalides", +"walking": "Marche", +"cycling": "Vélo", +"driving": "Voiture", +"get_direction": "Get Directions and Markers", +"missing_token": "Token d'accès manquant", +"geocoding_error": "Erreur lors du geocodage", +"no_found_place": "Lieu introuvable", +"upload_error": "Erreur lors de l'upload d'image", +"event_added": "Évènement ajouté", +"unknown_error": "Erreur inconnue", +"app_error": "Erreur d'application", +"at": "à", +"to_date": "jusqu'à" + +} \ No newline at end of file diff --git a/covas_mobile/lib/locale_provider.dart b/covas_mobile/lib/locale_provider.dart new file mode 100644 index 0000000..0d90807 --- /dev/null +++ b/covas_mobile/lib/locale_provider.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class LocaleProvider with ChangeNotifier { + static const _localeKey = 'locale_code'; + + Locale _locale = const Locale('en'); + + Locale get locale => _locale; + + LocaleProvider() { + _loadLocale(); + } + + Future _loadLocale() async { + final prefs = await SharedPreferences.getInstance(); + final code = prefs.getString(_localeKey); + if (code != null && L10n.all.contains(Locale(code))) { + _locale = Locale(code); + notifyListeners(); + } + } + + void setLocale(Locale locale) async { + if (!L10n.all.contains(locale)) return; + + _locale = locale; + notifyListeners(); + + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_localeKey, locale.languageCode); + } +} + +class L10n { + static final all = [ + const Locale('en'), + const Locale('fr'), + const Locale('de') + ]; +} diff --git a/covas_mobile/lib/main.dart b/covas_mobile/lib/main.dart index 76ea006..f01e1a3 100644 --- a/covas_mobile/lib/main.dart +++ b/covas_mobile/lib/main.dart @@ -1,19 +1,33 @@ import 'package:flutter/material.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:provider/provider.dart'; + import 'pages/LoginDemo.dart'; +import 'locale_provider.dart'; // <-- à adapter selon ton arborescence +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await MobileAds.instance.initialize(); - runApp(MyApp()); + runApp( + ChangeNotifierProvider( + create: (_) => LocaleProvider(), + child: MyApp(), + ), + ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + final localeProvider = Provider.of( + context); // écoute les changements de langue return MaterialApp( debugShowCheckedModeBanner: false, + locale: localeProvider.locale, // <-- utilise la locale courante + supportedLocales: L10n.all, + localizationsDelegates: AppLocalizations.localizationsDelegates, home: LoginDemo(), ); } diff --git a/covas_mobile/lib/pages/AddProfile.dart b/covas_mobile/lib/pages/AddProfile.dart index 5e7c8c0..5888f57 100644 --- a/covas_mobile/lib/pages/AddProfile.dart +++ b/covas_mobile/lib/pages/AddProfile.dart @@ -13,6 +13,10 @@ import '../variable/globals.dart' as globals; import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé plus loin void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -95,7 +99,11 @@ class _AddProfileState extends State with ShowAlertDialog { if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) { if (password != confirmedPassword) { - showAlertDialog(context, "Erreur", "Mot de passe different"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.password_different ?? + "Must write a different password"); return; } } @@ -117,24 +125,32 @@ class _AddProfileState extends State with ShowAlertDialog { })); print(responsePost.statusCode); if (responsePost.statusCode == 200) { - showAlertDialog(context, "Creation", "Votre utilisateur a été créé"); + showAlertDialog( + context, + AppLocalizations.of(context)?.create ?? "Creation", + AppLocalizations.of(context)?.user_create ?? "Your user created"); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => LoginDemo())); return; } final errorMessages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Utilisateur désactivé", - 410: "Token invalide", - 500: "Problème interne du serveur", + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; final text = errorMessages[responsePost.statusCode] ?? - "Problème d'authentification inconnu"; - showAlertDialog(context, "Erreur serveur", text); + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog( + context, AppLocalizations.of(context)?.error ?? "Error", text); } @override @@ -150,7 +166,9 @@ class _AddProfileState extends State with ShowAlertDialog { final _formKey = GlobalKey(); String? _validateField(String? value) { - return value!.isEmpty ? 'Champ requis' : null; + return value!.isEmpty + ? AppLocalizations.of(context)?.required_input ?? 'Required input' + : null; } @override @@ -158,7 +176,8 @@ class _AddProfileState extends State with ShowAlertDialog { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Create profile"), + title: Text( + AppLocalizations.of(context)?.create_profile ?? "Create profile"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -183,7 +202,8 @@ class _AddProfileState extends State with ShowAlertDialog { decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Pseudo', - hintText: 'Modifier le pseudo'), + hintText: AppLocalizations.of(context)?.edit_pseudo ?? + 'Edit pseudo'), ), ), Padding( @@ -196,8 +216,11 @@ class _AddProfileState extends State with ShowAlertDialog { obscureText: true, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Mot de passe', - hintText: 'Entrez le mot de passe'), + labelText: AppLocalizations.of(context)?.password ?? + 'Password', + hintText: + AppLocalizations.of(context)?.enter_password ?? + 'Enter the password'), ), ), Padding( @@ -209,9 +232,14 @@ class _AddProfileState extends State with ShowAlertDialog { validator: (value) => _validateField(value), obscureText: true, decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Confirmez le mot de passe', - hintText: 'Confirmez le mot de passe'), + border: OutlineInputBorder(), + labelText: + AppLocalizations.of(context)?.password_confirmed ?? + 'Password confirmed', + hintText: + AppLocalizations.of(context)?.password_confirmed ?? + 'Password confirmed', + ), ), ), Padding( @@ -223,8 +251,11 @@ class _AddProfileState extends State with ShowAlertDialog { validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Nom', - hintText: 'Modifier le nom'), + labelText: AppLocalizations.of(context)?.last_name ?? + 'Last name', + hintText: + AppLocalizations.of(context)?.edit_last_name ?? + 'Edit last name'), ), ), Padding( @@ -236,8 +267,11 @@ class _AddProfileState extends State with ShowAlertDialog { validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Prénom', - hintText: 'Modifier le prénom'), + labelText: AppLocalizations.of(context)?.first_name ?? + 'First name', + hintText: + AppLocalizations.of(context)?.edit_first_name ?? + 'Edit first name'), ), ), Padding( @@ -249,8 +283,10 @@ class _AddProfileState extends State with ShowAlertDialog { validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email', - hintText: 'Modifier l\'adresse mail'), + labelText: + AppLocalizations.of(context)?.email ?? 'Email', + hintText: AppLocalizations.of(context)?.edit_email ?? + 'Edit email'), ), ), Padding( @@ -263,8 +299,10 @@ class _AddProfileState extends State with ShowAlertDialog { validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de naissance', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.birth_date ?? + 'Birth date', + hintText: AppLocalizations.of(context)?.edit_birth ?? + 'Edit birth date'), onTap: () => onTapFunctionDatePicker(context: context)), ), SizedBox( @@ -283,7 +321,8 @@ class _AddProfileState extends State with ShowAlertDialog { } }, child: Text( - 'Créer le profil', + AppLocalizations.of(context)?.create_profile_button ?? + "Create profile", style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/lib/pages/Camera.dart b/covas_mobile/lib/pages/Camera.dart index 6c79f94..0189dc0 100644 --- a/covas_mobile/lib/pages/Camera.dart +++ b/covas_mobile/lib/pages/Camera.dart @@ -7,6 +7,10 @@ import 'DisplayPictureScreen.dart'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé plus loin Future main() async { // Ensure that plugin services are initialized so that `availableCameras()` @@ -92,7 +96,9 @@ class CameraState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Take a picture')), + appBar: AppBar( + title: Text(AppLocalizations.of(context)?.take_picture ?? + "Take a picture")), // You must wait until the controller is initialized before displaying the // camera preview. Use a FutureBuilder to display a loading spinner until the // controller has finished initializing. diff --git a/covas_mobile/lib/pages/CameraEdit.dart b/covas_mobile/lib/pages/CameraEdit.dart index 09034ce..408e8fc 100644 --- a/covas_mobile/lib/pages/CameraEdit.dart +++ b/covas_mobile/lib/pages/CameraEdit.dart @@ -8,6 +8,10 @@ import 'EditEvent.dart'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé Future main() async { // Ensure that plugin services are initialized so that `availableCameras()` @@ -94,7 +98,9 @@ class CameraEditState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Take a picture')), + appBar: AppBar( + title: Text(AppLocalizations.of(context)?.take_picture ?? + 'Take a picture')), // You must wait until the controller is initialized before displaying the // camera preview. Use a FutureBuilder to display a loading spinner until the // controller has finished initializing. diff --git a/covas_mobile/lib/pages/DisplayPictureScreen.dart b/covas_mobile/lib/pages/DisplayPictureScreen.dart index 5d0ee55..aea3380 100644 --- a/covas_mobile/lib/pages/DisplayPictureScreen.dart +++ b/covas_mobile/lib/pages/DisplayPictureScreen.dart @@ -17,6 +17,10 @@ import '../classes/MyDrawer.dart'; import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -98,8 +102,11 @@ class DisplayPictureScreenState extends State Future displayError(String e) async { print("problem gemini : ${e}"); - showAlertDialog(context, 'Error IA', - "L'IA de Google n'a pas su analyser l'image. Recommecer avec une autre"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? 'Error', + AppLocalizations.of(context)?.error_ia ?? + 'Google AI failed to analyze picture. Retry with another one'); } void _showErrorDialog(BuildContext context, String title, String message) { @@ -154,7 +161,10 @@ class DisplayPictureScreenState extends State final location = await _fetchGeolocation(place); if (location == null) { _showErrorDialog( - context, "Erreur serveur", "Aucune donnée geographique"); + context, + AppLocalizations.of(context)?.error ?? 'Error', + AppLocalizations.of(context)?.no_data_geo ?? + 'No geographical data'); return; } @@ -181,15 +191,23 @@ class DisplayPictureScreenState extends State builder: (_) => ItemMenu(title: events[0]["id"]))); } } else { - showAlertDialog(context, 'Erreur de reponse', - "response status code update : ${response.statusCode}"); + String error = AppLocalizations.of(context)?.response_status_update ?? + 'Response status update : ${response.statusCode}'; + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? 'Error', + "${error} : ${response.statusCode}"); } } else { - showAlertDialog(context, "Erreur de reponse", "Erreur de token"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? 'Error', + AppLocalizations.of(context)?.error_token ?? "Token error"); } } catch (e) { showAlertDialog( - context, "Erreur IA", "Erreur de format de donnée fourni par l'IA"); + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.error_format ?? + "Data format error given by AI"); } //showDescImageAddDialog(context, message); @@ -217,7 +235,9 @@ class DisplayPictureScreenState extends State @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Display the Picture')), + appBar: AppBar( + title: Text(AppLocalizations.of(context)?.display_picture ?? + "Display The Picture")), // The image is stored as a file on the device. Use the `Image.file` // constructor with the given path to display the image. drawer: MyDrawer(), @@ -233,12 +253,15 @@ class DisplayPictureScreenState extends State width: _bannerAd!.size.width.toDouble(), child: AdWidget(ad: _bannerAd!)), Text( - 'Analyse de l\'image en cours', + AppLocalizations.of(context)?.analyze_image ?? + 'Image analyze in progress', style: Theme.of(context).textTheme.titleLarge, ), CircularProgressIndicator( value: controller.value, - semanticsLabel: 'Loading progress', + semanticsLabel: + AppLocalizations.of(context)?.loading_progress ?? + 'Loading progress', ), ]))); } diff --git a/covas_mobile/lib/pages/EditEvent.dart b/covas_mobile/lib/pages/EditEvent.dart index 00900f1..7c885bc 100644 --- a/covas_mobile/lib/pages/EditEvent.dart +++ b/covas_mobile/lib/pages/EditEvent.dart @@ -22,6 +22,10 @@ import '../variable/globals.dart' as globals; import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -166,13 +170,19 @@ class _EditEventState extends State Future _updateEvent(BuildContext context) async { if (!_isEventInFuture()) { - _showErrorDialog(context, "Erreur evenement", "Evenement non futur"); + _showErrorDialog( + context, + AppLocalizations.of(context)?.error_event ?? "Event error", + AppLocalizations.of(context)?.no_future_event ?? "No future event"); return; } final accessToken = await _getAccessToken(); if (accessToken.isEmpty) { - _showErrorDialog(context, "Erreur utilisateur", "Champ vide"); + _showErrorDialog( + context, + AppLocalizations.of(context)?.error_user ?? "User error", + AppLocalizations.of(context)?.empty_input ?? "Empty input"); return; } @@ -181,27 +191,41 @@ class _EditEventState extends State final geolocation = await _fetchGeolocation(); if (geolocation == null) { _showErrorDialog( - context, "Erreur serveur", "Aucune donnée geographique"); + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.no_data_geo ?? + "No geographical data"); return; } if (await _isDuplicateEvent(accessToken, geolocation)) { - _showErrorDialog(context, "Info evenement", "Evenement deja existant"); + _showErrorDialog( + context, + AppLocalizations.of(context)?.info_event ?? "Event info", + AppLocalizations.of(context)?.event_already ?? + "Event already exists"); return; } if (widget.imgPath.isNotEmpty) { imgUrl = await _uploadImage(widget.imgPath); if (imgUrl.isEmpty) { - _showErrorDialog(context, "Erreur image", "Image non postée"); + _showErrorDialog( + context, + AppLocalizations.of(context)?.picture_error ?? "Error picture", + AppLocalizations.of(context)?.no_picture_published ?? + "No picture published"); return; } } await _updateEventData(accessToken, geolocation); - showEventDialog(context, "Evenement ${inputName.text} modifie"); + String message = + AppLocalizations.of(context)?.event_update ?? "Event updated"; + showEventDialog(context, "${message} : ${inputName.text}"); } catch (e) { - _showErrorDialog(context, "Erreur serveur", "$e"); + _showErrorDialog( + context, AppLocalizations.of(context)?.error ?? "Error", "$e"); } } @@ -297,15 +321,22 @@ class _EditEventState extends State void _handleErrorResponse(BuildContext context, int statusCode) { final messages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Utilisateur désactivé", - 410: "Token invalide", - 500: "Problème interne du serveur" + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - _showErrorDialog(context, "Erreur serveur", - messages[statusCode] ?? "Problème d'authentification inconnu"); + _showErrorDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + messages[statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"); } void _showErrorDialog(BuildContext context, String title, String message) { @@ -349,7 +380,9 @@ class _EditEventState extends State final _formKey = GlobalKey(); String? _validateField(String? value) { - return value!.isEmpty ? 'Champ requis' : null; + return value!.isEmpty + ? AppLocalizations.of(context)?.required_input ?? "Required input" + : null; } Future searchSuggestions(String input) async { @@ -360,11 +393,9 @@ class _EditEventState extends State 'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&types=poi,address,place'; var encoded = Uri.encodeFull(url); final response = await http.get(Uri.parse(encoded)); - print("response code suggesttion : ${response.statusCode}"); if (response.statusCode == 200) { final data = json.decode(response.body); - print("data suggestion : ${data}"); setState(() { suggestions = (data['features'] as List) .map((feature) => { @@ -389,7 +420,7 @@ class _EditEventState extends State TextField( controller: inputGeo, decoration: InputDecoration( - labelText: 'Lieu', + labelText: AppLocalizations.of(context)?.location ?? "Location", border: OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(Icons.clear), @@ -459,7 +490,8 @@ class _EditEventState extends State backgroundColor: Colors.white, drawer: MyDrawer(), appBar: AppBar( - title: Text("Add or Update a event"), + title: Text(AppLocalizations.of(context)?.add_event ?? + "Add or Update a event"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -516,7 +548,8 @@ class _EditEventState extends State child: ElevatedButton.icon( onPressed: popCamera, icon: Icon(Icons.edit, size: 16), // Edit icon - label: Text("Edit Image"), // Button text + label: Text(AppLocalizations.of(context)?.edit_image ?? + "Edit pictures"), // Button text style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Button color foregroundColor: Colors.white, // Text color @@ -533,8 +566,10 @@ class _EditEventState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Nom', - hintText: 'Modifier le nom de l\'évènement'), + labelText: AppLocalizations.of(context)?.name ?? "Name", + hintText: + AppLocalizations.of(context)?.edit_event_name ?? + "Edit event name"), ), ), _buildGeographicalZoneSearchField(), @@ -548,8 +583,10 @@ class _EditEventState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de debut', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.start_date ?? + "Start date", + hintText: AppLocalizations.of(context)?.select_date ?? + "Click to select a date"), onTap: () => onTapFunctionDatePicker( context: context, position: "start")), ), @@ -563,8 +600,10 @@ class _EditEventState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Heure de debut', - hintText: 'Cliquez ici pour selectionner une heure'), + labelText: AppLocalizations.of(context)?.start_time ?? + "Start time", + hintText: AppLocalizations.of(context)?.select_time ?? + "Click to select a time"), onTap: () => onTapFunctionTimePicker( context: context, position: "start")), ), @@ -578,8 +617,10 @@ class _EditEventState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de fin', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.end_date ?? + "End date", + hintText: AppLocalizations.of(context)?.select_time ?? + "Click to select a date"), onTap: () => onTapFunctionDatePicker( context: context, position: "end")), ), @@ -593,8 +634,10 @@ class _EditEventState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Heure de fin', - hintText: 'Cliquez ici pour selectionner une heure'), + labelText: AppLocalizations.of(context)?.end_time ?? + "End time", + hintText: AppLocalizations.of(context)?.select_time ?? + "Click to select a time"), onTap: () => onTapFunctionTimePicker( context: context, position: "end")), ), @@ -604,7 +647,8 @@ class _EditEventState extends State textSeparators: const [' ', ','], validator: (String tag) { if (_stringTagController.getTags!.contains(tag)) { - return 'Tu as deja rentre ce tag'; + return AppLocalizations.of(context)?.already_tag ?? + "You have already entered this tag"; } return null; }, @@ -619,10 +663,12 @@ class _EditEventState extends State onSubmitted: inputFieldValues.onTagSubmitted, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Tags', + labelText: + AppLocalizations.of(context)?.tag ?? 'Tags', hintText: inputFieldValues.tags.isNotEmpty ? '' - : "Enter tag...", + : AppLocalizations.of(context)?.enter_tag ?? + "Enter tag...", errorText: inputFieldValues.error, prefixIcon: inputFieldValues.tags.isNotEmpty ? SingleChildScrollView( @@ -699,7 +745,9 @@ class _EditEventState extends State textSeparators: const [','], validator: (String tag) { if (_stringOrgaController.getTags!.contains(tag)) { - return 'Cet organisateur est déjà rentré'; + return AppLocalizations.of(context) + ?.already_organiser ?? + "You have already entered this organizer"; } return null; }, @@ -714,10 +762,14 @@ class _EditEventState extends State onSubmitted: inputFieldValues.onTagSubmitted, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Organisateurs', + labelText: + AppLocalizations.of(context)?.organizer ?? + "Organizer", hintText: inputFieldValues.tags.isNotEmpty ? '' - : "Enter un organisateur...", + : AppLocalizations.of(context) + ?.enter_organizer ?? + "Enter a organizer", errorText: inputFieldValues.error, prefixIcon: inputFieldValues.tags.isNotEmpty ? SingleChildScrollView( @@ -798,8 +850,11 @@ class _EditEventState extends State maxLines: 10, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Description', - hintText: 'Décrire l\'evènement'), + labelText: AppLocalizations.of(context)?.description ?? + 'Description', + hintText: + AppLocalizations.of(context)?.describe_event ?? + 'Describe event'), ), ), SizedBox( @@ -818,7 +873,7 @@ class _EditEventState extends State } }, child: Text( - 'Ajouter', + AppLocalizations.of(context)?.add ?? 'Add', style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/lib/pages/EditProfile.dart b/covas_mobile/lib/pages/EditProfile.dart index c1aaea3..ff62847 100644 --- a/covas_mobile/lib/pages/EditProfile.dart +++ b/covas_mobile/lib/pages/EditProfile.dart @@ -18,6 +18,10 @@ import '../variable/globals.dart' as globals; import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -104,7 +108,11 @@ class _EditProfileState extends State if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) { if (password != confirmedPassword) { - showAlertDialog(context, "Erreur", "Mot de passe different"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.different_password_error ?? + "Different password"); return; } } @@ -131,24 +139,29 @@ class _EditProfileState extends State })); print(responsePut.statusCode); if (responsePut.statusCode == 200) { - showEventDialog(context, "Votre utilisateur a été modifié"); + showEventDialog(context, + AppLocalizations.of(context)?.user_update ?? "Your user updated"); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => EditProfile())); return; } - final errorMessages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Utilisateur désactivé", - 410: "Token invalide", - 500: "Problème interne du serveur", + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - - final text = errorMessages[responsePut.statusCode] ?? - "Problème d'authentification inconnu"; - showAlertDialog(context, "Erreur serveur", text); + final text = messages[responsePut.statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog( + context, AppLocalizations.of(context)?.error ?? "Error", text); } else { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => LoginDemo())); @@ -180,18 +193,22 @@ class _EditProfileState extends State return; } - final errorMessages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Utilisateur désactivé", - 410: "Token invalide", - 500: "Problème interne du serveur", + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - - final text = errorMessages[responseGet.statusCode] ?? - "Problème d'authentification inconnu"; - showAlertDialog(context, "Erreur serveur", text); + final text = messages[responseGet.statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog( + context, AppLocalizations.of(context)?.error ?? "Error", text); } else { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => LoginDemo())); @@ -213,7 +230,9 @@ class _EditProfileState extends State final _formKey = GlobalKey(); String? _validateField(String? value) { - return value!.isEmpty ? 'Champ requis' : null; + return value!.isEmpty + ? AppLocalizations.of(context)?.required_input ?? "Required input" + : null; } @override @@ -221,7 +240,8 @@ class _EditProfileState extends State return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Update profile"), + title: Text( + AppLocalizations.of(context)?.update_profile ?? "Update profile"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -246,8 +266,9 @@ class _EditProfileState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Pseudo', - hintText: 'Modifier le pseudo'), + labelText: AppLocalizations.of(context)?.name, + hintText: AppLocalizations.of(context)?.edit_pseudo ?? + "Edit pseudo"), ), ), Padding( @@ -259,8 +280,11 @@ class _EditProfileState extends State obscureText: true, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Mot de passe', - hintText: 'Entrez le mot de passe'), + labelText: AppLocalizations.of(context)?.password ?? + "Password", + hintText: + AppLocalizations.of(context)?.enter_password ?? + "Enter a password"), ), ), Padding( @@ -272,8 +296,12 @@ class _EditProfileState extends State obscureText: true, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Confirmez le mot de passe', - hintText: 'Confirmez le mot de passe'), + labelText: + AppLocalizations.of(context)?.password_confirmed ?? + "Must confirm password", + hintText: + AppLocalizations.of(context)?.password_confirmed ?? + "Must confirm password"), ), ), Padding( @@ -285,8 +313,11 @@ class _EditProfileState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Nom', - hintText: 'Modifier le nom'), + labelText: AppLocalizations.of(context)?.last_name ?? + "Last name", + hintText: + AppLocalizations.of(context)?.edit_last_name ?? + "Edit last name"), ), ), Padding( @@ -298,8 +329,11 @@ class _EditProfileState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Prénom', - hintText: 'Modifier le prénom'), + labelText: AppLocalizations.of(context)?.first_name ?? + "First name", + hintText: + AppLocalizations.of(context)?.edit_first_name ?? + "Edit first name"), ), ), Padding( @@ -311,8 +345,10 @@ class _EditProfileState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email', - hintText: 'Modifier l\'adresse mail'), + labelText: + AppLocalizations.of(context)?.email ?? "Email", + hintText: AppLocalizations.of(context)?.edit_email ?? + "Edit email"), ), ), Padding( @@ -325,8 +361,9 @@ class _EditProfileState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de naissance', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.birth_date, + hintText: AppLocalizations.of(context)?.edit_birth ?? + "Click to select a birth date"), onTap: () => onTapFunctionDatePicker(context: context)), ), SizedBox( @@ -345,7 +382,8 @@ class _EditProfileState extends State } }, child: Text( - 'Modifier le profil', + AppLocalizations.of(context)?.update_profile ?? + "Update profile ", style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/lib/pages/EditSettings.dart b/covas_mobile/lib/pages/EditSettings.dart index 0be4c14..c6e36f9 100644 --- a/covas_mobile/lib/pages/EditSettings.dart +++ b/covas_mobile/lib/pages/EditSettings.dart @@ -14,6 +14,11 @@ import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé + void main() async { WidgetsFlutterBinding.ensureInitialized(); await MobileAds.instance.initialize(); @@ -56,7 +61,10 @@ class _EditProfileState extends State SharedPreferences prefs = await SharedPreferences.getInstance(); if (kilometer != null) { prefs.setDouble("kilometer", kilometer?.toDouble() ?? 50); - showAlertDialog(context, "Update", "Mise à jour des paramètres"); + showAlertDialog( + context, + AppLocalizations.of(context)?.updated ?? "Updated", + AppLocalizations.of(context)?.settings_updated ?? "Settings updated"); } } @@ -78,7 +86,7 @@ class _EditProfileState extends State return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Settings"), + title: Text(AppLocalizations.of(context)?.settings ?? "Settings"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -103,7 +111,9 @@ class _EditProfileState extends State child: DropdownButtonFormField( decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Define kilometer', + labelText: + AppLocalizations.of(context)?.define_kilometer ?? + 'Define kilometer', ), value: kilometer, // Set the initial selected value here, or leave as `null` if unselected. @@ -147,7 +157,7 @@ class _EditProfileState extends State child: TextButton( onPressed: () {}, child: Text( - 'Mettre à jour', + AppLocalizations.of(context)?.update ?? "Update", style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/lib/pages/ForgotPassword.dart b/covas_mobile/lib/pages/ForgotPassword.dart index 8b57978..fb216c1 100644 --- a/covas_mobile/lib/pages/ForgotPassword.dart +++ b/covas_mobile/lib/pages/ForgotPassword.dart @@ -11,6 +11,11 @@ import '../classes/alert.dart'; import '../variable/globals.dart' as globals; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + void main() { runApp(MyApp()); } @@ -74,22 +79,32 @@ class _PasswordForgotState extends State with ShowAlertDialog { })); print(responsePost.statusCode); if (responsePost.statusCode == 200) { - showAlertDialog(context, "Creation", "Un email a été envoyé à ${email}"); + String message = + AppLocalizations.of(context)?.email_sent ?? "Email has been sent"; + showAlertDialog( + context, + AppLocalizations.of(context)?.create ?? "Creation", + "${message} : ${email}"); return; } - final errorMessages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Utilisateur désactivé", - 410: "Token invalide", - 500: "Problème interne du serveur", + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - final text = errorMessages[responsePost.statusCode] ?? - "Problème d'authentification inconnu"; - showAlertDialog(context, "Erreur serveur", text); + final text = messages[responsePost.statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog( + context, AppLocalizations.of(context)?.error ?? "Error", text); } @override @@ -107,7 +122,8 @@ class _PasswordForgotState extends State with ShowAlertDialog { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Mot de passe oublie"), + title: Text(AppLocalizations.of(context)?.forgot_password ?? + "Forgot password"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -125,8 +141,10 @@ class _PasswordForgotState extends State with ShowAlertDialog { validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Email', - hintText: 'Modifier l\'adresse mail'), + labelText: + AppLocalizations.of(context)?.email ?? 'Email', + hintText: AppLocalizations.of(context)?.enter_email ?? + 'Enter the email'), ), ), SizedBox( @@ -145,7 +163,7 @@ class _PasswordForgotState extends State with ShowAlertDialog { } }, child: Text( - 'Envoyer le mail', + AppLocalizations.of(context)?.send_email ?? 'Send email', style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/lib/pages/ItemMenu.dart b/covas_mobile/lib/pages/ItemMenu.dart index 8b18d0a..7451e50 100644 --- a/covas_mobile/lib/pages/ItemMenu.dart +++ b/covas_mobile/lib/pages/ItemMenu.dart @@ -25,6 +25,11 @@ import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + void main() async { WidgetsFlutterBinding.ensureInitialized(); await MobileAds.instance.initialize(); @@ -109,7 +114,8 @@ class _ItemMenuState extends State with ShowAlertDialog { final accessToken = prefs.getString("access_token") ?? ""; if (accessToken.isEmpty) { - showAlertDialog(context, "Erreur serveur", "Cache invalide"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache"); return; } @@ -119,27 +125,35 @@ class _ItemMenuState extends State with ShowAlertDialog { headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'}, ); - stderr.writeln('Response Get status: ${responseGet.statusCode}'); - if (responseGet.statusCode == 200) { final responseBody = utf8.decode(responseGet.bodyBytes); - stderr.writeln('Username : $responseBody'); final event = Events.fromJson(jsonDecode(responseBody)); - + final locale = Provider.of(context, listen: false) + .locale + ?.toString() ?? + 'en_US'; final startDate = DateTime.parse(event.startDate ?? DateTime.now().toString()); + //final date = DateFormat.yMd().format(startDate); + //final time = DateFormat.Hm().format(startDate); final endDate = DateTime.parse(event.endDate ?? DateTime.now().toString()); - + String separator = AppLocalizations.of(context)?.at ?? "at"; final formattedStartDate = - "${DateFormat.yMd().format(startDate)} ${DateFormat.Hm().format(startDate)}"; + DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale) + .format(startDate); + final formattedEndDate = - "${DateFormat.yMd().format(endDate)} ${DateFormat.Hm().format(endDate)}"; + DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale) + .format(endDate); + + String link = AppLocalizations.of(context)?.to_date ?? "to"; setState(() { eventName = event.name ?? ""; - eventStartDate = "$formattedStartDate à $formattedEndDate"; + + eventStartDate = "$formattedStartDate ${link} $formattedEndDate"; organizers = List.from(event.organizers ?? []); place = event.place ?? ""; imgUrl = event.imgUrl ?? ""; @@ -147,18 +161,23 @@ class _ItemMenuState extends State with ShowAlertDialog { tags = List.from(event.tags ?? []); }); } else { - final errorMessages = { - 400: "Requête mal construite", - 406: "Mot de passe incorrect", - 404: "Utilisateur inconnu", - 403: "Vous n'avez pas l'autorisation de faire cette action", - 410: "Token invalide", - 500: "Problème interne du serveur", + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - final errorMessage = errorMessages[responseGet.statusCode] ?? - "Problème d'authentification inconnu"; - showAlertDialog(context, "Erreur serveur", errorMessage); + final errorMessage = messages[responseGet.statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + errorMessage); } } @@ -223,7 +242,7 @@ class _ItemMenuState extends State with ShowAlertDialog { Row(children: [ Icon(Icons.event), Text( - "Date : ", + AppLocalizations.of(context)?.item_date ?? "Date : ", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold), ) ]), @@ -237,7 +256,7 @@ class _ItemMenuState extends State with ShowAlertDialog { Row(children: [ Icon(Icons.explore), Text( - "Carte : ", + AppLocalizations.of(context)?.item_maps ?? "Maps : ", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold), ) ]), @@ -260,7 +279,7 @@ class _ItemMenuState extends State with ShowAlertDialog { Row(children: [ Icon(Icons.group), Text( - "Organisateurs : ", + AppLocalizations.of(context)?.item_organizer ?? "Organizers : ", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold), ) ]), @@ -313,7 +332,9 @@ class _ItemMenuState extends State with ShowAlertDialog { ]), Row(children: [ Icon(Icons.description), - Text("Description : ", + Text( + AppLocalizations.of(context)?.item_description ?? + "Description : ", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold)) ]), Row(children: [ @@ -325,7 +346,7 @@ class _ItemMenuState extends State with ShowAlertDialog { ]), Row(children: [ Icon(Icons.category), - Text("Tags : ", + Text(AppLocalizations.of(context)?.item_tags ?? "Tags : ", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold)) ]), Row( @@ -394,7 +415,7 @@ class _ItemMenuState extends State with ShowAlertDialog { ); }, backgroundColor: Colors.blue, - tooltip: 'Recherche', + tooltip: AppLocalizations.of(context)?.search ?? 'Search', child: const Icon(Icons.edit, color: Colors.white), ), ); diff --git a/covas_mobile/lib/pages/ListItemByOrganizers.dart b/covas_mobile/lib/pages/ListItemByOrganizers.dart index 84ff5e8..e0bcf08 100644 --- a/covas_mobile/lib/pages/ListItemByOrganizers.dart +++ b/covas_mobile/lib/pages/ListItemByOrganizers.dart @@ -14,6 +14,11 @@ import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + // app starting point void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -76,7 +81,6 @@ class _MyHomePageState extends State { } Future _fetchData() async { - print("Counter : ${_fetchCount}"); if (_isLoading) return; setState(() { _isLoading = true; @@ -147,7 +151,8 @@ class _MyHomePageState extends State { return buildPosts(posts); } else { // if no data, show simple Text - return const Text("No data available"); + return Text( + AppLocalizations.of(context)?.no_data ?? "No data available"); } }, ), @@ -157,12 +162,14 @@ class _MyHomePageState extends State { // function to display fetched data on screen Widget buildPosts(List posts) { + String organizer = + AppLocalizations.of(context)?.item_organizer ?? "Organizer : "; // ListView Builder to show data in a list return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. - title: Text("Organisateur : ${widget.organizer}", + title: Text("${organizer}${widget.organizer}", overflow: TextOverflow.ellipsis), backgroundColor: Colors.blue, foregroundColor: Colors.white, @@ -176,8 +183,13 @@ class _MyHomePageState extends State { final post = posts[index]; final startDate = DateTime.parse(post.startDate!); + final locale = Provider.of(context, listen: false) + .locale + ?.toString() ?? + 'en_US'; + final dateLongue = - DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate); + DateFormat('EEEE d MMMM y', locale).format(startDate); return ListTile( title: Text('${post.name!}'), diff --git a/covas_mobile/lib/pages/ListItemByTags.dart b/covas_mobile/lib/pages/ListItemByTags.dart index 900e93d..97c8658 100644 --- a/covas_mobile/lib/pages/ListItemByTags.dart +++ b/covas_mobile/lib/pages/ListItemByTags.dart @@ -15,6 +15,11 @@ import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + // app starting point void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -150,7 +155,8 @@ class _MyHomePageState extends State { return buildPosts(posts); } else { // if no data, show simple Text - return const Text("No data available"); + return Text( + AppLocalizations.of(context)?.no_data ?? "No data available"); } }, ), @@ -161,11 +167,12 @@ class _MyHomePageState extends State { // function to display fetched data on screen Widget buildPosts(List posts) { // ListView Builder to show data in a list + String tag = AppLocalizations.of(context)?.item_tags ?? "Tags : "; return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. - title: Text("Tags : ${widget.tags}", overflow: TextOverflow.ellipsis), + title: Text("${tag}${widget.tags}", overflow: TextOverflow.ellipsis), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -177,8 +184,12 @@ class _MyHomePageState extends State { itemBuilder: (context, index) { final post = posts[index]; final startDate = DateTime.parse(post.startDate!); + final locale = Provider.of(context, listen: false) + .locale + ?.toString() ?? + 'en_US'; final dateLongue = - DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate); + DateFormat('EEEE d MMMM y', locale).format(startDate); return ListTile( title: Text('${post.name!}'), diff --git a/covas_mobile/lib/pages/ListItemMenu.dart b/covas_mobile/lib/pages/ListItemMenu.dart index 521816e..d518e9a 100644 --- a/covas_mobile/lib/pages/ListItemMenu.dart +++ b/covas_mobile/lib/pages/ListItemMenu.dart @@ -20,6 +20,9 @@ import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // Créé plus loin void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -27,7 +30,10 @@ void main() async { await MobileAds.instance.initialize(); await initializeDateFormatting("fr_FR", null); - runApp(const MyApp()); + runApp(ChangeNotifierProvider( + create: (_) => LocaleProvider(), + child: const MyApp(), + )); } class MyApp extends StatelessWidget { @@ -35,16 +41,17 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + final localeProvider = Provider.of(context); return MaterialApp( localizationsDelegates: [ + AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], - supportedLocales: [ - const Locale('fr', 'FR'), - ], - home: const ListItemMenu(), + supportedLocales: [const Locale('fr', 'FR'), const Locale('en')], + locale: localeProvider.locale, + home: Builder(builder: (context) => ListItemMenu()), debugShowCheckedModeBanner: false, ); } @@ -296,7 +303,6 @@ class _MyHomePageState extends State { } } catch (e) { fetchPostsByLocation(); - print("Error getting city and country: $e"); } } @@ -347,7 +353,8 @@ class _MyHomePageState extends State { } }); } else { - throw Exception('Failed to load suggestions'); + throw Exception(AppLocalizations.of(context)?.failed_suggestions ?? + 'Failed to load suggestions'); } } @@ -445,16 +452,13 @@ class _MyHomePageState extends State { "Content-Type": "application/json", HttpHeaders.cookieHeader: "access_token=$accessToken" }); - print("status code tags : ${response.statusCode}"); if (response.statusCode == 200) { final data = json.decode(utf8.decode(response.bodyBytes)); - print("tags ${data}"); setState(() { suggestionsTags = (data as List) .map((feature) => {'name': feature['name']}) .toList(); - print("suggesttion tag : ${suggestionsTags}"); if (suggestionsTags.isNotEmpty) { showInputGeo = false; showInputSearch = false; @@ -520,10 +524,10 @@ class _MyHomePageState extends State { Padding _buildDateField(String position) { TextEditingController datePicker = startDatepicker; - String hintText = "Date de début"; + String hintText = AppLocalizations.of(context)?.start_date ?? "Start date"; if (position == "end") { datePicker = endDatepicker; - hintText = "Date de fin"; + hintText = AppLocalizations.of(context)?.end_date ?? "End date"; } return Padding( padding: const EdgeInsets.all(8.0), @@ -608,9 +612,11 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final localeProvider = Provider.of(context); return Scaffold( appBar: AppBar( - title: const Text("Item list menu"), + title: Text(loc?.menu_list ?? "Item list menu"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -627,7 +633,7 @@ class _MyHomePageState extends State { if (showInputSearch) _buildSearchField( controller: inputItem, - labelText: 'Search by item', + labelText: loc?.search_item ?? "Search by item", onChanged: (value) { _fetchCount = 0; if (value.isNotEmpty) { @@ -673,7 +679,7 @@ class _MyHomePageState extends State { if ((showDateFields) && (showInputTag)) _buildSearchField( controller: inputTags, - labelText: 'Search by tags', + labelText: loc?.search_tag ?? "Search by tags", onChanged: (value) { _fetchCount = 0; if (value.isNotEmpty) { @@ -724,7 +730,8 @@ class _MyHomePageState extends State { if ((showDateFields) && (showInputGeo)) _buildSearchField( controller: inputGeo, - labelText: 'Search by geographical zone', + labelText: + loc?.search_geographical ?? 'Search by geographical zone', onChanged: (value) async { _fetchCount = 0; @@ -795,21 +802,26 @@ class _MyHomePageState extends State { : Icons.keyboard_arrow_down, color: Colors.blue, ), - tooltip: showDateFields ? 'Show Date Fields' : 'Hide Date Fields', + tooltip: showDateFields + ? loc?.show_date_field ?? 'Show Date Fields' + : loc?.hide_date_field ?? 'Hide Date Fields', ), Expanded( child: FutureBuilder>( future: postsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); + return Center(child: CircularProgressIndicator()); } else if (snapshot.hasData) { final posts = snapshot.data!; final displayedPosts = filteredPosts.isEmpty ? posts : filteredPosts; return buildPosts(displayedPosts); } else { - return const Text("No data available"); + return Center( + child: Text(AppLocalizations.of(context)?.no_data ?? + "No data available"), + ); } }, ), @@ -819,7 +831,7 @@ class _MyHomePageState extends State { floatingActionButton: FloatingActionButton( onPressed: popCamera, backgroundColor: Colors.blue, - tooltip: 'Recherche', + tooltip: loc?.search ?? 'Recherche', child: const Icon(Icons.photo_camera, color: Colors.white), ), ); @@ -827,14 +839,13 @@ class _MyHomePageState extends State { // Function to display fetched data on screen Widget buildPosts(List posts) { - print("posts : ${posts}"); - print("filteredposts : ${filteredPosts}"); final displayedPosts = filteredPosts; - print("results ${displayedPosts}"); // If filteredPosts is empty, show a message saying no data is available if (displayedPosts.isEmpty) { - return const Center( - child: Text('No events available for this location.', + return Center( + child: Text( + AppLocalizations.of(context)?.no_events ?? + 'No events available for this location.', style: TextStyle(fontSize: 18, color: Colors.grey)), ); } @@ -855,8 +866,11 @@ class _MyHomePageState extends State { final startDate = DateTime.parse(post.startDate!); //final date = DateFormat.yMd().format(startDate); //final time = DateFormat.Hm().format(startDate); + final locale = + Provider.of(context).locale?.toString() ?? + 'en_US'; final dateLongue = - DateFormat('EEEE d MMMM y', 'fr_FR').format(startDate); + DateFormat('EEEE d MMMM y', locale).format(startDate); return ListTile( title: Text('${post.name!}'), subtitle: Text('${post.place!}\n${dateLongue}'), diff --git a/covas_mobile/lib/pages/LoginDemo.dart b/covas_mobile/lib/pages/LoginDemo.dart index 070271c..d6dd224 100644 --- a/covas_mobile/lib/pages/LoginDemo.dart +++ b/covas_mobile/lib/pages/LoginDemo.dart @@ -8,6 +8,11 @@ import '../pages/ForgotPassword.dart'; import '../classes/alert.dart'; import '../classes/ad_helper.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + class LoginDemo extends StatefulWidget { @override _LoginDemoState createState() => _LoginDemoState(); @@ -25,7 +30,8 @@ class _LoginDemoState extends State with ShowAlertDialog { final password = inputPassword.text; if (pseudo.isEmpty || password.isEmpty) { - showAlertDialog(context, "Erreur", "Champ vide"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.empty_input ?? "Empty input"); return; } @@ -36,7 +42,8 @@ class _LoginDemoState extends State with ShowAlertDialog { Navigator.push( context, MaterialPageRoute(builder: (_) => ListItemMenu())); } else { - showAlertDialog(context, "Erreur", "Échec de l'authentification"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.failed_auth ?? "Authentication failed"); } } @@ -73,7 +80,7 @@ class _LoginDemoState extends State with ShowAlertDialog { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Login Page"), + title: Text(AppLocalizations.of(context)?.login_page ?? "Login Page"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -93,8 +100,10 @@ class _LoginDemoState extends State with ShowAlertDialog { controller: inputPseudo, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Pseudo', - hintText: 'Enter pseudo existant', + labelText: AppLocalizations.of(context)?.pseudo ?? 'Pseudo', + hintText: + AppLocalizations.of(context)?.enter_existing_pseudo ?? + 'Enter a existing pseudo', ), ), ), @@ -105,13 +114,16 @@ class _LoginDemoState extends State with ShowAlertDialog { obscureText: true, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Password', - hintText: 'Enter secure password', + labelText: + AppLocalizations.of(context)?.password ?? "Password", + hintText: AppLocalizations.of(context)?.enter_password ?? + "Enter the password", ), ), ), CheckboxListTile( - title: Text("Se souvenir de moi"), + title: Text( + AppLocalizations.of(context)?.remembr_me ?? "Remember me"), value: _rememberMe, onChanged: (newValue) { setState(() { @@ -124,7 +136,9 @@ class _LoginDemoState extends State with ShowAlertDialog { Navigator.push(context, MaterialPageRoute(builder: (_) => PasswordForgot())); }, - child: Text('Forgot Password', + child: Text( + AppLocalizations.of(context)?.forgot_password ?? + 'Forgot Password', style: TextStyle(color: Colors.blue, fontSize: 15)), ), Container( @@ -134,13 +148,14 @@ class _LoginDemoState extends State with ShowAlertDialog { color: Colors.blue, borderRadius: BorderRadius.circular(20)), child: TextButton( onPressed: () => _login(context), - child: Text('Login', + child: Text(AppLocalizations.of(context)?.sign_in ?? 'Sign in', style: TextStyle(color: Colors.white, fontSize: 25)), ), ), SizedBox(height: 130), InkWell( - child: Text('New User? Create Account'), + child: Text(AppLocalizations.of(context)?.new_user ?? + 'New User? Create Account'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (_) => AddProfile())); diff --git a/covas_mobile/lib/pages/MapboxPages.dart b/covas_mobile/lib/pages/MapboxPages.dart index 8e005e9..549bc96 100644 --- a/covas_mobile/lib/pages/MapboxPages.dart +++ b/covas_mobile/lib/pages/MapboxPages.dart @@ -13,6 +13,11 @@ import '../variable/globals.dart' as globals; import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + void main() async { await dotenv.load(fileName: ".env"); // Load .env file runApp(const MyApp()); @@ -70,7 +75,10 @@ class _MapboxPagesState extends State with ShowAlertDialog { mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; if (mapboxAccessToken.isEmpty) { showAlertDialog( - context, "Erreur Mapbox", "Mapbox Access Token is not available."); + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.map_token ?? + "Map Access Token is not available."); } } @@ -95,35 +103,29 @@ class _MapboxPagesState extends State with ShowAlertDialog { _handleErrorResponse(responseGet.statusCode); } } else { - showAlertDialog(context, "Erreur serveur", "Invalid cache."); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache."); } } void _handleErrorResponse(int statusCode) { - String text; - switch (statusCode) { - case 400: - text = "Bad Request."; - break; - case 406: - text = "Incorrect Password."; - break; - case 404: - text = "User Not Found."; - break; - case 403: - text = "Action not permitted."; - break; - case 410: - text = "Invalid Token."; - break; - case 500: - text = "Internal Server Error."; - break; - default: - text = "Unknown error."; - } - showAlertDialog(context, "Erreur serveur", text); + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" + }; + + final errorMessage = messages[statusCode] ?? + AppLocalizations.of(context)?.unknown_error_auth ?? + "Unknown error auth"; + showAlertDialog( + context, AppLocalizations.of(context)?.error ?? "Error", errorMessage); } Future _getUserLocation() async { @@ -131,7 +133,10 @@ class _MapboxPagesState extends State with ShowAlertDialog { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { showAlertDialog( - context, "Erreur de service", "Location services are disabled."); + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.geo_disabled ?? + "Location services are disabled."); return; } @@ -139,15 +144,21 @@ class _MapboxPagesState extends State with ShowAlertDialog { if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { - showAlertDialog(context, "Erreur de permission", - "Location permissions are denied."); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.permission_denied ?? + "Location permissions are denied."); return; } } if (permission == LocationPermission.deniedForever) { - showAlertDialog(context, "Erreur de permission", - "Location permissions are permanently denied. Enable them in settings."); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.enable_permission ?? + "Location permissions are permanently denied. Enable them in settings."); return; } const LocationSettings locationSettings = LocationSettings( @@ -158,17 +169,23 @@ class _MapboxPagesState extends State with ShowAlertDialog { locationSettings: locationSettings); } on LocationServiceDisabledException { // Handle location services disabled - print('Location services are disabled.'); position = await Geolocator.getLastKnownPosition(); if (position == null) { - print('No last known position available.'); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.no_last_position ?? + "No last known position available.."); } } catch (e) { // Handle other errors - print('Failed to get location: $e'); position = await Geolocator.getLastKnownPosition(); if (position == null) { - print('No last known position available.'); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.no_last_position ?? + "No last known position available"); } } if (position != null) { @@ -180,7 +197,11 @@ class _MapboxPagesState extends State with ShowAlertDialog { _initToken(); _getEventInfo(); } catch (e) { - showAlertDialog(context, "Erreur geo", "Failed to get user location: $e"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.failed_location ?? + "Failed to get user location"); } } @@ -200,16 +221,17 @@ class _MapboxPagesState extends State with ShowAlertDialog { }).toList(); }); } else { - showAlertDialog(context, "Erreur serveur", - "Failed to fetch the route: ${response.statusCode}"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.failed_fetch ?? + "Failed to fetch the route"); } } // Called when the map is created void _onStyleLoaded() async { // Log the map controller and coordinates - print("Mapbox controller initialized: $mapController"); - print("lat - long : $latitude - $longitude"); // Check if the mapController is really initialized if (mapController != null) { @@ -229,20 +251,30 @@ class _MapboxPagesState extends State with ShowAlertDialog { ); // Debugging symbol options - print("Adding symbol with options: $symbolOptions"); // Add symbol to map mapController!.addSymbol(symbolOptions); } else { - print("Error: Invalid coordinates, cannot add symbol."); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.invalid_coordinates_symbol ?? + "Error: Invalid coordinates, cannot add symbol."); } } catch (e) { // Handle any exception that occurs when adding the symbol - print("Error when adding symbol: $e"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.error_symbol ?? + "Error when adding symbol."); } } else { - print( - "Error: MapboxMapController is null at the time of symbol addition"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.error_symbol ?? + "Error when adding symbol."); } } @@ -253,8 +285,11 @@ class _MapboxPagesState extends State with ShowAlertDialog { currentRouteLine = null; } if (!isUserPositionInitialized) { - showAlertDialog(context, "Erreur de position", - "User position is not yet initialized. Try again."); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.position_not_init ?? + "User position is not yet initialized. Try again."); return; } @@ -293,8 +328,11 @@ class _MapboxPagesState extends State with ShowAlertDialog { _zoomToFitRoute(routeCoordinates); } } else { - showAlertDialog(context, "Erreur de coordonée", - "Invalid coordinates or user position."); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.invalid_coordinates ?? + "Invalid coordinates or user position."); } } @@ -348,7 +386,7 @@ class _MapboxPagesState extends State with ShowAlertDialog { children: [ Icon(Icons.directions_walk, color: Colors.blue), SizedBox(width: 8), - Text('Walking'), + Text(AppLocalizations.of(context)?.walking ?? 'Walking'), ], ), ), @@ -358,7 +396,7 @@ class _MapboxPagesState extends State with ShowAlertDialog { children: [ Icon(Icons.directions_bike, color: Colors.green), SizedBox(width: 8), - Text('Cycling'), + Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'), ], ), ), @@ -368,7 +406,7 @@ class _MapboxPagesState extends State with ShowAlertDialog { children: [ Icon(Icons.directions_car, color: Colors.red), SizedBox(width: 8), - Text('Driving'), + Text(AppLocalizations.of(context)?.driving ?? 'Driving'), ], ), ), @@ -403,7 +441,8 @@ class _MapboxPagesState extends State with ShowAlertDialog { child: FloatingActionButton( onPressed: _drawRouteAndMarkers, child: Icon(Icons.directions), - tooltip: 'Get Directions and Markers', + tooltip: AppLocalizations.of(context)?.get_direction ?? + 'Get Directions and Markers', ), ), ], diff --git a/covas_mobile/lib/pages/UpdateEventImage.dart b/covas_mobile/lib/pages/UpdateEventImage.dart index 2d46d2c..0a08e25 100644 --- a/covas_mobile/lib/pages/UpdateEventImage.dart +++ b/covas_mobile/lib/pages/UpdateEventImage.dart @@ -20,6 +20,11 @@ import '../classes/ad_helper.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../classes/auth_service.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import '../locale_provider.dart'; // + void main() async { WidgetsFlutterBinding.ensureInitialized(); await MobileAds.instance.initialize(); @@ -165,7 +170,8 @@ class _UpdateeventImageState extends State var endDate = "${endDateFormat}T${endTimepicker.text.replaceAll('-', ':')}"; if (!startDateCompare.isAfter(dateNow)) { - showAlertDialog(context, "Erreur evenement", "Evenement non futur"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.no_future_event ?? "No future event"); return; } @@ -173,7 +179,11 @@ class _UpdateeventImageState extends State var accessToken = prefs.getString("access_token") ?? ""; if (accessToken.isEmpty) { - showAlertDialog(context, "Erreur token", "Token d'accès manquant"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.missing_token ?? + "Missing access token"); return; } @@ -188,14 +198,18 @@ class _UpdateeventImageState extends State final searchboxResponse = await http.get(searchboxUrl); if (searchboxResponse.statusCode != 200) { - showAlertDialog(context, "Erreur map", - "Erreur lors de la géocodage avec Searchbox"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.geocoding_error ?? + "Error when geocoding"); return; } final searchboxData = json.decode(searchboxResponse.body); if (searchboxData['results'].isEmpty) { - showAlertDialog(context, "Erreur", "Lieu introuvable"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.no_found_place ?? "No found place"); return; } @@ -240,7 +254,10 @@ class _UpdateeventImageState extends State if (imgbbResponse.statusCode != 200) { showAlertDialog( - context, "Erreur serveur", "Erreur lors de l'upload d'image"); + context, + AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.upload_error ?? + "Error when image uploading"); return; } @@ -271,27 +288,37 @@ class _UpdateeventImageState extends State ); if (eventResponse.statusCode == 200 || eventResponse.statusCode == 201) { - showEventDialog(context, "Événement $name ajouté"); + String event_message = + AppLocalizations.of(context)?.event_added ?? "Event added"; + showEventDialog(context, "$event_message : $name"); } else { handleHttpError(eventResponse.statusCode, context); } } catch (e) { - showAlertDialog(context, "Erreur", "Erreur: ${e.toString()}"); + showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", + AppLocalizations.of(context)?.app_error ?? "Error application"); } } // Utility function to handle HTTP errors void handleHttpError(int statusCode, BuildContext context) { - final errorMessages = { - 400: "Requête mal construite", - 403: "Utilisateur désactivé", - 404: "Utilisateur inconnu", - 406: "Mot de passe incorrect", - 410: "Token invalide", - 500: "Problème interne du serveur", + final messages = { + 400: AppLocalizations.of(context)?.request_error ?? + "Poorly constructed query", + 406: AppLocalizations.of(context)?.incorrect_password ?? + "Incorrect password", + 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", + 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", + 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", + 500: AppLocalizations.of(context)?.internal_error_server ?? + "Internal error server" }; - showAlertDialog(context, "Erreur serveur", - errorMessages[statusCode] ?? "Erreur inconnue"); + showAlertDialog( + context, + AppLocalizations.of(context)?.error ?? "Error", + messages[statusCode] ?? + AppLocalizations.of(context)?.unknown_error ?? + "Unknown error"); } void start() async { @@ -330,7 +357,9 @@ class _UpdateeventImageState extends State final _formKey = GlobalKey(); String? _validateField(String? value) { - return value!.isEmpty ? 'Champ requis' : null; + return value!.isEmpty + ? AppLocalizations.of(context)?.required_input ?? 'Required input' + : null; } Future searchSuggestions(String input) async { @@ -344,11 +373,9 @@ class _UpdateeventImageState extends State // Perform the request final response = await http.get(searchboxUrl); - print("response code suggestion: ${response.statusCode}"); if (response.statusCode == 200) { final data = json.decode(response.body); - print("data suggestion: ${data}"); setState(() { // Map the results to extract name and full_address @@ -374,7 +401,7 @@ class _UpdateeventImageState extends State TextField( controller: inputGeo, decoration: InputDecoration( - labelText: 'Lieu', + labelText: AppLocalizations.of(context)?.location ?? 'Location', border: OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(Icons.clear), @@ -432,7 +459,8 @@ class _UpdateeventImageState extends State return Scaffold( backgroundColor: Colors.white, appBar: AppBar( - title: Text("Add or Update a event"), + title: Text(AppLocalizations.of(context)?.add_event ?? + "Add or Update a event"), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), @@ -467,8 +495,10 @@ class _UpdateeventImageState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Nom', - hintText: 'Modifier le nom de l\'évènement'), + labelText: AppLocalizations.of(context)?.name ?? "Name", + hintText: + AppLocalizations.of(context)?.edit_event_name ?? + "Edit event name"), ), ), _buildGeographicalZoneSearchField(), @@ -482,8 +512,10 @@ class _UpdateeventImageState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de debut', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.start_date ?? + "Start date", + hintText: AppLocalizations.of(context)?.select_date ?? + "Click to select a date"), onTap: () => onTapFunctionDatePicker( context: context, position: "start")), ), @@ -497,8 +529,10 @@ class _UpdateeventImageState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Heure de debut', - hintText: 'Cliquez ici pour selectionner une heure'), + labelText: AppLocalizations.of(context)?.start_time ?? + "Start time", + hintText: AppLocalizations.of(context)?.select_time ?? + "Click to select a time"), onTap: () => onTapFunctionTimePicker( context: context, position: "start")), ), @@ -512,8 +546,10 @@ class _UpdateeventImageState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Date de fin', - hintText: 'Cliquez ici pour selectionner une date'), + labelText: AppLocalizations.of(context)?.end_date ?? + "End date", + hintText: AppLocalizations.of(context)?.select_date ?? + "Click to select a date"), onTap: () => onTapFunctionDatePicker( context: context, position: "end")), ), @@ -527,8 +563,10 @@ class _UpdateeventImageState extends State validator: (value) => _validateField(value), decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Heure de fin', - hintText: 'Cliquez ici pour selectionner une heure'), + labelText: AppLocalizations.of(context)?.end_time ?? + "End time", + hintText: AppLocalizations.of(context)?.select_time ?? + "Click to select a time"), onTap: () => onTapFunctionTimePicker( context: context, position: "end")), ), @@ -538,7 +576,8 @@ class _UpdateeventImageState extends State textSeparators: const [' ', ','], validator: (String tag) { if (_stringTagController.getTags!.contains(tag)) { - return 'Tu as deja rentre ce tag'; + return AppLocalizations.of(context)?.already_tag ?? + 'You have already entered this tag'; } return null; }, @@ -553,10 +592,12 @@ class _UpdateeventImageState extends State onSubmitted: inputFieldValues.onTagSubmitted, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Tags', + labelText: + AppLocalizations.of(context)?.tag ?? 'Tags', hintText: inputFieldValues.tags.isNotEmpty ? '' - : "Enter tag...", + : AppLocalizations.of(context)?.enter_tag ?? + "Enter tag...", errorText: inputFieldValues.error, prefixIcon: inputFieldValues.tags.isNotEmpty ? SingleChildScrollView( @@ -633,7 +674,9 @@ class _UpdateeventImageState extends State textSeparators: const [','], validator: (String tag) { if (_stringOrgaController.getTags!.contains(tag)) { - return 'Cet organisateur est déjà rentré'; + return AppLocalizations.of(context) + ?.already_organiser ?? + 'You have already entered this organizer'; } return null; }, @@ -648,10 +691,14 @@ class _UpdateeventImageState extends State onSubmitted: inputFieldValues.onTagSubmitted, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Organisateurs', + labelText: + AppLocalizations.of(context)?.organizer ?? + 'Organizers', hintText: inputFieldValues.tags.isNotEmpty ? '' - : "Enter un organisateur...", + : AppLocalizations.of(context) + ?.enter_organizer ?? + "Enter un organisateur...", errorText: inputFieldValues.error, prefixIcon: inputFieldValues.tags.isNotEmpty ? SingleChildScrollView( @@ -732,8 +779,11 @@ class _UpdateeventImageState extends State maxLines: 10, decoration: InputDecoration( border: OutlineInputBorder(), - labelText: 'Description', - hintText: 'Décrire l\'evènement'), + labelText: AppLocalizations.of(context)?.description ?? + 'Description', + hintText: + AppLocalizations.of(context)?.describe_event ?? + 'Describe the event'), ), ), SizedBox( @@ -752,7 +802,7 @@ class _UpdateeventImageState extends State } }, child: Text( - 'Ajouter', + AppLocalizations.of(context)?.add_event ?? 'Add', style: TextStyle(color: Colors.white, fontSize: 25), ), ), diff --git a/covas_mobile/pubspec.lock b/covas_mobile/pubspec.lock index 292dc71..311394d 100644 --- a/covas_mobile/pubspec.lock +++ b/covas_mobile/pubspec.lock @@ -565,6 +565,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: "direct main" description: @@ -701,6 +709,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" shared_preferences: dependency: "direct main" description: diff --git a/covas_mobile/pubspec.yaml b/covas_mobile/pubspec.yaml index 6e0737c..614bc53 100644 --- a/covas_mobile/pubspec.yaml +++ b/covas_mobile/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: mapbox_gl: ^0.16.0 google_mobile_ads: ^5.3.1 encrypt_shared_preferences: ^0.8.8 + provider: ^6.1.2 # ou la dernière version dev_dependencies: flutter_test: