@croacroa/react-native-template 1.0.0 → 2.0.1
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/.github/workflows/ci.yml +187 -184
- package/.github/workflows/eas-build.yml +55 -55
- package/.github/workflows/eas-update.yml +50 -50
- package/CHANGELOG.md +106 -106
- package/CONTRIBUTING.md +377 -377
- package/README.md +399 -399
- package/__tests__/components/snapshots.test.tsx +131 -0
- package/__tests__/integration/auth-api.test.tsx +227 -0
- package/__tests__/performance/VirtualizedList.perf.test.tsx +362 -0
- package/app/(public)/onboarding.tsx +5 -5
- package/app.config.ts +45 -2
- package/assets/images/.gitkeep +7 -7
- package/components/onboarding/OnboardingScreen.tsx +370 -370
- package/components/onboarding/index.ts +2 -2
- package/components/providers/SuspenseBoundary.tsx +357 -0
- package/components/providers/index.ts +21 -0
- package/components/ui/Avatar.tsx +316 -316
- package/components/ui/Badge.tsx +416 -416
- package/components/ui/BottomSheet.tsx +307 -307
- package/components/ui/Checkbox.tsx +261 -261
- package/components/ui/OptimizedImage.tsx +369 -369
- package/components/ui/Select.tsx +240 -240
- package/components/ui/VirtualizedList.tsx +285 -0
- package/components/ui/index.ts +23 -18
- package/constants/config.ts +97 -54
- package/docs/adr/001-state-management.md +79 -79
- package/docs/adr/002-styling-approach.md +130 -130
- package/docs/adr/003-data-fetching.md +155 -155
- package/docs/adr/004-auth-adapter-pattern.md +144 -144
- package/docs/adr/README.md +78 -78
- package/hooks/index.ts +27 -25
- package/hooks/useApi.ts +102 -5
- package/hooks/useAuth.tsx +82 -0
- package/hooks/useBiometrics.ts +295 -295
- package/hooks/useDeepLinking.ts +256 -256
- package/hooks/useMFA.ts +499 -0
- package/hooks/useNotifications.ts +39 -0
- package/hooks/useOffline.ts +60 -6
- package/hooks/usePerformance.ts +434 -434
- package/hooks/useTheme.tsx +76 -0
- package/hooks/useUpdates.ts +358 -358
- package/i18n/index.ts +194 -77
- package/i18n/locales/ar.json +101 -0
- package/i18n/locales/de.json +101 -0
- package/i18n/locales/en.json +101 -101
- package/i18n/locales/es.json +101 -0
- package/i18n/locales/fr.json +101 -101
- package/jest.config.js +4 -4
- package/maestro/README.md +113 -113
- package/maestro/config.yaml +35 -35
- package/maestro/flows/login.yaml +62 -62
- package/maestro/flows/mfa-login.yaml +92 -0
- package/maestro/flows/mfa-setup.yaml +86 -0
- package/maestro/flows/navigation.yaml +68 -68
- package/maestro/flows/offline-conflict.yaml +101 -0
- package/maestro/flows/offline-sync.yaml +128 -0
- package/maestro/flows/offline.yaml +60 -60
- package/maestro/flows/register.yaml +94 -94
- package/package.json +175 -170
- package/services/analytics.ts +428 -428
- package/services/api.ts +340 -340
- package/services/authAdapter.ts +333 -333
- package/services/backgroundSync.ts +626 -0
- package/services/index.ts +54 -22
- package/services/security.ts +286 -0
- package/tailwind.config.js +47 -47
- package/utils/accessibility.ts +446 -446
- package/utils/index.ts +52 -43
- package/utils/validation.ts +2 -1
- package/utils/withAccessibility.tsx +272 -0
package/i18n/index.ts
CHANGED
|
@@ -1,77 +1,194 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Internationalization (i18n) setup with RTL support
|
|
3
|
+
* Provides multi-language support with automatic device language detection.
|
|
4
|
+
* @module i18n
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import i18n from "i18next";
|
|
8
|
+
import { initReactI18next } from "react-i18next";
|
|
9
|
+
import * as Localization from "expo-localization";
|
|
10
|
+
import { I18nManager } from "react-native";
|
|
11
|
+
import { storage } from "@/services/storage";
|
|
12
|
+
|
|
13
|
+
import en from "./locales/en.json";
|
|
14
|
+
import fr from "./locales/fr.json";
|
|
15
|
+
import es from "./locales/es.json";
|
|
16
|
+
import de from "./locales/de.json";
|
|
17
|
+
import ar from "./locales/ar.json";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Supported languages configuration.
|
|
21
|
+
* Each language includes its English name, native name, and RTL flag.
|
|
22
|
+
*/
|
|
23
|
+
export const LANGUAGES = {
|
|
24
|
+
en: { name: "English", nativeName: "English", rtl: false },
|
|
25
|
+
fr: { name: "French", nativeName: "Français", rtl: false },
|
|
26
|
+
es: { name: "Spanish", nativeName: "Español", rtl: false },
|
|
27
|
+
de: { name: "German", nativeName: "Deutsch", rtl: false },
|
|
28
|
+
ar: { name: "Arabic", nativeName: "العربية", rtl: true },
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export type LanguageCode = keyof typeof LANGUAGES;
|
|
32
|
+
|
|
33
|
+
const LANGUAGE_STORAGE_KEY = "app_language";
|
|
34
|
+
|
|
35
|
+
const resources = {
|
|
36
|
+
en: { translation: en },
|
|
37
|
+
fr: { translation: fr },
|
|
38
|
+
es: { translation: es },
|
|
39
|
+
de: { translation: de },
|
|
40
|
+
ar: { translation: ar },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the device's preferred language
|
|
45
|
+
* Falls back to 'en' if not supported
|
|
46
|
+
*/
|
|
47
|
+
function getDeviceLanguage(): LanguageCode {
|
|
48
|
+
const locale = Localization.getLocales()[0];
|
|
49
|
+
const languageCode = locale?.languageCode || "en";
|
|
50
|
+
|
|
51
|
+
// Check if we support this language
|
|
52
|
+
if (languageCode in LANGUAGES) {
|
|
53
|
+
return languageCode as LanguageCode;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return "en";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initialize i18n with the saved language or device language
|
|
61
|
+
*/
|
|
62
|
+
export async function initI18n(): Promise<void> {
|
|
63
|
+
// Try to get saved language preference
|
|
64
|
+
const savedLanguage = await storage.get<LanguageCode>(LANGUAGE_STORAGE_KEY);
|
|
65
|
+
const initialLanguage = savedLanguage || getDeviceLanguage();
|
|
66
|
+
|
|
67
|
+
await i18n.use(initReactI18next).init({
|
|
68
|
+
resources,
|
|
69
|
+
lng: initialLanguage,
|
|
70
|
+
fallbackLng: "en",
|
|
71
|
+
compatibilityJSON: "v3",
|
|
72
|
+
interpolation: {
|
|
73
|
+
escapeValue: false, // React already escapes values
|
|
74
|
+
},
|
|
75
|
+
react: {
|
|
76
|
+
useSuspense: false, // Prevents issues with SSR/async loading
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Change the app language and apply RTL settings if needed.
|
|
83
|
+
* Note: RTL changes require an app restart to take full effect.
|
|
84
|
+
*
|
|
85
|
+
* @param language - The language code to switch to
|
|
86
|
+
* @returns Promise that resolves when the language is changed
|
|
87
|
+
*/
|
|
88
|
+
export async function changeLanguage(language: LanguageCode): Promise<void> {
|
|
89
|
+
await i18n.changeLanguage(language);
|
|
90
|
+
await storage.set(LANGUAGE_STORAGE_KEY, language);
|
|
91
|
+
|
|
92
|
+
// Handle RTL layout direction
|
|
93
|
+
const isRTL = LANGUAGES[language].rtl;
|
|
94
|
+
if (I18nManager.isRTL !== isRTL) {
|
|
95
|
+
I18nManager.allowRTL(isRTL);
|
|
96
|
+
I18nManager.forceRTL(isRTL);
|
|
97
|
+
// Note: App restart is required for RTL changes to take full effect
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if the current language is RTL
|
|
103
|
+
*/
|
|
104
|
+
export function isCurrentLanguageRTL(): boolean {
|
|
105
|
+
const currentLang = getCurrentLanguage();
|
|
106
|
+
return LANGUAGES[currentLang]?.rtl ?? false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get all available languages as an array for UI selectors
|
|
111
|
+
*/
|
|
112
|
+
export function getAvailableLanguages(): Array<{
|
|
113
|
+
code: LanguageCode;
|
|
114
|
+
name: string;
|
|
115
|
+
nativeName: string;
|
|
116
|
+
rtl: boolean;
|
|
117
|
+
}> {
|
|
118
|
+
return Object.entries(LANGUAGES).map(([code, config]) => ({
|
|
119
|
+
code: code as LanguageCode,
|
|
120
|
+
...config,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get current language
|
|
126
|
+
*/
|
|
127
|
+
export function getCurrentLanguage(): LanguageCode {
|
|
128
|
+
return (i18n.language || "en") as LanguageCode;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Hook-friendly RTL detection
|
|
133
|
+
* Returns current RTL state from I18nManager
|
|
134
|
+
*/
|
|
135
|
+
export function isRTL(): boolean {
|
|
136
|
+
return I18nManager.isRTL;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get text alignment based on RTL
|
|
141
|
+
* Useful for styling text components
|
|
142
|
+
*/
|
|
143
|
+
export function getTextAlign(): "left" | "right" {
|
|
144
|
+
return I18nManager.isRTL ? "right" : "left";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get flex direction based on RTL
|
|
149
|
+
* Useful for horizontal layouts
|
|
150
|
+
*/
|
|
151
|
+
export function getFlexDirection(): "row" | "row-reverse" {
|
|
152
|
+
return I18nManager.isRTL ? "row-reverse" : "row";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get start/end values swapped for RTL
|
|
157
|
+
* Useful for margins, paddings, and positioning
|
|
158
|
+
*/
|
|
159
|
+
export function getStartEnd(): { start: "left" | "right"; end: "left" | "right" } {
|
|
160
|
+
return I18nManager.isRTL
|
|
161
|
+
? { start: "right", end: "left" }
|
|
162
|
+
: { start: "left", end: "right" };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Transform a value for RTL (e.g., for translateX animations)
|
|
167
|
+
* @param value - The original value
|
|
168
|
+
* @returns The transformed value (negated for RTL)
|
|
169
|
+
*/
|
|
170
|
+
export function rtlTransform(value: number): number {
|
|
171
|
+
return I18nManager.isRTL ? -value : value;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if a specific language requires RTL
|
|
176
|
+
*/
|
|
177
|
+
export function isLanguageRTL(languageCode: string): boolean {
|
|
178
|
+
const lang = LANGUAGES[languageCode as LanguageCode];
|
|
179
|
+
return lang?.rtl ?? false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Force app restart for RTL changes to take effect
|
|
184
|
+
* Call this after changing to/from an RTL language
|
|
185
|
+
*/
|
|
186
|
+
export async function applyRTLAndRestart(isRTL: boolean): Promise<void> {
|
|
187
|
+
I18nManager.allowRTL(isRTL);
|
|
188
|
+
I18nManager.forceRTL(isRTL);
|
|
189
|
+
// Note: In production, use expo-updates to reload the app
|
|
190
|
+
// await Updates.reloadAsync();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { i18n };
|
|
194
|
+
export default i18n;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"loading": "جاري التحميل...",
|
|
4
|
+
"error": "خطأ",
|
|
5
|
+
"success": "نجاح",
|
|
6
|
+
"cancel": "إلغاء",
|
|
7
|
+
"confirm": "تأكيد",
|
|
8
|
+
"save": "حفظ",
|
|
9
|
+
"delete": "حذف",
|
|
10
|
+
"edit": "تعديل",
|
|
11
|
+
"close": "إغلاق",
|
|
12
|
+
"back": "رجوع",
|
|
13
|
+
"next": "التالي",
|
|
14
|
+
"done": "تم",
|
|
15
|
+
"retry": "إعادة المحاولة",
|
|
16
|
+
"search": "بحث",
|
|
17
|
+
"noResults": "لم يتم العثور على نتائج",
|
|
18
|
+
"offline": "أنت غير متصل",
|
|
19
|
+
"online": "متصل مرة أخرى"
|
|
20
|
+
},
|
|
21
|
+
"auth": {
|
|
22
|
+
"signIn": "تسجيل الدخول",
|
|
23
|
+
"signUp": "إنشاء حساب",
|
|
24
|
+
"signOut": "تسجيل الخروج",
|
|
25
|
+
"email": "البريد الإلكتروني",
|
|
26
|
+
"password": "كلمة المرور",
|
|
27
|
+
"confirmPassword": "تأكيد كلمة المرور",
|
|
28
|
+
"name": "الاسم",
|
|
29
|
+
"forgotPassword": "نسيت كلمة المرور؟",
|
|
30
|
+
"resetPassword": "إعادة تعيين كلمة المرور",
|
|
31
|
+
"noAccount": "ليس لديك حساب؟",
|
|
32
|
+
"haveAccount": "لديك حساب بالفعل؟",
|
|
33
|
+
"welcomeBack": "مرحباً بعودتك!",
|
|
34
|
+
"signInToContinue": "سجل الدخول للمتابعة",
|
|
35
|
+
"createAccount": "إنشاء حساب",
|
|
36
|
+
"joinUs": "انضم إلينا اليوم",
|
|
37
|
+
"enterEmail": "أدخل بريدك الإلكتروني",
|
|
38
|
+
"enterPassword": "أدخل كلمة المرور",
|
|
39
|
+
"enterName": "أدخل اسمك",
|
|
40
|
+
"passwordHint": "8 أحرف على الأقل",
|
|
41
|
+
"invalidCredentials": "البريد الإلكتروني أو كلمة المرور غير صحيحة",
|
|
42
|
+
"accountCreated": "تم إنشاء الحساب بنجاح!",
|
|
43
|
+
"sessionExpired": "انتهت الجلسة. يرجى تسجيل الدخول مرة أخرى.",
|
|
44
|
+
"biometric": {
|
|
45
|
+
"title": "المصادقة البيومترية",
|
|
46
|
+
"prompt": "قم بالمصادقة للمتابعة",
|
|
47
|
+
"fallback": "استخدام رمز المرور",
|
|
48
|
+
"enabled": "تم تفعيل تسجيل الدخول البيومتري",
|
|
49
|
+
"disabled": "تم تعطيل تسجيل الدخول البيومتري",
|
|
50
|
+
"notAvailable": "المصادقة البيومترية غير متاحة"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"navigation": {
|
|
54
|
+
"home": "الرئيسية",
|
|
55
|
+
"profile": "الملف الشخصي",
|
|
56
|
+
"settings": "الإعدادات",
|
|
57
|
+
"notifications": "الإشعارات"
|
|
58
|
+
},
|
|
59
|
+
"profile": {
|
|
60
|
+
"title": "الملف الشخصي",
|
|
61
|
+
"editProfile": "تعديل الملف الشخصي",
|
|
62
|
+
"changePhoto": "تغيير الصورة",
|
|
63
|
+
"personalInfo": "المعلومات الشخصية",
|
|
64
|
+
"memberSince": "عضو منذ {{date}}"
|
|
65
|
+
},
|
|
66
|
+
"settings": {
|
|
67
|
+
"title": "الإعدادات",
|
|
68
|
+
"appearance": "المظهر",
|
|
69
|
+
"theme": "السمة",
|
|
70
|
+
"themeLight": "فاتح",
|
|
71
|
+
"themeDark": "داكن",
|
|
72
|
+
"themeSystem": "النظام",
|
|
73
|
+
"language": "اللغة",
|
|
74
|
+
"notifications": "الإشعارات",
|
|
75
|
+
"pushNotifications": "إشعارات الدفع",
|
|
76
|
+
"emailNotifications": "إشعارات البريد الإلكتروني",
|
|
77
|
+
"security": "الأمان",
|
|
78
|
+
"biometricAuth": "المصادقة البيومترية",
|
|
79
|
+
"changePassword": "تغيير كلمة المرور",
|
|
80
|
+
"privacy": "سياسة الخصوصية",
|
|
81
|
+
"terms": "شروط الخدمة",
|
|
82
|
+
"about": "حول",
|
|
83
|
+
"version": "الإصدار",
|
|
84
|
+
"deleteAccount": "حذف الحساب"
|
|
85
|
+
},
|
|
86
|
+
"errors": {
|
|
87
|
+
"generic": "حدث خطأ ما",
|
|
88
|
+
"network": "خطأ في الشبكة. يرجى التحقق من اتصالك.",
|
|
89
|
+
"timeout": "انتهت مهلة الطلب. يرجى المحاولة مرة أخرى.",
|
|
90
|
+
"unauthorized": "غير مصرح لك بتنفيذ هذا الإجراء.",
|
|
91
|
+
"notFound": "المورد غير موجود.",
|
|
92
|
+
"validation": "يرجى التحقق من المدخلات والمحاولة مرة أخرى."
|
|
93
|
+
},
|
|
94
|
+
"validation": {
|
|
95
|
+
"required": "هذا الحقل مطلوب",
|
|
96
|
+
"email": "يرجى إدخال بريد إلكتروني صالح",
|
|
97
|
+
"passwordMin": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
|
|
98
|
+
"passwordMatch": "كلمات المرور غير متطابقة",
|
|
99
|
+
"nameMin": "يجب أن يكون الاسم حرفين على الأقل"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"loading": "Wird geladen...",
|
|
4
|
+
"error": "Fehler",
|
|
5
|
+
"success": "Erfolg",
|
|
6
|
+
"cancel": "Abbrechen",
|
|
7
|
+
"confirm": "Bestätigen",
|
|
8
|
+
"save": "Speichern",
|
|
9
|
+
"delete": "Löschen",
|
|
10
|
+
"edit": "Bearbeiten",
|
|
11
|
+
"close": "Schließen",
|
|
12
|
+
"back": "Zurück",
|
|
13
|
+
"next": "Weiter",
|
|
14
|
+
"done": "Fertig",
|
|
15
|
+
"retry": "Erneut versuchen",
|
|
16
|
+
"search": "Suchen",
|
|
17
|
+
"noResults": "Keine Ergebnisse gefunden",
|
|
18
|
+
"offline": "Du bist offline",
|
|
19
|
+
"online": "Wieder online"
|
|
20
|
+
},
|
|
21
|
+
"auth": {
|
|
22
|
+
"signIn": "Anmelden",
|
|
23
|
+
"signUp": "Registrieren",
|
|
24
|
+
"signOut": "Abmelden",
|
|
25
|
+
"email": "E-Mail",
|
|
26
|
+
"password": "Passwort",
|
|
27
|
+
"confirmPassword": "Passwort bestätigen",
|
|
28
|
+
"name": "Name",
|
|
29
|
+
"forgotPassword": "Passwort vergessen?",
|
|
30
|
+
"resetPassword": "Passwort zurücksetzen",
|
|
31
|
+
"noAccount": "Noch kein Konto?",
|
|
32
|
+
"haveAccount": "Bereits ein Konto?",
|
|
33
|
+
"welcomeBack": "Willkommen zurück!",
|
|
34
|
+
"signInToContinue": "Melde dich an, um fortzufahren",
|
|
35
|
+
"createAccount": "Konto erstellen",
|
|
36
|
+
"joinUs": "Werde noch heute Mitglied",
|
|
37
|
+
"enterEmail": "E-Mail eingeben",
|
|
38
|
+
"enterPassword": "Passwort eingeben",
|
|
39
|
+
"enterName": "Name eingeben",
|
|
40
|
+
"passwordHint": "Mindestens 8 Zeichen",
|
|
41
|
+
"invalidCredentials": "Ungültige E-Mail oder Passwort",
|
|
42
|
+
"accountCreated": "Konto erfolgreich erstellt!",
|
|
43
|
+
"sessionExpired": "Sitzung abgelaufen. Bitte erneut anmelden.",
|
|
44
|
+
"biometric": {
|
|
45
|
+
"title": "Biometrische Authentifizierung",
|
|
46
|
+
"prompt": "Authentifiziere dich, um fortzufahren",
|
|
47
|
+
"fallback": "Code verwenden",
|
|
48
|
+
"enabled": "Biometrische Anmeldung aktiviert",
|
|
49
|
+
"disabled": "Biometrische Anmeldung deaktiviert",
|
|
50
|
+
"notAvailable": "Biometrische Authentifizierung nicht verfügbar"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"navigation": {
|
|
54
|
+
"home": "Startseite",
|
|
55
|
+
"profile": "Profil",
|
|
56
|
+
"settings": "Einstellungen",
|
|
57
|
+
"notifications": "Benachrichtigungen"
|
|
58
|
+
},
|
|
59
|
+
"profile": {
|
|
60
|
+
"title": "Profil",
|
|
61
|
+
"editProfile": "Profil bearbeiten",
|
|
62
|
+
"changePhoto": "Foto ändern",
|
|
63
|
+
"personalInfo": "Persönliche Informationen",
|
|
64
|
+
"memberSince": "Mitglied seit {{date}}"
|
|
65
|
+
},
|
|
66
|
+
"settings": {
|
|
67
|
+
"title": "Einstellungen",
|
|
68
|
+
"appearance": "Erscheinungsbild",
|
|
69
|
+
"theme": "Design",
|
|
70
|
+
"themeLight": "Hell",
|
|
71
|
+
"themeDark": "Dunkel",
|
|
72
|
+
"themeSystem": "System",
|
|
73
|
+
"language": "Sprache",
|
|
74
|
+
"notifications": "Benachrichtigungen",
|
|
75
|
+
"pushNotifications": "Push-Benachrichtigungen",
|
|
76
|
+
"emailNotifications": "E-Mail-Benachrichtigungen",
|
|
77
|
+
"security": "Sicherheit",
|
|
78
|
+
"biometricAuth": "Biometrische Authentifizierung",
|
|
79
|
+
"changePassword": "Passwort ändern",
|
|
80
|
+
"privacy": "Datenschutzrichtlinie",
|
|
81
|
+
"terms": "Nutzungsbedingungen",
|
|
82
|
+
"about": "Über",
|
|
83
|
+
"version": "Version",
|
|
84
|
+
"deleteAccount": "Konto löschen"
|
|
85
|
+
},
|
|
86
|
+
"errors": {
|
|
87
|
+
"generic": "Etwas ist schief gelaufen",
|
|
88
|
+
"network": "Netzwerkfehler. Bitte überprüfe deine Verbindung.",
|
|
89
|
+
"timeout": "Zeitüberschreitung. Bitte erneut versuchen.",
|
|
90
|
+
"unauthorized": "Du bist nicht berechtigt, diese Aktion auszuführen.",
|
|
91
|
+
"notFound": "Ressource nicht gefunden.",
|
|
92
|
+
"validation": "Bitte überprüfe deine Eingaben und versuche es erneut."
|
|
93
|
+
},
|
|
94
|
+
"validation": {
|
|
95
|
+
"required": "Dieses Feld ist erforderlich",
|
|
96
|
+
"email": "Bitte gib eine gültige E-Mail-Adresse ein",
|
|
97
|
+
"passwordMin": "Das Passwort muss mindestens 8 Zeichen lang sein",
|
|
98
|
+
"passwordMatch": "Passwörter stimmen nicht überein",
|
|
99
|
+
"nameMin": "Der Name muss mindestens 2 Zeichen lang sein"
|
|
100
|
+
}
|
|
101
|
+
}
|