refactor: major changes I18n

feature/issue-4/translation
Frédérik Benoist 2023-11-01 19:11:03 +01:00
parent 01e53128ee
commit aa88116123
20 changed files with 136 additions and 98 deletions

View File

@ -1,3 +0,0 @@
{
"text_description": "مرحبًا ، سيتغير هذا النص وفقًا للغة"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -1,3 +0,0 @@
{
"text_description": "नमस्कार, यह पाठ भाषा के अनुसार बदल जाएगा"
}

View File

@ -1,3 +0,0 @@
{
"text_description": "Halo, teks ini akan berubah menurut bahasanya"
}

View File

@ -1,3 +0,0 @@
{
"text_description": "สวัสดีข้อความนี้จะเปลี่ยนไปตามภาษา"
}

View File

@ -1,3 +0,0 @@
{
"text_description": "Merhaba, bu yazı dile göre değişecek"
}

View File

@ -1,3 +0,0 @@
{
"text_description": "您好,该文字会根据语言而变化"
}

View File

@ -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<AppLocalizations> {
@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<AppLocalizations> 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;
}

View File

@ -36,3 +36,12 @@ class SynchronizationEvent extends AppEvent {
@override
List<Object?> get props => [isRunning];
}
class ChangeLocaleEvent extends AppEvent {
ChangeLocaleEvent(this.language);
final String language;
@override
List<Object?> get props => [language];
}

View File

@ -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(),

View File

@ -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<List<VisitModel>> 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,

View File

@ -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;
}

View File

@ -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<AboutPage> {
),
elevation: GlobalStyle.appBarElevation,
title: Text(
'About',
AppLocalizations.of(context)!.translate('i18n_menu_about'),
style: GlobalStyle.appBarTitle,
),
backgroundColor: GlobalStyle.appBarBackgroundColor,

View File

@ -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<LogPage> {
),
elevation: GlobalStyle.appBarElevation,
title: Text(
'Show logs',
AppLocalizations.of(context)!.translate('i18n_menu_show_logs'),
style: GlobalStyle.appBarTitle,
),
backgroundColor: GlobalStyle.appBarBackgroundColor,

View File

@ -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<SettingsPage> {
),
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<SettingsPage> {
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<SettingsPage> {
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<SettingsPage> {
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<SettingsPage> {
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<SettingsPage> {
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<SettingsPage> {
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';

View File

@ -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<TabAccountPage>
),
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<TabAccountPage>
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<TabAccountPage>
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)),
],
),

View File

@ -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<HomePage>
icon: Icon(Icons.web),
),
BottomNavigationBarItem(
label: 'Account',
label: AppLocalizations.of(context)!.translate('i18n_menu_account'),
icon: Icon(Icons.person_outline),
),
],

View File

@ -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<TabHomePage>
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<TabHomePage>
_languageCubit = BlocProvider.of<LanguageCubit>(context);
_getLocale().then((val) {
setState(() {
defaultLang = val!;
});
});
loadData().then((_) {
setState(() {
// Listen refresh photo count event
@ -97,6 +89,26 @@ class _TabHomePageState extends State<TabHomePage>
loadData();
});
});
// Listen change locale event
subChangeLocaleEvent = eventBus.on<ChangeLocaleEvent>().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<TabHomePage>
void dispose() {
subRefreshCalendarEvent.cancel();
subVisitPhotoCountEvent.cancel();
subChangeLocaleEvent.cancel();
super.dispose();
}
Future<String?> _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<TabHomePage>
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,
),

View File

@ -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