@asafarim/shared-i18n 0.5.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.
@@ -0,0 +1,343 @@
1
+ {
2
+ "home": {
3
+ "hero": {
4
+ "title": "ASafariM • Multi-framework monorepo",
5
+ "subtitle": "Multi-framework frontends (React, Angular, Docusaurus)",
6
+ "description": ".NET 8+ API’s met clean architecture en getypeerde clients",
7
+ "tagline": "Gedeelde tokens en UI voor consistente theming"
8
+ },
9
+ "features": {
10
+ "whatBuilding": {
11
+ "title": "Wat ik nu aan het bouwen ben",
12
+ "description": "Een feed ‘Wat ik nu bouw’ met GitHub commits"
13
+ },
14
+ "ai": {
15
+ "title": "AI",
16
+ "description": "AI met clean architecture, getypeerde clients en SSO"
17
+ },
18
+ "core": {
19
+ "title": "Kernapplicaties",
20
+ "description": "Gebruikers & Projecten met clean architecture, getypeerde clients en SSO"
21
+ },
22
+ "jobs": {
23
+ "title": "Job Tracker",
24
+ "description": "Volg sollicitaties en interviews. Angular UI + .NET Jobs API"
25
+ },
26
+ "blog": {
27
+ "title": "Blog & Documentatie",
28
+ "description": "Docusaurus TypeScript met gedeelde header/footer en tokens"
29
+ }
30
+ },
31
+ "monorepo": {
32
+ "title": "Wat zit er in deze monorepo?",
33
+ "items": {
34
+ "tokens": "Gedeelde tokens (CSS-variabelen) voor thema-evenwicht",
35
+ "apps": "Web, Core, AI (React TS) + Jobs (Angular TS)",
36
+ "blog": "Docusaurus-blog met gedeelde header/footer",
37
+ "apis": ".NET API's (Identity, Core, AI, Jobs) met getypeerde clients"
38
+ }
39
+ }
40
+ },
41
+ "about": {
42
+ "title": "Over ASafariM",
43
+ "description": "Fullstack .NET & React Developer",
44
+ "subtitle": "Ik bouw schaalbare, onderhoudbare en interactieve applicaties — van backend-API’s in ASP.NET Core tot moderne UI’s in React & TypeScript. Gepassioneerd door prestaties, clean code en data-gedreven oplossingen.",
45
+ "viewResume": "Bekijk CV",
46
+ "contactMe": "Contacteer Mij",
47
+ "whatIDo": {
48
+ "title": "Wat Ik Doe",
49
+ "backend": {
50
+ "title": "Backend met .NET",
51
+ "description": "Beveiligde en schaalbare REST API’s bouwen met ASP.NET Core, EF Core en SQL-databanken."
52
+ },
53
+ "frontend": {
54
+ "title": "Frontend met React",
55
+ "description": "Interactieve, responsieve UI’s creëren met React, TypeScript en TailwindCSS."
56
+ },
57
+ "fullstack": {
58
+ "title": "Volledige Fullstack Oplossingen",
59
+ "description": "Backend + frontend combineren tot productieklare oplossingen met CI/CD en cloud deployment."
60
+ }
61
+ },
62
+ "skills": {
63
+ "title": "Belangrijkste Vaardigheden",
64
+ "frontend": "Frontend: React (TypeScript), Redux, Tailwind, Syncfusion",
65
+ "backend": "Backend: ASP.NET Core, Entity Framework, SignalR, REST API’s",
66
+ "databases": "Databanken: SQL Server, MySQL, MongoDB",
67
+ "data": "Data & Visualisatie: D3.js, R, R.Net",
68
+ "devops": "DevOps & Tools: Git, Azure DevOps, Docker, Swagger, TestCafe"
69
+ },
70
+ "experience": {
71
+ "title": "Recente Ervaring",
72
+ "xitechnix": {
73
+ "title": "XiTechniX (2020–2023)",
74
+ "description": "Fullstack Scientific App Developer. Ontwikkelde .NET Core + React-applicaties voor wetenschappelijke en zakelijke domeinen."
75
+ },
76
+ "irc": {
77
+ "title": "IRC Engineering (2020)",
78
+ "description": "Stage – Energieverbruiksvisualisaties ontwikkeld met C# en R.Net."
79
+ },
80
+ "vmm": {
81
+ "title": "Vlaamse Milieumaatschappij (2018–2019)",
82
+ "description": "Stage – Hydrologische modellen (FORTRAN, WetSpa) verbeterd voor rivierafvoersimulaties."
83
+ }
84
+ },
85
+ "background": {
86
+ "title": "Achtergrond",
87
+ "appliedIT": {
88
+ "title": "Toegepaste Informatica - Programmeren",
89
+ "school": "Thomas More, Sint-Katelijne-Waver",
90
+ "description": "Overgestapt naar softwareontwikkeling met focus op fullstack- en dataprojecten."
91
+ },
92
+ "phd": {
93
+ "title": "Doctoraat in Hydrologische Ingenieurswetenschappen",
94
+ "school": "VUB, Brussel",
95
+ "description": "Sterke expertise in modellering en data-analyse. Gespecialiseerd in wetenschappelijke softwareontwikkeling met nadruk op numerieke modellering."
96
+ },
97
+ "bsc": {
98
+ "title": "Bachelor & Master in Natuurlijke Hulpbronnen",
99
+ "school": "Universiteit van Teheran",
100
+ "description": "Stevige basis in milieu- en ingenieurswetenschappen."
101
+ }
102
+ }
103
+ },
104
+ "contact": {
105
+ "title": "Contact",
106
+ "hero": {
107
+ "title": "Laten we samen iets geweldig bouwen",
108
+ "subtitle": "Als fullstack developer gespecialiseerd in .NET en React help ik je project tot leven te brengen — of je nu een schaalbare API, een interactieve webapp of een end-to-end oplossing nodig hebt."
109
+ },
110
+ "services": {
111
+ "backend": {
112
+ "title": "Backend Ontwikkeling",
113
+ "description": "ASP.NET Core API’s, Entity Framework, SQL-databanken"
114
+ },
115
+ "frontend": {
116
+ "title": "Frontend Ontwikkeling",
117
+ "description": "React, TypeScript, Tailwind, Moderne UI/UX"
118
+ },
119
+ "fullstack": {
120
+ "title": "Volledige Projectrealisatie",
121
+ "description": "End-to-end oplossingen met CI/CD en cloud deployment"
122
+ }
123
+ },
124
+ "form": {
125
+ "newConversation": "Nieuwe conversatie starten",
126
+ "replyToConversation": "Antwoord op conversatie {{reference}}",
127
+ "referenceNumber": "Referentienummer",
128
+ "referencePrevious": "Verwijs naar vorige",
129
+ "selectConversation": "Selecteer een conversatie om naar te verwijzen",
130
+ "name": "Naam",
131
+ "email": "E-mail",
132
+ "projectType": "Projecttype",
133
+ "projectTypePlaceholder": "bv. Fullstack Applicatie, API Ontwikkeling, Frontend UI",
134
+ "projectDetails": "Projectdetails",
135
+ "projectDetailsPlaceholder": "Beschrijf je project, tijdlijn en eventuele vereisten...",
136
+ "attachments": {
137
+ "title": "Bijlagen & Links",
138
+ "addFile": "Bestand toevoegen",
139
+ "addLink": "Link toevoegen",
140
+ "enterUrl": "Voer een URL in:",
141
+ "invalidUrl": "Voer een geldige URL in"
142
+ },
143
+ "submit": {
144
+ "sending": "Verzenden...",
145
+ "submit": "Laten we je project bespreken",
146
+ "success": "Bedankt voor je bericht! We hebben je vraag ontvangen en zullen je snel terugkeren.",
147
+ "error": "Bericht kon niet worden verzonden"
148
+ }
149
+ },
150
+ "conversations": {
151
+ "title": "Mijn Conversaties",
152
+ "show": "Toon Conversaties",
153
+ "hide": "Verberg Conversaties",
154
+ "empty": "Geen conversaties gevonden",
155
+ "reply": "Antwoord op deze conversatie",
156
+ "referringTo": "Verwijst naar conversatie:",
157
+ "links": "Links:",
158
+ "attachments": "Bijlagen:"
159
+ },
160
+ "info": {
161
+ "whyWorkWithMe": "Waarom met mij werken?",
162
+ "expertise": "Expert in zowel frontend als backend ontwikkeling",
163
+ "scientific": "Sterke achtergrond in wetenschappelijke toepassingen",
164
+ "visualization": "Ervaring met complexe datavisualisatie",
165
+ "delivery": "Bewezen staat van succesvolle projectoplevering",
166
+ "contactInfo": "Contactinformatie",
167
+ "email": "contact@asafarim.com",
168
+ "location": "Hasselt, België",
169
+ "availability": "Beschikbaar voor freelanceprojecten"
170
+ }
171
+ },
172
+ "portfolio": {
173
+ "title": "Portfolio",
174
+ "description": "Bekijk mijn werk en projecten"
175
+ },
176
+ "showcases": {
177
+ "title": "Aanbevolen Applicaties",
178
+ "intro": "Verken mijn aanbevolen applicaties en lopende projecten.",
179
+ "visitButton": "App Bezoeken",
180
+ "projects": {
181
+ "testora-ui": {
182
+ "title": "Testora UI",
183
+ "description": "Testora user interface is gemaakt met React en TypeScript, en het is deel van het Testora project. Het biedt een dashboard voor het monitoren en beheren van testuitvoeringen. De backend is gemaakt met ASP.NET Core en Entity Framework, PostgreSQL database."
184
+ },
185
+ "testora-testrunner": {
186
+ "title": "Testora e2e TestRunner",
187
+ "description": "TestRunner is een Node.js-applicatie die een TestCafe engine biedt voor het uitvoeren van test suites. Het is gemaakt met Node.js en TypeScript, en het is deel van het Testora project. Via SignalR, kan het test suites in real-time uitvoeren."
188
+ },
189
+ "taskManagement": {
190
+ "title": "Taakbeheer",
191
+ "description": "Een productiviteitsapp voor het beheren van persoonlijke en teamtaken met authenticatie en dashboards."
192
+ },
193
+ "smartops":
194
+ {
195
+ "title": "SmartOps",
196
+ "description": "SmartOps is een webapplicatie die een dashboard biedt voor het monitoren en beheren van slimme processen. De applicatie is gebouwd met React en TypeScript en maakt deel uit van het SmartOps-project."
197
+ },
198
+ "identityPortal": {
199
+ "title": "Identiteitsportaal",
200
+ "description": "Een veilig gebruikersbeheer- en authenticatiesysteem geïntegreerd met ASafariM backend."
201
+ },
202
+ "coreApp": {
203
+ "title": "Kern-app",
204
+ "description": "Centraal platform voor het beheren van CV's, portfolio's en professionele inhoud."
205
+ },
206
+ "aiPlatform": {
207
+ "title": "AI-platform",
208
+ "description": "Geavanceerde AI-aangedreven tools en hulpprogramma's voor intelligente automatisering."
209
+ },
210
+ "jobsPortal": {
211
+ "title": "Vacatureportaal",
212
+ "description": "Vacatures en carrièremogelijkheden geïntegreerd met het kernplatform."
213
+ },
214
+ "blog": {
215
+ "title": "Blog",
216
+ "description": "Technische artikelen, inzichten en documentatie over mijn projecten."
217
+ }
218
+ }
219
+ },
220
+ "resume": {
221
+ "loading": "CV wordt geladen...",
222
+ "error": "CV-details konden niet worden geladen",
223
+ "notFound": "CV niet gevonden",
224
+ "backToResumes": "Terug naar CV’s",
225
+ "published": "Gepubliceerd",
226
+ "publishedAt": "Gepubliceerd: {{date}}",
227
+ "sections": {
228
+ "summary": {
229
+ "title": "Professionele Samenvatting"
230
+ },
231
+ "experience": {
232
+ "title": "💼 Werkervaring",
233
+ "count": "{{count}} ervaring",
234
+ "count_plural": "{{count}} ervaringen",
235
+ "present": "Heden",
236
+ "achievements": "Prestaties"
237
+ },
238
+ "skills": {
239
+ "title": "🛠️ Vaardigheden",
240
+ "count": "{{count}} vaardigheid",
241
+ "count_plural": "{{count}} vaardigheden",
242
+ "categories": {
243
+ "category": "🏷️ {{category}}",
244
+ "level": "🔢 {{level}}"
245
+ }
246
+ },
247
+ "education": {
248
+ "title": "🎓 Opleiding",
249
+ "count": "{{count}} opleiding",
250
+ "count_plural": "{{count}} opleidingen",
251
+ "degree": "{{degree}}",
252
+ "field": "{{field}}",
253
+ "present": "Heden"
254
+ },
255
+ "certificates": {
256
+ "title": "📜 Certificaten",
257
+ "count": "{{count}} certificaat",
258
+ "count_plural": "{{count}} certificaten",
259
+ "issued": "Uitgegeven: {{date}}",
260
+ "expires": "Verloopt: {{date}}",
261
+ "viewCertificate": "Bekijk Certificaat →",
262
+ "credentialId": "ID: {{id}}"
263
+ },
264
+ "projects": {
265
+ "title": "🏗️ Projecten",
266
+ "count": "{{count}} project",
267
+ "count_plural": "{{count}} projecten",
268
+ "visit": "Bezoek →"
269
+ },
270
+ "languages": {
271
+ "title": "🌎 Talen",
272
+ "count": "{{count}} taal",
273
+ "count_plural": "{{count}} talen",
274
+ "level": "{{level}}"
275
+ },
276
+ "awards": {
277
+ "title": "🏆 Prijzen & Erkenningen",
278
+ "count": "{{count}} prijs",
279
+ "count_plural": "{{count}} prijzen",
280
+ "awarded": "{{date}}",
281
+ "issuer": "{{issuer}}"
282
+ },
283
+ "references": {
284
+ "title": "👨🏻 Referenties",
285
+ "count": "{{count}} referentie",
286
+ "count_plural": "{{count}} referenties"
287
+ },
288
+ "socialLinks": {
289
+ "title": "📲 Sociale Links",
290
+ "count": "{{count}} link",
291
+ "count_plural": "{{count}} links"
292
+ }
293
+ },
294
+ "actions": {
295
+ "editResume": "🖊 CV Bewerken",
296
+ "publishResume": "🌊 Publiceer CV",
297
+ "unpublish": "🫷 Publicatie intrekken",
298
+ "copyShareLink": "🟩 Kopieer Deel-link",
299
+ "exportPDF": "📥 Exporteer PDF",
300
+ "backToResumes": "╰┈➤ Terug naar CV's"
301
+ },
302
+ "messages": {
303
+ "publishedSuccess": "CV succesvol gepubliceerd!",
304
+ "publishedInfo": "Deellink: {{url}}",
305
+ "unpublishedSuccess": "CV-publicatie ingetrokken",
306
+ "unpublishedError": "Kon publicatie niet intrekken",
307
+ "copySuccess": "Deellink gekopieerd naar klembord!",
308
+ "copyError": "Kopiëren mislukt. Kopieer handmatig: {{url}}",
309
+ "invalidLink": "Ongeldige CV-link",
310
+ "notAvailable": "Deze CV is niet beschikbaar of niet langer gepubliceerd.",
311
+ "privacyNotice": "Deze CV wordt publiek gedeeld met toestemming. Voor privacyvragen, raadpleeg ons Privacybeleid."
312
+ }
313
+ },
314
+ "navbar": {
315
+ "brand": {
316
+ "text": "ASafariM"
317
+ },
318
+ "links": {
319
+ "resumes": {
320
+ "label": "CV's",
321
+ "icon": "📄"
322
+ },
323
+ "about": {
324
+ "label": "Over ons",
325
+ "icon": "🙋🏻‍♂️"
326
+ },
327
+ "contact": {
328
+ "label": "Contact",
329
+ "icon": "ᯓ➤"
330
+ },
331
+ "portfolio": {
332
+ "label": "Mijn Portfolio",
333
+ "icon": "🗃️"
334
+ }
335
+ },
336
+ "auth": {
337
+ "notSignedIn": "Niet ingelogd!",
338
+ "signIn": "Inloggen",
339
+ "signOut": "Uitloggen",
340
+ "welcome": "Welkom"
341
+ }
342
+ }
343
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@asafarim/shared-i18n",
3
+ "version": "0.5.0",
4
+ "type": "module",
5
+ "files": [
6
+ "locales",
7
+ "config",
8
+ "hooks",
9
+ "utils",
10
+ "index.ts",
11
+ "package.json",
12
+ "tsconfig.json"
13
+ ],
14
+ "main": "index.ts",
15
+ "types": "index.ts",
16
+ "exports": {
17
+ ".": "./index.ts",
18
+ "./config": "./config/i18n.ts",
19
+ "./hooks": "./hooks/useLanguage.ts",
20
+ "./utils": "./utils/languageUtils.ts",
21
+ "./locales/*": "./locales/*"
22
+ },
23
+ "peerDependencies": {
24
+ "react": "^18.2.0 || ^19.0.0",
25
+ "react-dom": "^18.2.0 || ^19.0.0"
26
+ },
27
+ "dependencies": {
28
+ "i18next": "^25.6.0",
29
+ "react-i18next": "^16.0.1",
30
+ "i18next-browser-languagedetector": "^8.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^24.3.1",
34
+ "@types/react": "^18.2.0 || ^19.0.0",
35
+ "@types/react-dom": "^18.2.0 || ^19.0.0",
36
+ "typescript": "~5.8.3"
37
+ },
38
+ "keywords": [
39
+ "asafarim",
40
+ "react",
41
+ "i18n",
42
+ "i18next",
43
+ "internationalization",
44
+ "language",
45
+ "localization",
46
+ "react-i18next",
47
+ "react-internationalization",
48
+ "react-localization",
49
+ "react-language",
50
+ "react-localization",
51
+ "typescript"
52
+ ],
53
+ "author": "Ali Safari <asafarim@gmail.com>",
54
+ "license": "MIT",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/asafarim/shared-i18n.git"
58
+ },
59
+ "bugs": {
60
+ "url": "https://github.com/asafarim/shared-i18n/issues"
61
+ },
62
+ "homepage": "https://github.com/asafarim/shared-i18n#readme",
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "scripts": {
67
+ "build": "tsc -b",
68
+ "dev": "tsc -w",
69
+ "start": "pnpm run build",
70
+ "demo": "pnpm run build && cd demo && pnpm run dev",
71
+ "predeploy": "pnpm run build && cd demo && pnpm run build && cd ..",
72
+ "deploy": "gh-pages -d demo/dist"
73
+ }
74
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": ".",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "composite": true,
9
+ "jsx": "react-jsx",
10
+ "module": "ESNext",
11
+ "moduleResolution": "bundler",
12
+ "target": "ES2020",
13
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
14
+ "skipLibCheck": true,
15
+ "esModuleInterop": true,
16
+ "allowSyntheticDefaultImports": true,
17
+ "strict": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "resolveJsonModule": true,
20
+ "isolatedModules": true,
21
+ "noEmit": false
22
+ },
23
+ "include": [
24
+ "**/*.ts",
25
+ "**/*.tsx",
26
+ "**/*.json"
27
+ ],
28
+ "exclude": [
29
+ "node_modules",
30
+ "dist"
31
+ ]
32
+ }
@@ -0,0 +1,141 @@
1
+ import { LANGUAGE_COOKIE_NAME, DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES, type SupportedLanguage } from '../config/i18n';
2
+
3
+ /**
4
+ * Resolve Identity API base URL from environment or hostname
5
+ */
6
+ const getIdentityApiUrl = (): string => {
7
+ // Default to local identity API in development
8
+ let resolved = 'http://identity.asafarim.local:5101';
9
+
10
+ try {
11
+ // Prefer Vite env vars when available
12
+ // Guard access to import.meta for non-Vite environments
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ const viteEnv = (typeof import.meta !== 'undefined') ? ((import.meta as any).env) : undefined;
15
+
16
+ if (viteEnv && (viteEnv as any).VITE_IDENTITY_API_URL) {
17
+ resolved = (viteEnv as any).VITE_IDENTITY_API_URL as string;
18
+ } else if (typeof window !== 'undefined') {
19
+ // Fallback based on hostname for runtime environments
20
+ const isProdHost = /asafarim\.be$/i.test(window.location.hostname);
21
+ resolved = isProdHost ? 'https://identity.asafarim.be' : 'http://identity.asafarim.local:5101';
22
+ }
23
+ } catch {
24
+ // Keep default if any detection fails
25
+ }
26
+
27
+ return resolved;
28
+ };
29
+
30
+ /**
31
+ * Get language from cookie
32
+ */
33
+ export const getLanguageFromCookie = (): string | null => {
34
+ if (typeof document === 'undefined') return null;
35
+
36
+ const cookies = document.cookie.split(';');
37
+ const languageCookie = cookies.find(cookie =>
38
+ cookie.trim().startsWith(`${LANGUAGE_COOKIE_NAME}=`)
39
+ );
40
+
41
+ if (languageCookie) {
42
+ const value = languageCookie.split('=')[1];
43
+ return value || null;
44
+ }
45
+
46
+ return null;
47
+ };
48
+
49
+ /**
50
+ * Set language cookie for .asafarim.be domain
51
+ */
52
+ export const setLanguageCookie = (language: string): void => {
53
+ if (typeof document === 'undefined') return;
54
+
55
+ const domain = window.location.hostname.includes('asafarim.be')
56
+ ? '.asafarim.be'
57
+ : window.location.hostname;
58
+
59
+ // Set cookie with 1 year expiration
60
+ const expirationDate = new Date();
61
+ expirationDate.setFullYear(expirationDate.getFullYear() + 1);
62
+
63
+ document.cookie = `${LANGUAGE_COOKIE_NAME}=${language}; domain=${domain}; path=/; expires=${expirationDate.toUTCString()}; SameSite=Lax`;
64
+ };
65
+
66
+ /**
67
+ * Validate if language is supported
68
+ */
69
+ export const isSupportedLanguage = (lang: string): lang is SupportedLanguage => {
70
+ return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);
71
+ };
72
+
73
+ /**
74
+ * Get browser language with fallback
75
+ */
76
+ export const getBrowserLanguage = (): SupportedLanguage => {
77
+ if (typeof navigator === 'undefined') return DEFAULT_LANGUAGE;
78
+
79
+ const browserLang = navigator.language.split('-')[0];
80
+ return isSupportedLanguage(browserLang) ? browserLang : DEFAULT_LANGUAGE;
81
+ };
82
+
83
+ /**
84
+ * Get initial language from cookie, browser, or default
85
+ */
86
+ export const getInitialLanguage = (): SupportedLanguage => {
87
+ const cookieLang = getLanguageFromCookie();
88
+ if (cookieLang && isSupportedLanguage(cookieLang)) {
89
+ return cookieLang;
90
+ }
91
+
92
+ return getBrowserLanguage();
93
+ };
94
+
95
+ /**
96
+ * Update user language preference on backend
97
+ * Note: Language preference is stored in cookies across all apps
98
+ * No backend sync needed - cookies are shared across .asafarim.local domain
99
+ */
100
+ export const updateUserLanguagePreference = async (language: SupportedLanguage): Promise<boolean> => {
101
+ try {
102
+ const baseUrl = getIdentityApiUrl();
103
+ const response = await fetch(`${baseUrl}/api/me/preferences`, {
104
+ method: 'POST',
105
+ headers: { 'Content-Type': 'application/json' },
106
+ credentials: 'include',
107
+ body: JSON.stringify({ preferredLanguage: language }),
108
+ });
109
+
110
+ if (!response.ok) {
111
+ const text = await response.text().catch(() => '');
112
+ console.warn('Failed to sync language preference with backend:', response.status, text);
113
+ return false;
114
+ }
115
+
116
+ return true;
117
+ } catch (error) {
118
+ console.error('Failed to update language preference: ' + language, error);
119
+ return false;
120
+ }
121
+ };
122
+
123
+ /**
124
+ * Fetch user language preference from backend
125
+ * Note: Language preference is stored in cookies across all apps
126
+ * No backend sync needed - cookies are shared across .asafarim.local domain
127
+ */
128
+ export const fetchUserLanguagePreference = async (): Promise<SupportedLanguage | null> => {
129
+ try {
130
+ // Language is already available in cookie
131
+ // No backend call needed - cookies are the source of truth
132
+ const cookieLang = getLanguageFromCookie();
133
+ if (cookieLang && isSupportedLanguage(cookieLang)) {
134
+ return cookieLang;
135
+ }
136
+ } catch (error) {
137
+ console.error('Failed to fetch language preference:', error);
138
+ }
139
+
140
+ return null;
141
+ };