From aa881161230e9269ef9529030e69e5983ef01eb8 Mon Sep 17 00:00:00 2001 From: Frederik Benoist Date: Wed, 1 Nov 2023 19:11:03 +0100 Subject: [PATCH] refactor: major changes I18n --- assets/lang/ar.json | 3 -- assets/lang/en.json | 15 +++++-- assets/lang/fr.json | 14 ++++++- assets/lang/hi.json | 3 -- assets/lang/id.json | 3 -- assets/lang/th.json | 3 -- assets/lang/tk.json | 3 -- assets/lang/zh.json | 3 -- lib/cubit/language/app_localizations.dart | 22 ++++------ lib/events.dart | 9 +++++ lib/main.dart | 11 +---- lib/model/visit_model.dart | 8 ++-- lib/service/shared_prefs.dart | 21 ++++++++++ lib/ui/account/about.dart | 3 +- lib/ui/account/log.dart | 3 +- lib/ui/account/settings.dart | 26 +++++++++--- lib/ui/account/tab_account.dart | 24 ++++++++--- lib/ui/home.dart | 3 +- lib/ui/home/tab_home.dart | 49 +++++++++++------------ pubspec.yaml | 8 +--- 20 files changed, 136 insertions(+), 98 deletions(-) delete mode 100644 assets/lang/ar.json delete mode 100644 assets/lang/hi.json delete mode 100644 assets/lang/id.json delete mode 100644 assets/lang/th.json delete mode 100644 assets/lang/tk.json delete mode 100644 assets/lang/zh.json diff --git a/assets/lang/ar.json b/assets/lang/ar.json deleted file mode 100644 index 4c8063b..0000000 --- a/assets/lang/ar.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "مرحبًا ، سيتغير هذا النص وفقًا للغة" -} \ No newline at end of file diff --git a/assets/lang/en.json b/assets/lang/en.json index 0534439..4f9ec1e 100644 --- a/assets/lang/en.json +++ b/assets/lang/en.json @@ -1,5 +1,14 @@ { - "text_description": "Hello, this text will change according to the language", - "i18n_hello" : "Hello", - "i18n_take_pictures" : "Take pictures" + "i18n_label_hello" : "Hello", + "i18n_take_pictures" : "Take pictures", + "i18n_menu_settings" : "Settings", + "i18n_menu_about" : "About", + "i18n_menu_show_logs" : "Show logs", + "i18n_menu_check_version" : "Check version", + "i18n_menu_account" : "Account", + "i18n_label_sign_out" : "Sign Out", + "i18n_label_photo_quality" : "Photo quality", + "i18n_label_photo_resizing" : "Photo resizing", + "i18n_label_sound_photo" : "Play sound when taking photo", + "i18n_label_language" : "Language" } \ No newline at end of file diff --git a/assets/lang/fr.json b/assets/lang/fr.json index c3ba4db..8b2d444 100644 --- a/assets/lang/fr.json +++ b/assets/lang/fr.json @@ -1,4 +1,14 @@ { - "i18n_hello": "Bonjour", - "i18n_take_pictures" : "Prendre des photos" + "i18n_label_hello": "Bonjour", + "i18n_take_pictures" : "Prendre des photos", + "i18n_menu_settings" : "Paramètres", + "i18n_menu_about" : "A propos", + "i18n_menu_show_logs" : "Voir logs", + "i18n_menu_check_version" : "Vérifier version", + "i18n_menu_account" : "Profil", + "i18n_label_sign_out" : "Déconnecter", + "i18n_label_photo_quality" : "Qualité photo", + "i18n_label_photo_resizing" : "Redimensionnement photo", + "i18n_label_sound_photo" : "Jouer son quand prise photo", + "i18n_label_language" : "Langage" } \ No newline at end of file diff --git a/assets/lang/hi.json b/assets/lang/hi.json deleted file mode 100644 index 542ad7d..0000000 --- a/assets/lang/hi.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "नमस्कार, यह पाठ भाषा के अनुसार बदल जाएगा" -} \ No newline at end of file diff --git a/assets/lang/id.json b/assets/lang/id.json deleted file mode 100644 index 9e68159..0000000 --- a/assets/lang/id.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "Halo, teks ini akan berubah menurut bahasanya" -} \ No newline at end of file diff --git a/assets/lang/th.json b/assets/lang/th.json deleted file mode 100644 index 0eca8e5..0000000 --- a/assets/lang/th.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "สวัสดีข้อความนี้จะเปลี่ยนไปตามภาษา" -} \ No newline at end of file diff --git a/assets/lang/tk.json b/assets/lang/tk.json deleted file mode 100644 index 745b286..0000000 --- a/assets/lang/tk.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "Merhaba, bu yazı dile göre değişecek" -} \ No newline at end of file diff --git a/assets/lang/zh.json b/assets/lang/zh.json deleted file mode 100644 index 4823aff..0000000 --- a/assets/lang/zh.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text_description": "您好,该文字会根据语言而变化" -} \ No newline at end of file diff --git a/lib/cubit/language/app_localizations.dart b/lib/cubit/language/app_localizations.dart index c41d7c7..ffd7fcf 100644 --- a/lib/cubit/language/app_localizations.dart +++ b/lib/cubit/language/app_localizations.dart @@ -9,7 +9,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:mobdr/service/shared_prefs.dart'; class AppLocalizations { final Locale locale; @@ -36,8 +36,9 @@ class AppLocalizations { } // This method will be called from every widget which needs a localized text - String? translate(String key) { - return _localizedStrings[key]; + String translate(String key) { + final translatedString = _localizedStrings[key]; + return translatedString ?? 'I18N:' + key; } } @@ -47,24 +48,17 @@ class AppLocalizationsDelegate extends LocalizationsDelegate { @override bool isSupported(Locale locale) { // Include all of your supported language codes here - return ['fr', 'en', 'id', 'ar', 'zh', 'hi', 'th', 'tk'] - .contains(locale.languageCode); + return ['fr', 'en'].contains(locale.languageCode); } @override Future load(Locale locale) async { - final SharedPreferences _pref = await SharedPreferences.getInstance(); - String? lCode = _pref.getString('lCode'); - String? cCode = _pref.getString('cCode'); + locale = SharedPrefs().language; - if (lCode == null || cCode == null) { - await _pref.setString('lCode', locale.languageCode); - await _pref.setString('cCode', locale.countryCode!); - } else { - locale = Locale(lCode, cCode); - } AppLocalizations localizations = new AppLocalizations(locale); + await localizations.load(); + return localizations; } diff --git a/lib/events.dart b/lib/events.dart index 6fca52e..f8ebff7 100644 --- a/lib/events.dart +++ b/lib/events.dart @@ -36,3 +36,12 @@ class SynchronizationEvent extends AppEvent { @override List get props => [isRunning]; } + +class ChangeLocaleEvent extends AppEvent { + ChangeLocaleEvent(this.language); + + final String language; + + @override + List get props => [language]; +} diff --git a/lib/main.dart b/lib/main.dart index ad1ef16..be26e98 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -165,16 +165,7 @@ class MyApp extends StatelessWidget with WidgetsBindingObserver { }), ), // below is used for language feature - supportedLocales: [ - Locale('fr', 'FR'), - Locale('en', 'US'), - Locale('id', 'ID'), - Locale('ar', 'DZ'), - Locale('zh', 'HK'), - Locale('hi', 'IN'), - Locale('th', 'TH'), - Locale('tk', 'TK'), - ], + supportedLocales: [Locale('fr', 'FR'), Locale('en', 'US')], // These delegates make sure that the localization data for the proper language is loaded localizationsDelegates: [ AppLocalizationsDelegate(), diff --git a/lib/model/visit_model.dart b/lib/model/visit_model.dart index a9bc951..6434eb2 100644 --- a/lib/model/visit_model.dart +++ b/lib/model/visit_model.dart @@ -1,5 +1,6 @@ import 'package:intl/intl.dart'; import 'package:mobdr/main.dart'; +import 'package:mobdr/service/shared_prefs.dart'; class VisitModel { late int id; @@ -31,7 +32,6 @@ class VisitModel { static Future> getTodayVisits() async { // Retrieve all today visits from the database final visits = await objectbox.getTodayVisit(); - // Map each retrieved visit to VisiteModel final visitModelList = visits .map((visite) => VisitModel( @@ -41,7 +41,7 @@ class VisitModel { id_visite: visite.id_visite, name: visite.id_etab.toString() + ' - ' + visite.title, photoCount: objectbox.getVisitPhotoTaken(visite.id_visite), - date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR') + date: DateFormat('EEEE d MMMM HH:mm', SharedPrefs().locale) .format(visite.date_visite), image: visite.url_photo_principale, type_visite: visite.type_visite, @@ -66,7 +66,7 @@ class VisitModel { id_visite: visite.id_visite, name: visite.id_etab.toString() + ' - ' + visite.title, photoCount: objectbox.getVisitPhotoTaken(visite.id_visite), - date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR') + date: DateFormat('EEEE d MMMM HH:mm', SharedPrefs().locale) .format(visite.date_visite), image: visite.url_photo_principale, type_visite: visite.type_visite, @@ -91,7 +91,7 @@ class VisitModel { id_visite: visite.id_visite, name: visite.id_etab.toString() + ' - ' + visite.title, photoCount: objectbox.getVisitPhotoTaken(visite.id_visite), - date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR') + date: DateFormat('EEEE d MMMM HH:mm', SharedPrefs().locale) .format(visite.date_visite), image: visite.url_photo_principale, type_visite: visite.type_visite, diff --git a/lib/service/shared_prefs.dart b/lib/service/shared_prefs.dart index 0f563c5..8e10f19 100644 --- a/lib/service/shared_prefs.dart +++ b/lib/service/shared_prefs.dart @@ -1,4 +1,5 @@ import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/material.dart'; class SharedPrefs { static late SharedPreferences _sharedPrefs; @@ -241,4 +242,24 @@ class SharedPrefs { set systemVersion(String value) { _sharedPrefs.setString('key_system_version', value); } + + /// get/set language code + String get lCode => _sharedPrefs.getString('lcode') ?? "fr"; + + set lCode(String value) { + _sharedPrefs.setString('lcode', value); + } + + /// get/set language country code + String get cCode => _sharedPrefs.getString('ccode') ?? "FR"; + + set cCode(String value) { + _sharedPrefs.setString('ccode', value); + } + + /// get language object + Locale get language => Locale(lCode, cCode); + + /// get locale object + String get locale => lCode + "_" + cCode; } diff --git a/lib/ui/account/about.dart b/lib/ui/account/about.dart index 0ad8848..96fa80d 100644 --- a/lib/ui/account/about.dart +++ b/lib/ui/account/about.dart @@ -1,3 +1,4 @@ +import 'package:mobdr/cubit/language/app_localizations.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/service/shared_prefs.dart'; @@ -27,7 +28,7 @@ class _AboutPageState extends State { ), elevation: GlobalStyle.appBarElevation, title: Text( - 'About', + AppLocalizations.of(context)!.translate('i18n_menu_about'), style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, diff --git a/lib/ui/account/log.dart b/lib/ui/account/log.dart index 7664101..581d2d8 100644 --- a/lib/ui/account/log.dart +++ b/lib/ui/account/log.dart @@ -6,6 +6,7 @@ import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/db/box_log.dart'; import 'package:mobdr/service/plausible.dart'; import 'package:mobdr/service/logger_util.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; class LogPage extends StatefulWidget { @override @@ -123,7 +124,7 @@ class _LogPageState extends State { ), elevation: GlobalStyle.appBarElevation, title: Text( - 'Show logs', + AppLocalizations.of(context)!.translate('i18n_menu_show_logs'), style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, diff --git a/lib/ui/account/settings.dart b/lib/ui/account/settings.dart index 5ecdf9e..fe1ac00 100644 --- a/lib/ui/account/settings.dart +++ b/lib/ui/account/settings.dart @@ -2,10 +2,13 @@ import 'package:flutter/material.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/main.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/service/plausible.dart'; import 'package:mobdr/service/logger_util.dart'; +import 'package:mobdr/events.dart'; class SettingsPage extends StatefulWidget { @override @@ -63,7 +66,7 @@ class _SettingsPageState extends State { ), elevation: GlobalStyle.appBarElevation, title: Text( - 'Settings', + AppLocalizations.of(context)!.translate('i18n_menu_settings'), style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, @@ -100,7 +103,9 @@ class _SettingsPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('Photo quality', + Text( + AppLocalizations.of(context)! + .translate('i18n_label_photo_quality'), style: TextStyle(fontSize: 15, color: CHARCOAL)), Row( children: [ @@ -127,7 +132,8 @@ class _SettingsPageState extends State { child: SwitchListTile( contentPadding: EdgeInsets.zero, title: Text( - 'Photo resizing', + AppLocalizations.of(context)! + .translate('i18n_label_photo_resizing'), style: TextStyle(fontSize: 15, color: CHARCOAL), ), value: _photoResizing, @@ -146,7 +152,8 @@ class _SettingsPageState extends State { child: SwitchListTile( contentPadding: EdgeInsets.zero, title: Text( - 'Play sound when taking a photo', + AppLocalizations.of(context)! + .translate('i18n_label_sound_photo'), style: TextStyle(fontSize: 15, color: CHARCOAL), ), value: _photoSound, @@ -183,7 +190,10 @@ class _SettingsPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('Language', style: TextStyle(fontSize: 15, color: CHARCOAL)), + Text( + AppLocalizations.of(context)! + .translate('i18n_label_language'), + style: TextStyle(fontSize: 15, color: CHARCOAL)), Row( children: [ Text( @@ -253,7 +263,8 @@ class _SettingsPageState extends State { onChanged: (String? value) { setState(() { _photoQuality = value!; - SharedPrefs().photoQuality = value; + SharedPrefs().photoQuality = + value; // TODO : Passer par un numéro plutot qu'une chaine qui sera traduite ... }); Navigator.pop(context); }, @@ -309,12 +320,15 @@ class _SettingsPageState extends State { onChanged: (String? value) { setState(() { _language = value!; + switch (value) { case 'English': SharedPrefs().langage = 'en'; + eventBus.fire(ChangeLocaleEvent('en')); break; case 'French': SharedPrefs().langage = 'fr'; + eventBus.fire(ChangeLocaleEvent('fr')); break; default: _language = 'French'; diff --git a/lib/ui/account/tab_account.dart b/lib/ui/account/tab_account.dart index fae6cf8..671ad32 100644 --- a/lib/ui/account/tab_account.dart +++ b/lib/ui/account/tab_account.dart @@ -16,6 +16,7 @@ import 'package:flutter/material.dart'; import 'package:mobdr/ui/authentication/signin.dart'; import 'dart:convert'; import 'package:mobdr/main.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; class TabAccountPage extends StatefulWidget { @override @@ -51,7 +52,7 @@ class _TabAccountPageState extends State ), elevation: GlobalStyle.appBarElevation, title: Text( - 'Account', + AppLocalizations.of(context)!.translate('i18n_menu_account'), style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, @@ -73,13 +74,22 @@ class _TabAccountPageState extends State padding: EdgeInsets.all(16), children: [ _createAccountInformation(), - _createListMenu('Settings', SettingsPage()), + _createListMenu( + AppLocalizations.of(context)!.translate('i18n_menu_settings'), + SettingsPage()), _reusableWidget.divider1(), - _createListMenu('About', AboutPage()), + _createListMenu( + AppLocalizations.of(context)!.translate('i18n_menu_about'), + AboutPage()), _reusableWidget.divider1(), - _createListMenu('Show logs', LogPage()), + _createListMenu( + AppLocalizations.of(context)!.translate('i18n_menu_show_logs'), + LogPage()), _reusableWidget.divider1(), - _createListMenu('Check version', UpdateCheckPage()), + _createListMenu( + AppLocalizations.of(context)! + .translate('i18n_menu_check_version'), + UpdateCheckPage()), _reusableWidget.divider1(), Container( margin: EdgeInsets.fromLTRB(0, 18, 0, 0), @@ -111,7 +121,9 @@ class _TabAccountPageState extends State Icon(Icons.power_settings_new, size: 20, color: ASSENT_COLOR), SizedBox(width: 8), - Text('Sign Out', + Text( + AppLocalizations.of(context)! + .translate('i18n_label_sign_out'), style: TextStyle(fontSize: 15, color: ASSENT_COLOR)), ], ), diff --git a/lib/ui/home.dart b/lib/ui/home.dart index 78ec950..2799ee8 100644 --- a/lib/ui/home.dart +++ b/lib/ui/home.dart @@ -11,6 +11,7 @@ import 'package:mobdr/config/constant.dart'; import 'package:mobdr/events.dart'; import 'package:mobdr/service/plausible.dart'; import 'package:mobdr/service/logger_util.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; class HomePage extends StatefulWidget { @override @@ -144,7 +145,7 @@ class _HomePageState extends State icon: Icon(Icons.web), ), BottomNavigationBarItem( - label: 'Account', + label: AppLocalizations.of(context)!.translate('i18n_menu_account'), icon: Icon(Icons.person_outline), ), ], diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index a808e09..673a86e 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -8,7 +8,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:badges/badges.dart' as badges; import 'package:mobdr/main.dart'; @@ -39,13 +38,12 @@ class _TabHomePageState extends State late StreamSubscription subVisitPhotoCountEvent; late StreamSubscription subRefreshCalendarEvent; + late StreamSubscription subChangeLocaleEvent; // keep the state to do not refresh when switch navbar @override bool get wantKeepAlive => true; - String defaultLang = 'en'; - DateTime _todayDate = DateTime.now(); late LanguageCubit _languageCubit; @@ -60,12 +58,6 @@ class _TabHomePageState extends State _languageCubit = BlocProvider.of(context); - _getLocale().then((val) { - setState(() { - defaultLang = val!; - }); - }); - loadData().then((_) { setState(() { // Listen refresh photo count event @@ -97,6 +89,26 @@ class _TabHomePageState extends State loadData(); }); }); + + // Listen change locale event + subChangeLocaleEvent = eventBus.on().listen((e) { + setState(() { + switch (e.language) { + case 'en': + SharedPrefs().lCode = 'en'; + SharedPrefs().cCode = 'US'; + _languageCubit.changeLanguage(SharedPrefs().language); + break; + case 'fr': + SharedPrefs().lCode = 'fr'; + SharedPrefs().cCode = 'FR'; + _languageCubit.changeLanguage(SharedPrefs().language); + break; + } + + loadData(); + }); + }); }); } @@ -104,30 +116,17 @@ class _TabHomePageState extends State void dispose() { subRefreshCalendarEvent.cancel(); subVisitPhotoCountEvent.cancel(); + subChangeLocaleEvent.cancel(); super.dispose(); } - Future _getLocale() async { - final SharedPreferences _pref = await SharedPreferences.getInstance(); - String? lCode = _pref.getString('lCode'); - return lCode; - } - - void changeLocale(Locale locale) async { - final SharedPreferences _pref = await SharedPreferences.getInstance(); - await _pref.setString('lCode', locale.languageCode); - await _pref.setString('cCode', locale.countryCode!); - _languageCubit.changeLanguage(locale); - defaultLang = locale.languageCode; - } - @override Widget build(BuildContext context) { // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); super.build(context); final double boxImageSize = (MediaQuery.of(context).size.width / 3); final formattedDate = - DateFormat('EEEE d MMMM y', 'fr_FR').format(_todayDate); + DateFormat('EEEE d MMMM y', SharedPrefs().locale).format(_todayDate); return Scaffold( appBar: AppBar( @@ -136,7 +135,7 @@ class _TabHomePageState extends State title: Align( alignment: Alignment.centerLeft, child: Text( - AppLocalizations.of(context)!.translate('i18n_hello')! + + AppLocalizations.of(context)!.translate('i18n_label_hello') + ', ${SharedPrefs().prenom}', style: GlobalStyle.appBarTitle, ), diff --git a/pubspec.yaml b/pubspec.yaml index 26ca20c..0542ba9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -206,11 +206,5 @@ flutter: - assets/lang/fr.json - assets/lang/en.json - - assets/lang/id.json - - assets/lang/ar.json - - assets/lang/zh.json - - assets/lang/hi.json - - assets/lang/th.json - - assets/lang/tk.json - + - assets/sounds/camera-shutter-click.mp3 \ No newline at end of file