diff --git a/base_project/analysis_options.yaml b/base_project/analysis_options.yaml index 0d29021..bf8d421 100644 --- a/base_project/analysis_options.yaml +++ b/base_project/analysis_options.yaml @@ -7,6 +7,15 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/base_project/assets/translations_dynamic/en.json b/base_project/assets/translations_dynamic/en.json new file mode 100644 index 0000000..313285f --- /dev/null +++ b/base_project/assets/translations_dynamic/en.json @@ -0,0 +1,8 @@ +{ + "student": "Student", + "create_student": "Create Student", + "update_student": "Update Student", + "name": "Name", + "phone_number": "Phone Number", + "email": "Email" +} diff --git a/base_project/assets/translations_dynamic/hi.json b/base_project/assets/translations_dynamic/hi.json new file mode 100644 index 0000000..e75cfc5 --- /dev/null +++ b/base_project/assets/translations_dynamic/hi.json @@ -0,0 +1,8 @@ +{ + "student": "छात्र", + "create_student": "छात्र बनाएँ", + "update_student": "छात्र अपडेट करें", + "name": "नाम", + "phone_number": "फ़ोन नंबर", + "email": "ईमेल" +} diff --git a/base_project/l10n.yaml b/base_project/l10n.yaml new file mode 100644 index 0000000..af4d3c1 --- /dev/null +++ b/base_project/l10n.yaml @@ -0,0 +1,4 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-dir: lib/l10n diff --git a/base_project/lib/BuilderField/shared/fields/audio_upload_field.dart b/base_project/lib/BuilderField/shared/fields/audio_upload_field.dart index 22982bc..a89294e 100644 --- a/base_project/lib/BuilderField/shared/fields/audio_upload_field.dart +++ b/base_project/lib/BuilderField/shared/fields/audio_upload_field.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'dart:io'; import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'package:file_picker/file_picker.dart'; import 'base_field.dart'; import '../utils/entity_field_store.dart'; @@ -63,7 +64,9 @@ class AudioUploadField extends BaseField { } }, icon: const Icon(Icons.audiotrack_rounded), - label: const Text('Add Audio'), + label: Builder( + builder: (ctx) => + Text(AppLocalizations.of(ctx)!.addAudio)), ), ], ), diff --git a/base_project/lib/BuilderField/shared/fields/autocomplete_multiselect_field.dart b/base_project/lib/BuilderField/shared/fields/autocomplete_multiselect_field.dart index e5b1e8e..424baf2 100644 --- a/base_project/lib/BuilderField/shared/fields/autocomplete_multiselect_field.dart +++ b/base_project/lib/BuilderField/shared/fields/autocomplete_multiselect_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../../../shared/widgets/inputs/modern_text_field.dart'; @@ -255,7 +256,7 @@ class _AutocompleteMultiSelectWidgetState const SizedBox(width: 8), Expanded( child: Text( - 'No results', + AppLocalizations.of(context)!.noResults, style: TextStyle( color: widget.colorScheme.onSurfaceVariant), diff --git a/base_project/lib/BuilderField/shared/fields/barcode_field.dart b/base_project/lib/BuilderField/shared/fields/barcode_field.dart index 6090dc8..99b5794 100644 --- a/base_project/lib/BuilderField/shared/fields/barcode_field.dart +++ b/base_project/lib/BuilderField/shared/fields/barcode_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'package:barcode_widget/barcode_widget.dart'; import 'base_field.dart'; @@ -73,19 +74,22 @@ class BarcodeField extends BaseField { ), ), const SizedBox(width: 8), - IconButton( - tooltip: 'Scan Barcode', - icon: Icon(Icons.document_scanner_rounded, - color: colorScheme.primary), - onPressed: scanner == null - ? null - : () async { - final val = await scanner!.call(); - if (val != null) { - controller.text = val; - onChanged?.call(); - } - }, + Builder( + builder: (ctx) => IconButton( + tooltip: + AppLocalizations.of(ctx)!.scanBarcode, + icon: Icon(Icons.document_scanner_rounded, + color: colorScheme.primary), + onPressed: scanner == null + ? null + : () async { + final val = await scanner!.call(); + if (val != null) { + controller.text = val; + onChanged?.call(); + } + }, + ), ), ], ), diff --git a/base_project/lib/BuilderField/shared/fields/data_grid_field.dart b/base_project/lib/BuilderField/shared/fields/data_grid_field.dart index 8f08573..e0987af 100644 --- a/base_project/lib/BuilderField/shared/fields/data_grid_field.dart +++ b/base_project/lib/BuilderField/shared/fields/data_grid_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; /// Read-only Data Grid field @@ -96,7 +97,7 @@ class _DataGridFieldWidgetState extends State<_DataGridFieldWidget> { } final data = snapshot.data ?? const >[]; if (data.isEmpty) { - return Text('No data available', + return Text(AppLocalizations.of(context)!.noDataAvailable, style: TextStyle(color: colorScheme.onSurfaceVariant)); } final columns = data.first.keys.toList(); diff --git a/base_project/lib/BuilderField/shared/fields/dynamic_dropdown_field.dart b/base_project/lib/BuilderField/shared/fields/dynamic_dropdown_field.dart index 02d0ff5..748d186 100644 --- a/base_project/lib/BuilderField/shared/fields/dynamic_dropdown_field.dart +++ b/base_project/lib/BuilderField/shared/fields/dynamic_dropdown_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../../../shared/widgets/inputs/modern_text_field.dart'; @@ -152,7 +153,8 @@ class _DynamicDropdownWidgetState extends State<_DynamicDropdownWidget> { children: [ TextField( decoration: InputDecoration( - hintText: 'Search ${widget.label.toLowerCase()}', + hintText: AppLocalizations.of(context)! + .searchEntity(widget.label), prefixIcon: const Icon(Icons.search), border: const OutlineInputBorder(), ), @@ -162,7 +164,8 @@ class _DynamicDropdownWidgetState extends State<_DynamicDropdownWidget> { Flexible( child: filtered.isEmpty ? Center( - child: Text('No results', + child: Text( + AppLocalizations.of(context)!.noResults, style: TextStyle(color: cs.onSurfaceVariant)), ) : ListView.separated( diff --git a/base_project/lib/BuilderField/shared/fields/dynamic_multiselect_dropdown_field.dart b/base_project/lib/BuilderField/shared/fields/dynamic_multiselect_dropdown_field.dart index f0c2699..e622e81 100644 --- a/base_project/lib/BuilderField/shared/fields/dynamic_multiselect_dropdown_field.dart +++ b/base_project/lib/BuilderField/shared/fields/dynamic_multiselect_dropdown_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../../../shared/widgets/inputs/modern_text_field.dart'; @@ -146,7 +147,8 @@ class _DynamicMultiSelectWidgetState extends State<_DynamicMultiSelectWidget> { children: [ TextField( decoration: InputDecoration( - hintText: 'Search ${widget.label.toLowerCase()}', + hintText: AppLocalizations.of(context)! + .searchEntity(widget.label), prefixIcon: const Icon(Icons.search), border: const OutlineInputBorder(), ), @@ -156,7 +158,8 @@ class _DynamicMultiSelectWidgetState extends State<_DynamicMultiSelectWidget> { Flexible( child: filtered.isEmpty ? Center( - child: Text('No results', + child: Text( + AppLocalizations.of(context)!.noResults, style: TextStyle(color: cs.onSurfaceVariant)), ) : ListView.separated( @@ -188,7 +191,8 @@ class _DynamicMultiSelectWidgetState extends State<_DynamicMultiSelectWidget> { widget.onChanged?.call(); Navigator.of(context).pop(); }, - child: const Text('Clear'), + child: Text( + AppLocalizations.of(context)!.clearBtn), ), const Spacer(), ElevatedButton( @@ -199,7 +203,8 @@ class _DynamicMultiSelectWidgetState extends State<_DynamicMultiSelectWidget> { widget.onChanged?.call(); Navigator.of(context).pop(); }, - child: const Text('Done'), + child: Text( + AppLocalizations.of(context)!.doneBtn), ), ], ), diff --git a/base_project/lib/BuilderField/shared/fields/file_upload_field.dart b/base_project/lib/BuilderField/shared/fields/file_upload_field.dart index a41e91c..1ada638 100644 --- a/base_project/lib/BuilderField/shared/fields/file_upload_field.dart +++ b/base_project/lib/BuilderField/shared/fields/file_upload_field.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../utils/entity_field_store.dart'; @@ -62,7 +63,9 @@ class FileUploadField extends BaseField { } }, icon: const Icon(Icons.attach_file_rounded), - label: const Text('Add Files'), + label: Builder( + builder: (ctx) => + Text(AppLocalizations.of(ctx)!.addFiles)), ), ], ), diff --git a/base_project/lib/BuilderField/shared/fields/image_upload_field.dart b/base_project/lib/BuilderField/shared/fields/image_upload_field.dart index 750a2c0..4bf5973 100644 --- a/base_project/lib/BuilderField/shared/fields/image_upload_field.dart +++ b/base_project/lib/BuilderField/shared/fields/image_upload_field.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../utils/entity_field_store.dart'; @@ -57,7 +58,9 @@ class ImageUploadField extends BaseField { } }, icon: const Icon(Icons.image_rounded), - label: const Text('Add Image'), + label: Builder( + builder: (ctx) => + Text(AppLocalizations.of(ctx)!.addImage)), ), ], ), diff --git a/base_project/lib/BuilderField/shared/fields/one_to_many_field.dart b/base_project/lib/BuilderField/shared/fields/one_to_many_field.dart index 5e05bba..0835631 100644 --- a/base_project/lib/BuilderField/shared/fields/one_to_many_field.dart +++ b/base_project/lib/BuilderField/shared/fields/one_to_many_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../ui/entity_form.dart'; import 'dart:convert'; @@ -553,7 +554,9 @@ class _OneToManyWidgetState extends State<_OneToManyWidget> { child: ElevatedButton.icon( onPressed: () => _addRow(), icon: Icon(Icons.add, color: cs.onPrimary), - label: Text('Add ${widget.label}', + label: Text( + AppLocalizations.of(context)! + .addEntity(widget.label), style: TextStyle(color: cs.onPrimary)), style: ElevatedButton.styleFrom( backgroundColor: cs.primary, diff --git a/base_project/lib/BuilderField/shared/fields/realted_entity_insert_field.dart b/base_project/lib/BuilderField/shared/fields/realted_entity_insert_field.dart index 6a45438..9b62cfb 100644 --- a/base_project/lib/BuilderField/shared/fields/realted_entity_insert_field.dart +++ b/base_project/lib/BuilderField/shared/fields/realted_entity_insert_field.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; - +import '../../../l10n/app_localizations.dart'; import '../../../core/constants/ui_constants.dart'; import '../../../core/providers/dynamic_theme_provider.dart'; import '../ui/entity_form.dart'; @@ -147,7 +147,8 @@ class RelatedEntityInsertField extends BaseField { setLocal(() => isSubmitting = false); } }, - submitButtonText: 'Create', + submitButtonText: AppLocalizations.of(context)! + .createEntity(relationTitle), isLoading: isSubmitting, ), ), @@ -156,7 +157,8 @@ class RelatedEntityInsertField extends BaseField { onPressed: isSubmitting ? null : () => Navigator.of(dialogContext).pop(), - child: const Text('Cancel'), + child: Text( + AppLocalizations.of(dialogContext)!.cancel), ), ], ); @@ -402,7 +404,8 @@ class _RelatedEntityInsertWidgetState ElevatedButton.icon( onPressed: _creating ? null : _openInsertDialog, icon: Icon(Icons.add_rounded, color: cs.onPrimary, size: 18), - label: Text('Insert', + label: Text( + AppLocalizations.of(context)!.insertBtn, style: TextStyle( color: cs.onPrimary, fontWeight: FontWeight.w600)), style: ElevatedButton.styleFrom( @@ -491,7 +494,8 @@ class _RelatedEntityInsertWidgetState TextButton( onPressed: isSubmitting ? null : () => Navigator.of(context).pop(), - child: const Text('Cancel'), + child: Text( + AppLocalizations.of(context)!.cancel), ), ], ); diff --git a/base_project/lib/BuilderField/shared/fields/value_list_picker_field.dart b/base_project/lib/BuilderField/shared/fields/value_list_picker_field.dart index 02819c7..f23cadc 100644 --- a/base_project/lib/BuilderField/shared/fields/value_list_picker_field.dart +++ b/base_project/lib/BuilderField/shared/fields/value_list_picker_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../ui/entity_form.dart'; @@ -143,7 +144,8 @@ class _ValueListPickerWidgetState extends State<_ValueListPickerWidget> { strokeWidth: 2, color: cs.primary), ), const SizedBox(width: 12), - Text('Loading...', + Text( + AppLocalizations.of(context)!.loading, style: TextStyle(color: cs.onSurfaceVariant)), ], ), @@ -152,7 +154,7 @@ class _ValueListPickerWidgetState extends State<_ValueListPickerWidget> { if (snapshot.hasError || _loadError != null) { final String msg = snapshot.error?.toString() ?? _loadError ?? - 'Failed to load'; + AppLocalizations.of(context)!.errorLoadingData; return Padding( padding: const EdgeInsets.all(16), child: Container( @@ -186,13 +188,15 @@ class _ValueListPickerWidgetState extends State<_ValueListPickerWidget> { Icon(Icons.inbox_rounded, color: cs.onSurfaceVariant, size: 36), const SizedBox(height: 8), - Text('No data found', + Text( + AppLocalizations.of(context)!.noDataFound, style: TextStyle(color: cs.onSurfaceVariant)), const SizedBox(height: 12), ElevatedButton.icon( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close_rounded), - label: const Text('Close'), + label: Text( + AppLocalizations.of(context)!.close), style: ElevatedButton.styleFrom( backgroundColor: cs.primary, foregroundColor: cs.onPrimary, diff --git a/base_project/lib/BuilderField/shared/fields/video_upload_field.dart b/base_project/lib/BuilderField/shared/fields/video_upload_field.dart index 329abda..863726f 100644 --- a/base_project/lib/BuilderField/shared/fields/video_upload_field.dart +++ b/base_project/lib/BuilderField/shared/fields/video_upload_field.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; +import '../../../l10n/app_localizations.dart'; import 'base_field.dart'; import '../utils/entity_field_store.dart'; @@ -63,7 +64,9 @@ class VideoUploadField extends BaseField { } }, icon: const Icon(Icons.video_library_rounded), - label: const Text('Add Video'), + label: Builder( + builder: (ctx) => + Text(AppLocalizations.of(ctx)!.addVideo)), ), ], ), diff --git a/base_project/lib/BuilderField/shared/ui/entity_details.dart b/base_project/lib/BuilderField/shared/ui/entity_details.dart index d175bc1..e630596 100644 --- a/base_project/lib/BuilderField/shared/ui/entity_details.dart +++ b/base_project/lib/BuilderField/shared/ui/entity_details.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../core/providers/dynamic_theme_provider.dart'; import '../../../core/constants/ui_constants.dart'; import '../../../shared/widgets/app_bar/modern_app_bar.dart'; @@ -408,7 +409,7 @@ class _EntityDetailsState extends State children: [ Expanded( child: ModernButton( - text: 'Edit', + text: AppLocalizations.of(context)!.edit, type: ModernButtonType.secondary, size: ModernButtonSize.large, onPressed: () => widget.onEdit(widget.entity), @@ -418,7 +419,7 @@ class _EntityDetailsState extends State const SizedBox(width: UIConstants.spacing16), Expanded( child: ModernButton( - text: 'Delete', + text: AppLocalizations.of(context)!.delete, type: ModernButtonType.danger, size: ModernButtonSize.large, onPressed: () => _showDeleteDialog(colorScheme), diff --git a/base_project/lib/BuilderField/shared/ui/entity_form.dart b/base_project/lib/BuilderField/shared/ui/entity_form.dart index b3abf82..5d76677 100644 --- a/base_project/lib/BuilderField/shared/ui/entity_form.dart +++ b/base_project/lib/BuilderField/shared/ui/entity_form.dart @@ -386,6 +386,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../core/providers/dynamic_theme_provider.dart'; import '../fields/base_field.dart'; import '../fields/dependent_dropdown_field.dart'; @@ -562,7 +563,9 @@ class _EntityFormState extends State { final pass = passwordByGroup[gid] ?? ''; if (confirm.isNotEmpty && confirm != pass) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Passwords do not match')), + SnackBar( + content: Text(AppLocalizations.of(context)! + .passwordsDoNotMatch)), ); return; } diff --git a/base_project/lib/BuilderField/shared/ui/entity_list.dart b/base_project/lib/BuilderField/shared/ui/entity_list.dart index e7ba31e..0884f9e 100644 --- a/base_project/lib/BuilderField/shared/ui/entity_list.dart +++ b/base_project/lib/BuilderField/shared/ui/entity_list.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../../core/providers/dynamic_theme_provider.dart'; import '../../../core/constants/ui_constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../shared/widgets/app_bar/modern_app_bar.dart'; import 'entity_card.dart'; @@ -106,7 +107,7 @@ class _EntityListState extends State with TickerProviderStateMixin { TextButton( onPressed: widget.onInsertAction, child: Text( - widget.insertActionLabel ?? 'Unknown', + widget.insertActionLabel ?? AppLocalizations.of(context)!.unknown, style: TextStyle( color: colorScheme.primary, fontWeight: FontWeight.w600, @@ -117,7 +118,7 @@ class _EntityListState extends State with TickerProviderStateMixin { IconButton( onPressed: widget.onAddNew, icon: Icon(Icons.add_rounded, color: colorScheme.primary), - tooltip: 'Add New', + tooltip: AppLocalizations.of(context)!.addNew, ), ], ), @@ -175,7 +176,8 @@ class _EntityListState extends State with TickerProviderStateMixin { fontWeight: FontWeight.w500, ), decoration: InputDecoration( - hintText: 'Search ${widget.title.toLowerCase()}...', + hintText: AppLocalizations.of(context)! + .searchEntity(widget.title.toLowerCase()), hintStyle: TextStyle( color: colorScheme.onSurface.withOpacity(0.6), fontSize: 16, @@ -214,7 +216,7 @@ class _EntityListState extends State with TickerProviderStateMixin { color: colorScheme.onSurface.withOpacity(0.8), size: 22, ), - tooltip: 'Refresh', + tooltip: AppLocalizations.of(context)!.refresh, ), ], ), @@ -245,7 +247,7 @@ class _EntityListState extends State with TickerProviderStateMixin { CircularProgressIndicator(color: colorScheme.primary, strokeWidth: 3), const SizedBox(height: UIConstants.spacing16), Text( - 'Loading ${widget.title.toLowerCase()}...', + AppLocalizations.of(context)!.loadingEntity(widget.title.toLowerCase()), style: TextStyle( color: colorScheme.onSurface.withOpacity(0.7), fontSize: 16, @@ -277,7 +279,7 @@ class _EntityListState extends State with TickerProviderStateMixin { ), const SizedBox(height: UIConstants.spacing16), Text( - 'Error Loading Data', + AppLocalizations.of(context)!.errorLoadingData, style: TextStyle( color: colorScheme.onSurface, fontSize: 18, @@ -286,7 +288,7 @@ class _EntityListState extends State with TickerProviderStateMixin { ), const SizedBox(height: UIConstants.spacing8), Text( - widget.errorMessage ?? 'Something went wrong', + widget.errorMessage ?? AppLocalizations.of(context)!.somethingWentWrong, style: TextStyle( color: colorScheme.onSurface.withOpacity(0.7), fontSize: 14, @@ -299,7 +301,7 @@ class _EntityListState extends State with TickerProviderStateMixin { onPressed: widget.onRefresh, icon: Icon(Icons.refresh_rounded, color: colorScheme.onPrimary), label: Text( - 'Retry', + AppLocalizations.of(context)!.retry, style: TextStyle( color: colorScheme.onPrimary, fontWeight: FontWeight.w600, @@ -344,7 +346,7 @@ class _EntityListState extends State with TickerProviderStateMixin { ), const SizedBox(height: UIConstants.spacing16), Text( - 'No ${widget.title.toLowerCase()} found', + AppLocalizations.of(context)!.noEntityFound(widget.title.toLowerCase()), style: TextStyle( color: colorScheme.onSurface, fontSize: 18, @@ -354,8 +356,9 @@ class _EntityListState extends State with TickerProviderStateMixin { const SizedBox(height: UIConstants.spacing8), Text( widget.searchQuery.isNotEmpty - ? 'Try adjusting your search terms' - : 'Start by adding your first ${widget.title.toLowerCase()}', + ? AppLocalizations.of(context)!.adjustSearchTerms + : AppLocalizations.of(context)! + .startByAdding(widget.title.toLowerCase()), style: TextStyle( color: colorScheme.onSurface.withOpacity(0.7), fontSize: 14, @@ -369,7 +372,7 @@ class _EntityListState extends State with TickerProviderStateMixin { onPressed: widget.onAddNew, icon: Icon(Icons.add_rounded, color: colorScheme.onPrimary), label: Text( - 'Add ${widget.title}', + AppLocalizations.of(context)!.addEntity(widget.title), style: TextStyle( color: colorScheme.onPrimary, fontWeight: FontWeight.w600, diff --git a/base_project/lib/Reuseable/reusable_dropdown_field.dart b/base_project/lib/Reuseable/reusable_dropdown_field.dart index dc57686..9638c08 100644 --- a/base_project/lib/Reuseable/reusable_dropdown_field.dart +++ b/base_project/lib/Reuseable/reusable_dropdown_field.dart @@ -1,3 +1,4 @@ +import 'package:base_project/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; class ReusableDropdownField extends StatelessWidget { @@ -46,9 +47,10 @@ class ReusableDropdownField extends StatelessWidget { ), value: (value != null && value!.isNotEmpty) ? value : null, items: [ - const DropdownMenuItem( + DropdownMenuItem( value: '', - child: Text('Select an option'), + child: Text( + AppLocalizations.of(context)!.selectOption), ), ...options.map>( (item) => DropdownMenuItem( diff --git a/base_project/lib/commans/widgets/custome_drawer.dart b/base_project/lib/commans/widgets/custome_drawer.dart index 62a093d..af73faa 100644 --- a/base_project/lib/commans/widgets/custome_drawer.dart +++ b/base_project/lib/commans/widgets/custome_drawer.dart @@ -36,6 +36,7 @@ import '../../Entity/angulardatatype/Basicp1/Basicp1_viewModel/Basicp1_view_mode import 'package:base_project/utils/image_constant.dart'; import 'package:base_project/commans/widgets/custome_drawe_item.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/resources/app_colors.dart'; import 'package:base_project/routes/route_names.dart'; import 'package:base_project/utils/managers/user_manager.dart'; @@ -76,13 +77,14 @@ class MyCustomDrawer extends StatelessWidget { height: 60, ), ), - accountName: Text("Hello, $userName"), + accountName: Text( + AppLocalizations.of(context)!.helloUser(userName)), accountEmail: Text(email.toString()), ), DrawerItem( color: AppColors.primary, icon: Icons.person, - title: 'Profile', + title: AppLocalizations.of(context)!.profile, onTap: () { Navigator.pushNamed(context, RouteNames.profileView); }, @@ -90,7 +92,7 @@ class MyCustomDrawer extends StatelessWidget { DrawerItem( color: AppColors.primary, icon: Icons.system_security_update, - title: 'System Parameters', + title: AppLocalizations.of(context)!.systemParameters, onTap: () { // Add navigation or other logic here Navigator.pushNamed(context, RouteNames.systemParamsView); @@ -99,7 +101,7 @@ class MyCustomDrawer extends StatelessWidget { DrawerItem( color: AppColors.primary, icon: Icons.password, - title: 'change password', + title: AppLocalizations.of(context)!.changePassword, onTap: () { Navigator.pushNamed(context, RouteNames.changePasswordView); }, @@ -314,7 +316,7 @@ class MyCustomDrawer extends StatelessWidget { DrawerItem( icon: Icons.logout, color: Colors.red, - title: 'Logout', + title: AppLocalizations.of(context)!.logout, onTap: () async { await UserManager().clearUser(); Navigator.pushReplacementNamed(context, RouteNames.splashView); diff --git a/base_project/lib/core/localization/dynamic_strings.dart b/base_project/lib/core/localization/dynamic_strings.dart new file mode 100644 index 0000000..8dba0bb --- /dev/null +++ b/base_project/lib/core/localization/dynamic_strings.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; + +import '../providers/locale_provider.dart'; + +/// Translates stable, server/form-builder keys with a safe English fallback. +extension DynamicStrings on BuildContext { + String trDynamic(String key) => watch().dynamicText(key); +} diff --git a/base_project/lib/core/providers/locale_provider.dart b/base_project/lib/core/providers/locale_provider.dart new file mode 100644 index 0000000..0b76da0 --- /dev/null +++ b/base_project/lib/core/providers/locale_provider.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'dart:convert'; + +class LocaleProvider extends ChangeNotifier { + static const preferenceKey = 'app_locale_code'; + static const supportedCodes = ['en', 'hi']; + + Locale _locale = const Locale('en'); + Locale get locale => _locale; + final Map> _dynamicTables = {}; + + Future initialize() async { + final preferences = await SharedPreferences.getInstance(); + final savedCode = preferences.getString(preferenceKey); + if (savedCode != null && supportedCodes.contains(savedCode)) { + _locale = Locale(savedCode); + } + await _loadDynamicTables(); + notifyListeners(); + } + + Future _loadDynamicTables() async { + for (final code in supportedCodes) { + try { + final raw = await rootBundle.loadString('assets/translations_dynamic/$code.json'); + final decoded = jsonDecode(raw) as Map; + _dynamicTables[code] = decoded.map( + (key, value) => MapEntry(_normalizeKey(key), value.toString()), + ); + } catch (_) { + _dynamicTables[code] = {}; + } + } + } + + Future setLocale(String code) async { + if (!supportedCodes.contains(code) || code == _locale.languageCode) return; + _locale = Locale(code); + notifyListeners(); + final preferences = await SharedPreferences.getInstance(); + await preferences.setString(preferenceKey, code); + } + + String dynamicText(String key) { + final normalized = _normalizeKey(key); + return _dynamicTables[_locale.languageCode]?[normalized] ?? + _dynamicTables['en']?[normalized] ?? + key.replaceAll('_', ' '); + } + + String _normalizeKey(String key) => + key.trim().toLowerCase().replaceAll(RegExp(r'\s+'), '_'); +} diff --git a/base_project/lib/l10n/app_en.arb b/base_project/lib/l10n/app_en.arb new file mode 100644 index 0000000..0b4fada --- /dev/null +++ b/base_project/lib/l10n/app_en.arb @@ -0,0 +1,177 @@ +{ + "@@locale": "en", + "appTitle": "AuthSec Flutter", + "language": "Language", + "english": "English", + "hindi": "Hindi", + "helloUser": "Hello, {name}", + "@helloUser": {"placeholders": {"name": {"type": "String"}}}, + "user": "User", + "logout": "Logout", + "cancel": "Cancel", + "confirm": "Confirm", + "retry": "Retry", + "refresh": "Refresh", + "addNew": "Add new", + "searchEntity": "Search {entity}...", + "@searchEntity": {"placeholders": {"entity": {"type": "String"}}}, + "loadingEntity": "Loading {entity}...", + "@loadingEntity": {"placeholders": {"entity": {"type": "String"}}}, + "noEntityFound": "No {entity} found", + "@noEntityFound": {"placeholders": {"entity": {"type": "String"}}}, + "addEntity": "Add {entity}", + "@addEntity": {"placeholders": {"entity": {"type": "String"}}}, + "createEntity": "Create {entity}", + "@createEntity": {"placeholders": {"entity": {"type": "String"}}}, + "updateEntity": "Update {entity}", + "@updateEntity": {"placeholders": {"entity": {"type": "String"}}}, + "entityRequired": "{field} is required", + "@entityRequired": {"placeholders": {"field": {"type": "String"}}}, + "errorLoadingData": "Error loading data", + "somethingWentWrong": "Something went wrong", + "adjustSearchTerms": "Try adjusting your search terms", + "startByAdding": "Start by adding your first {entity}", + "@startByAdding": {"placeholders": {"entity": {"type": "String"}}}, + "unknown": "Unknown", + "scanBarcode": "Scan barcode", + "enterOrScanBarcode": "Enter or scan barcode", + "addAudio": "Add audio", + "selectAudioFile": "Select audio file", + "pickColor": "Pick a color", + "systemParameters": "System Parameters", + "editSystemParameters": "Edit System Parameters", + "goBack": "Go back", + "profile": "Profile", + "editProfile": "Edit profile", + "accountUpdate": "Account update", + "changePassword": "Change password", + "confirmLogout": "Confirm logout", + "confirmLogoutBody": "Are you sure you want to log out?", + "logoutAction": "Log out", + "passwordsDoNotMatch": "Passwords do not match", + "requiredField": "This field is required", + "email": "Email", + "password": "Password", + "signIn": "Sign in", + "signUp": "Sign up", + "forgotPassword": "Forgot password?", + "loading": "Loading...", + "save": "Save", + "delete": "Delete", + "edit": "Edit", + "close": "Close" + ,"welcomeBack": "Welcome back!" + ,"signInToContinue": "Sign in to continue to your account" + ,"emailAddress": "Email address" + ,"enterYourEmail": "Enter your email" + ,"emailRequired": "Email is required" + ,"validEmail": "Please enter a valid email" + ,"enterYourPassword": "Enter your password" + ,"passwordRequired": "Password is required" + ,"passwordMinLength": "Password must be at least 6 characters" + ,"noAccount": "Don't have an account?" + ,"orContinueWith": "Or continue with" + ,"verifyOtp": "Verify OTP" + ,"otpSentTo": "Enter the 6-digit code sent to" + ,"yourEmail": "your email" + ,"enterOtp": "Enter 6-digit OTP" + ,"didNotReceiveCode": "Didn't receive code?" + ,"resendIn": "Resend in {seconds}s" + ,"@resendIn": {"placeholders": {"seconds": {"type": "int"}}} + ,"resend": "Resend" + ,"dashboard": "Dashboard" + ,"settings": "Settings" + ,"security": "Security" + ,"reports": "Reports" + ,"manageAccount": "Manage your account" + ,"configureSystemSettings": "Configure system settings" + ,"updateYourPassword": "Update your password" + ,"getOtp": "Get OTP" + ,"otpEmailPrompt": "Enter your email to receive a one-time password" + ,"enterEmailAddress": "Enter your email address" + ,"sendOtp": "Send OTP" + ,"sendingOtp": "Sending OTP..." + ,"alreadyHaveAccount": "Already have an account?" + ,"backToLogin": "Back to login" + ,"emailLooksGood": "Email looks good" + ,"invalidEmailAddress": "Invalid email address" + ,"noDataAvailable": "No data available" + ,"noResults": "No results" + ,"clearBtn": "Clear" + ,"doneBtn": "Done" + ,"addFiles": "Add Files" + ,"addImage": "Add Image" + ,"addVideo": "Add Video" + ,"insertBtn": "Insert" + ,"noDataFound": "No data found" + ,"sysConfigTitle": "System Configuration" + ,"sysConfigSubtitle": "Configure system-wide parameters and settings" + ,"generatingTheme": "Generating dynamic theme..." + ,"themeApplied": "Dynamic theme applied! Colors extracted from logo." + ,"themeImmediateNote": "Theme is applied immediately! Click \"Upload Logo\" to save the logo permanently to the database. The dynamic theme will work even if upload fails." + ,"uploadNewLogo": "Upload New Logo" + ,"removeLogo": "Remove Logo" + ,"logoAlreadyUploaded": "Company logo is already uploaded and saved in the system. You can upload a new one or remove the existing logo." + ,"manualOverride": "Enable Manual Override" + ,"colorPrimary": "Primary" + ,"colorSecondary": "Secondary" + ,"colorTertiary": "Tertiary" + ,"resetLogoColors": "Reset to Logo Colors" + ,"resetDefault": "Reset to Default" + ,"userRegistration": "User Registration" + ,"allowUserRegistration": "Allow new users to register accounts" + ,"themeGenerated": "Dynamic Theme Generated" + ,"resetThemeTooltip": "Reset to Default Theme" + ,"colorPalette": "Color Palette" + ,"colorAccent1": "Accent 1" + ,"colorAccent2": "Accent 2" + ,"colorN": "Color {n}" + ,"@colorN": {"placeholders": {"n": {"type": "String"}}} + ,"themeInfo": "Theme Information" + ,"totalColors": "Total Colors" + ,"themeDescription": "Description" + ,"warmColors": "Warm Colors" + ,"coolColors": "Cool Colors" + ,"neutralColors": "Neutral Colors" + ,"yes": "Yes" + ,"no": "No" + ,"primaryButton": "Primary Button" + ,"secondaryButton": "Secondary Button" + ,"uploadLogo": "Upload Logo" + ,"clearLogo": "Clear Logo" + ,"saveConfiguration": "Save Configuration" + ,"drawerHome": "Home" + ,"drawerUsers": "Users" + ,"drawerRoles": "Roles" + ,"drawerDepartments": "Departments" + ,"drawerPositions": "Positions" + ,"drawerCharges": "Charges" + ,"drawerWeeks": "Weeks" + ,"drawerShifts": "Shifts" + ,"drawerBilling": "Billing" + ,"drawerEdi": "EDI Settings" + ,"drawerCompany": "Company" + ,"drawerLogo": "Logo" + ,"registerCompany": "Register Company" + ,"accCompanyName": "Company Name" + ,"accCompanyHint": "Enter company name" + ,"accWorkspace": "Workspace" + ,"accWorkspaceHint": "Enter workspace" + ,"accGst": "GST Number" + ,"accGstHint": "Enter GST number" + ,"accMobile": "Mobile" + ,"accMobileHint": "Enter mobile number" + ,"accEmailReadonly": "Email (read-only)" + ,"accPan": "PAN Card" + ,"accPanHint": "Enter PAN" + ,"accWorking": "Working" + ,"accWorkingHint": "Working status" + ,"requiredVal": "Required" + ,"nActive": "Active" + ,"saveChanges": "Save Changes" + ,"selectOption": "Select an option" + ,"removeBtn": "Remove" + ,"ohSnap": "Oh Snap!" + ,"alertTitle": "Alert" + ,"successTitle": "Success" +} diff --git a/base_project/lib/l10n/app_hi.arb b/base_project/lib/l10n/app_hi.arb new file mode 100644 index 0000000..e921c84 --- /dev/null +++ b/base_project/lib/l10n/app_hi.arb @@ -0,0 +1,176 @@ +{ + "@@locale": "hi", + "appTitle": "ऑथसेक फ्लटर", + "language": "भाषा", + "english": "अंग्रेज़ी", + "hindi": "हिन्दी", + "helloUser": "नमस्ते, {name}", + "@helloUser": {"placeholders": {"name": {"type": "String"}}}, + "user": "उपयोगकर्ता", + "logout": "लॉग आउट", + "cancel": "रद्द करें", + "confirm": "पुष्टि करें", + "retry": "पुनः प्रयास करें", + "refresh": "रीफ़्रेश", + "addNew": "नया जोड़ें", + "searchEntity": "{entity} खोजें...", + "@searchEntity": {"placeholders": {"entity": {"type": "String"}}}, + "loadingEntity": "{entity} लोड हो रहा है...", + "@loadingEntity": {"placeholders": {"entity": {"type": "String"}}}, + "noEntityFound": "कोई {entity} नहीं मिला", + "@noEntityFound": {"placeholders": {"entity": {"type": "String"}}}, + "addEntity": "{entity} जोड़ें", + "@addEntity": {"placeholders": {"entity": {"type": "String"}}}, + "createEntity": "{entity} बनाएँ", + "@createEntity": {"placeholders": {"entity": {"type": "String"}}}, + "updateEntity": "{entity} अपडेट करें", + "@updateEntity": {"placeholders": {"entity": {"type": "String"}}}, + "entityRequired": "{field} आवश्यक है", + "@entityRequired": {"placeholders": {"field": {"type": "String"}}}, + "errorLoadingData": "डेटा लोड करने में त्रुटि", + "somethingWentWrong": "कुछ गलत हो गया", + "adjustSearchTerms": "अपने खोज शब्द बदलें", + "startByAdding": "अपना पहला {entity} जोड़कर शुरू करें", + "@startByAdding": {"placeholders": {"entity": {"type": "String"}}}, + "unknown": "अज्ञात", + "scanBarcode": "बारकोड स्कैन करें", + "enterOrScanBarcode": "बारकोड दर्ज करें या स्कैन करें", + "addAudio": "ऑडियो जोड़ें", + "selectAudioFile": "ऑडियो फ़ाइल चुनें", + "pickColor": "रंग चुनें", + "systemParameters": "सिस्टम पैरामीटर", + "editSystemParameters": "सिस्टम पैरामीटर संपादित करें", + "goBack": "वापस जाएँ", + "profile": "प्रोफ़ाइल", + "editProfile": "प्रोफ़ाइल संपादित करें", + "accountUpdate": "खाता अपडेट", + "changePassword": "पासवर्ड बदलें", + "confirmLogout": "लॉग आउट की पुष्टि करें", + "confirmLogoutBody": "क्या आप वाकई लॉग आउट करना चाहते हैं?", + "logoutAction": "लॉग आउट", + "passwordsDoNotMatch": "पासवर्ड मेल नहीं खाते", + "requiredField": "यह फ़ील्ड आवश्यक है", + "email": "ईमेल", + "password": "पासवर्ड", + "signIn": "साइन इन", + "signUp": "साइन अप", + "forgotPassword": "पासवर्ड भूल गए?", + "loading": "लोड हो रहा है...", + "save": "सहेजें", + "delete": "हटाएँ", + "edit": "संपादित करें", + "close": "बंद करें" + ,"welcomeBack": "वापस स्वागत है!" + ,"signInToContinue": "अपने खाते में जारी रखने के लिए साइन इन करें" + ,"emailAddress": "ईमेल पता" + ,"enterYourEmail": "अपना ईमेल दर्ज करें" + ,"emailRequired": "ईमेल आवश्यक है" + ,"validEmail": "कृपया मान्य ईमेल दर्ज करें" + ,"enterYourPassword": "अपना पासवर्ड दर्ज करें" + ,"passwordRequired": "पासवर्ड आवश्यक है" + ,"passwordMinLength": "पासवर्ड कम से कम 6 वर्ण का होना चाहिए" + ,"noAccount": "क्या आपका खाता नहीं है?" + ,"orContinueWith": "या इसके साथ जारी रखें" + ,"verifyOtp": "ओटीपी सत्यापित करें" + ,"otpSentTo": "भेजा गया 6 अंकों का कोड दर्ज करें" + ,"yourEmail": "आपका ईमेल" + ,"enterOtp": "6 अंकों का ओटीपी दर्ज करें" + ,"didNotReceiveCode": "कोड नहीं मिला?" + ,"resendIn": "{seconds} सेकंड में पुनः भेजें" + ,"@resendIn": {"placeholders": {"seconds": {"type": "int"}}} + ,"resend": "पुनः भेजें" + ,"dashboard": "डैशबोर्ड" + ,"settings": "सेटिंग्स" + ,"security": "सुरक्षा" + ,"reports": "रिपोर्ट" + ,"manageAccount": "अपने खाते का प्रबंधन करें" + ,"configureSystemSettings": "सिस्टम सेटिंग्स कॉन्फ़िगर करें" + ,"updateYourPassword": "अपना पासवर्ड अपडेट करें" + ,"getOtp": "ओटीपी प्राप्त करें" + ,"otpEmailPrompt": "वन-टाइम पासवर्ड पाने के लिए अपना ईमेल दर्ज करें" + ,"enterEmailAddress": "अपना ईमेल पता दर्ज करें" + ,"sendOtp": "ओटीपी भेजें" + ,"sendingOtp": "ओटीपी भेजा जा रहा है..." + ,"alreadyHaveAccount": "क्या आपका पहले से खाता है?" + ,"backToLogin": "लॉगिन पर वापस जाएँ" + ,"emailLooksGood": "ईमेल सही है" + ,"invalidEmailAddress": "अमान्य ईमेल पता" + ,"noDataAvailable": "कोई डेटा उपलब्ध नहीं" + ,"noResults": "कोई परिणाम नहीं" + ,"clearBtn": "साफ़ करें" + ,"doneBtn": "हो गया" + ,"addFiles": "फ़ाइलें जोड़ें" + ,"addImage": "छवि जोड़ें" + ,"addVideo": "वीडियो जोड़ें" + ,"insertBtn": "डालें" + ,"noDataFound": "कोई डेटा नहीं मिला" + ,"sysConfigTitle": "सिस्टम कॉन्फ़िगरेशन" + ,"sysConfigSubtitle": "सिस्टम-व्यापी पैरामीटर और सेटिंग्स कॉन्फ़िगर करें" + ,"generatingTheme": "डायनामिक थीम बनाई जा रही है..." + ,"themeApplied": "डायनामिक थीम लागू! लोगो से रंग निकाले गए।" + ,"themeImmediateNote": "थीम तुरंत लागू होती है! लोगो को डेटाबेस में स्थायी रूप से सहेजने के लिए अपलोड लोगो पर क्लिक करें। अपलोड विफल होने पर भी डायनामिक थीम काम करेगी।" + ,"uploadNewLogo": "नया लोगो अपलोड करें" + ,"removeLogo": "लोगो हटाएं" + ,"logoAlreadyUploaded": "कंपनी का लोगो पहले से अपलोड है और सिस्टम में सहेजा गया है। आप नया अपलोड कर सकते हैं या मौजूदा लोगो हटा सकते हैं।" + ,"manualOverride": "मैन्युअल ओवरराइड सक्षम करें" + ,"colorPrimary": "प्राथमिक" + ,"colorSecondary": "द्वितीयक" + ,"colorTertiary": "तृतीयक" + ,"resetLogoColors": "लोगो रंगों पर रीसेट करें" + ,"resetDefault": "डिफ़ॉल्ट पर रीसेट करें" + ,"userRegistration": "उपयोगकर्ता पंजीकरण" + ,"allowUserRegistration": "नए उपयोगकर्ताओं को खाते पंजीकृत करने दें" + ,"themeGenerated": "डायनामिक थीम बनाई गई" + ,"resetThemeTooltip": "डिफ़ॉल्ट थीम पर रीसेट करें" + ,"colorPalette": "रंग पट्टिका" + ,"colorAccent1": "एक्सेंट 1" + ,"colorAccent2": "एक्सेंट 2" + ,"colorN": "रंग {n}" + ,"themeInfo": "थीम जानकारी" + ,"totalColors": "कुल रंग" + ,"themeDescription": "विवरण" + ,"warmColors": "गर्म रंग" + ,"coolColors": "ठंडे रंग" + ,"neutralColors": "तटस्थ रंग" + ,"yes": "हां" + ,"no": "नहीं" + ,"primaryButton": "प्राथमिक बटन" + ,"secondaryButton": "द्वितीयक बटन" + ,"uploadLogo": "लोगो अपलोड करें" + ,"clearLogo": "लोगो साफ़ करें" + ,"saveConfiguration": "कॉन्फ़िगरेशन सहेजें" + ,"drawerHome": "होम" + ,"drawerUsers": "उपयोगकर्ता" + ,"drawerRoles": "भूमिकाएं" + ,"drawerDepartments": "विभाग" + ,"drawerPositions": "पद" + ,"drawerCharges": "शुल्क" + ,"drawerWeeks": "सप्ताह" + ,"drawerShifts": "पारियां" + ,"drawerBilling": "बिलिंग" + ,"drawerEdi": "EDI सेटिंग्स" + ,"drawerCompany": "कंपनी" + ,"drawerLogo": "लोगो" + ,"registerCompany": "कंपनी पंजीकृत करें" + ,"accCompanyName": "कंपनी का नाम" + ,"accCompanyHint": "कंपनी का नाम दर्ज करें" + ,"accWorkspace": "कार्यक्षेत्र" + ,"accWorkspaceHint": "कार्यक्षेत्र दर्ज करें" + ,"accGst": "जीएसटी नंबर" + ,"accGstHint": "जीएसटी नंबर दर्ज करें" + ,"accMobile": "मोबाइल" + ,"accMobileHint": "मोबाइल नंबर दर्ज करें" + ,"accEmailReadonly": "ईमेल (केवल-पठन)" + ,"accPan": "पैन कार्ड" + ,"accPanHint": "पैन दर्ज करें" + ,"accWorking": "कार्य" + ,"accWorkingHint": "कार्य स्थिति" + ,"requiredVal": "आवश्यक" + ,"nActive": "सक्रिय" + ,"saveChanges": "परिवर्तन सहेजें" + ,"selectOption": "एक विकल्प चुनें" + ,"removeBtn": "हटाएं" + ,"ohSnap": "ओह स्नैप!" + ,"alertTitle": "चेतावनी" + ,"successTitle": "सफलता" +} diff --git a/base_project/lib/l10n/app_localizations.dart b/base_project/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..7a8a063 --- /dev/null +++ b/base_project/lib/l10n/app_localizations.dart @@ -0,0 +1,1111 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_hi.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('hi') + ]; + + /// No description provided for @appTitle. + /// + /// In en, this message translates to: + /// **'AuthSec Flutter'** + String get appTitle; + + /// No description provided for @language. + /// + /// In en, this message translates to: + /// **'Language'** + String get language; + + /// No description provided for @english. + /// + /// In en, this message translates to: + /// **'English'** + String get english; + + /// No description provided for @hindi. + /// + /// In en, this message translates to: + /// **'Hindi'** + String get hindi; + + /// No description provided for @helloUser. + /// + /// In en, this message translates to: + /// **'Hello, {name}'** + String helloUser(String name); + + /// No description provided for @user. + /// + /// In en, this message translates to: + /// **'User'** + String get user; + + /// No description provided for @logout. + /// + /// In en, this message translates to: + /// **'Logout'** + String get logout; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @confirm. + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// No description provided for @retry. + /// + /// In en, this message translates to: + /// **'Retry'** + String get retry; + + /// No description provided for @refresh. + /// + /// In en, this message translates to: + /// **'Refresh'** + String get refresh; + + /// No description provided for @addNew. + /// + /// In en, this message translates to: + /// **'Add new'** + String get addNew; + + /// No description provided for @searchEntity. + /// + /// In en, this message translates to: + /// **'Search {entity}...'** + String searchEntity(String entity); + + /// No description provided for @loadingEntity. + /// + /// In en, this message translates to: + /// **'Loading {entity}...'** + String loadingEntity(String entity); + + /// No description provided for @noEntityFound. + /// + /// In en, this message translates to: + /// **'No {entity} found'** + String noEntityFound(String entity); + + /// No description provided for @addEntity. + /// + /// In en, this message translates to: + /// **'Add {entity}'** + String addEntity(String entity); + + /// No description provided for @createEntity. + /// + /// In en, this message translates to: + /// **'Create {entity}'** + String createEntity(String entity); + + /// No description provided for @updateEntity. + /// + /// In en, this message translates to: + /// **'Update {entity}'** + String updateEntity(String entity); + + /// No description provided for @entityRequired. + /// + /// In en, this message translates to: + /// **'{field} is required'** + String entityRequired(String field); + + /// No description provided for @errorLoadingData. + /// + /// In en, this message translates to: + /// **'Error loading data'** + String get errorLoadingData; + + /// No description provided for @somethingWentWrong. + /// + /// In en, this message translates to: + /// **'Something went wrong'** + String get somethingWentWrong; + + /// No description provided for @adjustSearchTerms. + /// + /// In en, this message translates to: + /// **'Try adjusting your search terms'** + String get adjustSearchTerms; + + /// No description provided for @startByAdding. + /// + /// In en, this message translates to: + /// **'Start by adding your first {entity}'** + String startByAdding(String entity); + + /// No description provided for @unknown. + /// + /// In en, this message translates to: + /// **'Unknown'** + String get unknown; + + /// No description provided for @scanBarcode. + /// + /// In en, this message translates to: + /// **'Scan barcode'** + String get scanBarcode; + + /// No description provided for @enterOrScanBarcode. + /// + /// In en, this message translates to: + /// **'Enter or scan barcode'** + String get enterOrScanBarcode; + + /// No description provided for @addAudio. + /// + /// In en, this message translates to: + /// **'Add audio'** + String get addAudio; + + /// No description provided for @selectAudioFile. + /// + /// In en, this message translates to: + /// **'Select audio file'** + String get selectAudioFile; + + /// No description provided for @pickColor. + /// + /// In en, this message translates to: + /// **'Pick a color'** + String get pickColor; + + /// No description provided for @systemParameters. + /// + /// In en, this message translates to: + /// **'System Parameters'** + String get systemParameters; + + /// No description provided for @editSystemParameters. + /// + /// In en, this message translates to: + /// **'Edit System Parameters'** + String get editSystemParameters; + + /// No description provided for @goBack. + /// + /// In en, this message translates to: + /// **'Go back'** + String get goBack; + + /// No description provided for @profile. + /// + /// In en, this message translates to: + /// **'Profile'** + String get profile; + + /// No description provided for @editProfile. + /// + /// In en, this message translates to: + /// **'Edit profile'** + String get editProfile; + + /// No description provided for @accountUpdate. + /// + /// In en, this message translates to: + /// **'Account update'** + String get accountUpdate; + + /// No description provided for @changePassword. + /// + /// In en, this message translates to: + /// **'Change password'** + String get changePassword; + + /// No description provided for @confirmLogout. + /// + /// In en, this message translates to: + /// **'Confirm logout'** + String get confirmLogout; + + /// No description provided for @confirmLogoutBody. + /// + /// In en, this message translates to: + /// **'Are you sure you want to log out?'** + String get confirmLogoutBody; + + /// No description provided for @logoutAction. + /// + /// In en, this message translates to: + /// **'Log out'** + String get logoutAction; + + /// No description provided for @passwordsDoNotMatch. + /// + /// In en, this message translates to: + /// **'Passwords do not match'** + String get passwordsDoNotMatch; + + /// No description provided for @requiredField. + /// + /// In en, this message translates to: + /// **'This field is required'** + String get requiredField; + + /// No description provided for @email. + /// + /// In en, this message translates to: + /// **'Email'** + String get email; + + /// No description provided for @password. + /// + /// In en, this message translates to: + /// **'Password'** + String get password; + + /// No description provided for @signIn. + /// + /// In en, this message translates to: + /// **'Sign in'** + String get signIn; + + /// No description provided for @signUp. + /// + /// In en, this message translates to: + /// **'Sign up'** + String get signUp; + + /// No description provided for @forgotPassword. + /// + /// In en, this message translates to: + /// **'Forgot password?'** + String get forgotPassword; + + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loading; + + /// No description provided for @save. + /// + /// In en, this message translates to: + /// **'Save'** + String get save; + + /// No description provided for @delete. + /// + /// In en, this message translates to: + /// **'Delete'** + String get delete; + + /// No description provided for @edit. + /// + /// In en, this message translates to: + /// **'Edit'** + String get edit; + + /// No description provided for @close. + /// + /// In en, this message translates to: + /// **'Close'** + String get close; + + /// No description provided for @welcomeBack. + /// + /// In en, this message translates to: + /// **'Welcome back!'** + String get welcomeBack; + + /// No description provided for @signInToContinue. + /// + /// In en, this message translates to: + /// **'Sign in to continue to your account'** + String get signInToContinue; + + /// No description provided for @emailAddress. + /// + /// In en, this message translates to: + /// **'Email address'** + String get emailAddress; + + /// No description provided for @enterYourEmail. + /// + /// In en, this message translates to: + /// **'Enter your email'** + String get enterYourEmail; + + /// No description provided for @emailRequired. + /// + /// In en, this message translates to: + /// **'Email is required'** + String get emailRequired; + + /// No description provided for @validEmail. + /// + /// In en, this message translates to: + /// **'Please enter a valid email'** + String get validEmail; + + /// No description provided for @enterYourPassword. + /// + /// In en, this message translates to: + /// **'Enter your password'** + String get enterYourPassword; + + /// No description provided for @passwordRequired. + /// + /// In en, this message translates to: + /// **'Password is required'** + String get passwordRequired; + + /// No description provided for @passwordMinLength. + /// + /// In en, this message translates to: + /// **'Password must be at least 6 characters'** + String get passwordMinLength; + + /// No description provided for @noAccount. + /// + /// In en, this message translates to: + /// **'Don\'t have an account?'** + String get noAccount; + + /// No description provided for @orContinueWith. + /// + /// In en, this message translates to: + /// **'Or continue with'** + String get orContinueWith; + + /// No description provided for @verifyOtp. + /// + /// In en, this message translates to: + /// **'Verify OTP'** + String get verifyOtp; + + /// No description provided for @otpSentTo. + /// + /// In en, this message translates to: + /// **'Enter the 6-digit code sent to'** + String get otpSentTo; + + /// No description provided for @yourEmail. + /// + /// In en, this message translates to: + /// **'your email'** + String get yourEmail; + + /// No description provided for @enterOtp. + /// + /// In en, this message translates to: + /// **'Enter 6-digit OTP'** + String get enterOtp; + + /// No description provided for @didNotReceiveCode. + /// + /// In en, this message translates to: + /// **'Didn\'t receive code?'** + String get didNotReceiveCode; + + /// No description provided for @resendIn. + /// + /// In en, this message translates to: + /// **'Resend in {seconds}s'** + String resendIn(int seconds); + + /// No description provided for @resend. + /// + /// In en, this message translates to: + /// **'Resend'** + String get resend; + + /// No description provided for @dashboard. + /// + /// In en, this message translates to: + /// **'Dashboard'** + String get dashboard; + + /// No description provided for @settings. + /// + /// In en, this message translates to: + /// **'Settings'** + String get settings; + + /// No description provided for @security. + /// + /// In en, this message translates to: + /// **'Security'** + String get security; + + /// No description provided for @reports. + /// + /// In en, this message translates to: + /// **'Reports'** + String get reports; + + /// No description provided for @manageAccount. + /// + /// In en, this message translates to: + /// **'Manage your account'** + String get manageAccount; + + /// No description provided for @configureSystemSettings. + /// + /// In en, this message translates to: + /// **'Configure system settings'** + String get configureSystemSettings; + + /// No description provided for @updateYourPassword. + /// + /// In en, this message translates to: + /// **'Update your password'** + String get updateYourPassword; + + /// No description provided for @getOtp. + /// + /// In en, this message translates to: + /// **'Get OTP'** + String get getOtp; + + /// No description provided for @otpEmailPrompt. + /// + /// In en, this message translates to: + /// **'Enter your email to receive a one-time password'** + String get otpEmailPrompt; + + /// No description provided for @enterEmailAddress. + /// + /// In en, this message translates to: + /// **'Enter your email address'** + String get enterEmailAddress; + + /// No description provided for @sendOtp. + /// + /// In en, this message translates to: + /// **'Send OTP'** + String get sendOtp; + + /// No description provided for @sendingOtp. + /// + /// In en, this message translates to: + /// **'Sending OTP...'** + String get sendingOtp; + + /// No description provided for @alreadyHaveAccount. + /// + /// In en, this message translates to: + /// **'Already have an account?'** + String get alreadyHaveAccount; + + /// No description provided for @backToLogin. + /// + /// In en, this message translates to: + /// **'Back to login'** + String get backToLogin; + + /// No description provided for @emailLooksGood. + /// + /// In en, this message translates to: + /// **'Email looks good'** + String get emailLooksGood; + + /// No description provided for @invalidEmailAddress. + /// + /// In en, this message translates to: + /// **'Invalid email address'** + String get invalidEmailAddress; + + /// No description provided for @noDataAvailable. + /// + /// In en, this message translates to: + /// **'No data available'** + String get noDataAvailable; + + /// No description provided for @noResults. + /// + /// In en, this message translates to: + /// **'No results'** + String get noResults; + + /// No description provided for @clearBtn. + /// + /// In en, this message translates to: + /// **'Clear'** + String get clearBtn; + + /// No description provided for @doneBtn. + /// + /// In en, this message translates to: + /// **'Done'** + String get doneBtn; + + /// No description provided for @addFiles. + /// + /// In en, this message translates to: + /// **'Add Files'** + String get addFiles; + + /// No description provided for @addImage. + /// + /// In en, this message translates to: + /// **'Add Image'** + String get addImage; + + /// No description provided for @addVideo. + /// + /// In en, this message translates to: + /// **'Add Video'** + String get addVideo; + + /// No description provided for @insertBtn. + /// + /// In en, this message translates to: + /// **'Insert'** + String get insertBtn; + + /// No description provided for @noDataFound. + /// + /// In en, this message translates to: + /// **'No data found'** + String get noDataFound; + + /// No description provided for @sysConfigTitle. + /// + /// In en, this message translates to: + /// **'System Configuration'** + String get sysConfigTitle; + + /// No description provided for @sysConfigSubtitle. + /// + /// In en, this message translates to: + /// **'Configure system-wide parameters and settings'** + String get sysConfigSubtitle; + + /// No description provided for @generatingTheme. + /// + /// In en, this message translates to: + /// **'Generating dynamic theme...'** + String get generatingTheme; + + /// No description provided for @themeApplied. + /// + /// In en, this message translates to: + /// **'Dynamic theme applied! Colors extracted from logo.'** + String get themeApplied; + + /// No description provided for @themeImmediateNote. + /// + /// In en, this message translates to: + /// **'Theme is applied immediately! Click \"Upload Logo\" to save the logo permanently to the database. The dynamic theme will work even if upload fails.'** + String get themeImmediateNote; + + /// No description provided for @uploadNewLogo. + /// + /// In en, this message translates to: + /// **'Upload New Logo'** + String get uploadNewLogo; + + /// No description provided for @removeLogo. + /// + /// In en, this message translates to: + /// **'Remove Logo'** + String get removeLogo; + + /// No description provided for @logoAlreadyUploaded. + /// + /// In en, this message translates to: + /// **'Company logo is already uploaded and saved in the system. You can upload a new one or remove the existing logo.'** + String get logoAlreadyUploaded; + + /// No description provided for @manualOverride. + /// + /// In en, this message translates to: + /// **'Enable Manual Override'** + String get manualOverride; + + /// No description provided for @colorPrimary. + /// + /// In en, this message translates to: + /// **'Primary'** + String get colorPrimary; + + /// No description provided for @colorSecondary. + /// + /// In en, this message translates to: + /// **'Secondary'** + String get colorSecondary; + + /// No description provided for @colorTertiary. + /// + /// In en, this message translates to: + /// **'Tertiary'** + String get colorTertiary; + + /// No description provided for @resetLogoColors. + /// + /// In en, this message translates to: + /// **'Reset to Logo Colors'** + String get resetLogoColors; + + /// No description provided for @resetDefault. + /// + /// In en, this message translates to: + /// **'Reset to Default'** + String get resetDefault; + + /// No description provided for @userRegistration. + /// + /// In en, this message translates to: + /// **'User Registration'** + String get userRegistration; + + /// No description provided for @allowUserRegistration. + /// + /// In en, this message translates to: + /// **'Allow new users to register accounts'** + String get allowUserRegistration; + + /// No description provided for @themeGenerated. + /// + /// In en, this message translates to: + /// **'Dynamic Theme Generated'** + String get themeGenerated; + + /// No description provided for @resetThemeTooltip. + /// + /// In en, this message translates to: + /// **'Reset to Default Theme'** + String get resetThemeTooltip; + + /// No description provided for @colorPalette. + /// + /// In en, this message translates to: + /// **'Color Palette'** + String get colorPalette; + + /// No description provided for @colorAccent1. + /// + /// In en, this message translates to: + /// **'Accent 1'** + String get colorAccent1; + + /// No description provided for @colorAccent2. + /// + /// In en, this message translates to: + /// **'Accent 2'** + String get colorAccent2; + + /// No description provided for @colorN. + /// + /// In en, this message translates to: + /// **'Color {n}'** + String colorN(String n); + + /// No description provided for @themeInfo. + /// + /// In en, this message translates to: + /// **'Theme Information'** + String get themeInfo; + + /// No description provided for @totalColors. + /// + /// In en, this message translates to: + /// **'Total Colors'** + String get totalColors; + + /// No description provided for @themeDescription. + /// + /// In en, this message translates to: + /// **'Description'** + String get themeDescription; + + /// No description provided for @warmColors. + /// + /// In en, this message translates to: + /// **'Warm Colors'** + String get warmColors; + + /// No description provided for @coolColors. + /// + /// In en, this message translates to: + /// **'Cool Colors'** + String get coolColors; + + /// No description provided for @neutralColors. + /// + /// In en, this message translates to: + /// **'Neutral Colors'** + String get neutralColors; + + /// No description provided for @yes. + /// + /// In en, this message translates to: + /// **'Yes'** + String get yes; + + /// No description provided for @no. + /// + /// In en, this message translates to: + /// **'No'** + String get no; + + /// No description provided for @primaryButton. + /// + /// In en, this message translates to: + /// **'Primary Button'** + String get primaryButton; + + /// No description provided for @secondaryButton. + /// + /// In en, this message translates to: + /// **'Secondary Button'** + String get secondaryButton; + + /// No description provided for @uploadLogo. + /// + /// In en, this message translates to: + /// **'Upload Logo'** + String get uploadLogo; + + /// No description provided for @clearLogo. + /// + /// In en, this message translates to: + /// **'Clear Logo'** + String get clearLogo; + + /// No description provided for @saveConfiguration. + /// + /// In en, this message translates to: + /// **'Save Configuration'** + String get saveConfiguration; + + /// No description provided for @drawerHome. + /// + /// In en, this message translates to: + /// **'Home'** + String get drawerHome; + + /// No description provided for @drawerUsers. + /// + /// In en, this message translates to: + /// **'Users'** + String get drawerUsers; + + /// No description provided for @drawerRoles. + /// + /// In en, this message translates to: + /// **'Roles'** + String get drawerRoles; + + /// No description provided for @drawerDepartments. + /// + /// In en, this message translates to: + /// **'Departments'** + String get drawerDepartments; + + /// No description provided for @drawerPositions. + /// + /// In en, this message translates to: + /// **'Positions'** + String get drawerPositions; + + /// No description provided for @drawerCharges. + /// + /// In en, this message translates to: + /// **'Charges'** + String get drawerCharges; + + /// No description provided for @drawerWeeks. + /// + /// In en, this message translates to: + /// **'Weeks'** + String get drawerWeeks; + + /// No description provided for @drawerShifts. + /// + /// In en, this message translates to: + /// **'Shifts'** + String get drawerShifts; + + /// No description provided for @drawerBilling. + /// + /// In en, this message translates to: + /// **'Billing'** + String get drawerBilling; + + /// No description provided for @drawerEdi. + /// + /// In en, this message translates to: + /// **'EDI Settings'** + String get drawerEdi; + + /// No description provided for @drawerCompany. + /// + /// In en, this message translates to: + /// **'Company'** + String get drawerCompany; + + /// No description provided for @drawerLogo. + /// + /// In en, this message translates to: + /// **'Logo'** + String get drawerLogo; + + /// No description provided for @registerCompany. + /// + /// In en, this message translates to: + /// **'Register Company'** + String get registerCompany; + + /// No description provided for @accCompanyName. + /// + /// In en, this message translates to: + /// **'Company Name'** + String get accCompanyName; + + /// No description provided for @accCompanyHint. + /// + /// In en, this message translates to: + /// **'Enter company name'** + String get accCompanyHint; + + /// No description provided for @accWorkspace. + /// + /// In en, this message translates to: + /// **'Workspace'** + String get accWorkspace; + + /// No description provided for @accWorkspaceHint. + /// + /// In en, this message translates to: + /// **'Enter workspace'** + String get accWorkspaceHint; + + /// No description provided for @accGst. + /// + /// In en, this message translates to: + /// **'GST Number'** + String get accGst; + + /// No description provided for @accGstHint. + /// + /// In en, this message translates to: + /// **'Enter GST number'** + String get accGstHint; + + /// No description provided for @accMobile. + /// + /// In en, this message translates to: + /// **'Mobile'** + String get accMobile; + + /// No description provided for @accMobileHint. + /// + /// In en, this message translates to: + /// **'Enter mobile number'** + String get accMobileHint; + + /// No description provided for @accEmailReadonly. + /// + /// In en, this message translates to: + /// **'Email (read-only)'** + String get accEmailReadonly; + + /// No description provided for @accPan. + /// + /// In en, this message translates to: + /// **'PAN Card'** + String get accPan; + + /// No description provided for @accPanHint. + /// + /// In en, this message translates to: + /// **'Enter PAN'** + String get accPanHint; + + /// No description provided for @accWorking. + /// + /// In en, this message translates to: + /// **'Working'** + String get accWorking; + + /// No description provided for @accWorkingHint. + /// + /// In en, this message translates to: + /// **'Working status'** + String get accWorkingHint; + + /// No description provided for @requiredVal. + /// + /// In en, this message translates to: + /// **'Required'** + String get requiredVal; + + /// No description provided for @nActive. + /// + /// In en, this message translates to: + /// **'Active'** + String get nActive; + + /// No description provided for @saveChanges. + /// + /// In en, this message translates to: + /// **'Save Changes'** + String get saveChanges; + + /// No description provided for @selectOption. + /// + /// In en, this message translates to: + /// **'Select an option'** + String get selectOption; + + /// No description provided for @removeBtn. + /// + /// In en, this message translates to: + /// **'Remove'** + String get removeBtn; + + /// No description provided for @ohSnap. + /// + /// In en, this message translates to: + /// **'Oh Snap!'** + String get ohSnap; + + /// No description provided for @alertTitle. + /// + /// In en, this message translates to: + /// **'Alert'** + String get alertTitle; + + /// No description provided for @successTitle. + /// + /// In en, this message translates to: + /// **'Success'** + String get successTitle; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'hi'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'hi': + return AppLocalizationsHi(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/base_project/lib/l10n/app_localizations_en.dart b/base_project/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..00abbe2 --- /dev/null +++ b/base_project/lib/l10n/app_localizations_en.dart @@ -0,0 +1,526 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appTitle => 'AuthSec Flutter'; + + @override + String get language => 'Language'; + + @override + String get english => 'English'; + + @override + String get hindi => 'Hindi'; + + @override + String helloUser(String name) { + return 'Hello, $name'; + } + + @override + String get user => 'User'; + + @override + String get logout => 'Logout'; + + @override + String get cancel => 'Cancel'; + + @override + String get confirm => 'Confirm'; + + @override + String get retry => 'Retry'; + + @override + String get refresh => 'Refresh'; + + @override + String get addNew => 'Add new'; + + @override + String searchEntity(String entity) { + return 'Search $entity...'; + } + + @override + String loadingEntity(String entity) { + return 'Loading $entity...'; + } + + @override + String noEntityFound(String entity) { + return 'No $entity found'; + } + + @override + String addEntity(String entity) { + return 'Add $entity'; + } + + @override + String createEntity(String entity) { + return 'Create $entity'; + } + + @override + String updateEntity(String entity) { + return 'Update $entity'; + } + + @override + String entityRequired(String field) { + return '$field is required'; + } + + @override + String get errorLoadingData => 'Error loading data'; + + @override + String get somethingWentWrong => 'Something went wrong'; + + @override + String get adjustSearchTerms => 'Try adjusting your search terms'; + + @override + String startByAdding(String entity) { + return 'Start by adding your first $entity'; + } + + @override + String get unknown => 'Unknown'; + + @override + String get scanBarcode => 'Scan barcode'; + + @override + String get enterOrScanBarcode => 'Enter or scan barcode'; + + @override + String get addAudio => 'Add audio'; + + @override + String get selectAudioFile => 'Select audio file'; + + @override + String get pickColor => 'Pick a color'; + + @override + String get systemParameters => 'System Parameters'; + + @override + String get editSystemParameters => 'Edit System Parameters'; + + @override + String get goBack => 'Go back'; + + @override + String get profile => 'Profile'; + + @override + String get editProfile => 'Edit profile'; + + @override + String get accountUpdate => 'Account update'; + + @override + String get changePassword => 'Change password'; + + @override + String get confirmLogout => 'Confirm logout'; + + @override + String get confirmLogoutBody => 'Are you sure you want to log out?'; + + @override + String get logoutAction => 'Log out'; + + @override + String get passwordsDoNotMatch => 'Passwords do not match'; + + @override + String get requiredField => 'This field is required'; + + @override + String get email => 'Email'; + + @override + String get password => 'Password'; + + @override + String get signIn => 'Sign in'; + + @override + String get signUp => 'Sign up'; + + @override + String get forgotPassword => 'Forgot password?'; + + @override + String get loading => 'Loading...'; + + @override + String get save => 'Save'; + + @override + String get delete => 'Delete'; + + @override + String get edit => 'Edit'; + + @override + String get close => 'Close'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get signInToContinue => 'Sign in to continue to your account'; + + @override + String get emailAddress => 'Email address'; + + @override + String get enterYourEmail => 'Enter your email'; + + @override + String get emailRequired => 'Email is required'; + + @override + String get validEmail => 'Please enter a valid email'; + + @override + String get enterYourPassword => 'Enter your password'; + + @override + String get passwordRequired => 'Password is required'; + + @override + String get passwordMinLength => 'Password must be at least 6 characters'; + + @override + String get noAccount => 'Don\'t have an account?'; + + @override + String get orContinueWith => 'Or continue with'; + + @override + String get verifyOtp => 'Verify OTP'; + + @override + String get otpSentTo => 'Enter the 6-digit code sent to'; + + @override + String get yourEmail => 'your email'; + + @override + String get enterOtp => 'Enter 6-digit OTP'; + + @override + String get didNotReceiveCode => 'Didn\'t receive code?'; + + @override + String resendIn(int seconds) { + return 'Resend in ${seconds}s'; + } + + @override + String get resend => 'Resend'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get settings => 'Settings'; + + @override + String get security => 'Security'; + + @override + String get reports => 'Reports'; + + @override + String get manageAccount => 'Manage your account'; + + @override + String get configureSystemSettings => 'Configure system settings'; + + @override + String get updateYourPassword => 'Update your password'; + + @override + String get getOtp => 'Get OTP'; + + @override + String get otpEmailPrompt => + 'Enter your email to receive a one-time password'; + + @override + String get enterEmailAddress => 'Enter your email address'; + + @override + String get sendOtp => 'Send OTP'; + + @override + String get sendingOtp => 'Sending OTP...'; + + @override + String get alreadyHaveAccount => 'Already have an account?'; + + @override + String get backToLogin => 'Back to login'; + + @override + String get emailLooksGood => 'Email looks good'; + + @override + String get invalidEmailAddress => 'Invalid email address'; + + @override + String get noDataAvailable => 'No data available'; + + @override + String get noResults => 'No results'; + + @override + String get clearBtn => 'Clear'; + + @override + String get doneBtn => 'Done'; + + @override + String get addFiles => 'Add Files'; + + @override + String get addImage => 'Add Image'; + + @override + String get addVideo => 'Add Video'; + + @override + String get insertBtn => 'Insert'; + + @override + String get noDataFound => 'No data found'; + + @override + String get sysConfigTitle => 'System Configuration'; + + @override + String get sysConfigSubtitle => + 'Configure system-wide parameters and settings'; + + @override + String get generatingTheme => 'Generating dynamic theme...'; + + @override + String get themeApplied => + 'Dynamic theme applied! Colors extracted from logo.'; + + @override + String get themeImmediateNote => + 'Theme is applied immediately! Click \"Upload Logo\" to save the logo permanently to the database. The dynamic theme will work even if upload fails.'; + + @override + String get uploadNewLogo => 'Upload New Logo'; + + @override + String get removeLogo => 'Remove Logo'; + + @override + String get logoAlreadyUploaded => + 'Company logo is already uploaded and saved in the system. You can upload a new one or remove the existing logo.'; + + @override + String get manualOverride => 'Enable Manual Override'; + + @override + String get colorPrimary => 'Primary'; + + @override + String get colorSecondary => 'Secondary'; + + @override + String get colorTertiary => 'Tertiary'; + + @override + String get resetLogoColors => 'Reset to Logo Colors'; + + @override + String get resetDefault => 'Reset to Default'; + + @override + String get userRegistration => 'User Registration'; + + @override + String get allowUserRegistration => 'Allow new users to register accounts'; + + @override + String get themeGenerated => 'Dynamic Theme Generated'; + + @override + String get resetThemeTooltip => 'Reset to Default Theme'; + + @override + String get colorPalette => 'Color Palette'; + + @override + String get colorAccent1 => 'Accent 1'; + + @override + String get colorAccent2 => 'Accent 2'; + + @override + String colorN(String n) { + return 'Color $n'; + } + + @override + String get themeInfo => 'Theme Information'; + + @override + String get totalColors => 'Total Colors'; + + @override + String get themeDescription => 'Description'; + + @override + String get warmColors => 'Warm Colors'; + + @override + String get coolColors => 'Cool Colors'; + + @override + String get neutralColors => 'Neutral Colors'; + + @override + String get yes => 'Yes'; + + @override + String get no => 'No'; + + @override + String get primaryButton => 'Primary Button'; + + @override + String get secondaryButton => 'Secondary Button'; + + @override + String get uploadLogo => 'Upload Logo'; + + @override + String get clearLogo => 'Clear Logo'; + + @override + String get saveConfiguration => 'Save Configuration'; + + @override + String get drawerHome => 'Home'; + + @override + String get drawerUsers => 'Users'; + + @override + String get drawerRoles => 'Roles'; + + @override + String get drawerDepartments => 'Departments'; + + @override + String get drawerPositions => 'Positions'; + + @override + String get drawerCharges => 'Charges'; + + @override + String get drawerWeeks => 'Weeks'; + + @override + String get drawerShifts => 'Shifts'; + + @override + String get drawerBilling => 'Billing'; + + @override + String get drawerEdi => 'EDI Settings'; + + @override + String get drawerCompany => 'Company'; + + @override + String get drawerLogo => 'Logo'; + + @override + String get registerCompany => 'Register Company'; + + @override + String get accCompanyName => 'Company Name'; + + @override + String get accCompanyHint => 'Enter company name'; + + @override + String get accWorkspace => 'Workspace'; + + @override + String get accWorkspaceHint => 'Enter workspace'; + + @override + String get accGst => 'GST Number'; + + @override + String get accGstHint => 'Enter GST number'; + + @override + String get accMobile => 'Mobile'; + + @override + String get accMobileHint => 'Enter mobile number'; + + @override + String get accEmailReadonly => 'Email (read-only)'; + + @override + String get accPan => 'PAN Card'; + + @override + String get accPanHint => 'Enter PAN'; + + @override + String get accWorking => 'Working'; + + @override + String get accWorkingHint => 'Working status'; + + @override + String get requiredVal => 'Required'; + + @override + String get nActive => 'Active'; + + @override + String get saveChanges => 'Save Changes'; + + @override + String get selectOption => 'Select an option'; + + @override + String get removeBtn => 'Remove'; + + @override + String get ohSnap => 'Oh Snap!'; + + @override + String get alertTitle => 'Alert'; + + @override + String get successTitle => 'Success'; +} diff --git a/base_project/lib/l10n/app_localizations_hi.dart b/base_project/lib/l10n/app_localizations_hi.dart new file mode 100644 index 0000000..1252870 --- /dev/null +++ b/base_project/lib/l10n/app_localizations_hi.dart @@ -0,0 +1,526 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hindi (`hi`). +class AppLocalizationsHi extends AppLocalizations { + AppLocalizationsHi([String locale = 'hi']) : super(locale); + + @override + String get appTitle => 'ऑथसेक फ्लटर'; + + @override + String get language => 'भाषा'; + + @override + String get english => 'अंग्रेज़ी'; + + @override + String get hindi => 'हिन्दी'; + + @override + String helloUser(String name) { + return 'नमस्ते, $name'; + } + + @override + String get user => 'उपयोगकर्ता'; + + @override + String get logout => 'लॉग आउट'; + + @override + String get cancel => 'रद्द करें'; + + @override + String get confirm => 'पुष्टि करें'; + + @override + String get retry => 'पुनः प्रयास करें'; + + @override + String get refresh => 'रीफ़्रेश'; + + @override + String get addNew => 'नया जोड़ें'; + + @override + String searchEntity(String entity) { + return '$entity खोजें...'; + } + + @override + String loadingEntity(String entity) { + return '$entity लोड हो रहा है...'; + } + + @override + String noEntityFound(String entity) { + return 'कोई $entity नहीं मिला'; + } + + @override + String addEntity(String entity) { + return '$entity जोड़ें'; + } + + @override + String createEntity(String entity) { + return '$entity बनाएँ'; + } + + @override + String updateEntity(String entity) { + return '$entity अपडेट करें'; + } + + @override + String entityRequired(String field) { + return '$field आवश्यक है'; + } + + @override + String get errorLoadingData => 'डेटा लोड करने में त्रुटि'; + + @override + String get somethingWentWrong => 'कुछ गलत हो गया'; + + @override + String get adjustSearchTerms => 'अपने खोज शब्द बदलें'; + + @override + String startByAdding(String entity) { + return 'अपना पहला $entity जोड़कर शुरू करें'; + } + + @override + String get unknown => 'अज्ञात'; + + @override + String get scanBarcode => 'बारकोड स्कैन करें'; + + @override + String get enterOrScanBarcode => 'बारकोड दर्ज करें या स्कैन करें'; + + @override + String get addAudio => 'ऑडियो जोड़ें'; + + @override + String get selectAudioFile => 'ऑडियो फ़ाइल चुनें'; + + @override + String get pickColor => 'रंग चुनें'; + + @override + String get systemParameters => 'सिस्टम पैरामीटर'; + + @override + String get editSystemParameters => 'सिस्टम पैरामीटर संपादित करें'; + + @override + String get goBack => 'वापस जाएँ'; + + @override + String get profile => 'प्रोफ़ाइल'; + + @override + String get editProfile => 'प्रोफ़ाइल संपादित करें'; + + @override + String get accountUpdate => 'खाता अपडेट'; + + @override + String get changePassword => 'पासवर्ड बदलें'; + + @override + String get confirmLogout => 'लॉग आउट की पुष्टि करें'; + + @override + String get confirmLogoutBody => 'क्या आप वाकई लॉग आउट करना चाहते हैं?'; + + @override + String get logoutAction => 'लॉग आउट'; + + @override + String get passwordsDoNotMatch => 'पासवर्ड मेल नहीं खाते'; + + @override + String get requiredField => 'यह फ़ील्ड आवश्यक है'; + + @override + String get email => 'ईमेल'; + + @override + String get password => 'पासवर्ड'; + + @override + String get signIn => 'साइन इन'; + + @override + String get signUp => 'साइन अप'; + + @override + String get forgotPassword => 'पासवर्ड भूल गए?'; + + @override + String get loading => 'लोड हो रहा है...'; + + @override + String get save => 'सहेजें'; + + @override + String get delete => 'हटाएँ'; + + @override + String get edit => 'संपादित करें'; + + @override + String get close => 'बंद करें'; + + @override + String get welcomeBack => 'वापस स्वागत है!'; + + @override + String get signInToContinue => 'अपने खाते में जारी रखने के लिए साइन इन करें'; + + @override + String get emailAddress => 'ईमेल पता'; + + @override + String get enterYourEmail => 'अपना ईमेल दर्ज करें'; + + @override + String get emailRequired => 'ईमेल आवश्यक है'; + + @override + String get validEmail => 'कृपया मान्य ईमेल दर्ज करें'; + + @override + String get enterYourPassword => 'अपना पासवर्ड दर्ज करें'; + + @override + String get passwordRequired => 'पासवर्ड आवश्यक है'; + + @override + String get passwordMinLength => 'पासवर्ड कम से कम 6 वर्ण का होना चाहिए'; + + @override + String get noAccount => 'क्या आपका खाता नहीं है?'; + + @override + String get orContinueWith => 'या इसके साथ जारी रखें'; + + @override + String get verifyOtp => 'ओटीपी सत्यापित करें'; + + @override + String get otpSentTo => 'भेजा गया 6 अंकों का कोड दर्ज करें'; + + @override + String get yourEmail => 'आपका ईमेल'; + + @override + String get enterOtp => '6 अंकों का ओटीपी दर्ज करें'; + + @override + String get didNotReceiveCode => 'कोड नहीं मिला?'; + + @override + String resendIn(int seconds) { + return '$seconds सेकंड में पुनः भेजें'; + } + + @override + String get resend => 'पुनः भेजें'; + + @override + String get dashboard => 'डैशबोर्ड'; + + @override + String get settings => 'सेटिंग्स'; + + @override + String get security => 'सुरक्षा'; + + @override + String get reports => 'रिपोर्ट'; + + @override + String get manageAccount => 'अपने खाते का प्रबंधन करें'; + + @override + String get configureSystemSettings => 'सिस्टम सेटिंग्स कॉन्फ़िगर करें'; + + @override + String get updateYourPassword => 'अपना पासवर्ड अपडेट करें'; + + @override + String get getOtp => 'ओटीपी प्राप्त करें'; + + @override + String get otpEmailPrompt => + 'वन-टाइम पासवर्ड पाने के लिए अपना ईमेल दर्ज करें'; + + @override + String get enterEmailAddress => 'अपना ईमेल पता दर्ज करें'; + + @override + String get sendOtp => 'ओटीपी भेजें'; + + @override + String get sendingOtp => 'ओटीपी भेजा जा रहा है...'; + + @override + String get alreadyHaveAccount => 'क्या आपका पहले से खाता है?'; + + @override + String get backToLogin => 'लॉगिन पर वापस जाएँ'; + + @override + String get emailLooksGood => 'ईमेल सही है'; + + @override + String get invalidEmailAddress => 'अमान्य ईमेल पता'; + + @override + String get noDataAvailable => 'कोई डेटा उपलब्ध नहीं'; + + @override + String get noResults => 'कोई परिणाम नहीं'; + + @override + String get clearBtn => 'साफ़ करें'; + + @override + String get doneBtn => 'हो गया'; + + @override + String get addFiles => 'फ़ाइलें जोड़ें'; + + @override + String get addImage => 'छवि जोड़ें'; + + @override + String get addVideo => 'वीडियो जोड़ें'; + + @override + String get insertBtn => 'डालें'; + + @override + String get noDataFound => 'कोई डेटा नहीं मिला'; + + @override + String get sysConfigTitle => 'सिस्टम कॉन्फ़िगरेशन'; + + @override + String get sysConfigSubtitle => + 'सिस्टम-व्यापी पैरामीटर और सेटिंग्स कॉन्फ़िगर करें'; + + @override + String get generatingTheme => 'डायनामिक थीम बनाई जा रही है...'; + + @override + String get themeApplied => 'डायनामिक थीम लागू! लोगो से रंग निकाले गए।'; + + @override + String get themeImmediateNote => + 'थीम तुरंत लागू होती है! लोगो को डेटाबेस में स्थायी रूप से सहेजने के लिए अपलोड लोगो पर क्लिक करें। अपलोड विफल होने पर भी डायनामिक थीम काम करेगी।'; + + @override + String get uploadNewLogo => 'नया लोगो अपलोड करें'; + + @override + String get removeLogo => 'लोगो हटाएं'; + + @override + String get logoAlreadyUploaded => + 'कंपनी का लोगो पहले से अपलोड है और सिस्टम में सहेजा गया है। आप नया अपलोड कर सकते हैं या मौजूदा लोगो हटा सकते हैं।'; + + @override + String get manualOverride => 'मैन्युअल ओवरराइड सक्षम करें'; + + @override + String get colorPrimary => 'प्राथमिक'; + + @override + String get colorSecondary => 'द्वितीयक'; + + @override + String get colorTertiary => 'तृतीयक'; + + @override + String get resetLogoColors => 'लोगो रंगों पर रीसेट करें'; + + @override + String get resetDefault => 'डिफ़ॉल्ट पर रीसेट करें'; + + @override + String get userRegistration => 'उपयोगकर्ता पंजीकरण'; + + @override + String get allowUserRegistration => + 'नए उपयोगकर्ताओं को खाते पंजीकृत करने दें'; + + @override + String get themeGenerated => 'डायनामिक थीम बनाई गई'; + + @override + String get resetThemeTooltip => 'डिफ़ॉल्ट थीम पर रीसेट करें'; + + @override + String get colorPalette => 'रंग पट्टिका'; + + @override + String get colorAccent1 => 'एक्सेंट 1'; + + @override + String get colorAccent2 => 'एक्सेंट 2'; + + @override + String colorN(String n) { + return 'रंग $n'; + } + + @override + String get themeInfo => 'थीम जानकारी'; + + @override + String get totalColors => 'कुल रंग'; + + @override + String get themeDescription => 'विवरण'; + + @override + String get warmColors => 'गर्म रंग'; + + @override + String get coolColors => 'ठंडे रंग'; + + @override + String get neutralColors => 'तटस्थ रंग'; + + @override + String get yes => 'हां'; + + @override + String get no => 'नहीं'; + + @override + String get primaryButton => 'प्राथमिक बटन'; + + @override + String get secondaryButton => 'द्वितीयक बटन'; + + @override + String get uploadLogo => 'लोगो अपलोड करें'; + + @override + String get clearLogo => 'लोगो साफ़ करें'; + + @override + String get saveConfiguration => 'कॉन्फ़िगरेशन सहेजें'; + + @override + String get drawerHome => 'होम'; + + @override + String get drawerUsers => 'उपयोगकर्ता'; + + @override + String get drawerRoles => 'भूमिकाएं'; + + @override + String get drawerDepartments => 'विभाग'; + + @override + String get drawerPositions => 'पद'; + + @override + String get drawerCharges => 'शुल्क'; + + @override + String get drawerWeeks => 'सप्ताह'; + + @override + String get drawerShifts => 'पारियां'; + + @override + String get drawerBilling => 'बिलिंग'; + + @override + String get drawerEdi => 'EDI सेटिंग्स'; + + @override + String get drawerCompany => 'कंपनी'; + + @override + String get drawerLogo => 'लोगो'; + + @override + String get registerCompany => 'कंपनी पंजीकृत करें'; + + @override + String get accCompanyName => 'कंपनी का नाम'; + + @override + String get accCompanyHint => 'कंपनी का नाम दर्ज करें'; + + @override + String get accWorkspace => 'कार्यक्षेत्र'; + + @override + String get accWorkspaceHint => 'कार्यक्षेत्र दर्ज करें'; + + @override + String get accGst => 'जीएसटी नंबर'; + + @override + String get accGstHint => 'जीएसटी नंबर दर्ज करें'; + + @override + String get accMobile => 'मोबाइल'; + + @override + String get accMobileHint => 'मोबाइल नंबर दर्ज करें'; + + @override + String get accEmailReadonly => 'ईमेल (केवल-पठन)'; + + @override + String get accPan => 'पैन कार्ड'; + + @override + String get accPanHint => 'पैन दर्ज करें'; + + @override + String get accWorking => 'कार्य'; + + @override + String get accWorkingHint => 'कार्य स्थिति'; + + @override + String get requiredVal => 'आवश्यक'; + + @override + String get nActive => 'सक्रिय'; + + @override + String get saveChanges => 'परिवर्तन सहेजें'; + + @override + String get selectOption => 'एक विकल्प चुनें'; + + @override + String get removeBtn => 'हटाएं'; + + @override + String get ohSnap => 'ओह स्नैप!'; + + @override + String get alertTitle => 'चेतावनी'; + + @override + String get successTitle => 'सफलता'; +} diff --git a/base_project/lib/main.dart b/base_project/lib/main.dart index 7030472..c8815b2 100644 --- a/base_project/lib/main.dart +++ b/base_project/lib/main.dart @@ -7,14 +7,18 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'core/providers/dynamic_theme_provider.dart'; +import 'core/providers/locale_provider.dart'; import 'core/providers/theme_provider.dart'; import 'core/theme/app_theme.dart'; +import 'l10n/app_localizations.dart'; import 'routes/app_routes.dart'; import 'utils/managers/user_manager.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await UserManager().initialize(); + final localeProvider = LocaleProvider(); + await localeProvider.initialize(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); runApp(MultiProvider( @@ -24,6 +28,7 @@ void main() async { ChangeNotifierProvider(create: (context) => SystemParamsViewModel()), ChangeNotifierProvider(create: (context) => ThemeProvider()), ChangeNotifierProvider(create: (context) => DynamicThemeProvider()), + ChangeNotifierProvider.value(value: localeProvider), ], child: const MyApp(), )); @@ -63,8 +68,8 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer2( - builder: (context, themeProvider, dynamicThemeProvider, child) { + return Consumer3( + builder: (context, themeProvider, dynamicThemeProvider, localeProvider, child) { final theme = AppTheme.getTheme(context); // Set system UI overlay style SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( @@ -86,6 +91,9 @@ class MyApp extends StatelessWidget { initialRoute: RouteNames.splashView, onGenerateRoute: AppRoutes.generateRoutes, navigatorKey: navigatorKey, + locale: localeProvider.locale, + supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, builder: (context, child) { return MediaQuery( data: MediaQuery.of(context).copyWith( diff --git a/base_project/lib/shared/widgets/inputs/modern_image_picker.dart b/base_project/lib/shared/widgets/inputs/modern_image_picker.dart index eb7ea82..b246113 100644 --- a/base_project/lib/shared/widgets/inputs/modern_image_picker.dart +++ b/base_project/lib/shared/widgets/inputs/modern_image_picker.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'dart:typed_data'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import '../../../core/constants/ui_constants.dart'; import '../buttons/modern_button.dart'; @@ -208,7 +209,7 @@ class _ModernImagePickerState extends State if (hasImage && widget.onRemoveImage != null) ...[ const SizedBox(width: UIConstants.spacing12), ModernButton( - text: 'Remove', + text: AppLocalizations.of(context)!.removeBtn, type: ModernButtonType.danger, size: ModernButtonSize.medium, icon: Icon(Icons.delete_outline), diff --git a/base_project/lib/shared/widgets/language_switcher.dart b/base_project/lib/shared/widgets/language_switcher.dart new file mode 100644 index 0000000..08e0544 --- /dev/null +++ b/base_project/lib/shared/widgets/language_switcher.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../core/providers/locale_provider.dart'; +import '../../l10n/app_localizations.dart'; + +class LanguageSwitcher extends StatelessWidget { + const LanguageSwitcher({super.key}); + + @override + Widget build(BuildContext context) { + final locale = context.watch().locale.languageCode; + final strings = AppLocalizations.of(context)!; + return DropdownButtonHideUnderline( + child: DropdownButton( + value: locale, + icon: const Icon(Icons.language), + onChanged: (code) { + if (code != null) context.read().setLocale(code); + }, + items: [ + DropdownMenuItem(value: 'en', child: Text(strings.english)), + DropdownMenuItem(value: 'hi', child: Text(strings.hindi)), + ], + ), + ); + } +} diff --git a/base_project/lib/shared/widgets/navigation/modern_drawer.dart b/base_project/lib/shared/widgets/navigation/modern_drawer.dart index 9ba39b7..19530e1 100644 --- a/base_project/lib/shared/widgets/navigation/modern_drawer.dart +++ b/base_project/lib/shared/widgets/navigation/modern_drawer.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../../core/constants/ui_constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../utils/managers/user_manager.dart'; import '../../../view_model/profile/profile_view_model.dart'; import '../buttons/modern_button.dart'; +import '../language_switcher.dart'; class ModernDrawer extends StatelessWidget { final List items; @@ -52,6 +54,17 @@ class ModernDrawer extends StatelessWidget { // Footer if (footer != null) footer!, + Padding( + padding: const EdgeInsets.symmetric(horizontal: UIConstants.spacing24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(AppLocalizations.of(context)!.language), + const LanguageSwitcher(), + ], + ), + ), + // Logout Section _buildLogoutSection(context, theme, colorScheme), ], @@ -122,7 +135,8 @@ class ModernDrawer extends StatelessWidget { // User Name Text( - "Hello, ${userName ?? 'User'}", + AppLocalizations.of(context)! + .helloUser(userName ?? AppLocalizations.of(context)!.user), style: theme.textTheme.titleLarge?.copyWith( color: Colors.white, fontWeight: FontWeight.w600, @@ -216,7 +230,7 @@ class ModernDrawer extends StatelessWidget { return Container( padding: const EdgeInsets.all(UIConstants.spacing24), child: ModernButton( - text: 'Logout', + text: AppLocalizations.of(context)!.logout, type: ModernButtonType.danger, size: ModernButtonSize.medium, icon: Icon(Icons.logout), diff --git a/base_project/lib/shared/widgets/theme_preview.dart b/base_project/lib/shared/widgets/theme_preview.dart index 3d4e7fc..57c5ed2 100644 --- a/base_project/lib/shared/widgets/theme_preview.dart +++ b/base_project/lib/shared/widgets/theme_preview.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/constants/ui_constants.dart'; import '../../core/providers/dynamic_theme_provider.dart'; +import '../../l10n/app_localizations.dart'; class ThemePreview extends StatelessWidget { const ThemePreview({super.key}); @@ -56,7 +57,7 @@ class ThemePreview extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Dynamic Theme Generated', + AppLocalizations.of(context)!.themeGenerated, style: Theme.of(context).textTheme.titleLarge?.copyWith( color: colorScheme.onSurface, @@ -79,7 +80,8 @@ class ThemePreview extends StatelessWidget { Icons.refresh, color: colorScheme.primary, ), - tooltip: 'Reset to Default Theme', + tooltip: AppLocalizations.of(context)! + .resetThemeTooltip, ), ], ), @@ -88,7 +90,7 @@ class ThemePreview extends StatelessWidget { // Color Palette Text( - 'Color Palette', + AppLocalizations.of(context)!.colorPalette, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: colorScheme.onSurface, fontWeight: FontWeight.w600, @@ -103,11 +105,11 @@ class ThemePreview extends StatelessWidget { children: logoColors.take(5).map((color) { final index = logoColors.indexOf(color); final labels = [ - 'Primary', - 'Secondary', - 'Tertiary', - 'Accent 1', - 'Accent 2' + AppLocalizations.of(context)!.colorPrimary, + AppLocalizations.of(context)!.colorSecondary, + AppLocalizations.of(context)!.colorTertiary, + AppLocalizations.of(context)!.colorAccent1, + AppLocalizations.of(context)!.colorAccent2 ]; return Column( @@ -142,7 +144,8 @@ class ThemePreview extends StatelessWidget { Text( index < labels.length ? labels[index] - : 'Color ${index + 1}', + : AppLocalizations.of(context)! + .colorN((index + 1).toString()), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500, @@ -169,7 +172,7 @@ class ThemePreview extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Theme Information', + AppLocalizations.of(context)!.themeInfo, style: Theme.of(context).textTheme.titleSmall?.copyWith( color: colorScheme.onSurface, fontWeight: FontWeight.w600, @@ -177,24 +180,27 @@ class ThemePreview extends StatelessWidget { ), const SizedBox(height: UIConstants.spacing8), _buildInfoRow( - 'Total Colors', '${paletteInfo['totalColors']}'), - _buildInfoRow('Description', paletteInfo['description']), + AppLocalizations.of(context)!.totalColors, + '${paletteInfo['totalColors']}'), + _buildInfoRow( + AppLocalizations.of(context)!.themeDescription, + paletteInfo['description']), if (paletteInfo['characteristics'] != null) ...[ _buildInfoRow( - 'Warm Colors', + AppLocalizations.of(context)!.warmColors, paletteInfo['characteristics']['warm'] - ? 'Yes' - : 'No'), + ? AppLocalizations.of(context)!.yes + : AppLocalizations.of(context)!.no), _buildInfoRow( - 'Cool Colors', + AppLocalizations.of(context)!.coolColors, paletteInfo['characteristics']['cool'] - ? 'Yes' - : 'No'), + ? AppLocalizations.of(context)!.yes + : AppLocalizations.of(context)!.no), _buildInfoRow( - 'Neutral Colors', + AppLocalizations.of(context)!.neutralColors, paletteInfo['characteristics']['neutral'] - ? 'Yes' - : 'No'), + ? AppLocalizations.of(context)!.yes + : AppLocalizations.of(context)!.no), ], ], ), @@ -216,7 +222,8 @@ class ThemePreview extends StatelessWidget { BorderRadius.circular(UIConstants.radius12), ), ), - child: const Text('Primary Button'), + child: Text(AppLocalizations.of(context)! + .primaryButton), ), ), const SizedBox(width: UIConstants.spacing12), @@ -231,7 +238,8 @@ class ThemePreview extends StatelessWidget { BorderRadius.circular(UIConstants.radius12), ), ), - child: const Text('Secondary Button'), + child: Text(AppLocalizations.of(context)! + .secondaryButton), ), ), ], diff --git a/base_project/lib/view/auth/admin_reg_view.dart b/base_project/lib/view/auth/admin_reg_view.dart index 3d7ca77..f40bdb5 100644 --- a/base_project/lib/view/auth/admin_reg_view.dart +++ b/base_project/lib/view/auth/admin_reg_view.dart @@ -2,6 +2,7 @@ import 'package:base_project/commans/widgets/custom_textform_field.dart'; import 'package:base_project/commans/widgets/custome_elevated_button.dart'; import 'package:base_project/core/constants/ui_constants.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/utils/validator/text_feild_validator.dart'; import 'package:base_project/view/auth/sign_up_view.dart'; import 'package:base_project/view_model/auth/auth_view_model.dart'; @@ -231,7 +232,8 @@ class _AdminRegViewState extends State builder: (context, provider, child) { return MyCustomElevatedButton( isLoading: provider.isLoading, - child: const Text("Register Company"), + child: Text(AppLocalizations.of(context)! + .registerCompany), onPressed: () async { if (_formKey.currentState!.validate()) { final Map signUpData = { diff --git a/base_project/lib/view/auth/get_otp.dart b/base_project/lib/view/auth/get_otp.dart index 824e3cd..6847400 100644 --- a/base_project/lib/view/auth/get_otp.dart +++ b/base_project/lib/view/auth/get_otp.dart @@ -9,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../l10n/app_localizations.dart'; class GetOtpView extends StatefulWidget { const GetOtpView({super.key}); @@ -483,7 +484,7 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { child: Column( children: [ Text( - 'Get OTP', + AppLocalizations.of(context)!.getOtp, style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, @@ -493,7 +494,7 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { ), const SizedBox(height: UIConstants.spacing12), Text( - 'Enter your email to receive a one-time password', + AppLocalizations.of(context)!.otpEmailPrompt, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.onSurface.withOpacity(0.8), fontWeight: FontWeight.w400, @@ -543,8 +544,8 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { children: [ // Email Field _buildEnhancedTextField( - label: 'Email Address', - hint: 'Enter your email address', + label: AppLocalizations.of(context)!.emailAddress, + hint: AppLocalizations.of(context)!.enterEmailAddress, controller: _emailController, focusNode: _emailFocusNode, keyboardType: TextInputType.emailAddress, @@ -554,12 +555,12 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { textInputAction: TextInputAction.done, validator: (value) { if (value == null || value.isEmpty) { - return 'Email is required'; + return AppLocalizations.of(context)!.emailRequired; } if (!RegExp( r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') .hasMatch(value)) { - return 'Please enter a valid email'; + return AppLocalizations.of(context)!.validEmail; } return null; }, @@ -847,7 +848,9 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { ) ?? const TextStyle(), child: Text( - isLoading ? 'Sending OTP...' : 'Send OTP', + isLoading + ? AppLocalizations.of(context)!.sendingOtp + : AppLocalizations.of(context)!.sendOtp, ), ); } @@ -919,7 +922,7 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "Already have an account? ", + '${AppLocalizations.of(context)!.alreadyHaveAccount} ', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey.shade700, fontWeight: FontWeight.w500, @@ -928,7 +931,7 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { TextButton( onPressed: () => Navigator.pop(context), child: Text( - 'Back to Login', + AppLocalizations.of(context)!.backToLogin, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, @@ -982,8 +985,10 @@ class _GetOtpViewState extends State with TickerProviderStateMixin { ? Icons.info_outline : (_isEmailValid ? Icons.check_circle : Icons.error_outline); final msg = _emailController.text.isEmpty - ? 'Enter your email address' - : (_isEmailValid ? 'Email looks good' : 'Invalid email address'); + ? AppLocalizations.of(context)!.enterEmailAddress + : (_isEmailValid + ? AppLocalizations.of(context)!.emailLooksGood + : AppLocalizations.of(context)!.invalidEmailAddress); return Container( width: UIConstants.getResponsiveValue( diff --git a/base_project/lib/view/auth/login.dart b/base_project/lib/view/auth/login.dart index 2172e72..cd35d7a 100644 --- a/base_project/lib/view/auth/login.dart +++ b/base_project/lib/view/auth/login.dart @@ -7,6 +7,7 @@ import 'package:base_project/routes/route_names.dart'; import 'package:base_project/view_model/auth/auth_view_model.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../l10n/app_localizations.dart'; import '../../core/providers/dynamic_theme_provider.dart'; import '../../core/providers/theme_provider.dart'; @@ -497,7 +498,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { child: Column( children: [ Text( - 'Welcome Back!', + AppLocalizations.of(context)!.welcomeBack, style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, @@ -507,7 +508,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { ), const SizedBox(height: UIConstants.spacing12), Text( - 'Sign in to continue to your account', + AppLocalizations.of(context)!.signInToContinue, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.onSurface.withOpacity(0.8), fontWeight: FontWeight.w400, @@ -557,8 +558,8 @@ class _LoginViewState extends State with TickerProviderStateMixin { children: [ // Email Field _buildEnhancedTextField( - label: 'Email Address', - hint: 'Enter your email', + label: AppLocalizations.of(context)!.emailAddress, + hint: AppLocalizations.of(context)!.enterYourEmail, controller: _emailController, focusNode: _emailFocusNode, keyboardType: TextInputType.emailAddress, @@ -569,11 +570,11 @@ class _LoginViewState extends State with TickerProviderStateMixin { onSubmitted: (_) => _passwordFocusNode.requestFocus(), validator: (value) { if (value == null || value.isEmpty) { - return 'Email is required'; + return AppLocalizations.of(context)!.emailRequired; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') .hasMatch(value)) { - return 'Please enter a valid email'; + return AppLocalizations.of(context)!.validEmail; } return null; }, @@ -583,8 +584,8 @@ class _LoginViewState extends State with TickerProviderStateMixin { // Password Field _buildEnhancedTextField( - label: 'Password', - hint: 'Enter your password', + label: AppLocalizations.of(context)!.password, + hint: AppLocalizations.of(context)!.enterYourPassword, controller: _passwordController, focusNode: _passwordFocusNode, obscureText: !_isPasswordVisible, @@ -607,10 +608,10 @@ class _LoginViewState extends State with TickerProviderStateMixin { textInputAction: TextInputAction.done, validator: (value) { if (value == null || value.isEmpty) { - return 'Password is required'; + return AppLocalizations.of(context)!.passwordRequired; } if (value.length < 6) { - return 'Password must be at least 6 characters'; + return AppLocalizations.of(context)!.passwordMinLength; } return null; }, @@ -727,7 +728,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { ), const SizedBox(width: UIConstants.spacing6), Text( - 'Forgot Password?', + AppLocalizations.of(context)!.forgotPassword, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w600, @@ -771,7 +772,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { child: Consumer( builder: (context, provider, child) { return ModernButton( - text: 'Sign In', + text: AppLocalizations.of(context)!.signIn, type: ModernButtonType.primary, size: ModernButtonSize.large, isLoading: provider.isLoading, @@ -823,7 +824,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "Don't have an account? ", + '${AppLocalizations.of(context)!.noAccount} ', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey.shade700, fontWeight: FontWeight.w500, @@ -834,7 +835,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { Navigator.pushNamed(context, RouteNames.getOtpView); }, child: Text( - 'Sign Up', + AppLocalizations.of(context)!.signUp, style: Theme.of(context).textTheme.labelLarge?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, @@ -870,7 +871,7 @@ class _LoginViewState extends State with TickerProviderStateMixin { padding: const EdgeInsets.symmetric( horizontal: UIConstants.spacing16), child: Text( - 'Or continue with', + AppLocalizations.of(context)!.orContinueWith, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.white.withOpacity(0.9), fontWeight: FontWeight.w500, diff --git a/base_project/lib/view/auth/sign_up_view.dart b/base_project/lib/view/auth/sign_up_view.dart index 16a6581..52ec584 100644 --- a/base_project/lib/view/auth/sign_up_view.dart +++ b/base_project/lib/view/auth/sign_up_view.dart @@ -1,5 +1,6 @@ import 'package:base_project/core/constants/ui_constants.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/routes/route_names.dart'; import 'package:base_project/shared/widgets/app_bar/modern_app_bar.dart'; import 'package:base_project/shared/widgets/buttons/modern_button.dart'; @@ -322,7 +323,7 @@ class _SignUpViewState extends State with TickerProviderStateMixin { Consumer( builder: (context, provider, child) { return ModernButton( - text: 'Sign Up', + text: AppLocalizations.of(context)!.signUp, type: ModernButtonType.primary, size: ModernButtonSize.large, isLoading: provider.isLoading, diff --git a/base_project/lib/view/auth/signup.dart b/base_project/lib/view/auth/signup.dart index ac90a05..440d4bd 100644 --- a/base_project/lib/view/auth/signup.dart +++ b/base_project/lib/view/auth/signup.dart @@ -1,3 +1,4 @@ +import 'package:base_project/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; class SignupView extends StatelessWidget { @@ -14,7 +15,7 @@ class SignupView extends StatelessWidget { }, icon: Icon(Icons.adaptive.arrow_back), ), - title: const Text("SignUp"), + title: Text(AppLocalizations.of(context)!.signUp), ), ); } diff --git a/base_project/lib/view/auth/verify_otp.dart b/base_project/lib/view/auth/verify_otp.dart index 9cf6e9b..7322f1f 100644 --- a/base_project/lib/view/auth/verify_otp.dart +++ b/base_project/lib/view/auth/verify_otp.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:pinput/pinput.dart'; import 'package:base_project/commans/widgets/custome_elevated_button.dart'; import 'package:provider/provider.dart'; +import '../../l10n/app_localizations.dart'; import 'package:base_project/core/constants/ui_constants.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; @@ -83,7 +84,7 @@ class _VerifyOtpViewState extends State color: colorScheme.primary, ), title: Text( - 'Verify OTP', + AppLocalizations.of(context)!.verifyOtp, style: Theme.of(context).textTheme.titleLarge?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, @@ -147,7 +148,7 @@ class _VerifyOtpViewState extends State ), const SizedBox(height: UIConstants.spacing16), Text( - 'Enter the 6-digit code sent to', + AppLocalizations.of(context)!.otpSentTo, style: Theme.of(context) .textTheme .bodyMedium @@ -157,7 +158,7 @@ class _VerifyOtpViewState extends State textAlign: TextAlign.center, ), Text( - widget.email ?? 'your email', + widget.email ?? AppLocalizations.of(context)!.yourEmail, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: colorScheme.primary, @@ -233,7 +234,7 @@ class _VerifyOtpViewState extends State onPressed: () async { if (_otpController.text.length < 6) { ToastMessageUtil.showToast( - message: 'Enter 6-digit OTP', + message: AppLocalizations.of(context)!.enterOtp, toastType: ToastType.error, ); return; @@ -268,7 +269,7 @@ class _VerifyOtpViewState extends State ); } }, - child: const Text('Verify OTP'), + child: Text(AppLocalizations.of(context)!.verifyOtp), ), ], ); @@ -285,8 +286,9 @@ class _VerifyOtpViewState extends State children: [ Text( provider.isResendEnabled - ? "Didn't receive code?" - : 'Resend in ${provider.start}s', + ? AppLocalizations.of(context)!.didNotReceiveCode + : AppLocalizations.of(context)! + .resendIn(provider.start), style: Theme.of(context) .textTheme .bodyMedium @@ -304,7 +306,7 @@ class _VerifyOtpViewState extends State } : null, child: Text( - 'Resend', + AppLocalizations.of(context)!.resend, style: TextStyle( color: provider.isResendEnabled ? colorScheme.primary diff --git a/base_project/lib/view/dashboard/home.dart b/base_project/lib/view/dashboard/home.dart index f640e18..f21cee2 100644 --- a/base_project/lib/view/dashboard/home.dart +++ b/base_project/lib/view/dashboard/home.dart @@ -9,6 +9,7 @@ import 'package:base_project/utils/managers/user_manager.dart'; import 'package:provider/provider.dart'; import 'package:base_project/view_model/system_params/system_params_view_model.dart'; import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; class HomeView extends StatefulWidget { const HomeView({super.key}); @@ -32,54 +33,54 @@ class _HomeViewState extends State with TickerProviderStateMixin { // Dashboard stats (replace with real API data) List> _dashboardStats = []; - final List> _quickActions = [ + List> _quickActions(BuildContext context) => [ { - 'label': 'Profile', + 'label': AppLocalizations.of(context)!.profile, 'icon': Icons.person, 'onTap': () {}, 'backgroundColor': null, // Use theme default }, { - 'label': 'Settings', + 'label': AppLocalizations.of(context)!.settings, 'icon': Icons.settings, 'onTap': () {}, 'backgroundColor': null, }, { - 'label': 'Security', + 'label': AppLocalizations.of(context)!.security, 'icon': Icons.security, 'onTap': () {}, 'backgroundColor': null, }, { - 'label': 'Reports', + 'label': AppLocalizations.of(context)!.reports, 'icon': Icons.analytics, 'onTap': () {}, 'backgroundColor': null, }, ]; - final List _drawerItems = [ + List _drawerItems(BuildContext context) => [ DrawerItem( icon: Icons.person, - title: 'Profile', - subtitle: 'Manage your account', + title: AppLocalizations.of(context)!.profile, + subtitle: AppLocalizations.of(context)!.manageAccount, onTap: (context) { Navigator.pushNamed(context, RouteNames.profileView); }, ), DrawerItem( icon: Icons.system_security_update, - title: 'System Parameters', - subtitle: 'Configure system settings', + title: AppLocalizations.of(context)!.systemParameters, + subtitle: AppLocalizations.of(context)!.configureSystemSettings, onTap: (context) { Navigator.pushNamed(context, RouteNames.systemParamsView); }, ), DrawerItem( icon: Icons.password, - title: 'Change Password', - subtitle: 'Update your password', + title: AppLocalizations.of(context)!.changePassword, + subtitle: AppLocalizations.of(context)!.updateYourPassword, onTap: (context) { Navigator.pushNamed(context, RouteNames.changePasswordView); }, @@ -226,7 +227,7 @@ class _HomeViewState extends State with TickerProviderStateMixin { return Scaffold( backgroundColor: colorScheme.background, appBar: ModernAppBar( - title: 'Dashboard', + title: AppLocalizations.of(context)!.dashboard, centerTitle: false, showLogoInTitle: true, logoImage: _getDashboardLogoImage(colorScheme), @@ -237,7 +238,7 @@ class _HomeViewState extends State with TickerProviderStateMixin { Navigator.pushNamed(context, RouteNames.profileView); }, ), - drawer: ModernDrawer(items: _drawerItems), + drawer: ModernDrawer(items: _drawerItems(context)), body: Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -339,7 +340,8 @@ class _HomeViewState extends State with TickerProviderStateMixin { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Welcome back, ${userName ?? 'User'}! 👋', + AppLocalizations.of(context)! + .helloUser(userName ?? AppLocalizations.of(context)!.user), style: theme.textTheme.headlineMedium?.copyWith( color: colorScheme.onBackground, fontWeight: FontWeight.w700, @@ -468,9 +470,9 @@ class _HomeViewState extends State with TickerProviderStateMixin { mainAxisSpacing: UIConstants.spacing16, childAspectRatio: 1.0, ), - itemCount: _quickActions.length, + itemCount: _quickActions(context).length, itemBuilder: (context, index) { - final action = _quickActions[index]; + final action = _quickActions(context)[index]; return QuickActionButton( label: action['label'], icon: action['icon'], diff --git a/base_project/lib/view/dashboard/profile/account_update.dart b/base_project/lib/view/dashboard/profile/account_update.dart index 38cd058..1e37c97 100644 --- a/base_project/lib/view/dashboard/profile/account_update.dart +++ b/base_project/lib/view/dashboard/profile/account_update.dart @@ -1,5 +1,6 @@ import 'package:base_project/core/constants/ui_constants.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/shared/widgets/app_bar/modern_app_bar.dart'; import 'package:base_project/shared/widgets/inputs/modern_text_field.dart'; import 'package:base_project/shared/widgets/buttons/modern_button.dart'; @@ -160,33 +161,37 @@ class _AccountUpdateViewState extends State crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ModernTextField( - label: 'Company Name', - hint: 'Enter company name', + label: AppLocalizations.of(context)!.accCompanyName, + hint: AppLocalizations.of(context)!.accCompanyHint, controller: _companyName, prefixIcon: const Icon(Icons.business), validator: (v) => - (v == null || v.isEmpty) ? 'Required' : null, + (v == null || v.isEmpty) + ? AppLocalizations.of(context)!.requiredVal + : null, ), SizedBox(height: UIConstants.spacing20), ModernTextField( - label: 'Workspace', - hint: 'Enter workspace', + label: AppLocalizations.of(context)!.accWorkspace, + hint: AppLocalizations.of(context)!.accWorkspaceHint, controller: _workspace, prefixIcon: const Icon(Icons.apartment_outlined), validator: (v) => - (v == null || v.isEmpty) ? 'Required' : null, + (v == null || v.isEmpty) + ? AppLocalizations.of(context)!.requiredVal + : null, ), SizedBox(height: UIConstants.spacing20), ModernTextField( - label: 'GST Number', - hint: 'Enter GST number', + label: AppLocalizations.of(context)!.accGst, + hint: AppLocalizations.of(context)!.accGstHint, controller: _gstNumber, prefixIcon: const Icon(Icons.confirmation_number_outlined), ), SizedBox(height: UIConstants.spacing20), ModernTextField( - label: 'Mobile', - hint: 'Enter mobile number', + label: AppLocalizations.of(context)!.accMobile, + hint: AppLocalizations.of(context)!.accMobileHint, controller: _mobile, keyboardType: TextInputType.phone, inputFormatters: [FilteringTextInputFormatter.digitsOnly], @@ -195,23 +200,23 @@ class _AccountUpdateViewState extends State SizedBox(height: UIConstants.spacing20), AbsorbPointer( child: ModernTextField( - label: 'Email (read-only)', - hint: 'Email', + label: AppLocalizations.of(context)!.accEmailReadonly, + hint: AppLocalizations.of(context)!.email, controller: _email, prefixIcon: const Icon(Icons.email_outlined), ), ), SizedBox(height: UIConstants.spacing20), ModernTextField( - label: 'PAN Card', - hint: 'Enter PAN', + label: AppLocalizations.of(context)!.accPan, + hint: AppLocalizations.of(context)!.accPanHint, controller: _pancard, prefixIcon: const Icon(Icons.badge_outlined), ), SizedBox(height: UIConstants.spacing20), ModernTextField( - label: 'Working', - hint: 'Working status', + label: AppLocalizations.of(context)!.accWorking, + hint: AppLocalizations.of(context)!.accWorkingHint, controller: _working, prefixIcon: const Icon(Icons.work_outline), ), @@ -223,7 +228,7 @@ class _AccountUpdateViewState extends State onChanged: (v) => setState(() => _active = v), ), const SizedBox(width: 8), - const Text('Active') + Text(AppLocalizations.of(context)!.nActive) ], ), SizedBox( @@ -234,7 +239,7 @@ class _AccountUpdateViewState extends State desktop: UIConstants.spacing48, )), ModernButton( - text: 'Update Account', + text: AppLocalizations.of(context)!.accountUpdate, type: ModernButtonType.primary, size: ModernButtonSize.large, isLoading: vm.isUpdating, diff --git a/base_project/lib/view/dashboard/profile/change_password.dart b/base_project/lib/view/dashboard/profile/change_password.dart index 1bfa675..ab545c4 100644 --- a/base_project/lib/view/dashboard/profile/change_password.dart +++ b/base_project/lib/view/dashboard/profile/change_password.dart @@ -1,5 +1,6 @@ import 'package:base_project/commans/widgets/custom_textform_field.dart'; import 'package:base_project/commans/widgets/custome_elevated_button.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/resources/app_colors.dart'; import 'package:base_project/utils/managers/user_manager.dart'; import 'package:base_project/utils/validator/text_feild_validator.dart'; @@ -372,7 +373,7 @@ class _ChangePasswordState extends State Consumer( builder: (context, provider, _) { return ModernButton( - text: 'Change Password', + text: AppLocalizations.of(context)!.changePassword, type: ModernButtonType.primary, size: ModernButtonSize.large, isLoading: provider.isUpdating, diff --git a/base_project/lib/view/dashboard/profile/edit_profile.dart b/base_project/lib/view/dashboard/profile/edit_profile.dart index 13b1584..6c5eb41 100644 --- a/base_project/lib/view/dashboard/profile/edit_profile.dart +++ b/base_project/lib/view/dashboard/profile/edit_profile.dart @@ -1,5 +1,6 @@ import 'package:base_project/commans/widgets/custom_textform_field.dart'; import 'package:base_project/commans/widgets/custome_elevated_button.dart'; +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/model/user/user_profile.dart'; import 'package:base_project/resources/app_colors.dart'; import 'package:base_project/view_model/profile/profile_view_model.dart'; @@ -396,7 +397,7 @@ class _EditProfileState extends State Consumer( builder: (context, provider, _) { return ModernButton( - text: 'Save Changes', + text: AppLocalizations.of(context)!.saveChanges, type: ModernButtonType.primary, size: ModernButtonSize.large, isLoading: provider.isUpdating, diff --git a/base_project/lib/view/dashboard/profile/profile.dart b/base_project/lib/view/dashboard/profile/profile.dart index 18bcd46..13854e3 100644 --- a/base_project/lib/view/dashboard/profile/profile.dart +++ b/base_project/lib/view/dashboard/profile/profile.dart @@ -6,6 +6,7 @@ import 'package:base_project/utils/managers/user_manager.dart'; import 'package:base_project/view/dashboard/profile/edit_profile.dart'; import 'package:base_project/view_model/profile/profile_view_model.dart'; import 'package:flutter/material.dart'; +import '../../../l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; @@ -77,7 +78,7 @@ class _ProfileViewState extends State return Scaffold( appBar: ModernAppBar( - title: 'Profile', + title: AppLocalizations.of(context)!.profile, automaticallyImplyLeading: true, leading: IconButton( icon: Icon(Icons.arrow_back), @@ -393,7 +394,7 @@ class _ProfileViewState extends State context, colorScheme: colorScheme, icon: Icons.edit_outlined, - title: 'Edit Profile', + title: AppLocalizations.of(context)!.editProfile, subtitle: 'Update your personal information', onTap: () { Navigator.push( @@ -411,7 +412,7 @@ class _ProfileViewState extends State context, colorScheme: colorScheme, icon: Icons.manage_accounts_outlined, - title: 'Account Update', + title: AppLocalizations.of(context)!.accountUpdate, subtitle: 'Update company/workspace details', onTap: () { Navigator.push( @@ -427,7 +428,7 @@ class _ProfileViewState extends State context, colorScheme: colorScheme, icon: Icons.lock_outline, - title: 'Change Password', + title: AppLocalizations.of(context)!.changePassword, subtitle: 'Update your account security', onTap: () { Navigator.pushNamed(context, RouteNames.changePasswordView); @@ -438,7 +439,7 @@ class _ProfileViewState extends State context, colorScheme: colorScheme, icon: Icons.logout, - title: 'Logout', + title: AppLocalizations.of(context)!.logout, subtitle: 'Sign out of your account', iconColor: colorScheme.error, onTap: () => _showLogoutDialog(context, colorScheme), @@ -541,10 +542,10 @@ class _ProfileViewState extends State size: UIConstants.iconSizeMedium, ), const SizedBox(width: UIConstants.spacing12), - const Text("Confirm Logout"), + Text(AppLocalizations.of(context)!.confirmLogout), ], ), - content: const Text("Are you sure you want to log out?"), + content: Text(AppLocalizations.of(context)!.confirmLogoutBody), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), @@ -563,7 +564,7 @@ class _ProfileViewState extends State backgroundColor: colorScheme.error, foregroundColor: colorScheme.onError, ), - child: const Text("Log Out"), + child: Text(AppLocalizations.of(context)!.logoutAction), ), ], ); diff --git a/base_project/lib/view/system_parameters/edit_system_params.dart b/base_project/lib/view/system_parameters/edit_system_params.dart index 0fea03f..9a868f6 100644 --- a/base_project/lib/view/system_parameters/edit_system_params.dart +++ b/base_project/lib/view/system_parameters/edit_system_params.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; class EditSystemParams extends StatelessWidget { const EditSystemParams({super.key}); @@ -13,9 +14,9 @@ class EditSystemParams extends StatelessWidget { }, icon: Icon(Icons.adaptive.arrow_back), ), - title: const Text("Edit System Parameters"), + title: Text(AppLocalizations.of(context)!.editSystemParameters), ), ); } -} \ No newline at end of file +} diff --git a/base_project/lib/view/system_parameters/system_parameters.dart b/base_project/lib/view/system_parameters/system_parameters.dart index ecca05d..be406ed 100644 --- a/base_project/lib/view/system_parameters/system_parameters.dart +++ b/base_project/lib/view/system_parameters/system_parameters.dart @@ -10,6 +10,7 @@ import 'package:base_project/shared/widgets/theme_preview.dart'; import 'package:base_project/utils/managers/user_manager.dart'; import 'package:base_project/view_model/system_params/system_params_view_model.dart'; import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:base_project/core/providers/dynamic_theme_provider.dart'; @@ -235,7 +236,7 @@ class _SystemParametersViewState extends State return Scaffold( backgroundColor: colorScheme.background, appBar: ModernAppBar( - title: 'System Parameters', + title: AppLocalizations.of(context)!.systemParameters, userAvatar: null, userName: userName, onProfilePressed: () {}, @@ -409,7 +410,7 @@ class _SystemParametersViewState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'System Configuration', + AppLocalizations.of(context)!.sysConfigTitle, style: theme.textTheme.headlineMedium?.copyWith( color: colorScheme.onBackground, fontWeight: FontWeight.w700, @@ -417,7 +418,7 @@ class _SystemParametersViewState extends State ), const SizedBox(height: UIConstants.spacing8), Text( - 'Configure system-wide parameters and settings', + AppLocalizations.of(context)!.sysConfigSubtitle, style: theme.textTheme.bodyLarge?.copyWith( color: colorScheme.onSurfaceVariant, ), @@ -691,7 +692,8 @@ class _SystemParametersViewState extends State ), const SizedBox(width: UIConstants.spacing8), Text( - 'Generating dynamic theme...', + AppLocalizations.of(context)! + .generatingTheme, style: TextStyle( color: Colors.blue, fontSize: 12, @@ -720,7 +722,8 @@ class _SystemParametersViewState extends State ), const SizedBox(width: UIConstants.spacing8), Text( - 'Dynamic theme applied! Colors extracted from logo.', + AppLocalizations.of(context)! + .themeApplied, style: TextStyle( color: Colors.green, fontSize: 12, @@ -742,7 +745,7 @@ class _SystemParametersViewState extends State SizedBox( width: double.infinity, child: ModernButton( - text: 'Upload Logo', + text: AppLocalizations.of(context)!.uploadLogo, type: ModernButtonType.primary, size: ModernButtonSize.medium, icon: Icon(Icons.upload), @@ -757,7 +760,7 @@ class _SystemParametersViewState extends State SizedBox( width: double.infinity, child: ModernButton( - text: 'Clear Logo', + text: AppLocalizations.of(context)!.clearLogo, type: ModernButtonType.outline, size: ModernButtonSize.medium, icon: Icon(Icons.delete_outline), @@ -788,7 +791,8 @@ class _SystemParametersViewState extends State const SizedBox(width: UIConstants.spacing8), Expanded( child: Text( - 'Theme is applied immediately! Click "Upload Logo" to save the logo permanently to the database. The dynamic theme will work even if upload fails.', + AppLocalizations.of(context)! + .themeImmediateNote, style: TextStyle( color: AppColorScheme.info, fontSize: 12, @@ -806,7 +810,7 @@ class _SystemParametersViewState extends State SizedBox( width: double.infinity, child: ModernButton( - text: 'Upload New Logo', + text: AppLocalizations.of(context)!.uploadNewLogo, type: ModernButtonType.primary, size: ModernButtonSize.medium, icon: Icon(Icons.upload), @@ -821,7 +825,8 @@ class _SystemParametersViewState extends State SizedBox( width: double.infinity, child: ModernButton( - text: 'Remove Logo', + text: + AppLocalizations.of(context)!.removeLogo, type: ModernButtonType.danger, size: ModernButtonSize.medium, icon: Icon(Icons.delete_outline), @@ -852,7 +857,8 @@ class _SystemParametersViewState extends State const SizedBox(width: UIConstants.spacing8), Expanded( child: Text( - 'Company logo is already uploaded and saved in the system. You can upload a new one or remove the existing logo.', + AppLocalizations.of(context)! + .logoAlreadyUploaded, style: TextStyle( color: AppColorScheme.success, fontSize: 12, @@ -913,7 +919,7 @@ class _SystemParametersViewState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Enable Manual Override', + AppLocalizations.of(context)!.manualOverride, style: theme.textTheme.titleMedium?.copyWith( color: cs.onSurface, fontWeight: FontWeight.w600, @@ -938,7 +944,7 @@ class _SystemParametersViewState extends State ), const SizedBox(height: UIConstants.spacing12), _buildColorRow( - label: 'Primary', + label: AppLocalizations.of(context)!.colorPrimary, color: effectivePrimary, onPick: () async { final picked = await _pickColor(context, effectivePrimary); @@ -949,7 +955,7 @@ class _SystemParametersViewState extends State ), const SizedBox(height: UIConstants.spacing12), _buildColorRow( - label: 'Secondary', + label: AppLocalizations.of(context)!.colorSecondary, color: effectiveSecondary, onPick: () async { final picked = @@ -961,7 +967,7 @@ class _SystemParametersViewState extends State ), const SizedBox(height: UIConstants.spacing12), _buildColorRow( - label: 'Tertiary', + label: AppLocalizations.of(context)!.colorTertiary, color: effectiveTertiary, onPick: () async { final picked = await _pickColor(context, effectiveTertiary); @@ -975,7 +981,8 @@ class _SystemParametersViewState extends State children: [ Expanded( child: ModernButton( - text: 'Reset to Logo Colors', + text: AppLocalizations.of(context)! + .resetLogoColors, type: ModernButtonType.outline, size: ModernButtonSize.medium, icon: Icon(Icons.refresh), @@ -987,7 +994,8 @@ class _SystemParametersViewState extends State const SizedBox(width: UIConstants.spacing12), Expanded( child: ModernButton( - text: 'Reset to Default', + text: AppLocalizations.of(context)! + .resetDefault, type: ModernButtonType.danger, size: ModernButtonSize.medium, icon: Icon(Icons.settings_backup_restore), @@ -1041,7 +1049,7 @@ class _SystemParametersViewState extends State ), ), ModernButton( - text: 'Edit', + text: AppLocalizations.of(context)!.edit, type: ModernButtonType.text, size: ModernButtonSize.small, onPressed: () async { @@ -1073,7 +1081,7 @@ class _SystemParametersViewState extends State context: context, builder: (context) { return AlertDialog( - title: const Text('Pick a color'), + title: Text(AppLocalizations.of(context)!.pickColor), content: SizedBox( width: 320, child: GridView.builder( @@ -1105,7 +1113,7 @@ class _SystemParametersViewState extends State actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), + child: Text(AppLocalizations.of(context)!.cancel), ), ], ); @@ -1156,7 +1164,7 @@ class _SystemParametersViewState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'User Registration', + AppLocalizations.of(context)!.userRegistration, style: theme.textTheme.titleMedium?.copyWith( color: colorScheme.onSurface, fontWeight: FontWeight.w600, @@ -1164,7 +1172,7 @@ class _SystemParametersViewState extends State ), const SizedBox(height: UIConstants.spacing4), Text( - 'Allow new users to register accounts', + AppLocalizations.of(context)!.allowUserRegistration, style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.onSurfaceVariant, ), @@ -1194,7 +1202,7 @@ class _SystemParametersViewState extends State return SizedBox( width: double.infinity, child: ModernButton( - text: 'Save Configuration', + text: AppLocalizations.of(context)!.saveConfiguration, type: ModernButtonType.primary, size: ModernButtonSize.large, icon: Icon(Icons.save), @@ -1266,16 +1274,18 @@ class _SystemParametersViewState extends State padding: EdgeInsets.zero, children: [ _buildDrawerItem( - context, theme, colorScheme, Icons.home, 'Home', () { + context, theme, colorScheme, Icons.home, + AppLocalizations.of(context)!.drawerHome, () { Navigator.of(context).pushReplacementNamed('/'); }), _buildDrawerItem(context, theme, colorScheme, Icons.settings, - 'System Parameters', () { + AppLocalizations.of(context)!.systemParameters, () { Navigator.of(context) .pushReplacementNamed('/system_parameters'); }, isSelected: true), _buildDrawerItem( - context, theme, colorScheme, Icons.people, 'Users', () { + context, theme, colorScheme, Icons.people, + AppLocalizations.of(context)!.drawerUsers, () { Navigator.of(context).pushReplacementNamed('/users'); }), _buildDrawerItem( @@ -1283,41 +1293,49 @@ class _SystemParametersViewState extends State Navigator.of(context).pushReplacementNamed('/roles'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.business, 'Departments', + context, theme, colorScheme, Icons.business, + AppLocalizations.of(context)!.drawerDepartments, () { Navigator.of(context).pushReplacementNamed('/departments'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.work, 'Positions', () { + context, theme, colorScheme, Icons.work, + AppLocalizations.of(context)!.drawerPositions, () { Navigator.of(context).pushReplacementNamed('/positions'); }), _buildDrawerItem(context, theme, colorScheme, - Icons.monetization_on, 'Charges', () { + Icons.monetization_on, + AppLocalizations.of(context)!.drawerCharges, () { Navigator.of(context).pushReplacementNamed('/charges'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.calendar_today, 'Weeks', + context, theme, colorScheme, Icons.calendar_today, + AppLocalizations.of(context)!.drawerWeeks, () { Navigator.of(context).pushReplacementNamed('/weeks'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.schedule, 'Shifts', () { + context, theme, colorScheme, Icons.schedule, + AppLocalizations.of(context)!.drawerShifts, () { Navigator.of(context).pushReplacementNamed('/shifts'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.payment, 'Billing', () { + context, theme, colorScheme, Icons.payment, + AppLocalizations.of(context)!.drawerBilling, () { Navigator.of(context).pushReplacementNamed('/billing'); }), _buildDrawerItem(context, theme, colorScheme, Icons.swap_horiz, - 'EDI Settings', () { + AppLocalizations.of(context)!.drawerEdi, () { Navigator.of(context).pushReplacementNamed('/edi_settings'); }), _buildDrawerItem(context, theme, colorScheme, - Icons.business_center, 'Company', () { + Icons.business_center, + AppLocalizations.of(context)!.drawerCompany, () { Navigator.of(context).pushReplacementNamed('/company'); }), _buildDrawerItem( - context, theme, colorScheme, Icons.image, 'Logo', () { + context, theme, colorScheme, Icons.image, + AppLocalizations.of(context)!.drawerLogo, () { Navigator.of(context).pushReplacementNamed('/logo'); }), ], @@ -1328,7 +1346,7 @@ class _SystemParametersViewState extends State Container( padding: const EdgeInsets.all(UIConstants.spacing24), child: ModernButton( - text: 'Logout', + text: AppLocalizations.of(context)!.logoutAction, type: ModernButtonType.danger, size: ModernButtonSize.medium, icon: Icon(Icons.logout), diff --git a/base_project/lib/widgets/custom_messenger.dart b/base_project/lib/widgets/custom_messenger.dart index d19a364..f587fe0 100644 --- a/base_project/lib/widgets/custom_messenger.dart +++ b/base_project/lib/widgets/custom_messenger.dart @@ -1,3 +1,4 @@ +import 'package:base_project/l10n/app_localizations.dart'; import 'package:base_project/utils/image_constant.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -27,16 +28,16 @@ class ShowSnackAlert { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (color1 == Colors.red.shade400) - const Text( - 'Oh Snap!', + Text( + AppLocalizations.of(context)!.ohSnap, style: TextStyle(fontSize: 18, color: Colors.white), ), if (color1 == Colors.orange.shade400) - const Text('Alert', + Text(AppLocalizations.of(context)!.alertTitle, style: TextStyle(fontSize: 18, color: Colors.white)), if (color1 == Colors.green.shade600) - const Text('Success', + Text(AppLocalizations.of(context)!.successTitle, style: TextStyle(fontSize: 18, color: Colors.white)), Text( diff --git a/base_project/pubspec.lock b/base_project/pubspec.lock index b573056..55bcbb2 100644 --- a/base_project/pubspec.lock +++ b/base_project/pubspec.lock @@ -446,6 +446,11 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -692,10 +697,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" intl_phone_number_input: dependency: "direct main" description: @@ -844,10 +849,10 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -860,10 +865,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.19.0" mime: dependency: transitive description: @@ -1353,10 +1358,10 @@ packages: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.12" typed_data: dependency: transitive description: @@ -1473,10 +1478,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.3" video_player: dependency: "direct main" description: @@ -1590,5 +1595,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.0 <4.0.0" + dart: ">=3.11.0-0 <4.0.0" flutter: ">=3.35.0" diff --git a/base_project/pubspec.yaml b/base_project/pubspec.yaml index b7f2004..b1ffd26 100644 --- a/base_project/pubspec.yaml +++ b/base_project/pubspec.yaml @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter another_flushbar: ^1.12.30 cupertino_icons: ^1.0.8 path_provider: ^2.1.5 @@ -100,6 +102,7 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. @@ -110,6 +113,7 @@ flutter: - assets/ - assets/images/ - assets/icon/ + - assets/translations_dynamic/ # assets: # - images/a_dot_burr.jpeg diff --git a/base_project/test/localization_test.dart b/base_project/test/localization_test.dart new file mode 100644 index 0000000..ba4f176 --- /dev/null +++ b/base_project/test/localization_test.dart @@ -0,0 +1,20 @@ +import 'package:base_project/l10n/app_localizations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('English and Hindi catalogs expose the same essential messages', () { + final english = AppLocalizations(const Locale('en')); + final hindi = AppLocalizations(const Locale('hi')); + + expect(english.searchEntity('student'), 'Search student...'); + expect(hindi.searchEntity('छात्र'), 'छात्र खोजें...'); + expect(english.entityRequired('Email'), 'Email is required'); + expect(hindi.entityRequired('ईमेल'), 'ईमेल आवश्यक है'); + }); + + test('only configured locales are supported', () { + expect(AppLocalizations.supportedLocales, contains(const Locale('en'))); + expect(AppLocalizations.supportedLocales, contains(const Locale('hi'))); + }); +} diff --git a/base_project/test/widget_test.dart b/base_project/test/widget_test.dart deleted file mode 100644 index e132b41..0000000 --- a/base_project/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:base_project/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}