diff --git a/lib/core/constants/my_constants.dart b/lib/core/constants/my_constants.dart index d113b52..315960b 100644 --- a/lib/core/constants/my_constants.dart +++ b/lib/core/constants/my_constants.dart @@ -1,8 +1,13 @@ +import 'dart:ui'; + +import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart'; + class MyConstants { static const MyConstants _i = MyConstants._internal(); const MyConstants._internal(); factory MyConstants() => _i; + /// Shared Preferences static const String token = 'TOKEN'; static const String theme = 'THEME'; static const String levelBox = 'LEVEL_BOX'; @@ -17,5 +22,15 @@ class MyConstants { static const String effectAudioService = 'EFFECT_AUDIO_SERVICE'; static const String firstIntro = 'FIRST_INTRO'; static const String firstShowcase = 'FIRST_SHOWCASE'; + + /// Other static const double mainAudioVolume = 0.3; + static const String defaultLanguage = 'en'; + static const List languages = [ + LanguageEntity(title: 'English (English)', code: 'en', locale: Locale('en','US')), + LanguageEntity(title: 'French (Français)', code: 'fr', locale: Locale('fr','FR')), + LanguageEntity(title: 'Russian (Русский)', code: 'ru', locale: Locale('ru','RU')), + LanguageEntity(title: 'Turkish (Türkçe)', code: 'tr', locale: Locale('tr','TR')), + LanguageEntity(title: 'Arabic (العربية)', code: 'ar', locale: Locale('ar','AE')), + ]; } \ No newline at end of file diff --git a/lib/features/app/presentation/bloc/app_bloc.dart b/lib/features/app/presentation/bloc/app_bloc.dart new file mode 100644 index 0000000..def780c --- /dev/null +++ b/lib/features/app/presentation/bloc/app_bloc.dart @@ -0,0 +1,40 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_state.dart'; +import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart'; + +class AppBloc extends Bloc { + /// ------------constructor------------ + AppBloc() : super(const AppState()) { + on(_initLocaleEvent); + on(_changeLocaleEvent); + } + + /// ------------UseCases------------ + + /// ------------Variables------------ + + /// ------------Controllers------------ + + /// ------------Functions------------ + + + /// ------------Event Calls------------ + FutureOr _initLocaleEvent(InitLocaleEvent event, + Emitter emit) { + final String selectLanguage = LocalStorage.readData(key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; + final LanguageEntity findLanguage = MyConstants.languages.singleWhere( + (e) => e.code == selectLanguage, + ); + emit(state.copyWith(locale: findLanguage.locale)); + } + + FutureOr _changeLocaleEvent(ChangeLocaleEvent event, + Emitter emit) { + emit(state.copyWith(locale: event.locale)); + } +} diff --git a/lib/features/app/presentation/bloc/app_event.dart b/lib/features/app/presentation/bloc/app_event.dart new file mode 100644 index 0000000..e17c4f4 --- /dev/null +++ b/lib/features/app/presentation/bloc/app_event.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +sealed class AppEvent { + const AppEvent(); +} + +class InitLocaleEvent extends AppEvent {} + +class ChangeLocaleEvent extends AppEvent { + final Locale locale; + const ChangeLocaleEvent(this.locale); +} diff --git a/lib/features/app/presentation/bloc/app_state.dart b/lib/features/app/presentation/bloc/app_state.dart new file mode 100644 index 0000000..0b4a664 --- /dev/null +++ b/lib/features/app/presentation/bloc/app_state.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class AppState { + final Locale locale; + + const AppState({this.locale = const Locale('en', 'US')}); + + AppState copyWith({Locale? locale}) { + return AppState(locale: locale ?? this.locale); + } +} diff --git a/lib/features/download/data/datasource/download_datasource.dart b/lib/features/download/data/datasource/download_datasource.dart index 5c3ba31..587574d 100644 --- a/lib/features/download/data/datasource/download_datasource.dart +++ b/lib/features/download/data/datasource/download_datasource.dart @@ -80,7 +80,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { Future getAudios() async { final String filePath = '${StoragePath.documentDir.path}/audios.zip'; final String selectedLanguage = - LocalStorage.readData(key: MyConstants.selectLanguage) ?? 'fa'; + LocalStorage.readData(key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; if(LocalStorage.readData(key: MyConstants.downloadedAudio) != 'true'){ await httpRequest.download( @@ -134,7 +134,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { @override Future saveLevels() async { final String selectedLanguage = - LocalStorage.readData(key: MyConstants.selectLanguage) ?? 'fa'; + LocalStorage.readData(key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; final Box data = Hive.box(MyConstants.levelBox); final TotalDataEntity findData = data.values.singleWhere( (e) => e.code == selectedLanguage, diff --git a/lib/features/language/domain/entity/language_entity.dart b/lib/features/language/domain/entity/language_entity.dart index 2e76d7a..a60b57e 100644 --- a/lib/features/language/domain/entity/language_entity.dart +++ b/lib/features/language/domain/entity/language_entity.dart @@ -1,17 +1,21 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; class LanguageEntity extends Equatable { final String? title; final String? code; + final Locale? locale; const LanguageEntity({ this.title, this.code, + this.locale, }); @override List get props => [ title, code, + locale, ]; } diff --git a/lib/features/language/presentation/bloc/language_bloc.dart b/lib/features/language/presentation/bloc/language_bloc.dart index 5a19460..9dc85cb 100644 --- a/lib/features/language/presentation/bloc/language_bloc.dart +++ b/lib/features/language/presentation/bloc/language_bloc.dart @@ -2,15 +2,19 @@ import 'dart:async'; import 'dart:io'; import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart'; import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_event.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart'; +import 'package:hadi_hoda_flutter/init_bindings.dart'; class LanguageBloc extends Bloc { /// ------------constructor------------ @@ -23,11 +27,6 @@ class LanguageBloc extends Bloc { /// ------------UseCases------------ /// ------------Variables------------ - final List languages = [ - LanguageEntity(title: 'Persian (فارسی)', code: 'fa'), - LanguageEntity(title: 'English (English)', code: 'en'), - LanguageEntity(title: 'Arabic (عربی)', code: 'ar'), - ]; /// ------------Controllers------------ @@ -47,9 +46,12 @@ class LanguageBloc extends Bloc { await Future.wait([ LocalStorage.saveData( key: MyConstants.selectLanguage, - value: state.selectedLang.code ?? 'fa', + value: state.selectedLang.code ?? MyConstants.defaultLanguage, ), ]); + AppBloc appBloc = locator(); + appBloc.add( + ChangeLocaleEvent(state.selectedLang.locale ?? Locale('en', 'US'))); if (Directory( '${StoragePath.documentDir.path}/${state.selectedLang @@ -74,7 +76,7 @@ class LanguageBloc extends Bloc { FutureOr _initLanguageEvent(InitLanguageEvent event, Emitter emit) { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage) ?? 'fa'; + key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; emit(state.copyWith(selectedLang: LanguageEntity(code: selectedLanguage))); } } diff --git a/lib/features/language/presentation/bloc/language_state.dart b/lib/features/language/presentation/bloc/language_state.dart index 44d40bb..994f9ce 100644 --- a/lib/features/language/presentation/bloc/language_state.dart +++ b/lib/features/language/presentation/bloc/language_state.dart @@ -1,3 +1,4 @@ +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/status/base_status.dart'; import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart'; @@ -7,7 +8,7 @@ class LanguageState { const LanguageState({ this.saveLevelsStatus = const BaseInit(), - this.selectedLang = const LanguageEntity(code: 'fa'), + this.selectedLang = const LanguageEntity(code: MyConstants.defaultLanguage), }); LanguageState copyWith({ diff --git a/lib/features/language/presentation/ui/language_page.dart b/lib/features/language/presentation/ui/language_page.dart index 345b972..9165123 100644 --- a/lib/features/language/presentation/ui/language_page.dart +++ b/lib/features/language/presentation/ui/language_page.dart @@ -4,6 +4,7 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; @@ -75,7 +76,7 @@ class LanguagePage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( - context.read().languages.length, + MyConstants.languages.length, (index) => BlocBuilder( buildWhen: (previous, current) => @@ -84,11 +85,11 @@ class LanguagePage extends StatelessWidget { final LanguageBloc languageBloc = context.read(); return LanguageWidget( selected: state.selectedLang.code == - languageBloc.languages[index].code, + MyConstants.languages[index].code, onTap: () { - languageBloc.add(ChangeLanguageEvent(languageBloc.languages[index])); + languageBloc.add(ChangeLanguageEvent(MyConstants.languages[index])); }, - title: context.read().languages[index].title, + title: MyConstants.languages[index].title, ); } ), diff --git a/lib/features/level/data/datasource/level_datasource.dart b/lib/features/level/data/datasource/level_datasource.dart index a8fb58b..64cdcf9 100644 --- a/lib/features/level/data/datasource/level_datasource.dart +++ b/lib/features/level/data/datasource/level_datasource.dart @@ -18,7 +18,7 @@ class LocalLevelDatasourceImpl implements ILevelDatasource { Future> getLevels({required LevelParams params}) async { try { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage) ?? 'fa'; + key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; final Box levelBox = Hive.box(MyConstants.levelBox); final TotalDataEntity findData = levelBox.values.singleWhere( (e) => e.code == selectedLanguage, diff --git a/lib/features/question/data/datasource/question_datasource.dart b/lib/features/question/data/datasource/question_datasource.dart index c5e8b37..831f844 100644 --- a/lib/features/question/data/datasource/question_datasource.dart +++ b/lib/features/question/data/datasource/question_datasource.dart @@ -19,7 +19,7 @@ class QuestionDatasourceImpl implements IQuestionDatasource { Future getLevel({required QuestionParams params}) async { try { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage) ?? 'fa'; + key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; final Box levelBox = Hive.box(MyConstants.levelBox); final TotalDataEntity findData = levelBox.values.singleWhere( (e) => e.code == selectedLanguage, @@ -39,7 +39,7 @@ class QuestionDatasourceImpl implements IQuestionDatasource { Future getNextLevel({required QuestionParams params}) async { try { final String selectedLanguage = LocalStorage.readData( - key: MyConstants.selectLanguage) ?? 'fa'; + key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; final int index = int.parse( LocalStorage.readData(key: MyConstants.currentLevel) ?? '1'); final Box levelBox = Hive.box(MyConstants.levelBox); diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index 24c6e01..289dc8c 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -163,7 +163,6 @@ class QuestionBloc extends Bloc { Future getNextLevelEvent({required BuildContext context}) async { await _getNextLevelUseCase(QuestionParams()).then((value) => value.fold( (data) { - print(data.id); context.pushNamed( Routes.questionPage, pathParameters: {'id': '${data.id}'}, diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart index 5436dee..2feda58 100644 --- a/lib/features/question/presentation/ui/question_page.dart +++ b/lib/features/question/presentation/ui/question_page.dart @@ -22,52 +22,55 @@ class QuestionPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: MyPopScope( - child: Container( - height: context.heightScreen, - width: context.widthScreen, - padding: EdgeInsets.symmetric( - horizontal: setSize(context: context, - mobile: MySpaces.s16, - tablet: MySpaces.s30) ?? 0, - ), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0XFF6930DA), Color(0XFF263AA1)], + child: Directionality( + textDirection: TextDirection.ltr, + child: Container( + height: context.heightScreen, + width: context.widthScreen, + padding: EdgeInsets.symmetric( + horizontal: setSize(context: context, + mobile: MySpaces.s16, + tablet: MySpaces.s30) ?? 0, ), - image: DecorationImage( - image: AssetImage(MyAssets.pattern), - scale: 3, - repeat: ImageRepeat.repeat, - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.3), - BlendMode.srcIn, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0XFF6930DA), Color(0XFF263AA1)], ), - ), - ), - child: Column( - children: [ - setPlatform(android: MySpaces.s20, iOS: 50)?.gapHeight ?? SizedBox.shrink(), - _topButtons(context), - MySpaces.s10.gapHeight, - Expanded( - child: BlocBuilder( - buildWhen: (previous, current) => - (previous.currentQuestion?.order != - current.currentQuestion?.order), - builder: (context, state) { - if (state.currentQuestion?.order == - state.levelEntity?.questions?.length) { - return DiamondScreen(); - } else { - return QuestionScreen(); - } - }, + image: DecorationImage( + image: AssetImage(MyAssets.pattern), + scale: 3, + repeat: ImageRepeat.repeat, + colorFilter: ColorFilter.mode( + Colors.black.withValues(alpha: 0.3), + BlendMode.srcIn, ), ), - setPlatform(android: MySpaces.s20,)?.gapHeight ?? SizedBox.shrink(), - ], + ), + child: Column( + children: [ + setPlatform(android: MySpaces.s20, iOS: 50)?.gapHeight ?? SizedBox.shrink(), + _topButtons(context), + MySpaces.s10.gapHeight, + Expanded( + child: BlocBuilder( + buildWhen: (previous, current) => + (previous.currentQuestion?.order != + current.currentQuestion?.order), + builder: (context, state) { + if (state.currentQuestion?.order == + state.levelEntity?.questions?.length) { + return DiamondScreen(); + } else { + return QuestionScreen(); + } + }, + ), + ), + setPlatform(android: MySpaces.s20,)?.gapHeight ?? SizedBox.shrink(), + ], + ), ), ), ), diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index f81c487..2690c2f 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -5,6 +5,7 @@ import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request_impl.dart'; import 'package:hadi_hoda_flutter/core/services/audio_service.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart'; import 'package:hadi_hoda_flutter/features/download/data/datasource/download_datasource.dart'; import 'package:hadi_hoda_flutter/features/download/data/repository_impl/download_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; @@ -47,6 +48,7 @@ void initBindings() { AudioService(), instanceName: MyConstants.effectAudioService, ); + locator.registerSingleton(AppBloc()); /// Sample Feature locator.registerLazySingleton(() => SampleDatasourceImpl(locator())); diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb new file mode 100644 index 0000000..eac3c2f --- /dev/null +++ b/lib/l10n/app_ar.arb @@ -0,0 +1,39 @@ +{ + "@@locale": "ar", + "about_us": "معلومات عنا", + "about_us_desc": "تجمع Rive بين أداة تصميم تفاعلية، وتنسيق رسومي جديد يعتمد على الحالة، وبيئة تشغيل خفيفة متعددة المنصات، ومحرّك عرض متجهي سريع للغاية.\nتجعل هذه المنظومة المتكاملة الواجهات تنبض بالحياة من خلال الحركة، وتمنح المصممين والمطورين الأدوات اللازمة للابتكار.", + "select_language": "اختر اللغة", + "select": "اختيار", + "please_wait": "يرجى الانتظار قليلاً...", + "downloading_data": "جارٍ تنزيل البيانات الأولية", + "lost_connection": "فُقد الاتصال!", + "retry": "إعادة المحاولة", + "connected_to_internet": "يجب أن تكون متصلاً بالإنترنت لتنزيل بيانات اللعبة الأولية.", + "start": "ابدأ", + "step": "خطوة", + "question": "سؤال", + "be_cureful": "كن أكثر\nحذرًا.", + "wrong_answer": "إجابتك\nكانت غير صحيحة.", + "you_got_diamond": "لقد حصلت على الماسة", + "map": "الخريطة", + "next": "التالي", + "you_win": "لقد فزت!", + "skip": "تخطي", + "intro_1_1": "العشاء جاهز! أسرعوا وتعالوا واغسلوا أيديكم الجميلة!", + "intro_1_2": "أمي! أيدينا ليست متسخة كثيرًا! سنمسحها بمنديل فقط!", + "intro_2": "نقاء الإيمان...\nيعني أن النظافة من علامات الإيمان!", + "intro_3": "هذه الأعمال الطيبة تجعل أرواحنا قوية وجميلة!", + "intro_4": "هل ترغب في السفر إلى الحديقة الموعودة؟", + "intro_5": "نعممم...\nنحن جاهزون!", + "want_to_exit": "هل تريد الخروج؟", + "exit_dialog_desc": "عد مرة أخرى أيها البطل!\nالمغامرة لم تنتهِ بعد!", + "cancel": "إلغاء", + "exit": "خروج", + "play": "العب", + "no_hadith": "لا يوجد حديث لهذا السؤال", + "showcase_answer": "اضغط على الخيار الصحيح\nلتحديده.", + "showcase_notif": "سيقوم الراوي بقراءة\nالإجابات والخيارات لك.", + "showcase_stepper": "هنا سترى الأسئلة الخاصة\nبهذه المرحلة للوصول\nإلى الماسة.", + "showcase_hadith": "اطّلع على المصادر والأحاديث\nلهذا السؤال", + "showcase_guide": "هذا دليل سيساعدك\nفي رحلتك." +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 47737c9..cdb4691 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,4 +1,5 @@ { + "@@locale": "en", "about_us": "About us", "about_us_desc" : "Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.", "select_language": "Select language", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb new file mode 100644 index 0000000..8c008a6 --- /dev/null +++ b/lib/l10n/app_fr.arb @@ -0,0 +1,39 @@ +{ + "@@locale": "fr", + "about_us": "À propos de nous", + "about_us_desc": "Rive combine un outil de conception interactif, un nouveau format graphique avec état, un environnement d’exécution multiplateforme léger et un moteur de rendu vectoriel ultra-rapide. \nCette chaîne complète donne vie aux interfaces grâce au mouvement et offre aux concepteurs et développeurs les outils nécessaires pour créer.", + "select_language": "Choisissez la langue", + "select": "Sélectionner", + "please_wait": "Veuillez patienter quelques instants...", + "downloading_data": "Téléchargement des données initiales", + "lost_connection": "Connexion perdue !", + "retry": "Réessayer", + "connected_to_internet": "Vous devez être connecté à Internet pour télécharger les données initiales du jeu.", + "start": "Commencer", + "step": "Étape", + "question": "Question", + "be_cureful": "Sois plus\nprudent.", + "wrong_answer": "Ta réponse\nn’était pas correcte.", + "you_got_diamond": "Tu as obtenu le diamant", + "map": "Carte", + "next": "Suivant", + "you_win": "Tu as gagné !", + "skip": "Passer", + "intro_1_1": "Le dîner est prêt ! Venez vite et lavez vos jolies mains !", + "intro_1_2": "Maman ! Nos mains ne sont pas si sales ! On va juste les essuyer avec un mouchoir !", + "intro_2": "La pureté de la foi...\nCela signifie que la propreté est un signe de foi !", + "intro_3": "Ces bonnes actions rendent notre âme forte et belle !", + "intro_4": "Veux-tu voyager vers le Jardin Promis ?", + "intro_5": "Ouuuiii...\nNous sommes prêts !", + "want_to_exit": "Tu veux quitter ?", + "exit_dialog_desc": "Reviens, héros !\nL’aventure n’est pas encore finie !", + "cancel": "Annuler", + "exit": "Quitter", + "play": "JOUER", + "no_hadith": "Il n’y a aucun hadith pour cette question", + "showcase_answer": "Appuie sur la bonne\noption pour la choisir.", + "showcase_notif": "Le narrateur lira\nles réponses des\noptions pour toi.", + "showcase_stepper": "Ici, tu verras les\nquestions de cette\nétape pour atteindre\nle diamant.", + "showcase_hadith": "Consulte les sources et\nles hadiths pour cette\nquestion", + "showcase_guide": "Ceci est un guide\nqui t’aidera." +} diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 6606dd0..6b4956f 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -5,7 +5,11 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; +import 'app_localizations_ar.dart'; import 'app_localizations_en.dart'; +import 'app_localizations_fr.dart'; +import 'app_localizations_ru.dart'; +import 'app_localizations_tr.dart'; // ignore_for_file: type=lint @@ -92,7 +96,13 @@ abstract class AppLocalizations { ]; /// A list of this localizations delegate's supported locales. - static const List supportedLocales = [Locale('en')]; + static const List supportedLocales = [ + Locale('ar'), + Locale('en'), + Locale('fr'), + Locale('ru'), + Locale('tr'), + ]; /// No description provided for @about_us. /// @@ -322,7 +332,7 @@ class _AppLocalizationsDelegate @override bool isSupported(Locale locale) => - ['en'].contains(locale.languageCode); + ['ar', 'en', 'fr', 'ru', 'tr'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; @@ -331,8 +341,16 @@ class _AppLocalizationsDelegate AppLocalizations lookupAppLocalizations(Locale locale) { // Lookup logic when only language code is specified. switch (locale.languageCode) { + case 'ar': + return AppLocalizationsAr(); case 'en': return AppLocalizationsEn(); + case 'fr': + return AppLocalizationsFr(); + case 'ru': + return AppLocalizationsRu(); + case 'tr': + return AppLocalizationsTr(); } throw FlutterError( diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart new file mode 100644 index 0000000..fdbe46b --- /dev/null +++ b/lib/l10n/app_localizations_ar.dart @@ -0,0 +1,122 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Arabic (`ar`). +class AppLocalizationsAr extends AppLocalizations { + AppLocalizationsAr([String locale = 'ar']) : super(locale); + + @override + String get about_us => 'معلومات عنا'; + + @override + String get about_us_desc => + 'تجمع Rive بين أداة تصميم تفاعلية، وتنسيق رسومي جديد يعتمد على الحالة، وبيئة تشغيل خفيفة متعددة المنصات، ومحرّك عرض متجهي سريع للغاية.\nتجعل هذه المنظومة المتكاملة الواجهات تنبض بالحياة من خلال الحركة، وتمنح المصممين والمطورين الأدوات اللازمة للابتكار.'; + + @override + String get select_language => 'اختر اللغة'; + + @override + String get select => 'اختيار'; + + @override + String get please_wait => 'يرجى الانتظار قليلاً...'; + + @override + String get downloading_data => 'جارٍ تنزيل البيانات الأولية'; + + @override + String get lost_connection => 'فُقد الاتصال!'; + + @override + String get retry => 'إعادة المحاولة'; + + @override + String get connected_to_internet => + 'يجب أن تكون متصلاً بالإنترنت لتنزيل بيانات اللعبة الأولية.'; + + @override + String get start => 'ابدأ'; + + @override + String get step => 'خطوة'; + + @override + String get question => 'سؤال'; + + @override + String get be_cureful => 'كن أكثر\nحذرًا.'; + + @override + String get wrong_answer => 'إجابتك\nكانت غير صحيحة.'; + + @override + String get you_got_diamond => 'لقد حصلت على الماسة'; + + @override + String get map => 'الخريطة'; + + @override + String get next => 'التالي'; + + @override + String get you_win => 'لقد فزت!'; + + @override + String get skip => 'تخطي'; + + @override + String get intro_1_1 => 'العشاء جاهز! أسرعوا وتعالوا واغسلوا أيديكم الجميلة!'; + + @override + String get intro_1_2 => 'أمي! أيدينا ليست متسخة كثيرًا! سنمسحها بمنديل فقط!'; + + @override + String get intro_2 => 'نقاء الإيمان...\nيعني أن النظافة من علامات الإيمان!'; + + @override + String get intro_3 => 'هذه الأعمال الطيبة تجعل أرواحنا قوية وجميلة!'; + + @override + String get intro_4 => 'هل ترغب في السفر إلى الحديقة الموعودة؟'; + + @override + String get intro_5 => 'نعممم...\nنحن جاهزون!'; + + @override + String get want_to_exit => 'هل تريد الخروج؟'; + + @override + String get exit_dialog_desc => + 'عد مرة أخرى أيها البطل!\nالمغامرة لم تنتهِ بعد!'; + + @override + String get cancel => 'إلغاء'; + + @override + String get exit => 'خروج'; + + @override + String get play => 'العب'; + + @override + String get no_hadith => 'لا يوجد حديث لهذا السؤال'; + + @override + String get showcase_answer => 'اضغط على الخيار الصحيح\nلتحديده.'; + + @override + String get showcase_notif => 'سيقوم الراوي بقراءة\nالإجابات والخيارات لك.'; + + @override + String get showcase_stepper => + 'هنا سترى الأسئلة الخاصة\nبهذه المرحلة للوصول\nإلى الماسة.'; + + @override + String get showcase_hadith => 'اطّلع على المصادر والأحاديث\nلهذا السؤال'; + + @override + String get showcase_guide => 'هذا دليل سيساعدك\nفي رحلتك.'; +} diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart new file mode 100644 index 0000000..f870a7e --- /dev/null +++ b/lib/l10n/app_localizations_fr.dart @@ -0,0 +1,127 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for French (`fr`). +class AppLocalizationsFr extends AppLocalizations { + AppLocalizationsFr([String locale = 'fr']) : super(locale); + + @override + String get about_us => 'À propos de nous'; + + @override + String get about_us_desc => + 'Rive combine un outil de conception interactif, un nouveau format graphique avec état, un environnement d’exécution multiplateforme léger et un moteur de rendu vectoriel ultra-rapide. \nCette chaîne complète donne vie aux interfaces grâce au mouvement et offre aux concepteurs et développeurs les outils nécessaires pour créer.'; + + @override + String get select_language => 'Choisissez la langue'; + + @override + String get select => 'Sélectionner'; + + @override + String get please_wait => 'Veuillez patienter quelques instants...'; + + @override + String get downloading_data => 'Téléchargement des données initiales'; + + @override + String get lost_connection => 'Connexion perdue !'; + + @override + String get retry => 'Réessayer'; + + @override + String get connected_to_internet => + 'Vous devez être connecté à Internet pour télécharger les données initiales du jeu.'; + + @override + String get start => 'Commencer'; + + @override + String get step => 'Étape'; + + @override + String get question => 'Question'; + + @override + String get be_cureful => 'Sois plus\nprudent.'; + + @override + String get wrong_answer => 'Ta réponse\nn’était pas correcte.'; + + @override + String get you_got_diamond => 'Tu as obtenu le diamant'; + + @override + String get map => 'Carte'; + + @override + String get next => 'Suivant'; + + @override + String get you_win => 'Tu as gagné !'; + + @override + String get skip => 'Passer'; + + @override + String get intro_1_1 => + 'Le dîner est prêt ! Venez vite et lavez vos jolies mains !'; + + @override + String get intro_1_2 => + 'Maman ! Nos mains ne sont pas si sales ! On va juste les essuyer avec un mouchoir !'; + + @override + String get intro_2 => + 'La pureté de la foi...\nCela signifie que la propreté est un signe de foi !'; + + @override + String get intro_3 => 'Ces bonnes actions rendent notre âme forte et belle !'; + + @override + String get intro_4 => 'Veux-tu voyager vers le Jardin Promis ?'; + + @override + String get intro_5 => 'Ouuuiii...\nNous sommes prêts !'; + + @override + String get want_to_exit => 'Tu veux quitter ?'; + + @override + String get exit_dialog_desc => + 'Reviens, héros !\nL’aventure n’est pas encore finie !'; + + @override + String get cancel => 'Annuler'; + + @override + String get exit => 'Quitter'; + + @override + String get play => 'JOUER'; + + @override + String get no_hadith => 'Il n’y a aucun hadith pour cette question'; + + @override + String get showcase_answer => 'Appuie sur la bonne\noption pour la choisir.'; + + @override + String get showcase_notif => + 'Le narrateur lira\nles réponses des\noptions pour toi.'; + + @override + String get showcase_stepper => + 'Ici, tu verras les\nquestions de cette\nétape pour atteindre\nle diamant.'; + + @override + String get showcase_hadith => + 'Consulte les sources et\nles hadiths pour cette\nquestion'; + + @override + String get showcase_guide => 'Ceci est un guide\nqui t’aidera.'; +} diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart new file mode 100644 index 0000000..dbd16a8 --- /dev/null +++ b/lib/l10n/app_localizations_ru.dart @@ -0,0 +1,127 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Russian (`ru`). +class AppLocalizationsRu extends AppLocalizations { + AppLocalizationsRu([String locale = 'ru']) : super(locale); + + @override + String get about_us => 'О нас'; + + @override + String get about_us_desc => + 'Rive объединяет интерактивный инструмент для дизайна, новый формат графики с состоянием, легковесную многоплатформенную среду выполнения и сверхбыстрый векторный рендер. \nЭта сквозная система оживляет интерфейсы с помощью анимации и дает дизайнерам и разработчикам необходимые инструменты для создания.'; + + @override + String get select_language => 'Выберите язык'; + + @override + String get select => 'Выбрать'; + + @override + String get please_wait => 'Подождите несколько секунд...'; + + @override + String get downloading_data => 'Загрузка исходных данных'; + + @override + String get lost_connection => 'Соединение потеряно!'; + + @override + String get retry => 'Повторить'; + + @override + String get connected_to_internet => + 'Для загрузки исходных данных игры необходимо подключение к интернету.'; + + @override + String get start => 'Начать'; + + @override + String get step => 'Шаг'; + + @override + String get question => 'Вопрос'; + + @override + String get be_cureful => 'Будь\nвнимательнее.'; + + @override + String get wrong_answer => 'Твой ответ\nневерен.'; + + @override + String get you_got_diamond => 'Ты получил алмаз'; + + @override + String get map => 'Карта'; + + @override + String get next => 'Далее'; + + @override + String get you_win => 'Ты победил!'; + + @override + String get skip => 'Пропустить'; + + @override + String get intro_1_1 => + 'Ужин готов! Скорее приходите и помойте свои красивые руки!'; + + @override + String get intro_1_2 => + 'Мама! Наши руки не такие грязные! Мы просто вытрем их салфеткой!'; + + @override + String get intro_2 => + 'Чистота веры...\nОна означает, что чистота — это признак веры!'; + + @override + String get intro_3 => + 'Эти добрые дела делают нашу душу сильной и прекрасной!'; + + @override + String get intro_4 => 'Хочешь отправиться в Обетованный Сад?'; + + @override + String get intro_5 => 'Дааа...\nМы готовы!'; + + @override + String get want_to_exit => 'Хочешь выйти?'; + + @override + String get exit_dialog_desc => + 'Возвращайся, герой!\nПриключение еще не закончено!'; + + @override + String get cancel => 'Отмена'; + + @override + String get exit => 'Выход'; + + @override + String get play => 'ИГРАТЬ'; + + @override + String get no_hadith => 'Для этого вопроса нет хадиса'; + + @override + String get showcase_answer => 'Нажми на правильный\nвариант, чтобы выбрать.'; + + @override + String get showcase_notif => 'Диктор прочитает\nтебе все варианты\nответов.'; + + @override + String get showcase_stepper => + 'Здесь ты увидишь\nвопросы этого этапа,\nчтобы получить\nалмаз.'; + + @override + String get showcase_hadith => + 'Просмотри источники и\nхадисы по этому вопросу'; + + @override + String get showcase_guide => 'Это руководство,\nкоторое поможет тебе.'; +} diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart new file mode 100644 index 0000000..0ae805a --- /dev/null +++ b/lib/l10n/app_localizations_tr.dart @@ -0,0 +1,126 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Turkish (`tr`). +class AppLocalizationsTr extends AppLocalizations { + AppLocalizationsTr([String locale = 'tr']) : super(locale); + + @override + String get about_us => 'Hakkımızda'; + + @override + String get about_us_desc => + 'Rive, etkileşimli bir tasarım aracı, yeni bir durumsal grafik formatı, hafif çok platformlu bir çalışma zamanı ve son derece hızlı bir vektör oluşturucu sunar.\nBu uçtan uca süreç, arayüzleri hareketle canlandırır. Tasarımcılara ve geliştiricilere inşa etme gücü verir.'; + + @override + String get select_language => 'Dili seçin'; + + @override + String get select => 'Seç'; + + @override + String get please_wait => 'Lütfen birkaç saniye bekleyin...'; + + @override + String get downloading_data => 'Başlangıç verileri indiriliyor'; + + @override + String get lost_connection => 'Bağlantı kesildi!'; + + @override + String get retry => 'Yeniden dene'; + + @override + String get connected_to_internet => + 'Başlangıç oyun verilerini indirmek için internete bağlı olmalısınız.'; + + @override + String get start => 'Başla'; + + @override + String get step => 'Adım'; + + @override + String get question => 'Soru'; + + @override + String get be_cureful => 'Daha dikkatli\nol.'; + + @override + String get wrong_answer => 'Cevabın\nyanlıştı.'; + + @override + String get you_got_diamond => 'Elması kazandın'; + + @override + String get map => 'Harita'; + + @override + String get next => 'Sonraki'; + + @override + String get you_win => 'Kazandın!'; + + @override + String get skip => 'Geç'; + + @override + String get intro_1_1 => + 'Yemek hazır! Hadi hemen gelin ve güzel ellerinizi yıkayın!'; + + @override + String get intro_1_2 => + 'Anne! Ellerimiz o kadar kirli değil! Peçeteyle sileriz!'; + + @override + String get intro_2 => + 'İman saflığı...\nTemizliğin imanın bir parçası olduğunu gösterir!'; + + @override + String get intro_3 => 'Bu güzel davranışlar ruhumuzu güçlü ve güzel yapar!'; + + @override + String get intro_4 => 'Vadedilen Bahçe\'ye yolculuk etmek ister misin?'; + + @override + String get intro_5 => 'Eveeeet...\nHazırız!'; + + @override + String get want_to_exit => 'Çıkmak istiyor musun?'; + + @override + String get exit_dialog_desc => 'Geri dön, kahraman!\nMacera henüz bitmedi!'; + + @override + String get cancel => 'İptal'; + + @override + String get exit => 'Çıkış'; + + @override + String get play => 'OYNAT'; + + @override + String get no_hadith => 'Bu soru için hadis bulunamadı'; + + @override + String get showcase_answer => 'Doğru seçeneğe\ndokunarak seç.'; + + @override + String get showcase_notif => + 'Anlatıcı, seçeneklerin\ncevaplarını sana\nokuyacak.'; + + @override + String get showcase_stepper => + 'Burada, elmasa ulaşmak\niçin bu aşamadaki\nsoruları göreceksin.'; + + @override + String get showcase_hadith => + 'Bu soruya ait\nkaynakları ve hadisleri\nincele'; + + @override + String get showcase_guide => 'Bu, sana yardımcı olacak\nbir rehberdir.'; +} diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb new file mode 100644 index 0000000..c457456 --- /dev/null +++ b/lib/l10n/app_ru.arb @@ -0,0 +1,39 @@ +{ + "@@locale": "ru", + "about_us": "О нас", + "about_us_desc": "Rive объединяет интерактивный инструмент для дизайна, новый формат графики с состоянием, легковесную многоплатформенную среду выполнения и сверхбыстрый векторный рендер. \nЭта сквозная система оживляет интерфейсы с помощью анимации и дает дизайнерам и разработчикам необходимые инструменты для создания.", + "select_language": "Выберите язык", + "select": "Выбрать", + "please_wait": "Подождите несколько секунд...", + "downloading_data": "Загрузка исходных данных", + "lost_connection": "Соединение потеряно!", + "retry": "Повторить", + "connected_to_internet": "Для загрузки исходных данных игры необходимо подключение к интернету.", + "start": "Начать", + "step": "Шаг", + "question": "Вопрос", + "be_cureful": "Будь\nвнимательнее.", + "wrong_answer": "Твой ответ\nневерен.", + "you_got_diamond": "Ты получил алмаз", + "map": "Карта", + "next": "Далее", + "you_win": "Ты победил!", + "skip": "Пропустить", + "intro_1_1": "Ужин готов! Скорее приходите и помойте свои красивые руки!", + "intro_1_2": "Мама! Наши руки не такие грязные! Мы просто вытрем их салфеткой!", + "intro_2": "Чистота веры...\nОна означает, что чистота — это признак веры!", + "intro_3": "Эти добрые дела делают нашу душу сильной и прекрасной!", + "intro_4": "Хочешь отправиться в Обетованный Сад?", + "intro_5": "Дааа...\nМы готовы!", + "want_to_exit": "Хочешь выйти?", + "exit_dialog_desc": "Возвращайся, герой!\nПриключение еще не закончено!", + "cancel": "Отмена", + "exit": "Выход", + "play": "ИГРАТЬ", + "no_hadith": "Для этого вопроса нет хадиса", + "showcase_answer": "Нажми на правильный\nвариант, чтобы выбрать.", + "showcase_notif": "Диктор прочитает\nтебе все варианты\nответов.", + "showcase_stepper": "Здесь ты увидишь\nвопросы этого этапа,\nчтобы получить\nалмаз.", + "showcase_hadith": "Просмотри источники и\nхадисы по этому вопросу", + "showcase_guide": "Это руководство,\nкоторое поможет тебе." +} diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb new file mode 100644 index 0000000..e8b78c8 --- /dev/null +++ b/lib/l10n/app_tr.arb @@ -0,0 +1,39 @@ +{ + "@@locale": "tr", + "about_us": "Hakkımızda", + "about_us_desc": "Rive, etkileşimli bir tasarım aracı, yeni bir durumsal grafik formatı, hafif çok platformlu bir çalışma zamanı ve son derece hızlı bir vektör oluşturucu sunar.\nBu uçtan uca süreç, arayüzleri hareketle canlandırır. Tasarımcılara ve geliştiricilere inşa etme gücü verir.", + "select_language": "Dili seçin", + "select": "Seç", + "please_wait": "Lütfen birkaç saniye bekleyin...", + "downloading_data": "Başlangıç verileri indiriliyor", + "lost_connection": "Bağlantı kesildi!", + "retry": "Yeniden dene", + "connected_to_internet": "Başlangıç oyun verilerini indirmek için internete bağlı olmalısınız.", + "start": "Başla", + "step": "Adım", + "question": "Soru", + "be_cureful": "Daha dikkatli\nol.", + "wrong_answer": "Cevabın\nyanlıştı.", + "you_got_diamond": "Elması kazandın", + "map": "Harita", + "next": "Sonraki", + "you_win": "Kazandın!", + "skip": "Geç", + "intro_1_1": "Yemek hazır! Hadi hemen gelin ve güzel ellerinizi yıkayın!", + "intro_1_2": "Anne! Ellerimiz o kadar kirli değil! Peçeteyle sileriz!", + "intro_2": "İman saflığı...\nTemizliğin imanın bir parçası olduğunu gösterir!", + "intro_3": "Bu güzel davranışlar ruhumuzu güçlü ve güzel yapar!", + "intro_4": "Vadedilen Bahçe'ye yolculuk etmek ister misin?", + "intro_5": "Eveeeet...\nHazırız!", + "want_to_exit": "Çıkmak istiyor musun?", + "exit_dialog_desc": "Geri dön, kahraman!\nMacera henüz bitmedi!", + "cancel": "İptal", + "exit": "Çıkış", + "play": "OYNAT", + "no_hadith": "Bu soru için hadis bulunamadı", + "showcase_answer": "Doğru seçeneğe\ndokunarak seç.", + "showcase_notif": "Anlatıcı, seçeneklerin\ncevaplarını sana\nokuyacak.", + "showcase_stepper": "Burada, elmasa ulaşmak\niçin bu aşamadaki\nsoruları göreceksin.", + "showcase_hadith": "Bu soruya ait\nkaynakları ve hadisleri\nincele", + "showcase_guide": "Bu, sana yardımcı olacak\nbir rehberdir." +} diff --git a/lib/main.dart b/lib/main.dart index 77f64d4..b9bd653 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,14 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; import 'package:hadi_hoda_flutter/common_ui/theme/theme_service.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/utils/my_device.dart'; import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart'; +import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart'; import 'package:hadi_hoda_flutter/init_bindings.dart'; import 'package:hadi_hoda_flutter/l10n/app_localizations.dart'; +import 'features/app/presentation/bloc/app_state.dart'; + Future main() async { WidgetsFlutterBinding.ensureInitialized(); initBindings(); @@ -27,19 +33,26 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp.router( - theme: MyTheme.light, - darkTheme: MyTheme.dark, - themeMode: ThemeService.getTheme(), - locale: const Locale('en', 'US'), - supportedLocales: const [Locale('en', 'US')], - routerConfig: appPages, - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], + return BlocProvider( + create: (context) => locator()..add(InitLocaleEvent()), + child: BlocBuilder( + builder: (context, state) => MaterialApp.router( + theme: MyTheme.light, + darkTheme: MyTheme.dark, + themeMode: ThemeService.getTheme(), + locale: state.locale, + supportedLocales: MyConstants.languages.map( + (e) => e.locale ?? Locale('en', 'US'), + ), + routerConfig: appPages, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + ), + ), ); } }