@churchapps/apphelper-login 0.5.0 → 0.5.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/README.md +103 -103
- package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
- package/dist/helpers/AnalyticsHelper.js +21 -6
- package/dist/helpers/AnalyticsHelper.js.map +1 -1
- package/package.json +57 -57
- package/src/LoginPage.tsx +305 -305
- package/src/LogoutPage.tsx +43 -43
- package/src/components/Forgot.tsx +246 -246
- package/src/components/Login.tsx +305 -305
- package/src/components/LoginSetPassword.tsx +297 -297
- package/src/components/Register.tsx +370 -370
- package/src/components/SelectChurchModal.tsx +88 -88
- package/src/components/SelectChurchRegister.tsx +88 -88
- package/src/components/SelectChurchSearch.tsx +85 -85
- package/src/components/SelectableChurch.tsx +114 -114
- package/src/helpers/AnalyticsHelper.ts +44 -32
- package/src/helpers/Locale.ts +248 -248
- package/src/helpers/index.ts +1 -1
- package/src/index.ts +10 -10
- package/tsconfig.json +29 -29
package/src/helpers/Locale.ts
CHANGED
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
import i18n from "i18next";
|
|
2
|
-
import { initReactI18next } from "react-i18next/initReactI18next";
|
|
3
|
-
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
-
import Backend from "i18next-chained-backend";
|
|
5
|
-
|
|
6
|
-
interface TranslationResources {
|
|
7
|
-
[key: string]: {
|
|
8
|
-
translation: Record<string, unknown>;
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface ExtraLanguageCodes {
|
|
13
|
-
[key: string]: string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class Locale {
|
|
17
|
-
private static readonly supportedLanguages: string[] = [
|
|
18
|
-
"de",
|
|
19
|
-
"en",
|
|
20
|
-
"es",
|
|
21
|
-
"fr",
|
|
22
|
-
"hi",
|
|
23
|
-
"it",
|
|
24
|
-
"ko",
|
|
25
|
-
"no",
|
|
26
|
-
"pt",
|
|
27
|
-
"ru",
|
|
28
|
-
"tl",
|
|
29
|
-
"zh",
|
|
30
|
-
];
|
|
31
|
-
private static readonly extraCodes: ExtraLanguageCodes = { no: ["nb", "nn"] };
|
|
32
|
-
|
|
33
|
-
// Hard-coded English fallbacks for when locale files are not available
|
|
34
|
-
private static readonly englishFallbacks: Record<string, any> = {
|
|
35
|
-
"common": {
|
|
36
|
-
"pleaseWait": "Please wait...",
|
|
37
|
-
"search": "Search",
|
|
38
|
-
"cancel": "Cancel",
|
|
39
|
-
"save": "Save",
|
|
40
|
-
"delete": "Delete",
|
|
41
|
-
"edit": "Edit",
|
|
42
|
-
"add": "Add",
|
|
43
|
-
"close": "Close",
|
|
44
|
-
"date": "Date",
|
|
45
|
-
"error": "Error",
|
|
46
|
-
"submit": "Submit",
|
|
47
|
-
"update": "Update"
|
|
48
|
-
},
|
|
49
|
-
"login": {
|
|
50
|
-
"createAccount": "Create an Account",
|
|
51
|
-
"email": "Email",
|
|
52
|
-
"expiredLink": "The current link is expired.",
|
|
53
|
-
"forgot": "Forgot Password",
|
|
54
|
-
"goLogin": "Go to Login",
|
|
55
|
-
"login": "Login",
|
|
56
|
-
"password": "Password",
|
|
57
|
-
"register": "Register",
|
|
58
|
-
"registerThankYou": "Thank you for registering! Please check your email to verify your account.",
|
|
59
|
-
"requestLink": "Request a new reset link",
|
|
60
|
-
"reset": "Reset",
|
|
61
|
-
"resetInstructions": "Enter your email address to request a password reset.",
|
|
62
|
-
"resetPassword": "Reset Password",
|
|
63
|
-
"resetSent": "Password reset email sent!",
|
|
64
|
-
"setPassword": "Set Password",
|
|
65
|
-
"signIn": "Sign In",
|
|
66
|
-
"signInTitle": "Please Sign In",
|
|
67
|
-
"verifyPassword": "Verify Password",
|
|
68
|
-
"welcomeName": "Welcome back, <b>{}</b>! Please wait while we load your data.",
|
|
69
|
-
"welcomeBack": "Welcome back",
|
|
70
|
-
"validate": {
|
|
71
|
-
"email": "Please enter a valid email address.",
|
|
72
|
-
"firstName": "Please enter your first name.",
|
|
73
|
-
"invalid": "Invalid login. Please check your email or password.",
|
|
74
|
-
"lastName": "Please enter your last name.",
|
|
75
|
-
"password": "Please enter a password.",
|
|
76
|
-
"passwordLength": "Password must be at least 8 characters long.",
|
|
77
|
-
"passwordMatch": "Passwords do not match.",
|
|
78
|
-
"selectingChurch": "Error in selecting church. Please verify and try again"
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"selectChurch": {
|
|
82
|
-
"address1": "Address Line 1",
|
|
83
|
-
"address2": "Address Line 2",
|
|
84
|
-
"another": "Choose another church",
|
|
85
|
-
"city": "City",
|
|
86
|
-
"confirmRegister": "Are you sure you wish to register a new church?",
|
|
87
|
-
"country": "Country",
|
|
88
|
-
"name": "Church Name",
|
|
89
|
-
"noMatches": "No matches found.",
|
|
90
|
-
"register": "Register a New Church",
|
|
91
|
-
"selectChurch": "Select a Church",
|
|
92
|
-
"state": "State / Province",
|
|
93
|
-
"zip": "Zip / Postal Code",
|
|
94
|
-
"validate": {
|
|
95
|
-
"address": "Address cannot be blank.",
|
|
96
|
-
"city": "City cannot be blank.",
|
|
97
|
-
"country": "Country cannot be blank.",
|
|
98
|
-
"name": "Church name cannot be blank.",
|
|
99
|
-
"state": "State/Province cannot be blank.",
|
|
100
|
-
"zip": "Zip/Postal code cannot be blank."
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
static init = async (backends: string[]): Promise<void> => {
|
|
106
|
-
const resources: TranslationResources = {};
|
|
107
|
-
let langs = ["en"];
|
|
108
|
-
|
|
109
|
-
if (typeof navigator !== "undefined") {
|
|
110
|
-
const browserLang = navigator.language.split("-")[0];
|
|
111
|
-
const mappedLang
|
|
112
|
-
= Object.keys(this.extraCodes).find((code) =>
|
|
113
|
-
this.extraCodes[code].includes(browserLang),
|
|
114
|
-
) || browserLang;
|
|
115
|
-
const notSupported = this.supportedLanguages.indexOf(mappedLang) === -1;
|
|
116
|
-
langs = mappedLang === "en" || notSupported ? ["en"] : ["en", mappedLang];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Load translations for each language
|
|
120
|
-
for (const lang of langs) {
|
|
121
|
-
resources[lang] = { translation: {} };
|
|
122
|
-
try {
|
|
123
|
-
for (const backend of backends) {
|
|
124
|
-
const url = backend.replace("{{lng}}", lang);
|
|
125
|
-
try {
|
|
126
|
-
const response = await fetch(url);
|
|
127
|
-
if (response.ok) {
|
|
128
|
-
const data = await response.json();
|
|
129
|
-
resources[lang].translation = this.deepMerge(
|
|
130
|
-
resources[lang].translation,
|
|
131
|
-
data,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
} catch (error) {
|
|
135
|
-
console.warn(`Failed to load translations from ${url}:`, error);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.warn(`Failed to load translations for language ${lang}:`, error);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Initialize i18n
|
|
144
|
-
try {
|
|
145
|
-
await i18n
|
|
146
|
-
.use(Backend)
|
|
147
|
-
.use(LanguageDetector)
|
|
148
|
-
.use(initReactI18next)
|
|
149
|
-
.init({
|
|
150
|
-
resources,
|
|
151
|
-
fallbackLng: "en",
|
|
152
|
-
debug: false,
|
|
153
|
-
interpolation: {
|
|
154
|
-
escapeValue: false,
|
|
155
|
-
},
|
|
156
|
-
detection: {
|
|
157
|
-
order: ["navigator"],
|
|
158
|
-
caches: ["localStorage"],
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
} catch (error) {
|
|
162
|
-
console.warn("Failed to initialize i18n:", error);
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
private static deepMerge(
|
|
167
|
-
target: Record<string, unknown>,
|
|
168
|
-
source: Record<string, unknown>,
|
|
169
|
-
): Record<string, unknown> {
|
|
170
|
-
for (const key in source) {
|
|
171
|
-
if (this.isObject(source[key])) {
|
|
172
|
-
if (!target[key]) Object.assign(target, { [key]: {} });
|
|
173
|
-
this.deepMerge(
|
|
174
|
-
target[key] as Record<string, unknown>,
|
|
175
|
-
source[key] as Record<string, unknown>,
|
|
176
|
-
);
|
|
177
|
-
} else Object.assign(target, { [key]: source[key] });
|
|
178
|
-
}
|
|
179
|
-
return target;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private static isObject(obj: unknown): boolean {
|
|
183
|
-
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Helper method to get value from nested object using dot notation
|
|
187
|
-
private static getNestedValue(obj: Record<string, any>, path: string): any {
|
|
188
|
-
return path.split('.').reduce((current, key) => {
|
|
189
|
-
return current && current[key] !== undefined ? current[key] : undefined;
|
|
190
|
-
}, obj);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// New helper method that uses i18n with hard-coded English fallback
|
|
194
|
-
static t(key: string, options?: Record<string, unknown>): string {
|
|
195
|
-
try {
|
|
196
|
-
// Check if i18n is initialized and has the key
|
|
197
|
-
if (i18n && i18n.isInitialized && i18n.exists(key)) {
|
|
198
|
-
const translation = i18n.t(key, options);
|
|
199
|
-
// If translation is not the same as the key, return it
|
|
200
|
-
if (translation !== key) {
|
|
201
|
-
return translation;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
} catch (error) {
|
|
205
|
-
// If i18n fails, fall through to hard-coded fallback
|
|
206
|
-
console.warn(`i18n translation failed for key "${key}":`, error);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Fallback to hard-coded English translations
|
|
210
|
-
const fallbackValue = this.getNestedValue(this.englishFallbacks, key);
|
|
211
|
-
if (fallbackValue !== undefined) {
|
|
212
|
-
// Handle simple string interpolation for options
|
|
213
|
-
if (typeof fallbackValue === 'string' && options) {
|
|
214
|
-
let result = fallbackValue;
|
|
215
|
-
Object.keys(options).forEach(optionKey => {
|
|
216
|
-
const placeholder = `{{${optionKey}}}`;
|
|
217
|
-
if (result.includes(placeholder)) {
|
|
218
|
-
result = result.replace(new RegExp(placeholder, 'g'), String(options[optionKey]));
|
|
219
|
-
}
|
|
220
|
-
// Also handle {} placeholder for backward compatibility
|
|
221
|
-
if (result.includes('{}')) {
|
|
222
|
-
result = result.replace('{}', String(options[optionKey]));
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
return String(fallbackValue);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// If no fallback found, return the key itself
|
|
231
|
-
return key;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Keep the old method for backward compatibility
|
|
235
|
-
static label(key: string): string {
|
|
236
|
-
return this.t(key);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Helper method to check if i18n is initialized
|
|
240
|
-
static isInitialized(): boolean {
|
|
241
|
-
return i18n && i18n.isInitialized;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Method to set up basic fallback-only mode (no i18n)
|
|
245
|
-
static initFallbackMode(): void {
|
|
246
|
-
console.info("Locale: Running in fallback mode with English labels only");
|
|
247
|
-
}
|
|
248
|
-
}
|
|
1
|
+
import i18n from "i18next";
|
|
2
|
+
import { initReactI18next } from "react-i18next/initReactI18next";
|
|
3
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
+
import Backend from "i18next-chained-backend";
|
|
5
|
+
|
|
6
|
+
interface TranslationResources {
|
|
7
|
+
[key: string]: {
|
|
8
|
+
translation: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ExtraLanguageCodes {
|
|
13
|
+
[key: string]: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Locale {
|
|
17
|
+
private static readonly supportedLanguages: string[] = [
|
|
18
|
+
"de",
|
|
19
|
+
"en",
|
|
20
|
+
"es",
|
|
21
|
+
"fr",
|
|
22
|
+
"hi",
|
|
23
|
+
"it",
|
|
24
|
+
"ko",
|
|
25
|
+
"no",
|
|
26
|
+
"pt",
|
|
27
|
+
"ru",
|
|
28
|
+
"tl",
|
|
29
|
+
"zh",
|
|
30
|
+
];
|
|
31
|
+
private static readonly extraCodes: ExtraLanguageCodes = { no: ["nb", "nn"] };
|
|
32
|
+
|
|
33
|
+
// Hard-coded English fallbacks for when locale files are not available
|
|
34
|
+
private static readonly englishFallbacks: Record<string, any> = {
|
|
35
|
+
"common": {
|
|
36
|
+
"pleaseWait": "Please wait...",
|
|
37
|
+
"search": "Search",
|
|
38
|
+
"cancel": "Cancel",
|
|
39
|
+
"save": "Save",
|
|
40
|
+
"delete": "Delete",
|
|
41
|
+
"edit": "Edit",
|
|
42
|
+
"add": "Add",
|
|
43
|
+
"close": "Close",
|
|
44
|
+
"date": "Date",
|
|
45
|
+
"error": "Error",
|
|
46
|
+
"submit": "Submit",
|
|
47
|
+
"update": "Update"
|
|
48
|
+
},
|
|
49
|
+
"login": {
|
|
50
|
+
"createAccount": "Create an Account",
|
|
51
|
+
"email": "Email",
|
|
52
|
+
"expiredLink": "The current link is expired.",
|
|
53
|
+
"forgot": "Forgot Password",
|
|
54
|
+
"goLogin": "Go to Login",
|
|
55
|
+
"login": "Login",
|
|
56
|
+
"password": "Password",
|
|
57
|
+
"register": "Register",
|
|
58
|
+
"registerThankYou": "Thank you for registering! Please check your email to verify your account.",
|
|
59
|
+
"requestLink": "Request a new reset link",
|
|
60
|
+
"reset": "Reset",
|
|
61
|
+
"resetInstructions": "Enter your email address to request a password reset.",
|
|
62
|
+
"resetPassword": "Reset Password",
|
|
63
|
+
"resetSent": "Password reset email sent!",
|
|
64
|
+
"setPassword": "Set Password",
|
|
65
|
+
"signIn": "Sign In",
|
|
66
|
+
"signInTitle": "Please Sign In",
|
|
67
|
+
"verifyPassword": "Verify Password",
|
|
68
|
+
"welcomeName": "Welcome back, <b>{}</b>! Please wait while we load your data.",
|
|
69
|
+
"welcomeBack": "Welcome back",
|
|
70
|
+
"validate": {
|
|
71
|
+
"email": "Please enter a valid email address.",
|
|
72
|
+
"firstName": "Please enter your first name.",
|
|
73
|
+
"invalid": "Invalid login. Please check your email or password.",
|
|
74
|
+
"lastName": "Please enter your last name.",
|
|
75
|
+
"password": "Please enter a password.",
|
|
76
|
+
"passwordLength": "Password must be at least 8 characters long.",
|
|
77
|
+
"passwordMatch": "Passwords do not match.",
|
|
78
|
+
"selectingChurch": "Error in selecting church. Please verify and try again"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"selectChurch": {
|
|
82
|
+
"address1": "Address Line 1",
|
|
83
|
+
"address2": "Address Line 2",
|
|
84
|
+
"another": "Choose another church",
|
|
85
|
+
"city": "City",
|
|
86
|
+
"confirmRegister": "Are you sure you wish to register a new church?",
|
|
87
|
+
"country": "Country",
|
|
88
|
+
"name": "Church Name",
|
|
89
|
+
"noMatches": "No matches found.",
|
|
90
|
+
"register": "Register a New Church",
|
|
91
|
+
"selectChurch": "Select a Church",
|
|
92
|
+
"state": "State / Province",
|
|
93
|
+
"zip": "Zip / Postal Code",
|
|
94
|
+
"validate": {
|
|
95
|
+
"address": "Address cannot be blank.",
|
|
96
|
+
"city": "City cannot be blank.",
|
|
97
|
+
"country": "Country cannot be blank.",
|
|
98
|
+
"name": "Church name cannot be blank.",
|
|
99
|
+
"state": "State/Province cannot be blank.",
|
|
100
|
+
"zip": "Zip/Postal code cannot be blank."
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
static init = async (backends: string[]): Promise<void> => {
|
|
106
|
+
const resources: TranslationResources = {};
|
|
107
|
+
let langs = ["en"];
|
|
108
|
+
|
|
109
|
+
if (typeof navigator !== "undefined") {
|
|
110
|
+
const browserLang = navigator.language.split("-")[0];
|
|
111
|
+
const mappedLang
|
|
112
|
+
= Object.keys(this.extraCodes).find((code) =>
|
|
113
|
+
this.extraCodes[code].includes(browserLang),
|
|
114
|
+
) || browserLang;
|
|
115
|
+
const notSupported = this.supportedLanguages.indexOf(mappedLang) === -1;
|
|
116
|
+
langs = mappedLang === "en" || notSupported ? ["en"] : ["en", mappedLang];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Load translations for each language
|
|
120
|
+
for (const lang of langs) {
|
|
121
|
+
resources[lang] = { translation: {} };
|
|
122
|
+
try {
|
|
123
|
+
for (const backend of backends) {
|
|
124
|
+
const url = backend.replace("{{lng}}", lang);
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(url);
|
|
127
|
+
if (response.ok) {
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
resources[lang].translation = this.deepMerge(
|
|
130
|
+
resources[lang].translation,
|
|
131
|
+
data,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.warn(`Failed to load translations from ${url}:`, error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.warn(`Failed to load translations for language ${lang}:`, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Initialize i18n
|
|
144
|
+
try {
|
|
145
|
+
await i18n
|
|
146
|
+
.use(Backend)
|
|
147
|
+
.use(LanguageDetector)
|
|
148
|
+
.use(initReactI18next)
|
|
149
|
+
.init({
|
|
150
|
+
resources,
|
|
151
|
+
fallbackLng: "en",
|
|
152
|
+
debug: false,
|
|
153
|
+
interpolation: {
|
|
154
|
+
escapeValue: false,
|
|
155
|
+
},
|
|
156
|
+
detection: {
|
|
157
|
+
order: ["navigator"],
|
|
158
|
+
caches: ["localStorage"],
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.warn("Failed to initialize i18n:", error);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
private static deepMerge(
|
|
167
|
+
target: Record<string, unknown>,
|
|
168
|
+
source: Record<string, unknown>,
|
|
169
|
+
): Record<string, unknown> {
|
|
170
|
+
for (const key in source) {
|
|
171
|
+
if (this.isObject(source[key])) {
|
|
172
|
+
if (!target[key]) Object.assign(target, { [key]: {} });
|
|
173
|
+
this.deepMerge(
|
|
174
|
+
target[key] as Record<string, unknown>,
|
|
175
|
+
source[key] as Record<string, unknown>,
|
|
176
|
+
);
|
|
177
|
+
} else Object.assign(target, { [key]: source[key] });
|
|
178
|
+
}
|
|
179
|
+
return target;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private static isObject(obj: unknown): boolean {
|
|
183
|
+
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Helper method to get value from nested object using dot notation
|
|
187
|
+
private static getNestedValue(obj: Record<string, any>, path: string): any {
|
|
188
|
+
return path.split('.').reduce((current, key) => {
|
|
189
|
+
return current && current[key] !== undefined ? current[key] : undefined;
|
|
190
|
+
}, obj);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// New helper method that uses i18n with hard-coded English fallback
|
|
194
|
+
static t(key: string, options?: Record<string, unknown>): string {
|
|
195
|
+
try {
|
|
196
|
+
// Check if i18n is initialized and has the key
|
|
197
|
+
if (i18n && i18n.isInitialized && i18n.exists(key)) {
|
|
198
|
+
const translation = i18n.t(key, options);
|
|
199
|
+
// If translation is not the same as the key, return it
|
|
200
|
+
if (translation !== key) {
|
|
201
|
+
return translation;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
// If i18n fails, fall through to hard-coded fallback
|
|
206
|
+
console.warn(`i18n translation failed for key "${key}":`, error);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Fallback to hard-coded English translations
|
|
210
|
+
const fallbackValue = this.getNestedValue(this.englishFallbacks, key);
|
|
211
|
+
if (fallbackValue !== undefined) {
|
|
212
|
+
// Handle simple string interpolation for options
|
|
213
|
+
if (typeof fallbackValue === 'string' && options) {
|
|
214
|
+
let result = fallbackValue;
|
|
215
|
+
Object.keys(options).forEach(optionKey => {
|
|
216
|
+
const placeholder = `{{${optionKey}}}`;
|
|
217
|
+
if (result.includes(placeholder)) {
|
|
218
|
+
result = result.replace(new RegExp(placeholder, 'g'), String(options[optionKey]));
|
|
219
|
+
}
|
|
220
|
+
// Also handle {} placeholder for backward compatibility
|
|
221
|
+
if (result.includes('{}')) {
|
|
222
|
+
result = result.replace('{}', String(options[optionKey]));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
return String(fallbackValue);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// If no fallback found, return the key itself
|
|
231
|
+
return key;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Keep the old method for backward compatibility
|
|
235
|
+
static label(key: string): string {
|
|
236
|
+
return this.t(key);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Helper method to check if i18n is initialized
|
|
240
|
+
static isInitialized(): boolean {
|
|
241
|
+
return i18n && i18n.isInitialized;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Method to set up basic fallback-only mode (no i18n)
|
|
245
|
+
static initFallbackMode(): void {
|
|
246
|
+
console.info("Locale: Running in fallback mode with English labels only");
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Locale } from "./Locale";
|
|
1
|
+
export { Locale } from "./Locale";
|
|
2
2
|
export { AnalyticsHelper } from "./AnalyticsHelper";
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { LoginPage } from "./LoginPage";
|
|
2
|
-
export { LogoutPage } from "./LogoutPage";
|
|
3
|
-
export { Register } from "./components/Register";
|
|
4
|
-
export { Login } from "./components/Login";
|
|
5
|
-
export { Forgot } from "./components/Forgot";
|
|
6
|
-
export { LoginSetPassword } from "./components/LoginSetPassword";
|
|
7
|
-
export { SelectChurchModal } from "./components/SelectChurchModal";
|
|
8
|
-
export { SelectChurchRegister } from "./components/SelectChurchRegister";
|
|
9
|
-
export { SelectChurchSearch } from "./components/SelectChurchSearch";
|
|
10
|
-
export { SelectableChurch } from "./components/SelectableChurch";
|
|
1
|
+
export { LoginPage } from "./LoginPage";
|
|
2
|
+
export { LogoutPage } from "./LogoutPage";
|
|
3
|
+
export { Register } from "./components/Register";
|
|
4
|
+
export { Login } from "./components/Login";
|
|
5
|
+
export { Forgot } from "./components/Forgot";
|
|
6
|
+
export { LoginSetPassword } from "./components/LoginSetPassword";
|
|
7
|
+
export { SelectChurchModal } from "./components/SelectChurchModal";
|
|
8
|
+
export { SelectChurchRegister } from "./components/SelectChurchRegister";
|
|
9
|
+
export { SelectChurchSearch } from "./components/SelectChurchSearch";
|
|
10
|
+
export { SelectableChurch } from "./components/SelectableChurch";
|
|
11
11
|
export { ErrorMessages } from "@churchapps/apphelper";
|
package/tsconfig.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "ESNext",
|
|
4
|
-
"esModuleInterop": true,
|
|
5
|
-
"lib": ["ES2021", "DOM"],
|
|
6
|
-
"typeRoots": ["node_modules/@types"],
|
|
7
|
-
"target": "ES2020",
|
|
8
|
-
"noImplicitAny": true,
|
|
9
|
-
"moduleResolution": "node",
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"outDir": "dist",
|
|
12
|
-
"baseUrl": ".",
|
|
13
|
-
"strict": false,
|
|
14
|
-
"allowSyntheticDefaultImports": true,
|
|
15
|
-
"declaration": true,
|
|
16
|
-
"declarationMap": true,
|
|
17
|
-
"jsx": "react-jsx",
|
|
18
|
-
"skipLibCheck": true,
|
|
19
|
-
"forceConsistentCasingInFileNames": true,
|
|
20
|
-
"resolveJsonModule": true,
|
|
21
|
-
"isolatedModules": true
|
|
22
|
-
},
|
|
23
|
-
"include": [
|
|
24
|
-
"src/**/*"
|
|
25
|
-
],
|
|
26
|
-
"exclude": [
|
|
27
|
-
"node_modules",
|
|
28
|
-
"dist"
|
|
29
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ESNext",
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"lib": ["ES2021", "DOM"],
|
|
6
|
+
"typeRoots": ["node_modules/@types"],
|
|
7
|
+
"target": "ES2020",
|
|
8
|
+
"noImplicitAny": true,
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"strict": false,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"declarationMap": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"forceConsistentCasingInFileNames": true,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"isolatedModules": true
|
|
22
|
+
},
|
|
23
|
+
"include": [
|
|
24
|
+
"src/**/*"
|
|
25
|
+
],
|
|
26
|
+
"exclude": [
|
|
27
|
+
"node_modules",
|
|
28
|
+
"dist"
|
|
29
|
+
]
|
|
30
30
|
}
|