@fadyshawky/react-native-magic 1.0.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.vscode/settings.json +7 -0
  2. package/package.json +1 -1
  3. package/template/App.tsx +30 -9
  4. package/template/package-lock.json +181 -123
  5. package/template/package.json +2 -0
  6. package/template/src/common/ImageResources.g.ts +3 -5
  7. package/template/src/common/components/Background.tsx +66 -28
  8. package/template/src/common/components/Cards.tsx +116 -0
  9. package/template/src/common/components/Container.tsx +145 -0
  10. package/template/src/common/components/FlatListWrapper.tsx +1 -0
  11. package/template/src/common/components/ImageCropPickerButton.tsx +1 -1
  12. package/template/src/common/components/OTPInput.tsx +107 -0
  13. package/template/src/common/components/PhotoTakingButton.tsx +1 -4
  14. package/template/src/common/components/PrimaryButton.tsx +171 -157
  15. package/template/src/common/components/RTLAwareText.tsx +42 -0
  16. package/template/src/common/components/RTLAwareView.tsx +179 -0
  17. package/template/src/common/components/RadioButton.tsx +1 -3
  18. package/template/src/common/components/RadioIcon.tsx +1 -2
  19. package/template/src/common/components/SearchBar.tsx +179 -0
  20. package/template/src/common/components/Separator.tsx +7 -4
  21. package/template/src/common/components/TouchablePlatform.tsx +1 -3
  22. package/template/src/common/components/TryAgain.tsx +3 -3
  23. package/template/src/common/helpers/inAppReviewHelper.ts +0 -1
  24. package/template/src/common/helpers/stringsHelpers.ts +10 -0
  25. package/template/src/common/hooks/useFlatListActions.ts +1 -1
  26. package/template/src/common/localization/LocalizationProvider.tsx +152 -0
  27. package/template/src/common/localization/README.md +488 -0
  28. package/template/src/common/localization/localization.ts +12 -0
  29. package/template/src/common/localization/translations/profileLocalization.ts +24 -0
  30. package/template/src/common/validations/errorValidations.ts +1 -6
  31. package/template/src/common/validations/examples/TextInputWithValidation.tsx +229 -0
  32. package/template/src/common/validations/index.ts +28 -0
  33. package/template/src/common/validations/regex.js +83 -0
  34. package/template/src/common/validations/regexValidator.ts +240 -0
  35. package/template/src/common/validations/validationConstants.ts +2 -2
  36. package/template/src/core/api/errorHandler.ts +39 -0
  37. package/template/src/core/api/responseHandlers.ts +1 -26
  38. package/template/src/core/api/serverHeaders.ts +13 -23
  39. package/template/src/core/store/app/appSlice.ts +1 -2
  40. package/template/src/core/theme/ThemeProvider.tsx +63 -0
  41. package/template/src/core/theme/colors.ts +31 -42
  42. package/template/src/core/theme/commonConsts.ts +1 -1
  43. package/template/src/core/theme/commonStyles.ts +267 -210
  44. package/template/src/core/theme/fonts.ts +17 -1
  45. package/template/src/core/theme/scaling.ts +101 -0
  46. package/template/src/core/theme/themes.ts +214 -0
  47. package/template/src/core/theme/types.ts +51 -0
  48. package/template/src/navigation/AuthStack.tsx +25 -30
  49. package/template/src/navigation/HeaderComponents.tsx +18 -58
  50. package/template/src/navigation/MainNavigation.tsx +5 -6
  51. package/template/src/navigation/MainStack.tsx +3 -28
  52. package/template/src/navigation/RootNavigation.tsx +1 -7
  53. package/template/src/navigation/TabBar.tsx +2 -2
  54. package/template/src/navigation/TopTabBar.tsx +1 -1
  55. package/template/src/screens/Login/Login.tsx +3 -3
  56. package/template/src/screens/home/components/CarouselSection.tsx +7 -8
  57. package/template/src/screens/home/components/FeaturedCarousel.tsx +5 -6
  58. package/template/src/screens/registration/RegistrationScreen.tsx +2 -2
  59. package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +2 -2
  60. package/template/src/utils/stringBuilder.js +25 -0
