diff --git a/assets/lang/ar.json b/assets/lang/ar.json new file mode 100644 index 0000000..4c8063b --- /dev/null +++ b/assets/lang/ar.json @@ -0,0 +1,3 @@ +{ + "text_description": "مرحبًا ، سيتغير هذا النص وفقًا للغة" +} \ No newline at end of file diff --git a/assets/lang/en.json b/assets/lang/en.json new file mode 100644 index 0000000..0534439 --- /dev/null +++ b/assets/lang/en.json @@ -0,0 +1,5 @@ +{ + "text_description": "Hello, this text will change according to the language", + "i18n_hello" : "Hello", + "i18n_take_pictures" : "Take pictures" +} \ No newline at end of file diff --git a/assets/lang/fr.json b/assets/lang/fr.json new file mode 100644 index 0000000..c3ba4db --- /dev/null +++ b/assets/lang/fr.json @@ -0,0 +1,4 @@ +{ + "i18n_hello": "Bonjour", + "i18n_take_pictures" : "Prendre des photos" +} \ No newline at end of file diff --git a/assets/lang/hi.json b/assets/lang/hi.json new file mode 100644 index 0000000..542ad7d --- /dev/null +++ b/assets/lang/hi.json @@ -0,0 +1,3 @@ +{ + "text_description": "नमस्कार, यह पाठ भाषा के अनुसार बदल जाएगा" +} \ No newline at end of file diff --git a/assets/lang/id.json b/assets/lang/id.json new file mode 100644 index 0000000..9e68159 --- /dev/null +++ b/assets/lang/id.json @@ -0,0 +1,3 @@ +{ + "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 new file mode 100644 index 0000000..0eca8e5 --- /dev/null +++ b/assets/lang/th.json @@ -0,0 +1,3 @@ +{ + "text_description": "สวัสดีข้อความนี้จะเปลี่ยนไปตามภาษา" +} \ No newline at end of file diff --git a/assets/lang/tk.json b/assets/lang/tk.json new file mode 100644 index 0000000..745b286 --- /dev/null +++ b/assets/lang/tk.json @@ -0,0 +1,3 @@ +{ + "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 new file mode 100644 index 0000000..4823aff --- /dev/null +++ b/assets/lang/zh.json @@ -0,0 +1,3 @@ +{ + "text_description": "您好,该文字会根据语言而变化" +} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 56ff20c..39e7eef 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -11,6 +11,9 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) @@ -21,6 +24,7 @@ DEPENDENCIES: - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) SPEC REPOS: @@ -37,6 +41,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" @@ -46,6 +52,7 @@ SPEC CHECKSUMS: FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 diff --git a/lib/cubit/language/app_localizations.dart b/lib/cubit/language/app_localizations.dart new file mode 100644 index 0000000..c41d7c7 --- /dev/null +++ b/lib/cubit/language/app_localizations.dart @@ -0,0 +1,73 @@ +/* +This is the app localizations for multi language +This function is used to change the language +We used shared preferences to change a whole language of the apps + */ + +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class AppLocalizations { + final Locale locale; + + AppLocalizations(this.locale); + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + late Map _localizedStrings; + + Future load() async { + // Load the language JSON file from the "lang" folder + String jsonString = + await rootBundle.loadString('assets/lang/${locale.languageCode}.json'); + Map jsonMap = json.decode(jsonString); + + _localizedStrings = jsonMap.map((key, value) { + return MapEntry(key, value.toString()); + }); + + return true; + } + + // This method will be called from every widget which needs a localized text + String? translate(String key) { + return _localizedStrings[key]; + } +} + +class AppLocalizationsDelegate extends LocalizationsDelegate { + const AppLocalizationsDelegate(); + + @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); + } + + @override + Future load(Locale locale) async { + final SharedPreferences _pref = await SharedPreferences.getInstance(); + String? lCode = _pref.getString('lCode'); + String? cCode = _pref.getString('cCode'); + + 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; + } + + @override + bool shouldReload(AppLocalizationsDelegate old) => true; +} diff --git a/lib/cubit/language/initial_language.dart b/lib/cubit/language/initial_language.dart new file mode 100644 index 0000000..dbea859 --- /dev/null +++ b/lib/cubit/language/initial_language.dart @@ -0,0 +1,44 @@ +import 'package:mobdr/cubit/language/language_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class InitialLanguage extends StatefulWidget { + final Widget child; + + const InitialLanguage({required this.child}); + + @override + _InitialLanguageState createState() => _InitialLanguageState(); +} + +class _InitialLanguageState extends State { + late LanguageCubit _languageCubit; + + @override + void initState() { + _languageCubit = BlocProvider.of(context); + _getLocale().then((val) { + _languageCubit.changeLanguage(val); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } + + Future _getLocale() async { + final SharedPreferences _pref = await SharedPreferences.getInstance(); + String? lCode = _pref.getString('lCode'); + String? cCode = _pref.getString('cCode'); + if (lCode == null || cCode == null) { + await _pref.setString('lCode', 'fr'); + await _pref.setString('cCode', 'FR'); + return Locale('fr', 'FR'); + } else { + return Locale(lCode, cCode); + } + } +} diff --git a/lib/cubit/language/language_cubit.dart b/lib/cubit/language/language_cubit.dart new file mode 100644 index 0000000..43896dc --- /dev/null +++ b/lib/cubit/language/language_cubit.dart @@ -0,0 +1,12 @@ +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'language_state.dart'; + +class LanguageCubit extends Cubit{ + LanguageCubit() : super(LanguageInitial()); + + void changeLanguage(locale){ + emit(ChangeLanguageSuccess(locale)); + } +} \ No newline at end of file diff --git a/lib/cubit/language/language_state.dart b/lib/cubit/language/language_state.dart new file mode 100644 index 0000000..4ac92b1 --- /dev/null +++ b/lib/cubit/language/language_state.dart @@ -0,0 +1,11 @@ +part of 'language_cubit.dart'; + +@immutable +abstract class LanguageState {} + +class LanguageInitial extends LanguageState{} + +class ChangeLanguageSuccess extends LanguageState{ + final locale; + ChangeLanguageSuccess(this.locale); +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index b46fea0..de96b52 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,10 +4,17 @@ import 'dart:ui'; import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/cubit/language/language_cubit.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; +import 'package:mobdr/cubit/language/initial_language.dart'; + import 'package:mobdr/ui/splash_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + void main() { // this function makes application always run in portrait mode WidgetsFlutterBinding.ensureInitialized(); @@ -31,18 +38,61 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - scrollBehavior: MyCustomScrollBehavior(), - title: APP_NAME, - debugShowCheckedModeBanner: false, - theme: ThemeData( - visualDensity: VisualDensity.adaptivePlatformDensity, - pageTransitionsTheme: PageTransitionsTheme(builders: { - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.android: ZoomPageTransitionsBuilder(), + // Initialize all bloc provider used on this entire application here + return MultiBlocProvider( + providers: [ + // this bloc used for feature - change language + BlocProvider( + create: (BuildContext context) => LanguageCubit(), + ), + ], + // if you want to change default language, go to lib/ui/feature/multi_language/initial_language.dart and change en US to your default language + child: InitialLanguage( + child: BlocBuilder( + builder: (context, state) { + return MaterialApp( + scrollBehavior: MyCustomScrollBehavior(), + title: APP_NAME, + debugShowCheckedModeBanner: false, + theme: ThemeData( + visualDensity: VisualDensity.adaptivePlatformDensity, + pageTransitionsTheme: PageTransitionsTheme(builders: { + /* + Below is the example to change MaterialPageRoute default transition in iOS and Android : + FadeUpwardsPageTransitionsBuilder() <= Default MaterialPageRoute Transition + OpenUpwardsPageTransitionsBuilder() + ZoomPageTransitionsBuilder() + CupertinoPageTransitionsBuilder() + */ + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.android: ZoomPageTransitionsBuilder(), + }), + ), + // 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'), + ], + // These delegates make sure that the localization data for the proper language is loaded + localizationsDelegates: [ + AppLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate + ], + // Returns a locale which will be used by the app + locale: (state is ChangeLanguageSuccess) + ? state.locale + : Locale('fr', 'FR'), + home: SplashScreenPage(), + ); }), ), - home: SplashScreenPage(), ); } } diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index 8148761..84d310b 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -4,6 +4,9 @@ we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navba */ import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/cubit/language/language_cubit.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/model/wishlist_model.dart'; import 'package:mobdr/ui/general/chat_us.dart'; @@ -12,7 +15,7 @@ import 'package:mobdr/ui/general/product_detail/product_detail.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/cache_image_network.dart'; import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class TabHomePage extends StatefulWidget { @override @@ -31,8 +34,18 @@ class _TabHomePageState extends State @override bool get wantKeepAlive => true; + String defaultLang = 'en'; + + late LanguageCubit _languageCubit; + @override void initState() { + _languageCubit = BlocProvider.of(context); + _getLocale().then((val) { + setState(() { + defaultLang = val!; + }); + }); super.initState(); } @@ -41,6 +54,20 @@ class _TabHomePageState extends State 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); @@ -51,7 +78,8 @@ class _TabHomePageState extends State automaticallyImplyLeading: false, elevation: GlobalStyle.appBarElevation, title: Text( - 'Bonjour, Frédérik', + AppLocalizations.of(context)!.translate('i18n_hello')! + + ', Frédérik', style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, @@ -242,7 +270,8 @@ class _TabHomePageState extends State BorderSide(color: SOFT_BLUE, width: 1.0), )), child: Text( - 'Prendre des photos', + AppLocalizations.of(context)! + .translate('i18n_take_pictures')!, style: TextStyle( color: SOFT_BLUE, fontWeight: FontWeight.bold, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 4d98e47..63c4df1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,12 @@ import Foundation import package_info_plus import path_provider_foundation +import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 88a76c2..3657ea4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" + source: hosted + version: "8.1.1" boolean_selector: dependency: transitive description: @@ -126,6 +134,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" + source: hosted + version: "8.1.2" flutter_blurhash: dependency: transitive description: @@ -150,6 +166,11 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0-alpha.6" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -232,6 +253,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" numerus: dependency: transitive description: @@ -352,6 +381,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" rxdart: dependency: transitive description: @@ -360,6 +397,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + url: "https://pub.dev" + source: hosted + version: "2.0.17" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4 + url: "https://pub.dev" + source: hosted + version: "2.0.16" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7" + url: "https://pub.dev" + source: hosted + version: "2.0.5" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d + url: "https://pub.dev" + source: hosted + version: "2.1.4" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 37af339..dc14eec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,16 +31,23 @@ dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: 1.0.5 fluttertoast: 8.1.3 package_info_plus: 3.0.3 + flutter_bloc: 8.1.2 flutter_html: 3.0.0-alpha.6 + + #https://pub.dev/packages/intl intl: 0.17.0 + carousel_slider: 4.2.1 cached_network_image: 3.2.3 + shared_preferences: 2.0.17 universal_io: 2.2.0 dev_dependencies: @@ -107,4 +114,13 @@ flutter: - assets/images/mastercard.png - assets/images/logo.png - assets/images/logo_dark.png - - assets/images/onboarding/search_product.gif \ No newline at end of file + - assets/images/onboarding/search_product.gif + + - 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 \ No newline at end of file