@umituz/react-native-localization 1.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.
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/lib/domain/repositories/ILocalizationRepository.d.ts +17 -0
- package/lib/domain/repositories/ILocalizationRepository.d.ts.map +1 -0
- package/lib/domain/repositories/ILocalizationRepository.js +6 -0
- package/lib/domain/repositories/ILocalizationRepository.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/lib/infrastructure/components/LocalizationProvider.d.ts +11 -0
- package/lib/infrastructure/components/LocalizationProvider.d.ts.map +1 -0
- package/lib/infrastructure/components/LocalizationProvider.js +14 -0
- package/lib/infrastructure/components/LocalizationProvider.js.map +1 -0
- package/lib/infrastructure/config/i18n.d.ts +7 -0
- package/lib/infrastructure/config/i18n.d.ts.map +1 -0
- package/lib/infrastructure/config/i18n.js +49 -0
- package/lib/infrastructure/config/i18n.js.map +1 -0
- package/lib/infrastructure/config/languages.d.ts +34 -0
- package/lib/infrastructure/config/languages.d.ts.map +1 -0
- package/lib/infrastructure/config/languages.js +200 -0
- package/lib/infrastructure/config/languages.js.map +1 -0
- package/lib/infrastructure/config/languagesData.d.ts +7 -0
- package/lib/infrastructure/config/languagesData.d.ts.map +1 -0
- package/lib/infrastructure/config/languagesData.js +36 -0
- package/lib/infrastructure/config/languagesData.js.map +1 -0
- package/lib/infrastructure/locales/en-US/animation.json +30 -0
- package/lib/infrastructure/locales/en-US/audio.json +56 -0
- package/lib/infrastructure/locales/en-US/datetime.json +140 -0
- package/lib/infrastructure/locales/en-US/emoji.json +29 -0
- package/lib/infrastructure/locales/en-US/errors.json +43 -0
- package/lib/infrastructure/locales/en-US/forms.json +38 -0
- package/lib/infrastructure/locales/en-US/general.json +56 -0
- package/lib/infrastructure/locales/en-US/icons.json +34 -0
- package/lib/infrastructure/locales/en-US/index.d.ts +757 -0
- package/lib/infrastructure/locales/en-US/index.d.ts.map +1 -0
- package/lib/infrastructure/locales/en-US/index.js +35 -0
- package/lib/infrastructure/locales/en-US/index.js.map +1 -0
- package/lib/infrastructure/locales/en-US/location.json +49 -0
- package/lib/infrastructure/locales/en-US/media.json +49 -0
- package/lib/infrastructure/locales/en-US/navigation.json +52 -0
- package/lib/infrastructure/locales/en-US/onboarding.json +76 -0
- package/lib/infrastructure/locales/en-US/settings.json +65 -0
- package/lib/infrastructure/locales/en-US/toast.json +38 -0
- package/lib/infrastructure/storage/AsyncStorageWrapper.d.ts +12 -0
- package/lib/infrastructure/storage/AsyncStorageWrapper.d.ts.map +1 -0
- package/lib/infrastructure/storage/AsyncStorageWrapper.js +29 -0
- package/lib/infrastructure/storage/AsyncStorageWrapper.js.map +1 -0
- package/lib/infrastructure/storage/LocalizationStore.d.ts +30 -0
- package/lib/infrastructure/storage/LocalizationStore.d.ts.map +1 -0
- package/lib/infrastructure/storage/LocalizationStore.js +90 -0
- package/lib/infrastructure/storage/LocalizationStore.js.map +1 -0
- package/package.json +64 -0
- package/src/domain/repositories/ILocalizationRepository.ts +18 -0
- package/src/index.ts +24 -0
- package/src/infrastructure/components/LocalizationProvider.tsx +21 -0
- package/src/infrastructure/config/i18n.ts +57 -0
- package/src/infrastructure/config/languages.ts +245 -0
- package/src/infrastructure/config/languagesData.ts +38 -0
- package/src/infrastructure/locales/en-US/animation.json +30 -0
- package/src/infrastructure/locales/en-US/audio.json +56 -0
- package/src/infrastructure/locales/en-US/datetime.json +140 -0
- package/src/infrastructure/locales/en-US/emoji.json +29 -0
- package/src/infrastructure/locales/en-US/errors.json +43 -0
- package/src/infrastructure/locales/en-US/forms.json +38 -0
- package/src/infrastructure/locales/en-US/general.json +56 -0
- package/src/infrastructure/locales/en-US/icons.json +34 -0
- package/src/infrastructure/locales/en-US/index.ts +36 -0
- package/src/infrastructure/locales/en-US/location.json +49 -0
- package/src/infrastructure/locales/en-US/media.json +49 -0
- package/src/infrastructure/locales/en-US/navigation.json +52 -0
- package/src/infrastructure/locales/en-US/onboarding.json +76 -0
- package/src/infrastructure/locales/en-US/settings.json +65 -0
- package/src/infrastructure/locales/en-US/toast.json +38 -0
- package/src/infrastructure/storage/AsyncStorageWrapper.ts +30 -0
- package/src/infrastructure/storage/LocalizationStore.ts +118 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"validation": {
|
|
3
|
+
"required": "This field is required",
|
|
4
|
+
"invalidEmail": "Invalid email address",
|
|
5
|
+
"invalidPhone": "Invalid phone number",
|
|
6
|
+
"passwordTooShort": "Password must be at least 8 characters",
|
|
7
|
+
"passwordTooLong": "Password must be at most 128 characters",
|
|
8
|
+
"passwordsDontMatch": "Passwords do not match",
|
|
9
|
+
"nameTooShort": "Name must be at least 2 characters",
|
|
10
|
+
"nameTooLong": "Name must be at most 50 characters"
|
|
11
|
+
},
|
|
12
|
+
"fields": {
|
|
13
|
+
"email": "Email",
|
|
14
|
+
"password": "Password",
|
|
15
|
+
"passwordConfirmation": "Confirm Password",
|
|
16
|
+
"name": "Name",
|
|
17
|
+
"phone": "Phone Number",
|
|
18
|
+
"currentPassword": "Current Password",
|
|
19
|
+
"newPassword": "New Password",
|
|
20
|
+
"newPasswordConfirmation": "Confirm New Password",
|
|
21
|
+
"subject": "Subject",
|
|
22
|
+
"message": "Message"
|
|
23
|
+
},
|
|
24
|
+
"actions": {
|
|
25
|
+
"submit": "Submit",
|
|
26
|
+
"cancel": "Cancel",
|
|
27
|
+
"reset": "Reset",
|
|
28
|
+
"save": "Save",
|
|
29
|
+
"update": "Update"
|
|
30
|
+
},
|
|
31
|
+
"schemas": {
|
|
32
|
+
"login": "Login Form",
|
|
33
|
+
"register": "Registration Form",
|
|
34
|
+
"profile": "Profile Form",
|
|
35
|
+
"changePassword": "Change Password",
|
|
36
|
+
"contact": "Contact Form"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"back": "Back",
|
|
3
|
+
"cancel": "Cancel",
|
|
4
|
+
"confirm": "Confirm",
|
|
5
|
+
"continue": "Continue",
|
|
6
|
+
"delete": "Delete",
|
|
7
|
+
"edit": "Edit",
|
|
8
|
+
"error": "Error",
|
|
9
|
+
"loading": "Loading...",
|
|
10
|
+
"notNow": "Not Now",
|
|
11
|
+
"ok": "OK",
|
|
12
|
+
"optional": "Optional",
|
|
13
|
+
"save": "Save",
|
|
14
|
+
"something_went_wrong": "Something went wrong",
|
|
15
|
+
"success": "Success",
|
|
16
|
+
"close": "Close",
|
|
17
|
+
"done": "Done",
|
|
18
|
+
"next": "Next",
|
|
19
|
+
"previous": "Previous",
|
|
20
|
+
"skip": "Skip",
|
|
21
|
+
"retry": "Retry",
|
|
22
|
+
"refresh": "Refresh",
|
|
23
|
+
"search": "Search",
|
|
24
|
+
"filter": "Filter",
|
|
25
|
+
"sort": "Sort",
|
|
26
|
+
"add": "Add",
|
|
27
|
+
"remove": "Remove",
|
|
28
|
+
"update": "Update",
|
|
29
|
+
"create": "Create",
|
|
30
|
+
"view": "View",
|
|
31
|
+
"details": "Details",
|
|
32
|
+
"share": "Share",
|
|
33
|
+
"download": "Download",
|
|
34
|
+
"upload": "Upload",
|
|
35
|
+
"submit": "Submit",
|
|
36
|
+
"reset": "Reset",
|
|
37
|
+
"clear": "Clear",
|
|
38
|
+
"apply": "Apply",
|
|
39
|
+
"yes": "Yes",
|
|
40
|
+
"no": "No",
|
|
41
|
+
"all": "All",
|
|
42
|
+
"none": "None",
|
|
43
|
+
"of": "of",
|
|
44
|
+
"select": "Select",
|
|
45
|
+
"selected": "Selected",
|
|
46
|
+
"required": "Required",
|
|
47
|
+
"empty": "Empty",
|
|
48
|
+
"noData": "No data available",
|
|
49
|
+
"noResults": "No results found",
|
|
50
|
+
"tryAgain": "Try again",
|
|
51
|
+
"learnMore": "Learn more",
|
|
52
|
+
"getStarted": "Get started",
|
|
53
|
+
"viewAll": "View all",
|
|
54
|
+
"showMore": "Show more",
|
|
55
|
+
"showLess": "Show less"
|
|
56
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"libraries": {
|
|
3
|
+
"lucide": "Lucide Icons",
|
|
4
|
+
"material": "Material Design",
|
|
5
|
+
"fontawesome": "FontAwesome",
|
|
6
|
+
"ionicons": "Ionicons"
|
|
7
|
+
},
|
|
8
|
+
"categories": {
|
|
9
|
+
"all": "All Icons",
|
|
10
|
+
"navigation": "Navigation",
|
|
11
|
+
"action": "Action",
|
|
12
|
+
"social": "Social",
|
|
13
|
+
"communication": "Communication",
|
|
14
|
+
"media": "Media",
|
|
15
|
+
"business": "Business",
|
|
16
|
+
"weather": "Weather",
|
|
17
|
+
"symbols": "Symbols",
|
|
18
|
+
"emoji": "Emoji"
|
|
19
|
+
},
|
|
20
|
+
"picker": {
|
|
21
|
+
"title": "Select Icon",
|
|
22
|
+
"search": "Search icons...",
|
|
23
|
+
"searchPlaceholder": "Search",
|
|
24
|
+
"noResults": "No icons found",
|
|
25
|
+
"selected": "Selected",
|
|
26
|
+
"selectLibrary": "Select Library",
|
|
27
|
+
"selectCategory": "Select Category"
|
|
28
|
+
},
|
|
29
|
+
"actions": {
|
|
30
|
+
"select": "Select",
|
|
31
|
+
"cancel": "Cancel",
|
|
32
|
+
"clear": "Clear Selection"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* en-US translation modules
|
|
3
|
+
* All common translations for English (US)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import animation from './animation.json';
|
|
7
|
+
import audio from './audio.json';
|
|
8
|
+
import datetime from './datetime.json';
|
|
9
|
+
import emoji from './emoji.json';
|
|
10
|
+
import errors from './errors.json';
|
|
11
|
+
import forms from './forms.json';
|
|
12
|
+
import general from './general.json';
|
|
13
|
+
import icons from './icons.json';
|
|
14
|
+
import location from './location.json';
|
|
15
|
+
import media from './media.json';
|
|
16
|
+
import navigation from './navigation.json';
|
|
17
|
+
import onboarding from './onboarding.json';
|
|
18
|
+
import settings from './settings.json';
|
|
19
|
+
import toast from './toast.json';
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
animation,
|
|
23
|
+
audio,
|
|
24
|
+
datetime,
|
|
25
|
+
emoji,
|
|
26
|
+
errors,
|
|
27
|
+
forms,
|
|
28
|
+
general,
|
|
29
|
+
icons,
|
|
30
|
+
location,
|
|
31
|
+
media,
|
|
32
|
+
navigation,
|
|
33
|
+
onboarding,
|
|
34
|
+
settings,
|
|
35
|
+
toast,
|
|
36
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permission": {
|
|
3
|
+
"title": "Location Permission",
|
|
4
|
+
"message": "This app needs access to your location to provide location-based features.",
|
|
5
|
+
"request": "Grant Location Permission",
|
|
6
|
+
"denied": "Location permission denied",
|
|
7
|
+
"granted": "Location permission granted",
|
|
8
|
+
"unknown": "Location permission status unknown"
|
|
9
|
+
},
|
|
10
|
+
"status": {
|
|
11
|
+
"loading": "Getting your location...",
|
|
12
|
+
"success": "Location retrieved successfully",
|
|
13
|
+
"failed": "Unable to get location",
|
|
14
|
+
"unavailable": "Location services not available",
|
|
15
|
+
"cached": "Using cached location",
|
|
16
|
+
"timeout": "Location request timed out"
|
|
17
|
+
},
|
|
18
|
+
"accuracy": {
|
|
19
|
+
"high": "High accuracy",
|
|
20
|
+
"balanced": "Balanced accuracy",
|
|
21
|
+
"low": "Low accuracy",
|
|
22
|
+
"lowest": "Lowest accuracy"
|
|
23
|
+
},
|
|
24
|
+
"errors": {
|
|
25
|
+
"notAvailable": "Location services not available on this platform",
|
|
26
|
+
"permissionDenied": "Location permission denied. Please enable location access in settings.",
|
|
27
|
+
"timeout": "Location request timed out. Please try again.",
|
|
28
|
+
"unknown": "An unknown error occurred while getting location",
|
|
29
|
+
"noLocation": "Unable to determine your location"
|
|
30
|
+
},
|
|
31
|
+
"actions": {
|
|
32
|
+
"getCurrentLocation": "Get Current Location",
|
|
33
|
+
"refreshLocation": "Refresh Location",
|
|
34
|
+
"clearLocation": "Clear Location",
|
|
35
|
+
"openSettings": "Open Settings"
|
|
36
|
+
},
|
|
37
|
+
"labels": {
|
|
38
|
+
"latitude": "Latitude",
|
|
39
|
+
"longitude": "Longitude",
|
|
40
|
+
"accuracy": "Accuracy",
|
|
41
|
+
"address": "Address",
|
|
42
|
+
"timestamp": "Time",
|
|
43
|
+
"coordinates": "Coordinates"
|
|
44
|
+
},
|
|
45
|
+
"units": {
|
|
46
|
+
"meters": "meters",
|
|
47
|
+
"kilometers": "kilometers"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"picker": {
|
|
3
|
+
"selectImage": "Select Image",
|
|
4
|
+
"selectImages": "Select Images",
|
|
5
|
+
"selectVideo": "Select Video",
|
|
6
|
+
"takePhoto": "Take Photo",
|
|
7
|
+
"chooseFromLibrary": "Choose from Library",
|
|
8
|
+
"cancel": "Cancel"
|
|
9
|
+
},
|
|
10
|
+
"permissions": {
|
|
11
|
+
"cameraTitle": "Camera Permission",
|
|
12
|
+
"cameraMessage": "This app needs camera access to take photos",
|
|
13
|
+
"libraryTitle": "Photo Library Permission",
|
|
14
|
+
"libraryMessage": "This app needs access to your photo library",
|
|
15
|
+
"deniedTitle": "Permission Denied",
|
|
16
|
+
"deniedMessage": "Please enable permissions in Settings",
|
|
17
|
+
"openSettings": "Open Settings"
|
|
18
|
+
},
|
|
19
|
+
"actions": {
|
|
20
|
+
"resize": "Resize",
|
|
21
|
+
"crop": "Crop",
|
|
22
|
+
"rotate": "Rotate",
|
|
23
|
+
"flip": "Flip",
|
|
24
|
+
"compress": "Compress",
|
|
25
|
+
"save": "Save",
|
|
26
|
+
"delete": "Delete",
|
|
27
|
+
"edit": "Edit"
|
|
28
|
+
},
|
|
29
|
+
"errors": {
|
|
30
|
+
"pickFailed": "Failed to pick image",
|
|
31
|
+
"cameraFailed": "Failed to launch camera",
|
|
32
|
+
"saveFailed": "Failed to save image",
|
|
33
|
+
"manipulateFailed": "Failed to edit image",
|
|
34
|
+
"permissionDenied": "Permission denied",
|
|
35
|
+
"noImage": "No image selected",
|
|
36
|
+
"invalidImage": "Invalid image file",
|
|
37
|
+
"tooLarge": "File size too large"
|
|
38
|
+
},
|
|
39
|
+
"quality": {
|
|
40
|
+
"low": "Low",
|
|
41
|
+
"medium": "Medium",
|
|
42
|
+
"high": "High"
|
|
43
|
+
},
|
|
44
|
+
"format": {
|
|
45
|
+
"jpeg": "JPEG",
|
|
46
|
+
"png": "PNG",
|
|
47
|
+
"webp": "WebP"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"home": "Home",
|
|
3
|
+
"settings": "Settings",
|
|
4
|
+
"about": "About",
|
|
5
|
+
"back": "Back",
|
|
6
|
+
"explore": "Explore",
|
|
7
|
+
"search": "Search",
|
|
8
|
+
"favorites": "Favorites",
|
|
9
|
+
"activity": "Activity",
|
|
10
|
+
"discover": "Discover",
|
|
11
|
+
"library": "Library",
|
|
12
|
+
"stats": "Stats",
|
|
13
|
+
"wellness": "Wellness",
|
|
14
|
+
"exercises": "Exercises",
|
|
15
|
+
"nutrition": "Nutrition",
|
|
16
|
+
"progress": "Progress",
|
|
17
|
+
"workouts": "Workouts",
|
|
18
|
+
"dashboard": "Dashboard",
|
|
19
|
+
"calendar": "Calendar",
|
|
20
|
+
"tasks": "Tasks",
|
|
21
|
+
"notes": "Notes",
|
|
22
|
+
"projects": "Projects",
|
|
23
|
+
"goals": "Goals",
|
|
24
|
+
"habits": "Habits",
|
|
25
|
+
"budget": "Budget",
|
|
26
|
+
"expenses": "Expenses",
|
|
27
|
+
"income": "Income",
|
|
28
|
+
"accounts": "Accounts",
|
|
29
|
+
"reports": "Reports",
|
|
30
|
+
"courses": "Courses",
|
|
31
|
+
"lessons": "Lessons",
|
|
32
|
+
"practice": "Practice",
|
|
33
|
+
"learning": "Learning",
|
|
34
|
+
"meditation": "Meditation",
|
|
35
|
+
"journal": "Journal",
|
|
36
|
+
"timer": "Timer",
|
|
37
|
+
"tracking": "Tracking",
|
|
38
|
+
"analytics": "Analytics",
|
|
39
|
+
"community": "Community",
|
|
40
|
+
"feed": "Feed",
|
|
41
|
+
"browse": "Browse",
|
|
42
|
+
"categories": "Categories",
|
|
43
|
+
"saved": "Saved",
|
|
44
|
+
"history": "History",
|
|
45
|
+
"achievements": "Achievements",
|
|
46
|
+
"leaderboard": "Leaderboard",
|
|
47
|
+
"challenges": "Challenges",
|
|
48
|
+
"rewards": "Rewards",
|
|
49
|
+
"today": "Today",
|
|
50
|
+
"medicines": "Medicines",
|
|
51
|
+
"adherence": "Adherence"
|
|
52
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skip": "Skip",
|
|
3
|
+
"getStarted": "Get Started",
|
|
4
|
+
"welcome": {
|
|
5
|
+
"title": "Welcome!",
|
|
6
|
+
"subtitle": "Your journey starts here",
|
|
7
|
+
"getStarted": "Get Started",
|
|
8
|
+
"skip": "Skip"
|
|
9
|
+
},
|
|
10
|
+
"slides": {
|
|
11
|
+
"slide1": {
|
|
12
|
+
"title": "Discover Amazing Features",
|
|
13
|
+
"description": "Explore everything our app has to offer"
|
|
14
|
+
},
|
|
15
|
+
"slide2": {
|
|
16
|
+
"title": "Stay Organized",
|
|
17
|
+
"description": "Keep track of what matters most"
|
|
18
|
+
},
|
|
19
|
+
"slide3": {
|
|
20
|
+
"title": "Achieve Your Goals",
|
|
21
|
+
"description": "We're here to help you succeed"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"permissions": {
|
|
25
|
+
"title": "Permissions",
|
|
26
|
+
"subtitle": "We need some permissions to provide the best experience",
|
|
27
|
+
"location": {
|
|
28
|
+
"title": "Location",
|
|
29
|
+
"description": "Provide location-based features",
|
|
30
|
+
"enable": "Enable Location"
|
|
31
|
+
},
|
|
32
|
+
"camera": {
|
|
33
|
+
"title": "Camera",
|
|
34
|
+
"description": "Take photos and scan documents",
|
|
35
|
+
"enable": "Enable Camera"
|
|
36
|
+
},
|
|
37
|
+
"photos": {
|
|
38
|
+
"title": "Photos",
|
|
39
|
+
"description": "Access your photo library",
|
|
40
|
+
"enable": "Enable Photos"
|
|
41
|
+
},
|
|
42
|
+
"later": "I'll do this later",
|
|
43
|
+
"continue": "Continue"
|
|
44
|
+
},
|
|
45
|
+
"personalization": {
|
|
46
|
+
"title": "Personalize Your Experience",
|
|
47
|
+
"subtitle": "Tell us about yourself",
|
|
48
|
+
"goals": {
|
|
49
|
+
"title": "What are your goals?",
|
|
50
|
+
"options": [
|
|
51
|
+
"Stay healthy",
|
|
52
|
+
"Be productive",
|
|
53
|
+
"Learn new skills",
|
|
54
|
+
"Connect with others",
|
|
55
|
+
"Have fun"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"preferences": {
|
|
59
|
+
"title": "Set your preferences",
|
|
60
|
+
"theme": "Choose Theme",
|
|
61
|
+
"language": "Choose Language"
|
|
62
|
+
},
|
|
63
|
+
"continue": "Continue"
|
|
64
|
+
},
|
|
65
|
+
"complete": {
|
|
66
|
+
"title": "You're All Set!",
|
|
67
|
+
"subtitle": "Let's get started!",
|
|
68
|
+
"startButton": "Start Using App"
|
|
69
|
+
},
|
|
70
|
+
"navigation": {
|
|
71
|
+
"next": "Next",
|
|
72
|
+
"back": "Back",
|
|
73
|
+
"skip": "Skip",
|
|
74
|
+
"done": "Done"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lightMode": "Light Mode",
|
|
3
|
+
"darkMode": "Dark Mode",
|
|
4
|
+
"language": "Language",
|
|
5
|
+
"aboutDescription": "App information and version",
|
|
6
|
+
"sections": {
|
|
7
|
+
"appearance": "Appearance",
|
|
8
|
+
"general": "General",
|
|
9
|
+
"support": "Support Development",
|
|
10
|
+
"about": "About & Legal"
|
|
11
|
+
},
|
|
12
|
+
"about": {
|
|
13
|
+
"title": "About",
|
|
14
|
+
"description": "App information and version",
|
|
15
|
+
"appInfo": "App Information",
|
|
16
|
+
"appVersion": "App Version",
|
|
17
|
+
"moreApps": "More Apps",
|
|
18
|
+
"moreAppsDescription": "Check out our other apps",
|
|
19
|
+
"contact": "Contact",
|
|
20
|
+
"contactEmail": "Contact Email",
|
|
21
|
+
"website": "Website"
|
|
22
|
+
},
|
|
23
|
+
"disclaimer": {
|
|
24
|
+
"title": "Health Disclaimer",
|
|
25
|
+
"shortMessage": "Tap to read important health information",
|
|
26
|
+
"message": "This app is for informational and wellness support purposes only. It is NOT a substitute for professional medical or mental health care.\n\nIf you're experiencing a health crisis or emergency, contact your local emergency services or healthcare provider immediately.\n\nAlways consult qualified healthcare professionals for medical advice, diagnosis, or treatment. The content in this app should not be used as a substitute for professional medical judgment.\n\nYour privacy is important. All data stays private on your device unless you explicitly choose to share it."
|
|
27
|
+
},
|
|
28
|
+
"legal": {
|
|
29
|
+
"title": "Legal",
|
|
30
|
+
"description": "Privacy policy, terms of use, and EULA",
|
|
31
|
+
"privacy": "Privacy Policy",
|
|
32
|
+
"privacyDescription": "How we handle your data",
|
|
33
|
+
"terms": "Terms of Use",
|
|
34
|
+
"termsDescription": "Terms and conditions",
|
|
35
|
+
"eula": "End User License Agreement",
|
|
36
|
+
"eulaDescription": "License agreement"
|
|
37
|
+
},
|
|
38
|
+
"version": {
|
|
39
|
+
"title": "App Version"
|
|
40
|
+
},
|
|
41
|
+
"languageSelection": {
|
|
42
|
+
"title": "Select Language",
|
|
43
|
+
"description": "Choose your preferred language",
|
|
44
|
+
"currentLanguage": "Current Language",
|
|
45
|
+
"searchPlaceholder": "Search languages"
|
|
46
|
+
},
|
|
47
|
+
"appearance": {
|
|
48
|
+
"title": "Appearance",
|
|
49
|
+
"darkMode": "Dark Mode",
|
|
50
|
+
"darkTheme": "Dark Theme",
|
|
51
|
+
"lightTheme": "Light Theme",
|
|
52
|
+
"themeDescription": "Language and theme settings"
|
|
53
|
+
},
|
|
54
|
+
"notifications": {
|
|
55
|
+
"title": "Notifications",
|
|
56
|
+
"description": "Manage notification preferences",
|
|
57
|
+
"enableNotifications": "Enable Notifications",
|
|
58
|
+
"notificationPermission": "Notification Permission",
|
|
59
|
+
"notificationPermissionDescription": "Allow app to send notifications"
|
|
60
|
+
},
|
|
61
|
+
"donate": {
|
|
62
|
+
"title": "Support the Developer",
|
|
63
|
+
"description": "Buy me a coffee to support development"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"types": {
|
|
3
|
+
"success": "Success",
|
|
4
|
+
"error": "Error",
|
|
5
|
+
"info": "Info",
|
|
6
|
+
"warning": "Warning"
|
|
7
|
+
},
|
|
8
|
+
"position": {
|
|
9
|
+
"top": "Top",
|
|
10
|
+
"bottom": "Bottom"
|
|
11
|
+
},
|
|
12
|
+
"presets": {
|
|
13
|
+
"saved": "Saved",
|
|
14
|
+
"savedDescription": "Your changes have been saved",
|
|
15
|
+
"deleted": "Deleted",
|
|
16
|
+
"deletedDescription": "Item has been removed",
|
|
17
|
+
"copied": "Copied",
|
|
18
|
+
"copiedDescription": "Copied to clipboard",
|
|
19
|
+
"errorGeneric": "Error",
|
|
20
|
+
"errorGenericDescription": "Something went wrong. Please try again.",
|
|
21
|
+
"errorNetwork": "Network Error",
|
|
22
|
+
"errorNetworkDescription": "Please check your connection",
|
|
23
|
+
"errorValidation": "Validation Error",
|
|
24
|
+
"errorValidationDescription": "Please check your input",
|
|
25
|
+
"infoLoading": "Loading",
|
|
26
|
+
"infoLoadingDescription": "Please wait...",
|
|
27
|
+
"infoSyncing": "Syncing",
|
|
28
|
+
"infoSyncingDescription": "Syncing your data...",
|
|
29
|
+
"warningUnsaved": "Unsaved Changes",
|
|
30
|
+
"warningUnsavedDescription": "You have unsaved changes",
|
|
31
|
+
"warningLimit": "Limit Reached",
|
|
32
|
+
"warningLimitDescription": "You have reached the limit"
|
|
33
|
+
},
|
|
34
|
+
"labels": {
|
|
35
|
+
"visibilityTime": "Display Duration",
|
|
36
|
+
"autoHide": "Auto Hide"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncStorage Wrapper
|
|
3
|
+
* Simple wrapper for AsyncStorage operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
7
|
+
|
|
8
|
+
export const STORAGE_KEYS = {
|
|
9
|
+
LANGUAGE: '@localization:language',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export const StorageWrapper = {
|
|
13
|
+
async getString(key: string, defaultValue: string): Promise<string> {
|
|
14
|
+
try {
|
|
15
|
+
const value = await AsyncStorage.getItem(key);
|
|
16
|
+
return value ?? defaultValue;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.warn('[Localization] Failed to get storage value:', error);
|
|
19
|
+
return defaultValue;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async setString(key: string, value: string): Promise<void> {
|
|
24
|
+
try {
|
|
25
|
+
await AsyncStorage.setItem(key, value);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.warn('[Localization] Failed to set storage value:', error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Localization Store
|
|
3
|
+
* Zustand state management for language preferences with AsyncStorage persistence
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { create } from 'zustand';
|
|
7
|
+
import { StorageWrapper, STORAGE_KEYS } from './AsyncStorageWrapper';
|
|
8
|
+
import i18n from '../config/i18n';
|
|
9
|
+
import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE, getLanguageByCode, getDeviceLocale } from '../config/languages';
|
|
10
|
+
import type { Language } from '../../domain/repositories/ILocalizationRepository';
|
|
11
|
+
|
|
12
|
+
interface LocalizationState {
|
|
13
|
+
currentLanguage: string;
|
|
14
|
+
isRTL: boolean;
|
|
15
|
+
isInitialized: boolean;
|
|
16
|
+
supportedLanguages: Language[];
|
|
17
|
+
setLanguage: (languageCode: string) => Promise<void>;
|
|
18
|
+
initialize: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const useLocalizationStore = create<LocalizationState>((set, get) => ({
|
|
22
|
+
currentLanguage: DEFAULT_LANGUAGE,
|
|
23
|
+
isRTL: false,
|
|
24
|
+
isInitialized: false,
|
|
25
|
+
supportedLanguages: SUPPORTED_LANGUAGES,
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initialize localization
|
|
29
|
+
* DEVICE LOCALE DETECTION:
|
|
30
|
+
* - First launch (no saved language): Automatically detect device locale
|
|
31
|
+
* - After manual selection: Use saved language preference
|
|
32
|
+
* - Fallback: English (en-US) if device locale not supported
|
|
33
|
+
*/
|
|
34
|
+
initialize: async () => {
|
|
35
|
+
// ✅ CRITICAL FIX: Don't reset isInitialized if already initialized
|
|
36
|
+
// This prevents UI flash on re-initialization
|
|
37
|
+
const { isInitialized: alreadyInitialized } = get();
|
|
38
|
+
if (alreadyInitialized) return;
|
|
39
|
+
|
|
40
|
+
// Get saved language preference
|
|
41
|
+
const savedLanguage = await StorageWrapper.getString(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE);
|
|
42
|
+
|
|
43
|
+
// ✅ DEVICE LOCALE DETECTION: Use device locale on first launch
|
|
44
|
+
let languageCode: string;
|
|
45
|
+
if (savedLanguage && savedLanguage !== DEFAULT_LANGUAGE) {
|
|
46
|
+
// User has previously selected a language → Use their choice
|
|
47
|
+
languageCode = savedLanguage;
|
|
48
|
+
} else {
|
|
49
|
+
// First launch → Detect device locale automatically
|
|
50
|
+
languageCode = getDeviceLocale();
|
|
51
|
+
// Save detected locale for future launches
|
|
52
|
+
await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ✅ DEFENSIVE: Validate language exists, fallback to default
|
|
56
|
+
const language = getLanguageByCode(languageCode);
|
|
57
|
+
const finalLanguage = language ? languageCode : DEFAULT_LANGUAGE;
|
|
58
|
+
const finalLanguageObj = getLanguageByCode(finalLanguage);
|
|
59
|
+
|
|
60
|
+
await i18n.changeLanguage(finalLanguage);
|
|
61
|
+
set({
|
|
62
|
+
currentLanguage: finalLanguage,
|
|
63
|
+
isRTL: finalLanguageObj?.rtl || false,
|
|
64
|
+
isInitialized: true, // ✅ Always set true to unblock UI
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Change language
|
|
70
|
+
* Updates i18n, state, and persists to AsyncStorage
|
|
71
|
+
*/
|
|
72
|
+
setLanguage: async (languageCode: string) => {
|
|
73
|
+
const language = getLanguageByCode(languageCode);
|
|
74
|
+
|
|
75
|
+
// ✅ DEFENSIVE: Early return if unsupported language
|
|
76
|
+
if (!language) return;
|
|
77
|
+
|
|
78
|
+
// Update i18n
|
|
79
|
+
await i18n.changeLanguage(languageCode);
|
|
80
|
+
|
|
81
|
+
// Update state
|
|
82
|
+
set({
|
|
83
|
+
currentLanguage: languageCode,
|
|
84
|
+
isRTL: language.rtl || false,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Persist language preference
|
|
88
|
+
await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Hook to use localization
|
|
94
|
+
* Provides current language, RTL state, language switching, and translation function
|
|
95
|
+
*/
|
|
96
|
+
export const useLocalization = () => {
|
|
97
|
+
const {
|
|
98
|
+
currentLanguage,
|
|
99
|
+
isRTL,
|
|
100
|
+
isInitialized,
|
|
101
|
+
supportedLanguages,
|
|
102
|
+
setLanguage,
|
|
103
|
+
initialize,
|
|
104
|
+
} = useLocalizationStore();
|
|
105
|
+
|
|
106
|
+
const currentLanguageObject = getLanguageByCode(currentLanguage);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
t: i18n.t.bind(i18n), // Translation function
|
|
110
|
+
currentLanguage,
|
|
111
|
+
currentLanguageObject,
|
|
112
|
+
isRTL,
|
|
113
|
+
isInitialized,
|
|
114
|
+
supportedLanguages,
|
|
115
|
+
setLanguage,
|
|
116
|
+
initialize,
|
|
117
|
+
};
|
|
118
|
+
};
|