@@ -0,0 +1,488 @@
1
+ # Localization System
2
+
3
+ This document explains how to use the localization system in the application.
4
+
5
+ ## Overview
6
+
7
+ The localization system is built using `react-native-localization` and provides a simple way to translate text throughout the application. It supports multiple languages (English and Arabic) and provides a consistent API for accessing translations. The system also includes RTL (Right-to-Left) support for Arabic language.
8
+
9
+ ## Structure
10
+
11
+ The localization system consists of the following components:
12
+
13
+ - **LocalizationProvider**: A React context provider that manages the current language and provides translation functions.
14
+ - **Translation Files**: JSON files containing translations for different parts of the application.
15
+ - **Hooks**: Custom hooks for accessing translations in components.
16
+ - **RTL Components**: Utility components for handling RTL layouts.
17
+
18
+ ## Translation Files
19
+
20
+ Translation files are organized by feature and stored in the `src/common/localization/translations` directory:
21
+
22
+ - `commonLocalization.ts`: Common translations used throughout the app
23
+ - `loginLocalization.ts`: Translations for the login screen
24
+ - `homeLocalization.ts`: Translations for the home screen
25
+ - `otpLocalization.ts`: Translations for the OTP verification screen
26
+ - `passwordLocalization.ts`: Translations for password-related screens
27
+ - `navigationLocalization.ts`: Translations for navigation elements (screen names, tab names)
28
+ - `mainNavigationLocalization.ts`: Translations for main navigation tabs and screens
29
+ - etc.
30
+
31
+ Each translation file follows this structure:
32
+
33
+ ```typescript
34
+ export const featureLocalization = {
35
+ en: {
36
+ key1: 'Translation 1',
37
+ key2: 'Translation 2',
38
+ // Nested translations
39
+ nestedSection: {
40
+ key3: 'Translation 3',
41
+ },
42
+ },
43
+ ar: {
44
+ key1: 'الترجمة 1',
45
+ key2: 'الترجمة 2',
46
+ // Nested translations
47
+ nestedSection: {
48
+ key3: 'الترجمة 3',
49
+ },
50
+ },
51
+ };
52
+ ```
53
+
54
+ ## Using Translations in Components
55
+
56
+ ### 1. Using the `useTranslation` Hook
57
+
58
+ The easiest way to use translations is with the `useTranslation` hook:
59
+
60
+ ```typescript
61
+ import {useTranslation} from '../../common/localization/LocalizationProvider';
62
+
63
+ function MyComponent() {
64
+ const t = useTranslation();
65
+
66
+ return <Text>{t('welcome', 'login')}</Text>;
67
+ }
68
+ ```
69
+
70
+ The `t` function takes two parameters:
71
+
72
+ - `key`: The translation key
73
+ - `section` (optional): The section/feature the translation belongs to (defaults to 'common')
74
+
75
+ ### 2. Using Dot Notation for Nested Translations
76
+
77
+ For nested translations, you can use dot notation:
78
+
79
+ ```typescript
80
+ // Access nestedSection.key3 in the login section
81
+ t('nestedSection.key3', 'login');
82
+ ```
83
+
84
+ ## Screen Label Localization
85
+
86
+ The application supports localized screen labels and tab names. This is implemented in the navigation configuration.
87
+
88
+ ### 1. Screen Names in Navigation Stacks
89
+
90
+ Screen names are localized using the `navigation` translation section:
91
+
92
+ ```typescript
93
+ // In AuthStack.tsx
94
+ import {useTranslation} from '../common/localization/LocalizationProvider';
95
+
96
+ export function AuthStack() {
97
+ const t = useTranslation();
98
+
99
+ const AuthScreens = [
100
+ {
101
+ id: 'Login',
102
+ component: Login,
103
+ options: {
104
+ headerShown: false,
105
+ title: t('screens.Login', 'navigation'), // Localized screen title
106
+ },
107
+ },
108
+ // Other screens...
109
+ ];
110
+
111
+ // ...
112
+ }
113
+ ```
114
+
115
+ ### 2. Tab Names in Bottom Tab Navigator
116
+
117
+ Tab names are localized using the `mainNavigation` translation section:
118
+
119
+ ```typescript
120
+ // In MainStack.tsx
121
+ import {useTranslation} from '../common/localization/LocalizationProvider';
122
+
123
+ function MainTabs() {
124
+ const t = useTranslation();
125
+
126
+ const MainScreens = [
127
+ {
128
+ id: 'Main',
129
+ component: HomeScreen,
130
+ options: {
131
+ tabBarLabel: t('tabs.Main', 'mainNavigation'), // Localized tab label
132
+ headerShown: false,
133
+ icon: ImageResources.services,
134
+ selectedIcon: ImageResources.services,
135
+ },
136
+ },
137
+ // Other tabs...
138
+ ];
139
+
140
+ // ...
141
+ }
142
+ ```
143
+
144
+ The TabBar component also supports localization by using the translation hook:
145
+
146
+ ```typescript
147
+ // In TabBar.tsx
148
+ import {
149
+ useTranslation,
150
+ useRTL,
151
+ } from '../common/localization/LocalizationProvider';
152
+
153
+ export function TabBar({state, descriptors, navigation}) {
154
+ const t = useTranslation();
155
+ const isRTL = useRTL();
156
+
157
+ // ...
158
+
159
+ // Get localized tab name if not provided in options
160
+ const localizedName = t(`tabs.${route.name}`, 'mainNavigation');
161
+
162
+ // ...
163
+ }
164
+ ```
165
+
166
+ ### 3. RTL Support in TabBar
167
+
168
+ The TabBar component has full RTL support, automatically adjusting the tab order and layout for RTL languages:
169
+
170
+ ```typescript
171
+ export function TabBar({state, descriptors, navigation}) {
172
+ const isRTL = useRTL();
173
+
174
+ // Create a copy of routes array to avoid modifying the original
175
+ const routesToRender = [...state.routes];
176
+
177
+ // If RTL, reverse the order of tabs
178
+ if (isRTL) {
179
+ routesToRender.reverse();
180
+ }
181
+
182
+ return (
183
+ <RTLAwareView style={styles.container}>
184
+ {routesToRender.map((route, index) => {
185
+ // Calculate the correct index in the original array for focused state
186
+ const originalIndex = isRTL ? state.routes.length - 1 - index : index;
187
+ const isFocused = state.index === originalIndex;
188
+
189
+ // ... render tab
190
+ })}
191
+ </RTLAwareView>
192
+ );
193
+ }
194
+ ```
195
+
196
+ This implementation ensures that:
197
+
198
+ - Tabs are displayed in the correct order for both LTR and RTL languages
199
+ - The focused state is preserved when switching between LTR and RTL
200
+ - Tab labels are properly aligned based on the text direction
201
+ - The entire tab bar layout adapts to the current language direction
202
+
203
+ ### 4. Adding New Screen Names
204
+
205
+ To add a new screen name translation:
206
+
207
+ 1. Add the screen name to the appropriate translation file:
208
+
209
+ ```typescript
210
+ // For auth screens
211
+ export const navigationLocalization = {
212
+ en: {
213
+ screens: {
214
+ NewScreen: 'New Screen',
215
+ // Other screen names...
216
+ },
217
+ },
218
+ ar: {
219
+ screens: {
220
+ NewScreen: 'شاشة جديدة',
221
+ // Other screen names...
222
+ },
223
+ },
224
+ };
225
+
226
+ // For main navigation tabs
227
+ export const mainNavigationLocalization = {
228
+ en: {
229
+ tabs: {
230
+ NewTab: 'New Tab',
231
+ // Other tab names...
232
+ },
233
+ },
234
+ ar: {
235
+ tabs: {
236
+ NewTab: 'تبويب جديد',
237
+ // Other tab names...
238
+ },
239
+ },
240
+ };
241
+ ```
242
+
243
+ 2. Use the translation in your navigation configuration:
244
+
245
+ ```typescript
246
+ {
247
+ id: 'NewScreen',
248
+ component: NewScreenComponent,
249
+ options: {
250
+ title: t('screens.NewScreen', 'navigation'),
251
+ // For tabs
252
+ tabBarLabel: t('tabs.NewTab', 'mainNavigation'),
253
+ },
254
+ }
255
+ ```
256
+
257
+ ## RTL Support
258
+
259
+ The application supports Right-to-Left (RTL) languages like Arabic. The system automatically handles RTL layout when the language is set to Arabic.
260
+
261
+ ### 1. Using RTL-aware Components
262
+
263
+ Use the provided RTL-aware components to automatically handle RTL layout:
264
+
265
+ ```typescript
266
+ import {RTLAwareText} from '../../common/components/RTLAwareText';
267
+ import {RTLAwareView} from '../../common/components/RTLAwareView';
268
+ import {useRTL} from '../../common/localization/LocalizationProvider';
269
+
270
+ function MyComponent() {
271
+ const isRTL = useRTL();
272
+
273
+ return (
274
+ <RTLAwareView style={{flexDirection: 'row'}}>
275
+ <RTLAwareText style={{textAlign: 'left'}}>
276
+ This text will be properly aligned in RTL mode
277
+ </RTLAwareText>
278
+ </RTLAwareView>
279
+ );
280
+ }
281
+ ```
282
+
283
+ ### 2. Using the `useRTL` Hook
284
+
285
+ You can use the `useRTL` hook to check if the current language is RTL:
286
+
287
+ ```typescript
288
+ import {useRTL} from '../../common/localization/LocalizationProvider';
289
+
290
+ function MyComponent() {
291
+ const isRTL = useRTL();
292
+
293
+ return (
294
+ <View
295
+ style={{
296
+ flexDirection: isRTL ? 'row-reverse' : 'row',
297
+ alignItems: 'center',
298
+ }}>
299
+ {/* Your content */}
300
+ </View>
301
+ );
302
+ }
303
+ ```
304
+
305
+ ### 3. Using the `rtlStyles` Helper
306
+
307
+ You can use the `rtlStyles` helper to create RTL-aware styles:
308
+
309
+ ```typescript
310
+ import {useRTL} from '../../common/localization/LocalizationProvider';
311
+ import {rtlStyles} from '../../common/components/RTLAwareView';
312
+
313
+ function MyComponent() {
314
+ const isRTL = useRTL();
315
+ const rtlStyle = rtlStyles(isRTL);
316
+
317
+ return (
318
+ <View style={[rtlStyle.row, rtlStyle.textLeft]}>{/* Your content */}</View>
319
+ );
320
+ }
321
+ ```
322
+
323
+ ### 4. RTL in Navigation Components
324
+
325
+ Navigation components like the TabBar have special RTL handling to ensure proper layout and interaction:
326
+
327
+ - Tab order is reversed in RTL mode
328
+ - Focus state is preserved when switching between LTR and RTL
329
+ - Text alignment is automatically adjusted
330
+ - Icons and other visual elements are properly positioned
331
+
332
+ ## Adding New Translations
333
+
334
+ To add new translations:
335
+
336
+ 1. Add the translation key and value to the appropriate translation file for both English and Arabic
337
+ 2. If creating a new feature, create a new translation file in the `translations` directory
338
+ 3. Register the new translation file in `localization.ts`
339
+
340
+ Example of adding a new translation file:
341
+
342
+ ```typescript
343
+ // 1. Create the file: src/common/localization/translations/newFeatureLocalization.ts
344
+ export const newFeatureLocalization = {
345
+ en: {
346
+ title: 'New Feature',
347
+ description: 'This is a new feature',
348
+ },
349
+ ar: {
350
+ title: 'ميزة جديدة',
351
+ description: 'هذه ميزة جديدة',
352
+ },
353
+ };
354
+
355
+ // 2. Register in localization.ts
356
+ import {newFeatureLocalization} from './translations/newFeatureLocalization';
357
+
358
+ export const localization = {
359
+ // Existing localizations...
360
+ newFeature: new LocalizedStrings(newFeatureLocalization),
361
+ };
362
+ ```
363
+
364
+ ## Changing the Language
365
+
366
+ To change the application language:
367
+
368
+ ```typescript
369
+ import {useLocalization} from '../../common/localization/LocalizationProvider';
370
+ import {Languages} from '../../common/localization/localization';
371
+
372
+ function LanguageSelector() {
373
+ const {changeLanguage} = useLocalization();
374
+
375
+ return (
376
+ <>
377
+ <Button title="English" onPressIn={() => changeLanguage(Languages.en)} />
378
+ <Button title="العربية" onPressIn={() => changeLanguage(Languages.ar)} />
379
+ </>
380
+ );
381
+ }
382
+ ```
383
+
384
+ ### App Restart on Language Change
385
+
386
+ When changing between RTL and LTR languages (e.g., from English to Arabic or vice versa), the app will automatically restart to properly apply the layout changes. This is necessary because React Native requires a restart to fully apply RTL layout changes, especially on Android.
387
+
388
+ The restart is handled automatically by the `LocalizationProvider` when you call the `changeLanguage` function. No additional code is needed in your components to handle the restart.
389
+
390
+ ```typescript
391
+ // Inside LocalizationProvider
392
+ const changeLanguage = (language: Languages) => {
393
+ if (language !== currentLanguage) {
394
+ setCurrentLanguage(language);
395
+
396
+ // Set RTL configuration and restart the app
397
+ const shouldBeRTL = language === Languages.ar;
398
+ if (I18nManager.isRTL !== shouldBeRTL) {
399
+ I18nManager.allowRTL(shouldBeRTL);
400
+ I18nManager.forceRTL(shouldBeRTL);
401
+
402
+ // Restart the app to apply RTL/LTR changes properly
403
+ try {
404
+ // Check if RNRestart is available
405
+ if (RNRestart && typeof RNRestart.restart === 'function') {
406
+ RNRestart.restart();
407
+ } else if (Platform.OS === 'android') {
408
+ // Fallback for Android using DevSettings
409
+ const DevSettings = NativeModules.DevSettings;
410
+ if (DevSettings && DevSettings.reload) {
411
+ DevSettings.reload();
412
+ }
413
+ }
414
+ } catch (error) {
415
+ console.error('Failed to restart the app:', error);
416
+ }
417
+ }
418
+ }
419
+ };
420
+ ```
421
+
422
+ #### Confirmation Dialog
423
+
424
+ When changing the language in the Profile screen, a confirmation dialog is shown to inform the user that the app will restart:
425
+
426
+ ```typescript
427
+ // In Profile.tsx
428
+ const handleLanguageToggle = () => {
429
+ Alert.alert(
430
+ t('changeLanguage', 'profile'),
431
+ t('changeLanguageConfirmation', 'profile'),
432
+ [
433
+ {
434
+ text: t('cancel', 'common'),
435
+ style: 'cancel',
436
+ },
437
+ {
438
+ text: t('change', 'common'),
439
+ onPress: () => {
440
+ changeLanguage(isArabic ? Languages.en : Languages.ar);
441
+ },
442
+ },
443
+ ],
444
+ {cancelable: true},
445
+ );
446
+ };
447
+ ```
448
+
449
+ This provides a better user experience by informing the user about the restart before it happens.
450
+
451
+ ## Best Practices
452
+
453
+ 1. **Use RTL-aware components**: Always use `RTLAwareText` and `RTLAwareView` instead of regular `Text` and `View` for content that needs to adapt to RTL languages.
454
+ 2. **Use the translation hook**: Always use the `useTranslation` hook instead of directly accessing the localization object.
455
+ 3. **Organize translations by feature**: Keep translations organized by feature to make them easier to maintain.
456
+ 4. **Use meaningful keys**: Use descriptive keys that make it clear what the translation is for.
457
+ 5. **Provide default values**: When a translation is missing, the key will be displayed as a fallback.
458
+ 6. **Add comments**: Add comments to explain the context of translations when necessary.
459
+ 7. **Test in both LTR and RTL modes**: Always test your UI in both Left-to-Right and Right-to-Left modes to ensure it looks correct in both.
460
+ 8. **Localize screen labels**: Always localize screen titles and tab names for a consistent user experience.
461
+ 9. **Separate navigation translations**: Keep auth navigation and main navigation translations separate for better organization.
462
+ 10. **Handle RTL in navigation components**: Ensure navigation components like TabBar properly handle RTL layout and interaction.
463
+
464
+ ## Adding a New Language
465
+
466
+ To add a new language:
467
+
468
+ 1. Update the `Languages` enum in `localization.ts`:
469
+
470
+ ```typescript
471
+ export enum Languages {
472
+ en = 'en',
473
+ fr = 'fr', // Add new language
474
+ }
475
+ ```
476
+
477
+ 2. Add translations for the new language in each translation file:
478
+
479
+ ```typescript
480
+ export const commonLocalization = {
481
+ en: {
482
+ welcome: 'Welcome',
483
+ },
484
+ fr: {
485
+ welcome: 'Bienvenue',
486
+ },
487
+ };
488
+ ```
@@ -8,9 +8,11 @@ import {setDateLocale} from './dateFormatter';
8
8
  import {loginLocalization} from './translations/loginLocalization';
9
9
  import {homeLocalization} from './translations/homeLocalization';
10
10
  import {profileLocalization} from './translations/profileLocalization';
11
+ import {I18nManager} from 'react-native';
11
12
 
12
13
  export enum Languages {
13
14
  en = 'en',
15
+ ar = 'ar',
14
16
  }
15
17
 
16
18
  export const localization = {
@@ -47,5 +49,15 @@ export function setLanguage(language?: Languages): void {
47
49
  }
48
50
  }
49
51
 
52
+ // Set RTL for Arabic language
53
+ const isRTL = localizationLanguage === Languages.ar;
54
+ if (I18nManager.isRTL !== isRTL) {
55
+ I18nManager.allowRTL(isRTL);
56
+ I18nManager.forceRTL(isRTL);
57
+ }
58
+
50
59
  setDateLocale(localizationLanguage);
51
60
  }
61
+
62
+ // Default language is Arabic
63
+ export const DEFAULT_LANGUAGE = Languages.ar;
@@ -2,5 +2,29 @@ export const profileLocalization = {
2
2
  en: {
3
3
  studentProfile: 'Student Profile',
4
4
  allDetails: 'All Details',
5
+ settings: 'Settings',
6
+ darkMode: 'Dark Mode',
7
+ language: 'Language',
8
+ account: 'Account',
9
+ editProfile: 'Edit Profile',
10
+ changePassword: 'Change Password',
11
+ logout: 'Logout',
12
+ changeLanguage: 'Change Language',
13
+ changeLanguageConfirmation:
14
+ 'Are you sure you want to change the language? The app will restart to apply changes.',
15
+ },
16
+ ar: {
17
+ studentProfile: 'الملف الشخصي للطالب',
18
+ allDetails: 'جميع التفاصيل',
19
+ settings: 'الإعدادات',
20
+ darkMode: 'الوضع الداكن',
21
+ language: 'اللغة',
22
+ account: 'الحساب',
23
+ editProfile: 'تعديل الملف الشخصي',
24
+ changePassword: 'تغيير كلمة المرور',
25
+ logout: 'تسجيل الخروج',
26
+ changeLanguage: 'تغيير اللغة',
27
+ changeLanguageConfirmation:
28
+ 'هل أنت متأكد أنك تريد تغيير اللغة؟ سيتم إعادة تشغيل التطبيق لتطبيق التغييرات.',
5
29
  },
6
30
  };
@@ -3,8 +3,7 @@ import {unwrapResult} from '@reduxjs/toolkit';
3
3
  import {Alert} from 'react-native';
4
4
  import {IErrorResult, ErrorRepresentationType} from '../../../types';
5
5
  import Snackbar from 'react-native-snackbar';
6
- import {Colors} from '../../core/theme/colors';
7
-
6
+ import {NewColors} from '../../core/theme/colors';
8
7
  export function handlePromiseResult(
9
8
  promiseAction: Promise<any>,
10
9
  successMessage?: string,
@@ -19,8 +18,6 @@ export function handlePromiseResult(
19
18
  Snackbar.show({
20
19
  text: successMessage,
21
20
  duration: Snackbar.LENGTH_SHORT,
22
- textColor: Colors.white,
23
- backgroundColor: Colors.green,
24
21
  });
25
22
  successAction && successAction();
26
23
  })
@@ -45,8 +42,6 @@ export function handleErrorPostProcessing(
45
42
  Snackbar.show({
46
43
  text: error.message,
47
44
  duration: Snackbar.LENGTH_SHORT,
48
- textColor: Colors.white,
49
- backgroundColor: Colors.red,
50
45
  });
51
46
  break;
52
47
  default: